Make ring of vectors "flat" relative to world space - unity3d

I am trying to simulate liquid conformity in a container. The container is a Unity cylinder and so is the liquid. I track current volume and max volume and use them to determine the coordinates of the center of where the surface should be. When the container is tilted, each vertex in the upper ring of the cylinder should maintain it's current local x and z values but have a new local y value that is the same height in the global space as the surface center.
In my closest attempt, the surface is flat relative to the world space but the liquid does not touch the walls of the container.
Vector3 v = verts[i];
Vector3 newV = new Vector3(v.x, globalSurfaceCenter.y, v.z);
verts[i] = transform.InverseTransformPoint(newV);
(I understand that inversing the point after using v.x and v.z changes them, but if I change them after the fact the surface is no longer flat...)
I have tried many different approaches and I always end up at this same point or a stranger one.
Also, I'm not looking for any fundamentally different approach to the problem. It's important that I alter the vertices of a cylinder.
EDIT
Thank you, everyone, for your feedback. It helped me make progress with this problem but I've reached another roadblock. I made my code more presentable and took some screenshots of some results as well as a graph model to help you visualize what's happening and give variable names to refer to.
In the following images, colored cubes are instantiated and given the coordinates of some of the different vectors I am using to get my results.
F(red) A(green) B(blue)
H(green) E(blue)
Graphed Model
NOTE: when I refer to capital A and B, I'm referring to the Vector3's in my code.
The cylinders in the images have the following rotations (left to right):
(0,0,45) (45,45,0) (45,0,20)
As you can see from image 1, F is correct when only one dimension of rotation is applied. When two or more are applied, the surface is flat, but not oriented correctly.
If I adjust the rotation of the cylinder after generating these results, I can get the orientation of the surface to make sense, but the number are not what you might expect.
For example: cylinder 3 (on the right side), adjusted to have a surface flat to the world space, would need a rotation of about (42.2, 0, 27.8).
Not sure if that's helpful but it is something that increases my confusion.
My code: (refer to graph model for variable names)
Vector3 v = verts[iter];
Vector3 D = globalSurfaceCenter;
Vector3 E = transform.TransformPoint(new Vector3(v.x, surfaceHeight, v.z));
Vector3 H = new Vector3(gsc.x, E.y, gsc.z);
float a = Vector3.Distance(H, D);
float b = Vector3.Distance(H, E);
float i = (a / b) * a;
Vector3 A = H - D;
Vector3 B = H - E;
Vector3 F = ((A + B)) + ((A + B) * i);
Instantiate(greenPrefab, transform).transform.position = H;
Instantiate(bluePrefab, transform).transform.position = E;
//Instantiate(redPrefab, transform).transform.position = transform.TransformPoint(F);
//Instantiate(greenPrefab, transform).transform.position = transform.TransformPoint(A);
//Instantiate(bluePrefab, transform).transform.position = transform.TransformPoint(B);
Some of the variables in my code and in the graphed model may not be necessary in the end, but my hope is it gives you more to work with.
Bear in mind that I am less than proficient in geometry and math in general. Please use Laymans's terms. Thank you!
And thanks again for taking the time to help me.

As a first step, we can calculate the normal of the upper cylinder surface in the cylinder's local coordinate system. Given the world transform of your cylinder transform, this is simply:
localNormal = inverse(transform) * (0, 1, 0, 0)
Using this normal and the cylinder height h, we can define the plane of the upper cylinder in normal form as
dot(localNormal, (x, y, z) - (0, h / 2, 0)) = 0
I am assuming that your cylinder is centered around the origin.
Using this, we can calculate the y-coordinate for any x/z pair as
y = h / 2 - (localNormal.x * x + localNormal.z * z) / localNormal.y

Related

How to reposition a circle to be outside of circumference of two other circles?

