SceneKit: orient one node toward another in 3Dspace - rotation

I need to orient one node to point its Z-axis at another node in 3D. Yeah, the perfect job for the LookAtConstraint. And for most of my work LookAt is fine. But when I apply LookAt to a particular node, I can no longer animate that node's translation with SCNAction. Picture a hydrogen atom leaving a molecule as it ionizes. The orientation is needed to properly rotate the bond (a cylinder) bewteen the hydrogen and an oxygen atom on the molecule.
I can orient the bond FROM the oxygen TO the hydrogen and animate. But this disorients most of the other bonds which were getting by just fine with LookAt's.
I gave this a mighty try before realizing it answers a somewhat different question:
Calculate rotations to look at a 3D point?

I had a similar issue with a project. What I eventually realized was that I need to use multiple constraints. One for translation (movement) and the other using the look at constraint.
I would move the object and then apply the look at constraint; in this case, it was a camera following an objects being moved using actions. Code snippet follows:
let targetNodeConstraint = SCNLookAtConstraint(target: someObject)
targetNodeConstraint.gimbalLockEnabled = true
let followObjectConstraint = SCNTransformConstraint(inWorldSpace: true, withBlock: { (node, matrix) -> SCNMatrix4 in
let transformMatrix = SCNMatrix4MakeTranslation(
self.someObject.position.x - 1.0,
self.someObject.position.y, self.someObject.position.z + 1.0)
return transformMatrix
})
// Position the object behind the other object & rotate it to
roadCamera.constraints = [followObjectConstraint, targetNodeConstraint]
The important thing to note is the order in which the constraints are added to the object using an array. In the code above, I am ignoring the current matrix before I apply a transform matrix (I should re-write this code someday)
The complete source code of this "experiment" is on GitHub as I try things out.
https://github.com/ManjitBedi/CubeTrip
Hopefully, this is helpful.

My solution here. Deal with situation that node continuously translate in space and should always toward a position.
#discardableResult
func yew(_ node:SCNNode, toPosition position:SCNVector3) -> Float
{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
forward = GLKVector3Normalize(GLKVector3Make(forward.x, 0, forward.z))
toWard = GLKVector3Normalize(GLKVector3Make(toWard.x, 0, toWard.z))
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var yew = acos(dotProduct)
if yew < 0 {
assert(false)
}
//toward is clockwise of forward
let isCW = GLKVector3CrossProduct(forward, toWard).y < 0
if isCW {
yew = -yew
}
eularAngle.y = yew
node.eulerAngles = SCNVector3Make(eularAngle.x + wrapperNode.eulerAngles.x,
eularAngle.y + wrapperNode.eulerAngles.y,
eularAngle.z + wrapperNode.eulerAngles.z)
return yew
}
#discardableResult
func pitch(_ node:SCNNode, toPosition position:SCNVector3) -> Float{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
forward = GLKVector3Normalize(forward)
toWard = GLKVector3Normalize(toWard)
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var pitch = acos(dotProduct)
//toward is clockwise of forward, if right vector of model and crossProfuct.x has same direction
let crossProduct = GLKVector3CrossProduct(forward, toWard)
let isCW = (crossProduct.x <= 0) != (tranform.m11 <= 0)
if isCW {
pitch = -pitch
}
eularAngle.x = pitch
node.eulerAngles = SCNVector3Make(eularAngle.x + node.eulerAngles.x,
eularAngle.y + node.eulerAngles.y,
eularAngle.z + node.eulerAngles.z)
return pitch
}
func orient(_ node:SCNNode, toPosition position:SCNVector3) {
self.yew(node, toPosition: position)
self.pitch(node, toPosition: position)
}

Related

2d circle rect collision and reflection doesnt work

