How Is a Swift Cgvector Created with Dx and Dy (Derivative)

How is a Swift CGVector created with dx and dy (derivative)?

There are many possible representations of vectors, one is as the
"distance" or "displacement" from one point to another point (compare Euclidean vector: Representations).

In that sense, the vector from (0,0) to (5,5) is identical to the
vector from (2,3) to (7, 8), and the vector from point A to point B
can be computed as

let pA = CGPoint(x: 2, y: 3) // Point A(2, 3)
let pB = CGPoint(x: 7, y: 8) // Point B(7, 8)
let vecAB = CGVector(dx: pB.x - pA.x, dy: pB.y - pA.y) // Vector from A to B
print(vecAB) // CGVector(dx: 5.0, dy: 5.0)

So dx, dy stand for "delta X" and "delta Y", the distance of
the points in x- and y-direction. In the above case, you can
read vecAB as "move 5 units in x-direction and 5 units
in y-direction", and you would get the same result for A(0, 0)
and B(5, 5).

A "line segment" from (0, 0) to (5, 5) or from (2, 3) to (7, 8)
cannot be represented by a
vector alone. You would need two points, or one point and one vector.

Swift: Calculate trajectory of node with applied impulse

(an answer from standpoint of physics, I'm not an expert in either swift or sprite-kit)

The choice of 'units' (and what physical distance a point represents) is entirely yours, the math that you have up to the point where you call applyImpulse() is consistent in assuming 1point = 1meter. This may or may not be what you want, but it is an OK choice - BTW, the only thing that (sort-of) fixes a point to a real-world meter in your code is the gravity value of 9.8, otherwise you can just call them point with no change in real meaning - the physics engine doesn't really care.

The consecutive ball positions that you put into array[] in your code are made on the assumption that the launched ball will cover the distance from start_of_drag to end_of_drag in one second (because you simply use the distance as a velocity value, i.e., 1m becomes 1m/sec, 2m = 2m/s, etc.).
(NOTE I don't quite know how the 12-point predicted path worked correctly in your case, because to get a vector pointing in the direction of the drag you'd normally do end_drag - start_drag, while your code does start - end)

The problem you have likely has to do with the fact that applyImpulse expects values in units of newton*second (and the velocity change will depend on the ball's mass: delta-V = impulse / mass), while you call the function with those same "distance-that-became-velocity" units.

If the ball is always stationary prior to the drag, and you want it to start instantly going somewhere at a chosen velocity, it might be easier to simply assign its velocity to the desired value and let it fly, rather than try to use applyImpulse, e.g.:

(edit: as @knight0fdragon pointed out, SK's units of distance are points, but velocity is measured in units of an abstract 'meter' = 150pt/sec, so if you did the math in points and points/sec so far, you'd have to scale the velocity before modifying a physicsBody vector: divide it by 150.0).

ball.physicsBody?.velocity = CGVector(dx:velocityX, dy:velocityY)

If the ball already has some velocity and you want to modify it by some amount (which is expressed in velocity units for you rather than in N*m) and disregarding the mass, add that velocity change to the ball's current velocity.

Swift - Increase speed of a moving Node using SpriteKit

Ok, here you go! The ball CONSTANTLY gets faster, no matter what!!! Adjust amount to determine how much the ball gains speed each frame.

class GameScene: SKScene, SKPhysicsContactDelegate {

let ball = SKShapeNode(circleOfRadius: 20)
var initialDY = CGFloat(0)
var initialDX = CGFloat(0)

override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
self.physicsWorld.gravity = CGVector.zero

// Configure ball pb:
let pb = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody = pb
addChild(ball)

pb.applyImpulse(CGVector(dx: 10, dy: 10))
}

override func update(_ currentTime: TimeInterval) {
initialDX = abs(ball.physicsBody!.velocity.dx)
initialDY = abs(ball.physicsBody!.velocity.dy)
}

override func didSimulatePhysics() {
guard let pb = ball.physicsBody else { fatalError() }

// decrease this to adjust the amount of speed gained each frame :)
let amount = CGFloat(5)

// When bouncing off a wall, speed decreases... this corrects that to _increase speed_ off bounces.
if abs(pb.velocity.dx) < abs(initialDX) {
if pb.velocity.dx < 0 { pb.velocity.dx = -initialDX - amount }
else if pb.velocity.dx > 0 { pb.velocity.dx = initialDX + amount }
}

if abs(pb.velocity.dy) < abs(initialDY) {
if pb.velocity.dy < 0 { pb.velocity.dy = -initialDY - amount }
else if pb.velocity.dy > 0 { pb.velocity.dy = initialDY + amount }
}
}
}

you can modify this to only increase speed every 5 seconds with an SKAction.repeatForever(.sequence([.wait(forDuration: TimeInterval, .run( { code } ))) but IMO having it constantly gain speed is a bit more awesome and easier to implement.

UITesting Xcode 7: How to tell if XCUIElement is visible?

Looks like this is a known bug :-(

https://forums.developer.apple.com/thread/9934

Find the tangent of a point on a cubic bezier curve

Here is fully tested code to copy and paste:

It draws approxidistant points along the curve, and it draws the tangents.

bezierInterpolation finds the points

bezierTangent finds the tangents

There are TWO VERSIONS of bezierInterpolation supplied below:

bezierInterpolation works perfectly.

altBezierInterpolation is exactly the same, BUT it is written in an expanded, clear, explanatory manner. It makes the arithmetic much easier to understand.

Use either of those two routines: the results are identical.

In both cases, use bezierTangent to find the tangents. (Note: Michal's fabulous code base here.)

A full example of how to use with drawRect: is also included.

// MBBezierView.m    original BY MICHAL stackoverflow #4058979

#import "MBBezierView.h"

CGFloat bezierInterpolation(
CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d) {
// see also below for another way to do this, that follows the 'coefficients'
// idea, and is a little clearer
CGFloat t2 = t * t;
CGFloat t3 = t2 * t;
return a + (-a * 3 + t * (3 * a - a * t)) * t
+ (3 * b + t * (-6 * b + b * 3 * t)) * t
+ (c * 3 - c * 3 * t) * t2
+ d * t3;
}

CGFloat altBezierInterpolation(
CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
// here's an alternative to Michal's bezierInterpolation above.
// the result is absolutely identical.
// of course, you could calculate the four 'coefficients' only once for
// both this and the slope calculation, if desired.
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );

// it's now easy to calculate the point, using those coefficients:
return ( C1*t*t*t + C2*t*t + C3*t + C4 );
}

CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
// note that abcd are aka x0 x1 x2 x3

/* the four coefficients ..
A = x3 - 3 * x2 + 3 * x1 - x0
B = 3 * x2 - 6 * x1 + 3 * x0
C = 3 * x1 - 3 * x0
D = x0

and then...
Vx = 3At2 + 2Bt + C */

// first calcuate what are usually know as the coeffients,
// they are trivial based on the four control points:

CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a ); // (not needed for this calculation)

// finally it is easy to calculate the slope element,
// using those coefficients:

return ( ( 3.0 * C1 * t* t ) + ( 2.0 * C2 * t ) + C3 );

// note that this routine works for both the x and y side;
// simply run this routine twice, once for x once for y
// note that there are sometimes said to be 8 (not 4) coefficients,
// these are simply the four for x and four for y,
// calculated as above in each case.
}

@implementation MBBezierView

- (void)drawRect:(CGRect)rect {
CGPoint p1, p2, p3, p4;

p1 = CGPointMake(30, rect.size.height * 0.33);
p2 = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
p3 = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
p4 = CGPointMake(-30 + CGRectGetMaxX(rect), rect.size.height * 0.66);

[[UIColor blackColor] set];
[[UIBezierPath bezierPathWithRect:rect] fill];
[[UIColor redColor] setStroke];
UIBezierPath *bezierPath = [[[UIBezierPath alloc] init] autorelease];
[bezierPath moveToPoint:p1];
[bezierPath addCurveToPoint:p4 controlPoint1:p2 controlPoint2:p3];
[bezierPath stroke];

[[UIColor brownColor] setStroke];

// now mark in points along the bezier!

for (CGFloat t = 0.0; t <= 1.00001; t += 0.05) {
[[UIColor brownColor] setStroke];

CGPoint point = CGPointMake(
bezierInterpolation(t, p1.x, p2.x, p3.x, p4.x),
bezierInterpolation(t, p1.y, p2.y, p3.y, p4.y));

// there, use either bezierInterpolation or altBezierInterpolation,
// identical results for the position

// just draw that point to indicate it...
UIBezierPath *pointPath =
[UIBezierPath bezierPathWithArcCenter:point
radius:5 startAngle:0 endAngle:2*M_PI clockwise:YES];
[pointPath stroke];

// now find the tangent if someone on stackoverflow knows how
CGPoint vel = CGPointMake(
bezierTangent(t, p1.x, p2.x, p3.x, p4.x),
bezierTangent(t, p1.y, p2.y, p3.y, p4.y));

// the following code simply draws an indication of the tangent
CGPoint demo = CGPointMake( point.x + (vel.x*0.3),
point.y + (vel.y*0.33) );
// (the only reason for the .3 is to make the pointers shorter)
[[UIColor whiteColor] setStroke];
UIBezierPath *vp = [UIBezierPath bezierPath];
[vp moveToPoint:point];
[vp addLineToPoint:demo];
[vp stroke];
}
}

@end

to draw that class...
MBBezierView *mm = [[MBBezierView alloc]
initWithFrame:CGRectMake(400,20, 600,700)];
[mm setNeedsDisplay];
[self addSubview:mm];

Here are the two routines to calculate approximately equidistant points, and the tangents of those, along a bezier cubic.

For clarity and reliability, these routines are written in the simplest, most explanatory, way possible.

CGFloat bezierPoint(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );

return ( C1*t*t*t + C2*t*t + C3*t + C4 );
}

CGFloat bezierTangent(CGFloat t, CGFloat a, CGFloat b, CGFloat c, CGFloat d)
{
CGFloat C1 = ( d - (3.0 * c) + (3.0 * b) - a );
CGFloat C2 = ( (3.0 * c) - (6.0 * b) + (3.0 * a) );
CGFloat C3 = ( (3.0 * b) - (3.0 * a) );
CGFloat C4 = ( a );

return ( ( 3.0 * C1 * t* t ) + ( 2.0 * C2 * t ) + C3 );
}

The four precalculated values, C1 C2 C3 C4, are sometimes called the coefficients of the bezier. (Recall that a b c d are usually called the four control points.)

Of course, t runs from 0 to 1, for example every 0.05.

Simply call these routines once for X, and then once separately for Y.

Hope it helps someone!


Important facts:

(1) It is an absolute fact that: unfortunately, there is, definitely, NO method, provided by Apple, to extract points from a UIBezierPath. True as of 2019.

(2) Don't forget it's as easy as pie to animate something along a UIBezierPath. Google many examples.

(3) Many ask, "Can't CGPathApply be used to extract the points from a UIBezierPath?" No, CGPathApply is totally unrelated: it simply gives you a list of your "instructions in making any path" (so, "start here", "draw a straight line to this point", etc etc.) The name is confusing but CGPathApply is totally unrelated to bezier paths.


For game programmers - as @Engineer points out you may well want the normal of the tangent, fortunately Apple has vector math built-in:

https://developer.apple.com/documentation/accelerate/simd/working_with_vectors

https://developer.apple.com/documentation/simd/2896658-simd_normalize

Distance from point to ellipse

x = a cos(phi), y = b sin (phi) is an ellipse with the center at
the origin, and the approach described in your question can be realized like this:

// Point on ellipse in the direction of `p`:
let phi = atan2(a*p.y, b*p.x)
let p2 = CGPoint(x: a * cos(phi), y: b * sin(phi))

// Vector from `p2` to `p`:
let v = CGVector(dx: p.x - p2.x, dy: p.y - p2.y)

// Length of `v`:
let distance = hypot(v.dx, v.dy)

You are right that this does not give the shortest distance
of the point to the ellipse. That would require to solve 4th degree
polynomial equations, see for example distance from given point to given ellipse or
Calculating Distance of a Point from an Ellipse Border.

Here is a possible implementation of the algorithm
described in http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf:

// From http://wwwf.imperial.ac.uk/~rn/distance2ellipse.pdf .

func pointOnEllipse(center: CGPoint, a: CGFloat, b: CGFloat, closestTo p: CGPoint) -> CGPoint {

let maxIterations = 10
let eps = CGFloat(0.1/max(a, b))

let p1 = CGPoint(x: p.x - center.x, y: p.y - center.y)

// Intersection of straight line from origin to p with ellipse
// as the first approximation:
var phi = atan2(a * p1.y, b * p1.x)

// Newton iteration to find solution of
// f(θ) := (a^2 − b^2) cos(phi) sin(phi) − x a sin(phi) + y b cos(phi) = 0:
for i in 0..<maxIterations {
// function value and derivative at phi:
let (c, s) = (cos(phi), sin(phi))
let f = (a*a - b*b)*c*s - p1.x*a*s + p1.y*b*c
let f1 = (a*a - b*b)*(c*c - s*s) - p1.x*a*c - p1.y*b*s

let delta = f/f1
phi = phi - delta
print(i)
if abs(delta) < eps { break }
}

return CGPoint(x: center.x + a * cos(phi), y: center.y + b * sin(phi))
}

You may have to adjust the maximum iterations and epsilon
according to your needs, but those values worked well for me.
For points outside of the ellipse, at most 3 iterations were required
to find a good approximation of the solution.

Using that you would calculate the distance as

let p2 = pointOnEllipse(a: a, b: b, closestTo: p)
let v = CGVector(dx: p.x - p2.x, dy: p.y - p2.y)
let distance = hypot(v.dx, v.dy)


Related Topics



Leave a reply



Submit