Swift - SpriteKit - distance between a moving node and a fixed node - sprite-kit

I want to set the width of a node to be the distance between a moving node and a fixed node. In other words, I hope that the width of the node changes as another node moving. Is there a function to do this ?

Use the hypot function:
let distance = hypot(movingNode.position.x - fixedNode.Position.x,
movingNode.position.y - fixedNode.Position.y)
This returns the length of the hypotenuse of a right-angle triangle of width x and height y, which we can envisage as being drawn with your two nodes being on the non-right angled vertices.
https://developer.apple.com/documentation/coregraphics/1456251-hypot

Related

How to drag SCNNode along specific axis after rotation?

I am currently working on Swift ARKit project.
I am trying to figure out how can I drag node object at specific axis even after some rotations. For example I want to move node along Y axis after rotations but It's axis directions stays same so even if I change Y position it's still move along World Y. SCNNode.localup is static and returns SCNVector3(0, 1, 0) and as far as I see there is no function for node's local up. If I remember correctly, it was enough to increase the local axis to drag after rotating in Unity.
Node object before rotation
Before applying some rotations to drag object all you need to do is increasing or decreasing specific axis.
Node object after rotation
After rotate green Y axis rotates too but when I increase or decrease local Y value object still moves along World Y.
Sorry for my bad English. Thanks for your helps.
Out of curiosity, how are you currently applying the rotation?
A straightforward way to achieve this without needing to dig into quaternion math would be to wrap your node in question inside a parent node, and apply those transformations separately. You can apply the rotation to the parent node, and then the drag motion along the axis to the child node.
If introducing this layer would be problematic outside of this operation, you can add/rotate/translate/remove as a single atomic operation, using node.convertPosition(_:to:) to interchange between local and world coordinates after you've applied all the transformations.
let parent = SCNNode()
rootNode.addChildNode(parent)
parent.simdPosition = node.simdPosition
node.simdPosition = .zero
parent.simdRotation = /../
node.simdPosition = simd_float3(0, localYAxisShift, 0)
node.simdPosition = rootNode.convertPosition(node.simdPosition, from: parent)
rootNode.addChildNode(node)
rootNode.removeChildNode(parent)
I didn't test the above code, but the general approach should work. In general, compound motion as you describe is a bit more complex to do directly on the node itself, and under the hood SceneKit is doing all of that for you when using the above approach.
Edit
Here's a version that just does the matrix transform directly rather than relying on the built in accommodations.
let currentTransform = node.transform
let yShift = SCNMatrix4MakeTranslation(0, localYAxisShift, 0)
node.transform = SCNMatrix4Mult(yShift, currentTransform)
This should shift your object along the 'local' y axis. Note that matrix multiplication is non-commutative, i.e. the order of parameters in the SCNMatrix4Mult call is important (try reversing them to illustrate).

Unity3D - Relative position on y-axis independent of rotation?