I have game with map built by rectangles, darker rectangles (named "closed") mean its place where balls should be able to move, ball should reflect from the lighter rectangles(named "open") border. In future I'll add more balls and they will reflect from each other.
The problem is with new Vector after collision.
I force function circleRectGetCollisionNormal() to return vector(-1,0) what i think its normal for this case (ball is moving in right direction).
Ball is starting with degrees and change it simply to vector, this reflection worked for 45 degrees but when I change angle to 10 degrees ball moved into lighter rectangles(named "open").
Here is how it looks like (Picture)
I'm doing like this:
1-check if ball collided with lighter rectangle,
2-if it collided, I want to change direction so I return vector, for example for right side of ball colliding with rectangle return [-1,0] (I think its normal of vertical line, and its pointing left direction).
3-calculate new ball move Vector from this equation: newMoveVector = oldMoveVector − (2 * dotProduct(oldMoveVector, normalVector) * normalVector)
Here is code for each step:
1.
circleRect(circlePos, circleSize, rectPos, rectSize) {
//its rectRect collision but it doesnt matter because reflection surface is always horizontal or vertical
let r1 = {
left: circlePos.x - circleSize.x/2,
right: circlePos.x + circleSize.x/2,
top: circlePos.y - circleSize.y/2,
bottom: circlePos.y + circleSize.y/2
};
let r2 = {
left: rectPos.x,
right: rectPos.x + rectSize.x,
top: rectPos.y,
bottom: rectPos.y + rectSize.y
};
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
isOnOpenTile(pos: Vector, size: Vector) {
let openTiles = this.getTiles('open');
let result = false;
openTiles.forEach(element => {
if( this.circleRect(pos,size,element.pos,element.size) ){
result = element;
return;
}
});
return result;
}
2.
circleRectGetCollisionNormal(c, r) {
if(c.pos.y <= r.pos.y - (r.size.y/2)) return new Vector(0,-1);
//Hit was from below the brick
if(c.pos.y >= r.pos.y + (r.size.y/2)) return new Vector(0,1);
//Hit was from above the brick
if(c.pos.x < r.pos.x) return new Vector(1,0);
//Hit was on left
if(c.pos.x > r.pos.x) return new Vector(-1,0);
//Hit was on right
return false;
}
3.
getNewMoveVector(moveVector, normalVector) {
normalVector = this.normalize(normalVector);
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
return moveVector.sub(dotProduct.mult(normalVector).mult(new Vector(2,2)));
}
normalize(v) {
let length = Math.sqrt((v.x*v.x) + (v.y*v.y));
return new Vector(v.x/length,v.y/length);
}
And here is main function for this
getMoveVectorOnCollision(circle) {
let coll = this.isOnOpenTile( circle.pos, circle.size );
if( coll != false) {
let vector = this.circleRectGetCollisionNormal(circle, coll);
return this.getNewMoveVector(circle.moveVector, vector);
} else return false;
}
Object Vector always contain 2 values all of function (mult, sub, div, add) work like here.
sub(vector: Vector) {
return new Vector(this.x - vector.x, this.y - vector.y);
}
Please give me advice, actual solution or tell about different way to do this reflection. I wasted more than 3 days trying to solve this, I have to move on.
Yor dot product calculation is erroneous. Change these lines:
let dot = (moveVector.x * moveVector.y) + (normalVector.x * normalVector.y);
let dotProduct = new Vector(dot, dot);
by this one line:
let dotProduct = (moveVector.x * normalVector.x + moveVector.y * normalVector.y);
Note that dotProduct is scalar value, not vector, so you have to make vector for subtraction as
subvec.x = 2 * dotProduct * normalVector.x
subvec.y = 2 * dotProduct * normalVector.y
and
return moveVector.sub(subvec);

pixijs very slow in mobile compared to css

I'm testing PIXIjs for a simple 2D graphics, basically I'm sliding tiles with some background color and borders animation, plus I'm masking some parts of the layout.
While it works great in desktops it's really slower than the same slide+animations made with pure css in mobile devices (where by the way I'm using crosswalk+cordova so the browser is always the same)
For moving tiles and animating color I'm calling requestAnimationFrame for each tile and I've disabled PIXI's ticker:
ticker.autoStart = false;
ticker.stop();
This slowness could be due to a weaker GPU on mobiles? or is just about the way I use PIXI?
I'm not showing the full code because is quite long ~ 800 lines.
The following is the routine I use for each tile once a slide is captured:
const animateTileBorderAndText = (tileObj, steps, _color, radius, textSize, strokeThickness, _config) => {
let pixiTile = tileObj.tile;
let s = 0;
let graphicsData = pixiTile.graphicsData[0];
let shape = graphicsData.shape;
let textStyle = pixiTile.children[0].style;
let textInc = (textSize - textStyle.fontSize) / steps;
let strokeInc = (strokeThickness - textStyle.strokeThickness) / steps;
let prevColor = graphicsData.fillColor;
let color = _color !== null ? _color : prevColor;
let alpha = pixiTile.alpha;
let h = shape.height;
let w = shape.width;
let rad = shape.radius;
let radiusInc = (radius - rad) / steps;
let r = (prevColor & 0xFF0000) >> 16;
let g = (prevColor & 0x00FF00) >> 8;
let b = prevColor & 0x0000FF;
let rc = (color & 0xFF0000) >> 16;
let rg = (color & 0x00FF00) >> 8;
let rb = color & 0x0000FF;
let redStep = (rc - r) / steps;
let greenStep = (rg - g) / steps;
let blueStep = (rb - b) / steps;
let paintColor = prevColor;
let goPaint = color !== prevColor;
let animate = (t) => {
if (s === steps) {
textStyle.fontSize = textSize;
textStyle.strokeThickness = strokeThickness;
//pixiTile.tint = color;
if (!_config.SEMAPHORES.slide) {
_config.SEMAPHORES.slide = true;
PUBSUB.publish(_config.SLIDE_CODE, _config.torusModel.getData());
}
return true;
}
if (goPaint) {
r += redStep;
g += greenStep;
b += blueStep;
paintColor = (r << 16) + (g << 8) + b;
}
textStyle.fontSize += textInc;
textStyle.strokeThickness += strokeInc;
pixiTile.clear()
pixiTile.beginFill(paintColor, alpha)
pixiTile.drawRoundedRect(0, 0, h, w, rad + radiusInc * (s + 1))
pixiTile.endFill();
s++;
return requestAnimationFrame(animate);
};
return animate();
};
the above function is called after the following one, which is called for each tile to make it slide.
const slideSingleTile = (tileObj, delta, axe, conf, SEM, tilesMap) => {
let tile = tileObj.tile;
let steps = conf.animationSteps;
SEM.slide = false;
let s = 0;
let stepDelta = delta / steps;
let endPos = tile[axe] + delta;
let slide = (time) => {
if (s === steps) {
tile[axe] = endPos;
tileObj.resetPosition();
tilesMap[tileObj.row][tileObj.col] = tileObj;
return tileObj.onSlideEnd(axe == 'x' ? 0 : 2);
}
tile[axe] += stepDelta;
s++;
return requestAnimationFrame(slide);
};
return slide();
};
For each finger gesture a single column/row (of NxM matrix of tiles) is slided and animated using the above two functions.
It's the first time I use canvas.
I red that canvas is way faster then DOM animations and I red very good review of PIXIjs, so I believe I'm doing something wrong.
Can someone help?
In the end I'm a complete donk...
The issue is not with pixijs.
Basically I was forcing 60fps! The number of steps to complete the animation is set to 12 that implies 200ms animation at 60FPS (using requestAnimationFrame) but in low end devices its going to be obviously slower.
Css animation works with timing as parameter so it auto adapt FPS to devices hardware.
To solve the issue I'm adapting the number of steps during animations, basically if animations takes longer than 200ms I just reduce number of steps proportionally.
I hope this could be of help for each web developer used to css animation who have just started developing canvas.

