I'm getting confused.
Using vision, I transform bottom-left coordinates to top-left by
CGAffineTransform(scaleX: 1, y: -1).translatedBy(x:0, y: -1)
however to rotate the camera view according to the orientation I
CGAffineTransform(translationX: 1, y: 0), rotated(by: -CGFloat.pi / 2)
why in the second case do we use CGAffineTransform(translationX... rather than CGAffineTransform(scaleX..
What is the difference between the two?
So why to transform bottom-left coordinates to top left do we use scale
So your question really is: Why is
CGAffineTransform(scaleX: 1, y: -1).translatedBy(x:0, y: -1)
the way to flip?
Let's start with the scale part. Scaling the y coordinate system to -1 is a multiplication: it reverses the scale so that up is down. That's the flip. (Scaling the x to 1 just means "leave it alone".)
The translate part is because transforms take place around the origin (the bottom left corner, originally). So when we flip by scaling, we flip ourselves right off the screen. In order to compensate for that, we slide back onto the screen.
They have a completely different meaning from each other, as per Apple doc:
.translatedBy
Returns an affine transformation matrix constructed by translating an
existing affine transform.
.scaledBy
Returns an affine transformation matrix constructed by scaling an
existing affine transform.
Related
This Question was posted, but never answered.
Similar to This Question, I am trying to understand SCNNode.rotation as a 4D vector. The prior question utilizes an example that only manipulates 1 axis, i.e.,
SCNNode.rotation = (0, 0, 1, degToRad(45)) //Rotate about z-axis by 45 degrees
which makes sense; however, what if I wanted to rotate the X axis by 20 degrees, Y axis by 45 degrees and then Z axis by 78 degrees?
SCNNode.rotation = ??
I would provide code I've tried, but I don't understand conceptually the notion of a 4D rotation vector.
Every node just has a transform with 4x4 matrix. So all the rotation operations are reflecting in the changing the transform.
In this case, if you change either of rotation, eulerAngles and orientation, you are supposed to get same value.
If rotating about three axises, I suggested using eulerAngles.
node.eulerAnges = SCNVector3(x:degToRad(20),y:degToRad(45), z:degToRad(78))
After you set this, go back and check to value of rotation:
SCNVector4(x: -0.16975601, y: 0.5943193, z: 0.786109, w: 1.448788)
This means there is an axis going through point(-0.16975601, 0.5943193, 0.786109) and origin (0,0,0), and node is rotating around it for 1.448788 (82 degree).
This can be reproduced using the default Xcode 3D game template (SpaceShip).
Remove the runAction line that rotates the ship in the GameViewController.
Add this line. The ship correctly faces away from the camera.
Note the very small x component in the at: parameter.
ship.look(at: SCNVector3(0.001, 0, -100.0), up: SCNVector3(0, 1, 0), localFront: SCNVector3(0, 0, 1)) // Works! Ship faces away from camera.
However, it the x component of the at: parameter is exactly zero, the ship's orientation does not change. The ship continues to face the camera instead of facing away from it.
ship.look(at: SCNVector3(0, 0, -100.0), up: SCNVector3(0, 1, 0), localFront: SCNVector3(0, 0, 1)) // DOES NOT work
Seeing the same problem with simdLook()
For starters, if you show the world origin sceneView.debugOptions = ARSCNDebugOptions.showWorldOrigin], you will see that the x and y axis are actually opposite to what they would normally be, i.e. x is now y and y is now x.
Second, you are changing the x axis and not z axis. If you want the spaceship to look away or at you, that runs along the z axis. Negative(-) z will look at you where Positive(+)z will look away from you. Trying just changing the -100 to +100.
Suppose you have two points in 3-D space. Call the first o for origin and the other t for target. The rotation axes of each are alligned with the world/parent coordinate system (and each other). Place a third point r coincident with the origin, same position and rotation.
How, in Swift, can you rotate r such that its y-axis points at t? If pointing the z-axis is easier, I'll take that instead. The resulting orientation of the other two axes is immaterial for my needs.
I've been through many discussions related to this but none satisfy. I have learned, from reading and experience, that Euler angles is probably not the way to go. We didn't cover this in calculus and that was 50 years ago anyway.
Got it! Incredibly simple when you add a container node. The following seems to work for any positions in any quadrants.
// pointAt_c is a container node located at, and child of, the originNode
// pointAtNode is its child, position coincident with pointAt_c (and originNode)
// get deltas (positions of target relative to origin)
let dx = targetNode.position.x - originNode.position.x
let dy = targetNode.position.y - originNode.position.y
let dz = targetNode.position.z - originNode.position.z
// rotate container node about y-axis (pointAtNode rotated with it)
let y_angle = atan2(dx, dz)
pointAt_c.rotation = SCNVector4(0.0, 1.0, 0.0, y_angle)
// now rotate the pointAtNode about its z-axis
let dz_dx = sqrt((dz * dz) + (dx * dx))
// (due to rotation the adjacent side of this angle is now a hypotenuse)
let x_angle = atan2(dz_dx, dy)
pointAtNode.rotation = SCNVector4(1.0, 0.0, 0.0, x_angle)
I needed this to replace lookAt constraints which cannot, easily anyway, be archived with a node tree. I'm pointing the y-axis because that's how SCN cylinders and capsules are directed.
If anyone knows how to obviate the container node please do tell. Everytime I try to apply sequential rotations to a single node, the last overwrites the previous one. I haven't the knowledge to formulate a rotation expression to do it in one shot.
I've been playing with the SCNNode object for a while now and I'm lost with the Pivot. How can I change the pivot of a SCNNode (SCNBox as a bar) and place the pivot on one of the edge of the bar?
A node's pivot is a transformation matrix, the inverse of which is applied to the node before its transform property takes effect. For example, take a look at this bit from the default SceneKit Game template in Xcode:
let boxNode = SCNNode()
boxNode.geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0.02)
If you set the boxNode's position, that point corresponds to the center of the cube, and if you rotate it (as the template does in an animation), it spins around its center.
To change the anchor point, set the pivot to a translation transform:
boxNode.pivot = SCNMatrix4MakeTranslation(0.5, 0.5, 0.5)
Now, when you set the position that point corresponds to the top-right-front corner of the cube, and when you rotate the cube it spins around that corner.
More generally, a pivot transforms the contents of a node relative to the node's own transform. Suppose you wanted to model the precession of the Earth's axis of rotation. You could do this by creating two animations: one that animates pivot to spin the node around its own Y axis, and another that animates rotation to move that axis relative to the space containing the node.
On the pivot topic:
Just in case you do not have dimensions for your geometry/node something like this might help (especially for SCNText).
var minVec = SCNVector3Zero
var maxVec = SCNVector3Zero
if node.getBoundingBoxMin(&minVec, max: &maxVec) {
let bound = SCNVector3(x: maxVec.x + minVec.x,
y: maxVec.y + minVec.y,
z: maxVec.z + minVec.z)
node.pivot = SCNMatrix4MakeTranslation(bound.x / 2,
bound.y / 2,
bound.z / 2)
}
Core Graphics does not provide an anchor point property, and all transforming/translating assumes an anchor point 0,0 (lower left). Core Animation, does provide an anchor point, but we are not using CA.
Does anyone know how to modify a transformation matrix (used with CGAffineTransform) so that we can simulate different anchor point locations (e.g. bottom middle, center, etc)?
Thanks
Sure. Translate the desired point to 0, 0. Then apply whatever transformation you want. Then apply the inverse translation.
Say you want to rotate about the point 25, 25. Do this:
CGAffineTransform *t = CGAffineTransformMake();
t = CGAffineTransformTranslate(t, -25, -25);
t = CGAffineTransformRotate(t, angle);
t = CGAffineTransformTranslate(t, 25, 25);
At this point, t is a transform which will rotate by angle about 25, 25.