I have an object (A) with another object (B) next to it. I am trying to calculate the "height" of object B, so that i can position another object at that height relative to the position of object A. I know this sounds like gibberish (i'm a bit tired) so i have put diagrams to try and explain.
So in the left image the yellow line represents what i am trying to calculate. I have an position (orange) on the surface of a cylinder (grey) (calculated position using mesh data) which i am trying to use to calculate the radius of the object (black line). To do this i need a position at the center of the object (grey) at the same height (red dot) so i can calculate the direction from one to the other and use the length (.magnitude) as the radius.
My problem is i can't work out, how i can calculate the height (yellow line) without rotation having any effect.
I currently use projectOnPlane however if i rotate the object as seen in the second image, the radius decreases significantly when it should be consistent as the object is not changing size.
Vector3 RadDirection = (Vector3.ProjectOnPlane(orangePoint, grey.transform.up) - Vector3.ProjectOnPlane(grey.transform.position, grey.transform.up));
float radius = RadDirection.magnitude;
Any help would be much appreciated, thanks.
**UPDATE: The grey block in the diagram is a vector3 position rather than a game object. The radius calculation i am trying to do happens during runtime so i can't parent an object to the grey and review the inspector.
**UPDATE 2: Sorry, something i should have mentioned. The object i'm doing this on will not always be a perfect cylinder, it could be something such as a wine glass, where i need to calculate the radius of the glass not the stem. Another example could be a chemistry beaker which normally tapers to a point, so i would need to calculate the radius at the height of the orange point. Sorry i should have put that in the question.
Here's a diagram to illustrate what i mean in update 2. Again the orange dot is acting as a visual representation of a Vector3 position on the surface of the object's (in this case a beaker) mesh.
**Update 3: I appear to have solved the issue and so i have posted my answer below but at the time of writing i can't accept it (have to wait 2 days) and so cant close/answer the question. I would like to thank everyone that contributed and tried to help me solve this problem. I hope i can help you all someday :)
You can use transform.TransformDirection() or transform.InverseTransformDirection()
To get the height take the result of the subtraction and set the X, Z coordinate to zero:
Vector3 height = orangeBox.position - greenPoint.position;
height.x = 0;
height.z = 0;
Complete solution:
Vector3 direction = orangeBox.position - greenPoint.position;
direction = greyBox.transform.InverseTransformDirection(direction);
direction.x = 0;
direction.z = 0;
height = direction.magnitude;
redPoint.position = greenPoint.position + greyBox.transform.up * height;
I appear to have solved my problem using the following:
public static Vector3 ProjectPointOnLine(Vector3 linePoint, Vector3 lineVec, Vector3 point)
{
//get vector from point on line to point in space
Vector3 linePointToPoint = point - linePoint;
float t = Vector3.Dot(linePointToPoint, lineVec);
return linePoint + lineVec * t;
}
I found this here, it has lot's of other useful looking functions:
http://wiki.unity3d.com/index.php/3d_Math_functions
I basically pass in the origin of the beaker (green point) as the "linePoint", the upwards direction of the beaker "lineVec" with beaker.transform.up and finally i pass in the world vector3 point on the surface of the beaker mesh (orange point) it returns back a point in the middle of the beaker at the same height as my orange dot. I then just subtract the one from the other and take the magnitude as the radius. The radius value calculated is correct in that's it the value i was expecting and only changes after the sixth decimal place during rotation which gives plenty of accuracy as i only need three or four decimal places.
I'm happy to provide further details or help if anyone else needs help doing this.

ARKit: Plot a Node at a specific pixel at a specific Z distance from Camera

Referring to the image above. I have a Red Node at the center of the screen with a distance of 1.0 unit (1 meter away) [See iPhone Portrait Top View]
What I do is I capture a screenshot of the iPhone screen and the resulting image is 750 x 1334 pixels [See iPhone Portrait Front View]
sceneView.snapshot()
What I want to do is put 4 Red Square Nodes located on the four sides of the iPhone screen relative to the Red Circle (at the dead center of the screen). I am making this to mark where I did a snapshot. What I want to know is how can I plot a box node precisely at a certain x,y point given z distance. (The value of Z is not fixed, I just used 1.0 as a sample scenario).. I want to plot (0,0), (750,0), (0, 1334) and (750, 1334) at a given z of 1.0 and assuming I am on a tripod, the plotted nodes would appear on the four sides of my iPhone screen.
I am very terrible at math and this problem is so complicated for me to solve alone with my current math skills. Can anyone help? Please?
Since you need ARnchor nodes (to mark where a snapshot was done) using the real time information from the camera instead of a snapshot would be probably easier. In special due to pixels in a 2D image are referenced from left to right and from top to bottom (with "0,0" coords located in the top left side)
... and we know the AR nodes are referenced in 3D coordinates with the center of the local node as the 0,0,0 coords.
I haven't yet code to test but I think the following properties should help:
let pPoint = sceneView.projectPoint(self.centerBall.position)
let fieldW = sceneView.session.currentFrame?.camera.imageResolution.width
let fieldH = sceneView.session.currentFrame?.camera.imageResolution.height
The "pPoint" should return the 2D coords corresponding to the (0,0,0) 3D coords of "centerBall" from there it should be just add or subtract calculations to obtain all 4 corners in 2D
Finally passing the 2D coords of every corner to the "unprojectPoint(:)" method should provide the 3D "world" coords and that can be converted to "centerBall" coordinates with the "convertPosition( position: SCNVector3, from node: SCNNode?)" method
It seems interesting so will try to code this before weekend
At the end I suspect ARanchor nodes may not be 100% stables

Orient SCNNode in an ARKit scene using real-world bearing

I have a simple SCNNode that I want to place in the real-world position, the node corresponds to a landmark with known coordinates.
The ARKit configuration has the worldAlignment property set to .gravityAndHeading so the x and z axes should be oriented with the world already.
After creating the node, I am setting the position at 100m away from the
node.position = SCNVector3(0, 0, -100)
Then I would like to project the node but with the correct bearing (from user and landmark coordinates). I am trying to rotate the node on the y-axis rotation(yaw)
node.eulerAngles = SCNVector3Make(0, bearingRadians, 0)
However the node still points to north direction, no matter what values I have for bearingRadians.
Do I need to do an extra transformation?
With eulerAngles you just rotate your node in its own coord system.
What you actually need is to perform a full transform of your node relative to the camera position. The transformation is a translation on -z axis followed by a (negative) rotation on y-axis according to your bearing.
Check out this ARKit WindRose repo which align and project the cardinal directions in the real world: https://github.com/vasile/ARKit-CompassRose

How to increase (animate) the width of the square on both ends?

I have created a square that is 40x40, as shown above. I have a 4x40 strip that I'd like to use to animate (increase) the width of my square till it takes the the width of the whole screen within a second, regardless of the square's position. Quite similar to that of a progress bar loading on both sides.
UPDATE
I forgot to mention that the square is a physics body, hence the physics body must also increase as the sprite increases.
What you want to do is use SKAction.scaleXTo to achieve what you are looking for:
SKAction.scaleXTo(sceneWidth / spriteWidth, duration: 1).
Now if you want the left and right side to not scale evenly, but instead reach both edges at the same time, what you can do is change the anchor point.
The math behind this assumes that your original anchor point is (0.5,0.5)
sprite.anchorPoint = CGPointMake(sprite.position.x / scene.width,sprite.anchorPoint.y)
E.G. Scene size width is 100, sprite is at x 75
What this is basically saying is that your sprite is at some percentage of the scene, in case of the example, 75%. so by changing the anchor point to .75, what is going to happen is the left side will fill faster than the right side when you are expanding your width since the left side of the anchor point has 75% of the width, and the right side has 25% of the width .
Lets say we set the scale to 2, that means the left side of the anchor point will now be at 150%, while the right side will be at 50%.
In general, assuming the origin of your objects are in the top-left (or at least the left, since we're only changing things on one axis) if you set start_x to the original x position of your square, start_width to its width, target_x to the x position of your strip, and target_width to its width, then:
x = start_x + (target_x - start_x) * a;
and
width = start_width + (target_width - start_width) * a;
And as a goes from 0.0 to 1.0, x and width will grow to match the strip.
Hope this helps.