How can I generate echo path for concave polygon

I need an algorithm to draw echo path for an arbitrary polygon. If a polygon is convex problem is quite easy to solve. To understand what I mean please look at the picture bellow where in black is original polygon and in red color echoed polygons generated from original one.
d is distance between echo paths which is given
β angle is easy to calculate knowing coordinates of vertices which we have
So as you can see for each vertex we can calculate L and thus have new vertices for next echo path.
The problem is when we have concave polygon at some point we will get an ugly picture of self crossing polygon. Please take a look to this picture.
What I want to do is generate echo polygon without self crossing part i.e. without part that is with dash lines. An algorithm or java code would be very helpful. Thanks.
Edit
Just adding piece of code that generates echo path for convex polygon as it was asked in comment.
public List<MyPath> createEchoCoCentral( List<Point> pointsOriginal, float encoderEchoDistance, int appliqueEchoCount){
List<Point> contourPoints = pointsOriginal;
List<MyPath> echoPaths = new ArrayList<>();
for (int round = 0; round < appliqueEchoCount; round++) {
List<Point> echoedPoints = new ArrayList<>();
int size = contourPoints.size()+1;//+1 because we connect end to start
Point previousPoint = contourPoints.get(contourPoints.size() - 1);
for (int i = 0; i < size; i++) {
Point currentPoint;
if (i == contourPoints.size()) {
currentPoint = new Point(contourPoints.get(0));
} else {
currentPoint = contourPoints.get(i);
}
final Point nextPoint;
if (i + 1 == contourPoints.size()) {
nextPoint = contourPoints.get(0);
} else if (i == contourPoints.size()) {
nextPoint = contourPoints.get(1);
} else {
nextPoint = contourPoints.get(i + 1);
}
if (currentPoint.x == previousPoint.x && currentPoint.y == previousPoint.y) continue;
if (currentPoint.x == nextPoint.x && currentPoint.y == nextPoint.y) continue;
// signs needed o determine to which side of polygon new point will go
float currentSlope = (float) (Math.atan((previousPoint.y - currentPoint.y) / (previousPoint.x - currentPoint.x)));
float signX = Math.signum((previousPoint.x - currentPoint.x));
float signY = Math.signum((previousPoint.y - currentPoint.y));
signX = signX == 0 ? 1 : signX;
signY = signY == 0 ? 1 : signY;
float nextSignX = Math.signum((currentPoint.x - nextPoint.x));
float nextSignY = Math.signum((currentPoint.y - nextPoint.y));
nextSignX = nextSignX == 0 ? 1 : nextSignX;
nextSignY = nextSignY == 0 ? 1 : nextSignY;
float nextSlope = (float) (Math.atan((currentPoint.y - nextPoint.y) / (currentPoint.x - nextPoint.x)));
float nextSlopeD = (float) Math.toDegrees(nextSlope);
//calculateMidAngle - is a bit tricky function that calculates angle between two adjacent edges
double S = calculateMidAngle(currentSlope, nextSlope, signX, signY, nextSignX, nextSignY);
Point p2 = new Point();
double ew = encoderEchoDistance / Math.cos(S - (Math.PI / 2));
p2.x = (int) (currentPoint.x + (Math.cos(currentSlope - S)) * ew * signX);
p2.y = (int) (currentPoint.y + (Math.sin(currentSlope - S)) * ew * signX);
echoedPoints.add(p2);
previousPoint = currentPoint;
}
//createPathFromPoints just creates MyPath objects from given Poins set
echoPaths.add(createPathFromPoints(echoedPoints));
//remove last point since it was just to connect end to first point
echoedPoints.remove(echoedPoints.size() - 1);
contourPoints = echoedPoints;
}
return echoPaths;
}
You are looking for the straight skeleton:
(Image from Wikipedia.)
This problem is called computing polygon offsetting. There are two common ways to solve this problem:
1) the most effective way is to compute offset polygon by computing winding numbers ( As I understood, this algorithm is used by Clipper library)
2) computing straight skeleton graph, which help you to build offset polygon
Interesting articles on this topic :
Chen, polygon offsetting by computing winding numbers
Felkel's algorithm to compute straight skeleton
Ok, found a library that can do what I need. It called Clipper
There is also java implementation for it here if anybody interested.
With Java library couple lines of code do the trick
Path originalPath = new Path();
for (PointF areaPoint:pointsOriginal){
originalPath.add(new LongPoint((long)areaPoint.x, (long)areaPoint.y));
}
final ClipperOffset clo = new ClipperOffset();
Paths clips = new Paths();
Paths solution = new Paths();
clips.add(originalPath);
clo.addPaths( clips, Clipper.JoinType.SQUARE, Clipper.EndType.CLOSED_LINE );
float encoderEchoDistance = (float) UnitUtils.convertInchOrMmUnitsToEncoderUnits(this, inchOrMm, appliqueEchoDistance);
clo.execute( solution, encoderEchoDistance );
// Now solution.get(0) will contain path that has offset from original path
// and what is most important it will not have self intersections.
It is open source so I will dive in for implementation details. Thanks everyone who tried to help.