This is a question for Unity people or Math geniuses.
I'm making a game where I have a circle object that I can move, but I don't want it to intersect or go into other (static) circles in the world (Physics system isn't good enough in Unity to simply use that, btw).
It's in 3D world, but the circles only ever move on 2 axis.
I was able to get this working perfectly if circle hits only 1 other circle, but not 2 or more.
FYI: All circles are the same size.
Here's my working formula for 1 circle to move it to the edge of the colliding circle if intersecting:
newPosition = PositionOfStaticCircleThatWasJustIntersected + ((positionCircleWasMovedTo - PositionOfStaticCircleThatWasJustIntersected).normalized * circleSize);
But I can't figure out a formula if the moving circle hits 2 (or more) static circles at the same time.
One of the things that confuse me the most is the direction issue depending on how all the circles are positioned and what direction the moving circle is coming from.
Here's an example image of what I'm trying to do.
Since we're operating in a 2D space, let's approach this with some geometry. Taking a close look at your desired outcome, a particular shape become apparent:
There's a triangle here! And since all circles are the same radius, we know even more: this is an isosceles triangle, where two sides are the same length. With that information in hand, the problem basically boils down to:
We know what d is, since it's the distance between the two circles being collided with. And we know what a is, since it's the radius of all the circles. With that information, we can figure out where to place the moved circle. We need to move it d/2 between the two circles (since the point will be equidistant between them), and h away from them.
Calculating the height h is straightforward, since this is a right-angle triangle. According to the Pythagorean theorem:
// a^2 + b^2 = c^2, or rewritten as:
// a = root(c^2 - b^2)
float h = Mathf.Sqrt(Mathf.Pow(2 * a, 2) - Mathf.Pow(d / 2, 2))
Now need to turn these scalar quantities into vectors within our game space. For the vector between the two circles, that's easy:
Vector3 betweenVector = circle2Position - circle1Position
But what about the height vector along the h direction? Well, since all movement is on 2D space, find a direction that your circles don't move along and use it to get the cross product (the perpendicular vector) with the betweenVector using Vector3.Cross(). For
example, if the circles only move laterally:
Vector3 heightVector = Vector3.Cross(betweenVector, Vector3.up)
Bringing this all together, you might have a method like:
Vector3 GetNewPosition(Vector3 movingCirclePosition, Vector3 circle1Position,
Vector3 circle2Position, float radius)
{
float halfDistance = Vector3.Distance(circle1Position, circle2Position) / 2;
float height = Mathf.Sqrt(Mathf.Pow(2 * radius, 2) - Mathf.Pow(halfDistance, 2));
Vector3 betweenVector = circle2Position - circle1Position;
Vector3 heightVector = Vector3.Cross(betweenVector, Vector3.up);
// Two possible positions, on either side of betweenVector
Vector3 candidatePosition1 = circle1Position
+ betweenVector.normalized * halfDistance
+ heightVector.normalized * height;
Vector3 candidatePosition2 = circle1Position
+ betweenVector.normalized * halfDistance
- heightVector.normalized * height;
// Absent any other information, the closer position will be assumed as correct
float distToCandidate1 = Vector3.Distance(movingCirclePosition, candidatePosition1);
float distToCandidate2 = Vector3.Distance(movingCirclePosition, candidatePosition2);
if (distToCandidate1 < distToCandidate2){
return candidatePosition1;
}
else{
return candidatePosition2;
}
}

how do I get mouse world position. X Y plane only in unity

how do I get mouse world position. X Y plane only in unity . ScreenToWorldPosition isn't working. I think I need to cast a ray to mouse but not sure.
This is what I am using. doesnt seem to give the correct coordinates or right plane. need for targeting and raycasting.
private void Get3dMousePoint()
{
var screenPosition = Input.mousePosition;
screenPosition.z = 1;
worldPosition = mainCamera.ScreenToWorldPoint(screenPosition);
worldPosition.z = 0;
}
Just need XY coords.
I tried with ScreenToWorldPoint () and it works.
The key I think is in understanding the z coordinate of the position.
Geometrically, in 3D space we need 3 coordinates to define a point. With only 2 coordinates we have a straight line with variable z parameter. To obtain a point from that line, we must choose at what distance (i.e. set z) we want the point sought to be.
Obviously, since the camera is perspective, the coordinates you have at z = 1 are different from those at z = 100, differently from the 2D plane.
If you can figure out how far away, that is, to set the z correctly, you can find the point you want.
Just remember that the z must be greater than the minimum rendering distance of the chamber. I set that very value in the script.
Also remember that the resulting vector will have the z equal to the z position of the camera + the z value of the vector used in ScreenToWorldPoint.
void Get3dMousePoint()
{
Vector3 worldPosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane));
print(worldPosition);
}
if you think my answer helped you, you can mark it as accepted and vote positively. I would very much appreciate it :)

