Do you have to bridge to ObjectiveC for float in swift? - cocoa-touch

Is it really necessary to bridge to objective c like i'm doing for my Doubles below? Seems to work without this step in the playground but doesn't compile in a regular swift file.
Is there a cleaner way?
var prevX = 0.0
var prevY = 0.0
var count = 0
for item : AnyObject in myGraph.vertices {
count++
if let v = item as? Vertex {
x = Double(x)
y = Double(y)
x = x - width / 2.0
y = y - width / 2.0
//var fwidth = Float(width)
var rect = CGRect()
rect.origin.x = x.bridgeToObjectiveC().floatValue
rect.origin.y = y.bridgeToObjectiveC().floatValue
rect.size.width = width.bridgeToObjectiveC().floatValue
rect.size.height = rect.size.width

No, you can use a float constructor to do it
rect.origin.x = Float(x)
rect.origin.y = Float(y)
rect.size.width = Float(width)
rect.size.height = rect.size.width
N.B. I've opened a bug report with Apple about this. At the least, the behavior should match.

Related

Calculate an intercept velocity between two SKSpriteNodes

I created a "ship" node to move along the circular path as follows:
self.orbit = [OrbitManager getCirclePathWithCenter:centerRealPt radius:radius startingAngle:angelFromCenter isClockwise:self.isClockwise];
SKAction* orbitAction = [SKAction followPath:self.orbit asOffset:NO orientToPath:YES speed:300];
[self.ship.node runAction:orbitAction];
and I have a cannon which shoots a bullet by applying a velocity to it as follows:
bullet.node.physicsBody.velocity = [ActionHelper getVelocityFrom:bullet.node toNodeB:self.target speed:bullet.speed];
as the ship is moving along the path. But the bullet will miss every time. How can I calculate the position which the cannon should aim at, with a given speed?
This is my Objective-C (it is actually a C function) Solution to fire a projectile in to a moving target.
You can look at the derivations In this SO topic
This will give you a hit point and an angle to shoot,
you can simply translate it to velocity because you know the angle and a projectile speed, it will be something like:
`CGVector Velocity = CGVectorMake(speed * cos(theta), speed * sin(theta));`
BOOL calculateAngleToShoot(CGVector targetVelocity, CGPoint targetPosition, CGPoint selfPos,CGFloat projectileSpeed,CGFloat *theta, CGPoint * hitPoint)
{
CGFloat dx = targetPosition.x - selfPos.x;
CGFloat dy = targetPosition.y - selfPos.y;
CGVector v = targetVelocity;
CGFloat a = v.dx * v.dx + v.dy * v.dy - projectileSpeed * projectileSpeed;
CGFloat b = 2 * (v.dx * dx + v.dy * dy);
CGFloat c = v.dx * v.dx + v.dy * v.dy;
CGFloat q = b * b - 4 * a * c;
if (q < 0)
{
//Dead End;
return NO;
}
CGFloat t = ((a < 0 ? -1 : 1) * sqrt(q) - b) / (2 * a);
// Aim for where the target will be after time t
dx += t * v.dx;
dy += t * v.dy;
*theta = atan2(dy, dx);
*hitPoint = CGPointMake(targetPosition.x + v.dx * t, targetPosition.y + v.dy * t);
return YES;
}
After some investigation I got how to get the answer
first I need to get the distance(d) between the target and the center
and the time for the bullet from center to the target.
since the ship is moving along the circle, so the radius is also equals to distance(d)
CGFloat timeToArriveTarget = bullet.speed/distance;
CGFloat angularSpeed = bullet.speed/distance;
Find the angle moved within this period of time
CGFloat angle = angularSpeed * timeToArriveTarget;
CGFloat x = self.target.position.x;
CGFloat y = self.target.position.y;
CGFloat a = bullet.node.position.x;
CGFloat b = bullet.node.position.y;
and finally using this formula:
details are give by this link https://math.stackexchange.com/a/266837
CGPoint targetPt = CGPointMake((x - a) * cos(angle) - (y - b) * sin(angle) + a, (x - a) * sin(angle) + (y - b) * cos(angle) + b);
bullet.node.physicsBody.velocity = [ActionHelper getVelocityFrom:bullet.node.position toPtB:targetPt speed:bullet.speed];
the getVelocity function is given by
+(CGVector)getVelocityFrom:(CGPoint)ptA toPtB:(CGPoint)ptB speed:(CGFloat)speed{
CGPoint targetPosition = ptB;
CGPoint currentPosition = ptA;
double angle = [MathHelper getRotateAngleFrom:currentPosition toTargetPosition:targetPosition];
double velocityX = speed * cos(angle);
double velocityY = speed * sin(angle);
CGVector newVelocty = CGVectorMake(velocityX, velocityY);
return newVelocty;
}

Objective-C to Swift: NSScreen

I'm learning Swift. As a test, I'm translating some of my old Objective-C programs to swift. But I have a crazy error: In Objective-C I have the following code:
- (CGSize)makeSizeFromCentimetersWidth: (CGFloat)width andY: (CGFloat)height {
NSScreen *screen = [NSScreen mainScreen];
NSDictionary *description = [screen deviceDescription];
NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
CGSize displayPhysicalSize = CGDisplayScreenSize([[description objectForKey:#"NSScreenNumber"] unsignedIntValue]);
CGFloat resolution = (displayPixelSize.width / displayPhysicalSize.width) * 25.4f;
CGFloat pixelsWidth = 0.394 * width * resolution;
CGFloat pixelsHeight = 0.394 * height * resolution;
return CGSizeMake(pixelsWidth, pixelsHeight);
}
In swift I have translated to this:
func makeSizeFromCentimeters(width: CGFloat, height: CGFloat) -> CGSize {
var screen: NSScreen = NSScreen.mainScreen()!
var description: NSDictionary = screen.deviceDescription
var displayPixelSize: NSSize = description.objectForKey(NSDeviceSize)!.sizeValue
var displayPhysicalSize: CGSize = CGDisplayScreenSize(description.objectForKey("NSScreenNumber")!.unsignedIntValue)
var resolution = (displayPixelSize.width / displayPhysicalSize.width) * 25.4
var pixelsWidth: CGFloat = 0.394 * width * resolution
var pixelsHeight: CGFloat = 0.394 * height * resolution
return CGSizeMake(pixelsWidth, pixelsHeight)
}
In Objective-C the code does what it should: Calculate a size from centimeters to pixels, to give out (in my case) an NSImageView with exactly the size of the given centimeters. But in Swift, the returned size, is always 0:
NSLog("%f", makeSizeFromCentimeters(2, height: 2).width)
NSLog("%f", makeSizeFromCentimeters(2, height: 2).height)
Is there an translating error? Which variable is 0? (No idea why it should be 0 if it's not caused by a variable).
Thank you for your help!

How to calculate positions and randoms in scene. SpriteKit

So here is a code :
static inline CGFloat randomInRange(CGFloat low, CGFloat high) {
CGFloat value = arc4random_uniform(UINT32_MAX) / (CGFloat)UINT32_MAX;
return value * (high - low) +low;
}
static const CGFloat HALO_LOW_ANGLE = 200.0 * M_PI / 180;
static const CGFloat HALO_HIGH_ANGLE = 340.0 * M_PI / 180;
static const CGFloat HALO_SPEED = 100.0;
-(void) spawnHalo {
SKSpriteNode *halo = [SKSpriteNode spriteNodeWithImageNamed:#"Halo"];
halo.position = CGPointMake(randomInRange(halo.size.width / 2, self.size.width - (halo.size.width / 2)), self.size.height + (halo.size.height / 2));
halo.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:16];
CGVector direction = radiansToVector(randomInRange(HALO_LOW_ANGLE, HALO_HIGH_ANGLE));
halo.physicsBody.velocity = CGVectorMake(direction.dx * HALO_SPEED, direction.dy * HALO_SPEED);
halo.physicsBody.restitution = 1.0;
halo.physicsBody.linearDamping = 0.0;
halo.physicsBody.friction = 0.0;
[self.mainLayer addChild:halo];
I get that first we wanted to get a random value between 0-1. but what i really don't understand is how to calculate the coordinates.
What if i want to spawn the sprite from anywhere, make it the right,left or the bottom of the scene. How do i actually calculate that ?
This should do the trick:
CGPoint randomPosition = CGPointMake(arc4random() % (int)CGRectGetWidth(yourSceneInstance.frame),
arc4random() % (int)CGRectGetHeight(yourSceneInstance.frame));

Testing intersection of rotated CGRects [duplicate]

I don't know where to start with this one. Obviously CGRectIntersectsRect will not work in this case, and you'll see why.
I have a subclass of UIView that has a UIImageView inside it that is placed in the exact center of the UIView:
I then rotate the custom UIView to maintain the frame of the inner UIImageView while still being able to perform a CGAffineRotation. The resulting frame looks something like this:
I need to prevent users from making these UIImageViews intersect, but I have no idea how to check intersection between the two UIImageViews, since not only do their frames not apply to the parent UIView, but also, they are rotated without it affecting their frames.
The only results from my attempts have been unsuccessful.
Any ideas?
The following algorithm can be used to check if two (rotated or otherwise transformed) views overlap:
Use [view convertPoint:point toView:nil] to convert the 4 boundary points of both views
to a common coordinate system (the window coordinates).
The converted points form two convex quadrilaterals.
Use the SAT (Separating Axis Theorem) to check if the quadrilaterals intersect.
This: http://www.geometrictools.com/Documentation/MethodOfSeparatingAxes.pdf is another description of the algorithm containing pseudo-code, more can be found by googling for "Separating Axis Theorem".
Update: I have tried to create a Objective-C method for the "Separating Axis Theorem", and this is what I got. Up to now, I did only a few tests, so I hope that there are not too many errors.
- (BOOL)convexPolygon:(CGPoint *)poly1 count:(int)count1 intersectsWith:(CGPoint *)poly2 count:(int)count2;
tests if 2 convex polygons intersect. Both polygons are given as a CGPoint array of the vertices.
- (BOOL)view:(UIView *)view1 intersectsWith:(UIView *)view2
tests (as described above) if two arbitrary views intersect.
Implementation:
- (void)projectionOfPolygon:(CGPoint *)poly count:(int)count onto:(CGPoint)perp min:(CGFloat *)minp max:(CGFloat *)maxp
{
CGFloat minproj = MAXFLOAT;
CGFloat maxproj = -MAXFLOAT;
for (int j = 0; j < count; j++) {
CGFloat proj = poly[j].x * perp.x + poly[j].y * perp.y;
if (proj > maxproj)
maxproj = proj;
if (proj < minproj)
minproj = proj;
}
*minp = minproj;
*maxp = maxproj;
}
-(BOOL)convexPolygon:(CGPoint *)poly1 count:(int)count1 intersectsWith:(CGPoint *)poly2 count:(int)count2
{
for (int i = 0; i < count1; i++) {
// Perpendicular vector for one edge of poly1:
CGPoint p1 = poly1[i];
CGPoint p2 = poly1[(i+1) % count1];
CGPoint perp = CGPointMake(- (p2.y - p1.y), p2.x - p1.x);
// Projection intervals of poly1, poly2 onto perpendicular vector:
CGFloat minp1, maxp1, minp2, maxp2;
[self projectionOfPolygon:poly1 count:count1 onto:perp min:&minp1 max:&maxp1];
[self projectionOfPolygon:poly2 count:count1 onto:perp min:&minp2 max:&maxp2];
// If projections do not overlap then we have a "separating axis"
// which means that the polygons do not intersect:
if (maxp1 < minp2 || maxp2 < minp1)
return NO;
}
// And now the other way around with edges from poly2:
for (int i = 0; i < count2; i++) {
CGPoint p1 = poly2[i];
CGPoint p2 = poly2[(i+1) % count2];
CGPoint perp = CGPointMake(- (p2.y - p1.y), p2.x - p1.x);
CGFloat minp1, maxp1, minp2, maxp2;
[self projectionOfPolygon:poly1 count:count1 onto:perp min:&minp1 max:&maxp1];
[self projectionOfPolygon:poly2 count:count1 onto:perp min:&minp2 max:&maxp2];
if (maxp1 < minp2 || maxp2 < minp1)
return NO;
}
// No separating axis found, then the polygons must intersect:
return YES;
}
- (BOOL)view:(UIView *)view1 intersectsWith:(UIView *)view2
{
CGPoint poly1[4];
CGRect bounds1 = view1.bounds;
poly1[0] = [view1 convertPoint:bounds1.origin toView:nil];
poly1[1] = [view1 convertPoint:CGPointMake(bounds1.origin.x + bounds1.size.width, bounds1.origin.y) toView:nil];
poly1[2] = [view1 convertPoint:CGPointMake(bounds1.origin.x + bounds1.size.width, bounds1.origin.y + bounds1.size.height) toView:nil];
poly1[3] = [view1 convertPoint:CGPointMake(bounds1.origin.x, bounds1.origin.y + bounds1.size.height) toView:nil];
CGPoint poly2[4];
CGRect bounds2 = view2.bounds;
poly2[0] = [view2 convertPoint:bounds2.origin toView:nil];
poly2[1] = [view2 convertPoint:CGPointMake(bounds2.origin.x + bounds2.size.width, bounds2.origin.y) toView:nil];
poly2[2] = [view2 convertPoint:CGPointMake(bounds2.origin.x + bounds2.size.width, bounds2.origin.y + bounds2.size.height) toView:nil];
poly2[3] = [view2 convertPoint:CGPointMake(bounds2.origin.x, bounds2.origin.y + bounds2.size.height) toView:nil];
return [self convexPolygon:poly1 count:4 intersectsWith:poly2 count:4];
}
Swift version. (Added this behaviour to UIView via an extension)
extension UIView {
func projection(of polygon: [CGPoint], perpendicularVector: CGPoint) -> (CGFloat, CGFloat) {
var minproj = CGFloat.greatestFiniteMagnitude
var maxproj = -CGFloat.greatestFiniteMagnitude
for j in 0..<polygon.count {
let proj = polygon[j].x * perpendicularVector.x + polygon[j].y * perpendicularVector.y
if proj > maxproj {
maxproj = proj
}
if proj < minproj {
minproj = proj
}
}
return (minproj, maxproj)
}
func convex(polygon: [CGPoint], intersectsWith polygon2: [CGPoint]) -> Bool {
//
let count1 = polygon.count
for i in 0..<count1 {
let p1 = polygon[i]
let p2 = polygon[(i+1) % count1]
let perpendicularVector = CGPoint(x: -(p2.y - p1.y), y: p2.x - p1.x)
let m1 = projection(of: polygon, perpendicularVector: perpendicularVector)
let minp1 = m1.0
let maxp1 = m1.1
let m2 = projection(of: polygon2, perpendicularVector: perpendicularVector)
let minp2 = m2.0
let maxp2 = m2.1
if maxp1 < minp2 || maxp2 < minp1 {
return false
}
}
//
let count2 = polygon2.count
for i in 0..<count2 {
let p1 = polygon2[i]
let p2 = polygon2[(i+1) % count2]
let perpendicularVector = CGPoint(x: -(p2.y - p1.y), y: p2.x - p1.x)
let m1 = projection(of: polygon, perpendicularVector: perpendicularVector)
let minp1 = m1.0
let maxp1 = m1.1
let m2 = projection(of: polygon2, perpendicularVector: perpendicularVector)
let minp2 = m2.0
let maxp2 = m1.0
if maxp1 < minp2 || maxp2 < minp1 {
return false
}
}
//
return true
}
func intersects(with someView: UIView) -> Bool {
//
var points1 = [CGPoint]()
let bounds1 = bounds
let p11 = convert(bounds1.origin, to: nil)
let p21 = convert(CGPoint(x: bounds1.origin.x + bounds1.size.width, y: bounds1.origin.y), to: nil)
let p31 = convert(CGPoint(x: bounds1.origin.x + bounds1.size.width, y: bounds1.origin.y + bounds1.size.height) , to: nil)
let p41 = convert(CGPoint(x: bounds1.origin.x, y: bounds1.origin.y + bounds1.size.height), to: nil)
points1.append(p11)
points1.append(p21)
points1.append(p31)
points1.append(p41)
//
var points2 = [CGPoint]()
let bounds2 = someView.bounds
let p12 = someView.convert(bounds2.origin, to: nil)
let p22 = someView.convert(CGPoint(x: bounds2.origin.x + bounds2.size.width, y: bounds2.origin.y), to: nil)
let p32 = someView.convert(CGPoint(x: bounds2.origin.x + bounds2.size.width, y: bounds2.origin.y + bounds2.size.height) , to: nil)
let p42 = someView.convert(CGPoint(x: bounds2.origin.x, y: bounds2.origin.y + bounds2.size.height), to: nil)
points2.append(p12)
points2.append(p22)
points2.append(p32)
points2.append(p42)
//
return convex(polygon: points1, intersectsWith: points2)
}

help to calculate atan2 properly

I need to calculate the angle between lines. I need to calculate atan. So I am using such code
static inline CGFloat angleBetweenLinesInRadians2(CGPoint line1Start, CGPoint line1End)
{
CGFloat dx = 0, dy = 0;
dx = line1End.x - line1Start.x;
dy = line1End.y - line1Start.y;
NSLog(#"\ndx = %f\ndy = %f", dx, dy);
CGFloat rads = fabs(atan2(dy, dx));
return rads;
}
But I can't get over 180 degrees(( After 179 deg going 178..160..150 and so on.
I need to rotate on 360 degrees. How can I do it? What's wrong?
maby this helps:
//Tells the receiver when one or more fingers associated with an event move within a view or window.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *Touches = [touches allObjects];
UITouch *first = [Touches objectAtIndex:0];
CGPoint b = [first previousLocationInView:[self imgView]]; //prewious position
CGPoint c = [first locationInView:[self imgView]]; //current position
CGFloat rad1 = angleBetweenLinesInRadians2(center, b); //first angel
CGFloat rad2 = angleBetweenLinesInRadians2(center, c); //second angel
CGFloat radAngle = fabs(rad2 - rad1); //angel between two lines
if (tempCount <= gradus)
{
[imgView setTransform: CGAffineTransformRotate([imgView transform], radAngle)];
tempCount += radAngle;
}
}
atan2 returns results in [-180,180] (or -pi, pi in radians). To get results from 0,360 use:
float radians = atan2(dy, dx);
if (radians < 0) {
radians += M_PI*2.0f;
}
It should be noted that it is typical to express rotations in [-pi,pi] and thusly you can just use the result of atan2 without worrying about the sign.
Remove the fabs call and simply make it:
CGFloat rads = atan2(dy, dx);
Use this function in Swift. This makes sure the angle from "fromPoint" to "toPoint" lands between 0 to <360 (not including 360). Please note, the following function assumes that CGPointZero is at top left corner.
func getAngle(fromPoint: CGPoint, toPoint: CGPoint) -> CGFloat {
let dx: CGFloat = fromPoint.x - toPoint.x
let dy: CGFloat = fromPoint.y - toPoint.y
let twoPi: CGFloat = 2 * CGFloat(M_PI)
let radians: CGFloat = (atan2(dy, -dx) + twoPi) % twoPi
return radians * 360 / twoPi
}
For the case where the origin is at the bottom left corner
let twoPi = 2 * Float(M_PI)
let radians = (atan2(-dy, -dx) + twoPi) % twoPi
let angle = radians * 360 / twoPi
Your problem is that the result of atan2 is between -180 and +180 degrees.
If you want it to be between 0 and 360 then move the result to sure be a positive value, and then do a modulo. For example:
let angle = fmod(atan2(dx,dy) + .pi * 2, .pi * 2)