NSBezierPath: How to control numbers of segments with LineDashStyle ?

I want to draw a square, with NSBezierPath. The border of square must to be discontinue so i use dashStyle, but I don't have any control on numbers of segments created.
On Apple documentation the explanation is a bit vague. They said that "When setting a line dash pattern, you specify the width (in points) of each successive solid or transparent swatch".
So i think, I need a way to get the length of a curved bezier.
Does anyone have an idea how i can achive that ?
extension NSBezierPath {
var lenght:Double {
get{
let flattenedPath = self.bezierPathByFlatteningPath
let segments = flattenedPath.elementCount
var lastPoint:NSPoint = NSZeroPoint
var point:NSPoint = NSZeroPoint
var size :Double = 0
for i in 0...segments - 1 {
let e:NSBezierPathElement = flattenedPath.elementAtIndex(i, associatedPoints: &point)
if e == .MoveToBezierPathElement {
lastPoint = point
} else {
let distance:Double = sqrt(pow(Double(point.x - lastPoint.x) , 2) + pow(Double(point.y - lastPoint.y) , 2))
size += distance
lastPoint = point
}
}
return size
}
}
}
With this extension i get the "approximative" length of a bezier. After that, everything is simple:
let myPath = NSBezierPath(roundedRect:myRect, xRadius:50, yRadius:50)
let pattern = myPath.length / (numbersOfSegments * 2) // we divide the length to double of segments we need.
myPath.setLineDash([CGFloat(pattern),CGFloat(pattern)], countL:2 , phase: 0)
myPath.stroke()