Cheapest way to find Vector magnitude from a given point and angle

I am trying to determine a players depth position on a plane, which defines the walkable ground in a 2D brawler game. The problem is depictured in the following drawing:
C represents the players current position. I need to find the magnitude of vector V. Since I am not strong on linear algebra, the one thing I can think of is: determining the intersection point P of L1 and L2, and then take the magnitude from AP. However, I get the feeling there must be an easier way to find V, since I already know the angle the vector should have, given by vector from AB.
Any input would be appreciated, since I am looking forward to step up my linear algebra game.
Edit: As it is unclear thanks to my lack of drawing skills: the geometry depicted above is a parallelogram. The vector V I am looking for is parallel to the left and right side of the parallelogram. Depth does not mean, that I am looking for the vector perpendicular to the top side, but it refers to the fake depth of a purely 2D game. The parallelogram is therefore used as a means for creating the feeling of walking along a z axis.
The depth of your player (length of V) as measured from the top line in your drawing, is just the difference between A.y and C.y. This is seperate from the slant in the parralelogram, as we're just looking at depth.
example:
float v;
Vector2 a = new Vector2(100, 100); //The point you're measuring from
Vector2 c = new Vector2(150, 150); //Your character position
v = c.y - a.y; // This is the length of V.
//In numbers: 50 = 150 - 100
Illustrated: image not to scale
This works for any coördinate in your plane.
Now if you'd want to get the length of AC is when you'd need to apply some pythagoras, which is a² + b² = c². In the example that would mean in code:
Vector2 a = new Vector2(100, 100);
Vector2 c = new Vector2(150, 150);
float ac1 = Mathf.Sqrt(Mathf.Pow(c.x - a.x, 2) + Mathf.Pow(c.y - a.y, 2));
Now that is quite a sore to have to type out every time, and looks quite scary. But Unity has you covered! There is a Vector method called Distance
float ac2 = Vector2.Distance(a, c);
Which both return 70.71068 which is the length of AC.
This works because for any point c in your area you can draw a right angled triangle from a to c.
Edit as per comment:
If you want your "depth" vector to be parallel with the sides of the paralellogram we can just create a triangle in the parallelogram of which we calculate the hypotenuse.
Since we want the new hypotenuse of our triangle to be parallel to the parallelogram we can use the same angle θ as point B has in your drawing (indicated by pink in mine), of which I understood you know the value.
We also know the length of the adjacent (indicated in blue) side of this new triangle, as that is the height we calculated earlier (c.y - a.y).
Using these two values we can use cosine to find the length of hypotenuse (indicated in red) of the triangle, which is equal to the vector V, in parallel with the parallelogram.
the formula for that is: hypotenuse = adjacent/cos(θ)
Now if we were to put some numbers in this, and for my example I took 55 for the angle θ. It would look like this
float v = 50/(cos(55));
image not to scale
Let's call the lower right vertex of the parallelogram D.
If the long sides of the parallelogram are horizontal, you can find magnitude of V vector by:
V.magnitude = (c.y - a.y) / sin(BAD)
Or if you prefer:
V.magnitude = AB.magnitude * (c.y - a.y)/(b.y - a.y)

Approximating relative angle between two line segments on sphere surface