Walk a line between two points in a 3D voxel space visiting all cells

I have a line-of-sight problem I need to solve by visiting all possible cells in a 3D voxel space between two (non-grid-aligned) points.
I have considered using a 3D Bresenham algorithm, but it will skip out some cells.
A naive implementation might be to just check points along the line at a higher resolution than the voxel grid, but I was hoping for a more intelligent solution.
Anyone got any leads?
Came up with this, or see: http://jsfiddle.net/wivlaro/mkaWf/6/
function visitAll(gx0, gy0, gz0, gx1, gy1, gz1, visitor) {
var gx0idx = Math.floor(gx0);
var gy0idx = Math.floor(gy0);
var gz0idx = Math.floor(gz0);
var gx1idx = Math.floor(gx1);
var gy1idx = Math.floor(gy1);
var gz1idx = Math.floor(gz1);
var sx = gx1idx > gx0idx ? 1 : gx1idx < gx0idx ? -1 : 0;
var sy = gy1idx > gy0idx ? 1 : gy1idx < gy0idx ? -1 : 0;
var sz = gz1idx > gz0idx ? 1 : gz1idx < gz0idx ? -1 : 0;
var gx = gx0idx;
var gy = gy0idx;
var gz = gz0idx;
//Planes for each axis that we will next cross
var gxp = gx0idx + (gx1idx > gx0idx ? 1 : 0);
var gyp = gy0idx + (gy1idx > gy0idx ? 1 : 0);
var gzp = gz0idx + (gz1idx > gz0idx ? 1 : 0);
//Only used for multiplying up the error margins
var vx = gx1 === gx0 ? 1 : gx1 - gx0;
var vy = gy1 === gy0 ? 1 : gy1 - gy0;
var vz = gz1 === gz0 ? 1 : gz1 - gz0;
//Error is normalized to vx * vy * vz so we only have to multiply up
var vxvy = vx * vy;
var vxvz = vx * vz;
var vyvz = vy * vz;
//Error from the next plane accumulators, scaled up by vx*vy*vz
// gx0 + vx * rx === gxp
// vx * rx === gxp - gx0
// rx === (gxp - gx0) / vx
var errx = (gxp - gx0) * vyvz;
var erry = (gyp - gy0) * vxvz;
var errz = (gzp - gz0) * vxvy;
var derrx = sx * vyvz;
var derry = sy * vxvz;
var derrz = sz * vxvy;
do {
visitor(gx, gy, gz);
if (gx === gx1idx && gy === gy1idx && gz === gz1idx) break;
//Which plane do we cross first?
var xr = Math.abs(errx);
var yr = Math.abs(erry);
var zr = Math.abs(errz);
if (sx !== 0 && (sy === 0 || xr < yr) && (sz === 0 || xr < zr)) {
gx += sx;
errx += derrx;
}
else if (sy !== 0 && (sz === 0 || yr < zr)) {
gy += sy;
erry += derry;
}
else if (sz !== 0) {
gz += sz;
errz += derrz;
}
} while (true);
}
As far as I remember the original Bresenham algorithm assumes that movement along diagonals is allowed, in your case is makes sense to disallow it.
But the main idea is the same - for every voxel answer the question "what's next?"
Every voxel has 6 faces each leading to a different neighbour. Just check centre of which voxel is closer to the line than others. That's the next voxel.
Note: this assumes that voxel has the same size along every axis, if that's not the case, you should calculate modified distance (every component should be divided by voxel size along corresponding axis)
I think 3d Bresenham is the way to go, just tweaked a bit. As a first pass at the problem, proceed as Bresenham, but be suspicious when you're about to take a step, or you've just taken a step, as these are the places where the line could pass through extra cells.
For simplicity, let's assume that z is dominant, meaning that z increments every step. The 3d Bresenham question is: "when do we increment/decrement in x or y?" The answer is when accumulated error in x reaches .5, or when the error in y does, or both.
For your case, I think you need to have a secondary threshold that uses slopeY = deltaY/deltaZto decide if the line is about to cross into a neighboring cell. If stepZ is the change in z along the line for each pixel, then a test like error > .5 - slopeY/stepZ should tell you to get cells on both sides of the line in y. A similar test tells you if you have to get the extra cell in x. If you have to get the extra cell in both x AND y, then you have to get the cell diagonal to the Bresenham cell as well.
If you've detected that you added a cell in y before the increment, you won't add a cell after. If you haven't added a y cell before, you will have to after, unless you happened to pass through a cell corner. How you handle that depends on your use-case.
These are my thoughts on the issue, I haven't tested anything, but something like it should work.
Here is a public link to a recent port of my voxel ray from C++ into javascript:
https://github.com/jeremykentbgross/EmpathicCivGameEngine/blob/master/engine/public/scripts/Ray2D.js
Note: the port is currently in 2D on a quadtree (instead of 3D on an octtree), but only because one dimension is commented out for my 2D javascript engine. It works fine in my 3D C++ engine (where I ported it from) so if you uncomment the Z axis lines it will work. The file also has a lot of inline comments on how the math works.
You should also reference the RayTracer2D.js (in the same directory) which uses the ray to find all intersected objects and their intersection points in the order they are hit.
For reference the quad tree structure it is tracing through is also in the same folder: QuadTree.js
Note that you could also ray trace lower LOD's simply by limiting how deep you traverse into the tree during the trace.
Hope that helps.
https://code.activestate.com/recipes/578112-bresenhams-line-algorithm-in-n-dimensions/
Here is a numpy implementation for N-D bresenham line drawing in case someone came across this thread from googling 'bresenham 3d python'.

Resources