I am in need of an idea! I want to model the vascular network on the eye in 3D. I have made statistics on the branching behaviour in relation to vessel diameter, length etc. What I am stuck at right now is the visualization:
The eye is approximated as a sphere E with center in origo C = [0, 0, 0] and a radius r.
What I want to achieve is that based on the following input parameters, it should be able to draw a segment on the surface/perimeter of E:
Input:
Cartesian position of previous segment ending: P_0 = [x_0, y_0, z_0]
Segment length: L
Segment diameter: d
Desired angle relative to the previous segment: a (1)
Output:
Cartesian position of resulting segment ending: P_1 = [x_1, y_1, z_1]
What I do now, is the following:
From P_0, generate a sphere with radius L, representing all the points we could possibly draw to with the correct length. This set is called pool.
Limit pool to only include points with a distance to C between r*0.95 and r, so only the points around the perimeter of the eye are included.
Select only the point that would generate a relative angle (2) closest to the desired angle a.
The problem is, that whatever angle a I desire, is actually not what is measured by the dot product. Say I want an angle at 0 (i.e. that the new segment is following the same direction as the previous`, what I actually get is an angle around 30 degrees because of the curvature of the sphere. I guess what I want is more the 2D angle when looking from an angle orthogonal from the sphere to the branching point. Please take a look at the screenshots below for a visualization.
Any ideas?
(1) The reason for this is, that the child node with the greatest diameter is usually follows the path of the previous segment, whereas smaller child nodes tend to angle differently.
(2) Calculated by acos(dot(v1/norm(v1), v2/norm(v2)))
Screenshots explaining the problem:
Yellow line: previous segment
Red line: "new" segment to one of the points (not neccesarily the correct one)
Blue x'es: Pool (text=angle in radians)
I will restate the problem with my own notation:
Given two points P and Q on the surface of a sphere centered at C with radius r, find a new point T such that the angle of the turn from PQ to QT is A and the length of QT is L.
Because the segments are small in relation to the sphere, we will use a locally-planar approximation of the sphere at the pivot point Q. (If this isn't an okay assumption, you need to be more explicit in your question.)
You can then compute T as follows.
// First compute an aligned orthonormal basis {U,V,W}.
// - {U,V} should be a basis for the plane tangent at Q.
// - W should be normal to the plane tangent at Q.
// - U should be in the direction PQ in the plane tangent at Q
W = normalize(Q - C)
U = normalize(Q - P)
U = normalize(U - W * dotprod(W, U))
V = normalize(crossprod(W, U))
// Next compute the next point S in the plane tangent at Q.
// In a regular plane, the parametric equation of a unit circle
// centered at the origin is:
// f(A) = (cos A, sin A) = (1,0) cos A + (0,1) sin A
// We just do the same thing, but with the {U,V} basis instead
// of the standard basis {(1,0),(0,1)}.
S = Q + L * (U cos A + V sin A)
// Finally project S onto the sphere, obtaining the segment QT.
T = C + r * normalize(S - C)

3d movement around a sphere

I'm working in 3d for the first time in a long time. Basically I'm rotating a sphere and projecting x y z cords to place things on the surface based on the spheres X and Y rotation.
Heres the code im using:
#define piover180 0.01745329252f
GLfloat cosy = cos(yrot * piover180);
island[i].x = rad * sin(xrot * piover180)* cosy;
island[i].y = rad * sin(yrot * piover180);
island[i].z = rad * cos(xrot * piover180) * cosy;
Problem is the Xrot positioning works fine but the Yrot placement always draw the objects into the north and south pole so they all cross at the top, which isn't correct for rotating. I need a way to solve this. Here's a picture to help explain:
Any help would be greatly appreciated, let me know if you need any more information?
The code sample you pasted is incomplete, because you didn't show how you applied these calculations via glRotate et al. Here's how I would do this. Although you could certainly optimize it by doing the matrix calculations yourself in one step, it's likely not necessary.
// Move object out to its radius
glTranslatef(radius, 0, 0);
// Apply latitudinal rotation (aka "Yrot")
glRotatef(latitude, 0, 1, 0);
// Apply longitudinal rotation (aka "Xrot")
glRotatef(longitude, 0, 0, 1);
After that, you can do the drawing. You'll also want to wrap the whole thing in calls to glPushMatrix and glPopMatrix to isolate this transformation.
I ended up solving it using the Spherical Coordinate System.
Here's the code:
island[i].x = rad*sin(xrot*(PI/180))*cos(yrot*(PI/180));
island[i].y = rad*sin(xrot*(PI/180))*sin(yrot*(PI/180));
island[i].z = cos(xrot*(PI/180));
Here are the equations:
x = r sinq cosf
y = r sinq sinf
z = r cosq
r = (x2 + y2 + z2)1/2
q = tan-1(z/(x2+y2)1/2)
f = tan-1(y/x)
Just in case anyone could do with it, it's perfect for camera control or any exact 3d coord calculations you need to do.
Reference: http://electron9.phys.utk.edu/vectors/3dcoordinates.htm