Calculate the distance between two CGPoints - iphone

i need to calculate the distance between two CGPoints. I refered this and this, but I don't get it.

Well, with stuff your refering too where is the full code:
CGPoint p2; //[1]
CGPoint p1;
//Assign the coord of p2 and p1...
//End Assign...
CGFloat xDist = (p2.x - p1.x); //[2]
CGFloat yDist = (p2.y - p1.y); //[3]
CGFloat distance = sqrt((xDist * xDist) + (yDist * yDist)); //[4]
The distance is the variable distance.
What is going on here:
So first off we make two points...
Then we find the distance between x coordinates of the points.
Now we find the distance between the y coordinates.
These lengths are two sides of the triangle, infact they are the legs, time to find the hypotenuse which means after doing some math to rearragne c^2 = a^2 + b^2 we get the hypotenuse to equal sqrt((xDist^2) + (yDist^2)). xDist^2 = (xDist * xDist). And likewise: yDist^2 = (yDist * yDist)
You can't really make a CGPoint be the distance, distance doesn't have an x and y component. It is just 1 number.
If you think CGPoint is a unit of measurement (for example feet is a unit of measurement) it is not.

Short Answer
CGPoint p1, p2; // Having two points
CGFloat distance = hypotf((p1.x-p2.x), (p1.y-p2.y));
Longer Explination
If you have two points p1 and p2 it is obviously easy to find the difference between their height and width (e.g. ABS(p1.x - p2.x)) but to find a true representation of their distance you really want the hypothenuse (H below).
p1
|\
| \
| \ H
| \
| \
|_ _ _\
p2
Thankfully there is a built in macro for this: hypotf (or hypot for doubles):
// Returns the hypothenuse (the distance between p1 & p2)
CGFloat dist = hypotf((p1.x-p2.x), (p1.y-p2.y));
(original reference)

I've had to do this by hand 10,000 times so I wrote a function for it and stuck it in my personal library that I always dump in at the beginning of a new program so I forget it's not cannon.
- (float)distanceBetween:(CGPoint)p1 and:(CGPoint)p2
{
return sqrt(pow(p2.x-p1.x,2)+pow(p2.y-p1.y,2));
}
so you call it like this (say you want to know how far you moved your finger):
float moveDistance = [self distanceBetween:touchStart and:touchEnd];
This is useful in movement functions as well for spot checking in a scrolling menu:
if([self distanceBetween:touchStart and:touchAt] > 20*scalePoints)
isItATap = FALSE;
Set "isItATap" true in touchesBegan, put the above in touchesMoved, then you know the player moved their finger too far for the touch to be a "tap", so you can have it NOT select the object the player touched and instead scroll the object around.
As for scale, that should be based on whether or not you have retina display and what size of a device you're on (divide by 2 for retina display since a physical distance of 5 "points" on a regular screen as the user's finger feels it will come up as 10 "pixels" on a retina display screen, since each point is 4 pixels, so you'll wind up with a situation where the player has a very hard time tapping on retina display (which is a common oversight)

Sounds like you probably want the vector from p1 to p2 (or difference) rather than the distance.
const CGPoint p1 = {10, 10};
const CGPoint p2 = {510, 310};
const CGPoint diff = {p2.x - p1.x, p2.y - p1.y} // == (CGPoint){500, 300}

In Swift, you can add an extension to CGPoint:
extension CGPoint {
func distance(to point: CGPoint) -> CGFloat {
return sqrt(pow((point.x - x), 2) + pow((point.y - y), 2))
}
}
and use it like this:
let distance = p1.distance(to: p2)

In Apple's sample projects, they use hypot. This returns hypothenuse (distance) between two points as explained in this answer.
extension CGPoint {
func distance(from point: CGPoint) -> CGFloat {
return hypot(point.x - x, point.y - y)
}
}

only this...
float distance = ccpLength(ccpSub(p1,p2));
where p1 and p2 are objects of CGPoint

extension CGPoint {
func magnitude() -> CGFloat {
return sqrt(x * x + y * y)
}
func distance(to: CGPoint) -> CGFloat {
return CGPoint(x: to.x - x, y: to.y - y).magnitude()
}
}

Swift 4, Swift 3 solution
extension CGPoint {
static func distanceBetween(point p1: CGPoint,
andPoint p2: CGPoint) -> CGFloat {
return sqrt(pow((p2.x - p1.x), 2) + pow((p2.y - p1.y), 2))
}
}

Euclidean distance to another point with Vision api.
Starting from iOS 14.
import Vision
extension CGPoint {
public func distance(to point: CGPoint) -> Double {
VNPoint(location: self).distance(VNPoint(location: point))
}
}
print(CGPoint(x: 1, y: 1).distance(to: .zero)) // 1.4142135623730951

I extended above function to count distance between two CGRects. I count it by counting distance between coners of both CGRects and then returning the smallest distance. I copied function counting intersection point between two lines from:
func distanceBetweenRectangles(r1: CGRect, r2: CGRect) -> CGFloat { // returns distance between boundaries of two rectangles or -1 if they intersect
let cornerPointsR1: [CGPoint] = [
CGPoint(x: r1.minX, y: r1.minY),CGPoint(x: r1.minX, y: r1.maxY),CGPoint(x: r1.maxX, y: r1.minY),CGPoint(x: r1.maxX, y: r1.maxY)]
let cornerPointsR2: [CGPoint] = [
CGPoint(x: r2.minX, y: r2.minY),CGPoint(x: r2.minX, y: r2.maxY),CGPoint(x: r2.maxX, y: r2.minY),CGPoint(x: r2.maxX, y: r2.maxY)]
for i in 0..<cornerPointsR1.count {
if (r2.contains(cornerPointsR1[i])) {
return -1
}
}
var distances: [CGFloat] = []
for i in 0..<cornerPointsR1.count {
for j in 0..<cornerPointsR2.count {
distances.append(distanceBetweenPoints(p1: cornerPointsR1[i], p2: cornerPointsR2[j]))
}
}
distances.sort()
return distances[0]
}

Related

SWIFT Syntax question with regards to a CLOSURE

maybe one could be so kind as to explain me this snippet
There is this nice tutorial about Core Graphics on raywenderlich. Unfortunately, the comments on that page are closed
The author declares
//Weekly sample data
var graphPoints = [4, 2, 6, 4, 5, 8, 3]
Note the "s" at the end of graphPoints. Then, to calculate the y coordinate for a chart with such figures, he uses graphPoint (without an "s" at the end) within a closure. Nevertheless the code runs just fine to my confusion.
// calculate the y point
let topBorder = Constants.topBorder
let bottomBorder = Constants.bottomBorder
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints.max()!
let columnYPoint = { (graphPoint: Int) -> CGFloat in
let y = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
return graphHeight + topBorder - y // Flip the graph
}
And there is no further use of graphPoint in this project (that I am aware of, using "find"). So I wonder, how are graphPoints with an "s" linked to columnYPoint.
Though I currently have no idea how the y values flow into the closure, let me already extend my question: if my values are in a 2D array with the structure [[x1, x2], [y1, y2]], how would I pass only my y (or only my x) values into this closure?
Cheers!
UPDATE
This is how columnYPoint is used, afterwards, to draw the graph:
// draw the line graph
UIColor.white.setFill()
UIColor.white.setStroke()
// set up the points line
let graphPath = UIBezierPath()
// go to start of line
graphPath.move(to: CGPoint(x: columnXPoint(0), y: columnYPoint(graphPoints[0])))
// add points for each item in the graphPoints array
// at the correct (x, y) for the point
for i in 1..<graphPoints.count {
let nextPoint = CGPoint(x: columnXPoint(i), y: columnYPoint(graphPoints[i]))
graphPath.addLine(to: nextPoint)
}
graphPath.stroke()
As you have correctly identified, this is a closure (put into the variable called columnYPoint, giving it a name):
let columnYPoint = { (graphPoint: Int) -> CGFloat in
let y = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
return graphHeight + topBorder - y // Flip the graph
}
So really, it's like a function called columnYPoint:
func columnYPoint(_ graphPoint: Int) -> CGFloat {
let y = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
return graphHeight + topBorder - y // Flip the graph
}
Why did the author wrote a closure and put it into a variable, instead of writing a function? I have no idea, because I can't read minds. It's a stylistic choice by the author.
And if you look at how it is being called, this function/closure calculates the Y coordinate of the bar, given the height of the bar, graphPoint. graphPoint is the parameter of the function, so of course it is not used in the rest of the code. As you can see from the caller:
graphPath.move(to: CGPoint(x: columnXPoint(0), y: columnYPoint(graphPoints[0])))
// and
let nextPoint = CGPoint(x: columnXPoint(i), y: columnYPoint(graphPoints[i]))
columnYPoint will be called for each element in graphPoints, so graphPoint will be each value in graphPoints. We need to calculate the coordinates of every bar, after all.
There seems to also be a columnYPoint closure mentioned earlier, which calculates the X coordinate given a given bar index. You can combine these two closures to give you a single closure that gives you a single CGPoint:
let margin = Constants.margin
let graphWidth = width - margin * 2 - 4
let topBorder = Constants.topBorder
let bottomBorder = Constants.bottomBorder
let graphHeight = height - topBorder - bottomBorder
let maxValue = graphPoints.max()!
let columnPoint = { (column: Int, graphPoint: Int) -> CGPoint in
//Calculate the gap between points
let spacing = graphWidth / CGFloat(self.graphPoints.count - 1)
let x = CGFloat(column) * spacing + margin + 2
let y = CGFloat(graphPoint) / CGFloat(maxValue) * graphHeight
return CGPoint(x: x, y: graphHeight + topBorder - y) // Flip the graph
}

How to make UIBezierPath smooth in Swift

I am trying to create a line chart which represents a set of values (x and y) in a smooth bezier curve. This works fine, except when the x-values are close to each other and the y-values go from a continuous line to a lower or higher value. The values are not shown in the chart itself, but here is an image illustrating my problem:
As you can see, the line makes a backwards movement before continuing to the next point. I would like this to not happen and smoothen out. To generate the data points, I use this library from Minh Nguyen, which has helped me a lot. The only problem is this issue still. For easiness, here is the code I currently use:
private func controlPointsFrom(points: [CGPoint]) -> [CurvedSegment] {
var result: [CurvedSegment] = []
let delta: CGFloat = 0.3
for i in 1..<points.count {
let A = points[i-1]
let B = points[i]
let controlPoint1 = CGPoint(x: A.x + delta*(B.x-A.x), y: A.y + delta*(B.y - A.y))
let controlPoint2 = CGPoint(x: B.x - delta*(B.x-A.x), y: B.y - delta*(B.y - A.y))
let curvedSegment = CurvedSegment(controlPoint1: controlPoint1, controlPoint2: controlPoint2)
result.append(curvedSegment)
}
for i in 1..<points.count-1 {
let M = result[i-1].controlPoint2
let N = result[i].controlPoint1
let A = points[i]
let MM = CGPoint(x: 2 * A.x - M.x, y: 2 * A.y - M.y)
let NN = CGPoint(x: 2 * A.x - N.x, y: 2 * A.y - N.y)
result[i].controlPoint1 = CGPoint(x: (MM.x + N.x)/2, y: (MM.y + N.y)/2)
result[i-1].controlPoint2 = CGPoint(x: (NN.x + M.x)/2, y: (NN.y + M.y)/2)
}
return result
}
func createCurvedPath(_ dataPoints: [CGPoint]) -> UIBezierPath? {
let path = UIBezierPath()
path.move(to: dataPoints[0])
var curveSegments: [CurvedSegment] = []
let useDataPoints = dataPoints.filter { ($0.y < 1000) }
curveSegments = controlPointsFrom(points: useDataPoints)
for i in 1..<useDataPoints.count {
path.addCurve(to: useDataPoints[i], controlPoint1: curveSegments[i - 1].controlPoint1, controlPoint2: curveSegments[i - 1].controlPoint2)
}
return path
}
For documentation, I would refer to the tutorial/blogpost I linked earlier. I figure the issue should be somewhere in the calculation of controlPoint1 and controlPoint2 in the controlPointsFrom function. When I remove the delta or make it 0, it just become straight lines but then the issue doesn't occur either. So the math should be different I think, to keep track of the previous value and perhaps don't create a control point with a higher or lower y-value when the next point is lower or higher, respectively. But I am unable to figure out how to make it work. Any smart mind who can make this happen?
Would be forever grateful!
try this:
Smooth UIBezierPath
https://medium.com/#ramshandilya/draw-smooth-curves-through-a-set-of-points-in-ios-34f6d73c8f9

Identify the top left, top right, bottom left and bottom right position of a rectangle in Swift (elegant solution)

I'd like to find an elegant solution to identify the corners of a rectangle given a list of points (that I'm sure will define a rectangle).
Let's say we have this array of CGPoint:
var points:[CGPoint] = []
points.append(CGPoint(x:1, y:0)) //TL
points.append(CGPoint(x:3, y:0)) //TR
points.append(CGPoint(x:1, y:2)) //BL
points.append(CGPoint(x:3, y:2)) //BR
Which would be an elegant solution to understand that TopLeft corner is at index 0, Top Right at index 1... and so on?
I could cycle through the array multiple times and find it using a comparison... can you think at a better solution maybe using sort or filter ?
EDIT: Please note that the points array is unordered. I don't have a precise sequence of points.
These are Core Graphics structs, so ask Core Graphics to help you. Construct a path from any point through each of the other points in any order and ask for its bounding box. Now you have a CGRect whose corner points are your points, but now you know which is which, and matching them up to yours is trivial.
Example:
var points:[CGPoint] = []
points.append(CGPoint(x:1, y:0)) //TL
points.append(CGPoint(x:3, y:0)) //TR
points.append(CGPoint(x:1, y:2)) //BL
points.append(CGPoint(x:3, y:2)) //BR
let path = CGMutablePath()
path.move(to: points[0])
for ix in 1...3 {path.addLine(to: points[ix])}
let rect = path.boundingBox
The answer is CGRect(x:1.0, y:0.0, width:2.0, height:2.0) and now you know its minX, minY, maxX, and maxY and can easily match those up to your original points.
And you get the same result regardless of the order in which the points were supplied.
You can use map / reduce to achieve relatively simple syntax. Assuming:
var points: [CGPoint] = []
points.append(CGPoint(x: 3, y: 6))
points.append(CGPoint(x: 4, y: 6))
points.append(CGPoint(x: 4, y: 2))
points.append(CGPoint(x: 3, y: 2))
You can then:
let minX = points.map { $0.x } .reduce(points[0].x) { min($0,$1) }
At which point minX = 3. You could also use a sorted(by:)
let minX = points.sorted { $0.x < $1.x }.first!.x
Both have the advantages of working on any shape built from points. Matt also suggested using min() on the array, which looks like this:
let minX = points.min { $0.x < $1.x }!.x
I guess that's as clean as can be.
Oh, one more for the books... if you are going to be converting points to CGRect in a lot of places.. you can create yourself a CGRect extension:
extension CGRect {
init(from points: [CGPoint]) {
let xAxis = points.sorted { $0.x < $1.x }
let yAxis = points.sorted { $0.y < $1.y }
self.init(x: xAxis.first!.x, y: yAxis.first!.y, width: xAxis.last!.x - xAxis.first!.x, height: yAxis.last!.y - yAxis.first!.y)
}
}
Which you can then use with:
let rect = CGRect(from: points)
// rect.minX
Cheers!

Spritekit rotating multiple nodes around center

I would like to apply a rotation to multiple nodes (selected nodes in my game) using the UIRotationGesture, based on the center point of all those nodes. I can already rotate a single node simply changing it's zRotation.
The problem with multiple nodes is that it changes position and zRotation based on a center node, and I cannot seem to understand how to manage that.
I would like something like this:
What I have to rotate a single node is this:
During the rotation gesture
theRotation = CGFloat(sender.rotation) + self.offset
theRotation = theRotation * -1
node.rotation = theRotation
After the rotation gesture
self.offset = theRotation * -1
Would you have an idea on how set the correct position and angle for my nodes during the rotation?
What I tried:
I tried to add a node in the center (where the white dot is in my pictures, which represents the center) and change the parent of my nodes to be this one, then apply the zRotation on this node, and then replace the right parents. This did not work as I cannot seem to change a parent (my nodes disappear), this is another one of my Stack Questions.
I tried to change the anchor point of my nodes to fit the center point and than rotate them using theRotation. It did not work as I cannot seem to set the anchor point at the center position (that I have). I tried changing the coordinates system of the center's position to fit the node's one, but this is still not working. node.convertPoint(center, fromNode: Self) gives me coordinated like -58;-74 when it's about -1;-.5 (or something like that). I do not understand this.
So now I am thinking to calculate the position and rotation myself, as those did not work, but I would need an idea on how to calculate those as I am not very good with trigonometry/linear algebra, sadly enough.
Thank you for you help!
How I calculate my center:
var maxX = nodesSelected[0].position.x
var minX = nodesSelected[0].position.x
var maxY = nodesSelected[0].position.y
var minY = nodesSelected[0].position.y
for node in nodesSelected{
if node.position.x > maxX{
maxX = node.position.x
}
if node.position.x < minX{
minX = node.position.x
}
if node.position.y > maxY{
maxY = node.position.y
}
if node.position.y > maxY{
minY = node.position.y
}
}
return CGPoint(x: (maxX-minX)/2+minX, y: (maxY-minY)+minY/2)
How I calculate the radius of the rotation (distance between a node and the center):
extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
return abs(CGFloat(hypotf(Float(point.x - x), Float(point.y - y))))
}
How I get my rotation:
sender.rotation
Given a rotationAngle, you can calculate the new position of each node with the code below, you need to know a bit of trigonometry to understand the code.
Here I have an array of SKShapeNode that I called dots (It would be the equivalent of your green nodes in the image). And the centralDot would be your central SKSpriteNode.
for dot in dots {
let dx = dot.position.x - centralDot!.position.x // Get distance X from center
let dy = dot.position.y - centralDot!.position.y // Get distance Y from center
let current_angle = atan(dy / dx) // Current angle is the arctan of dy / dx
let next_angle = current_angle - rotationAngle // Sum how much you want to rotate in radians
// the new x is: center + radius*cos(x)
// the new y is: center + radius*sin(y)
// if dx < 0 you need to get the oposite value of the position
let new_x = dx >= 0 ? centralDot!.position.x + rotationRadius * cos(next_angle) : centralDot!.position.x - rotationRadius * cos(next_angle)
let new_y = dx >= 0 ? centralDot!.position.y + rotationRadius * sin(next_angle) : centralDot!.position.y - rotationRadius * sin(next_angle)
let new_point = CGPoint(x: new_x, y: new_y)
let action = SKAction.moveTo(new_point, duration: 0.2)
dot.runAction(action)
}
Hope this helps
Update:
The first code didn't helped, so I tried another one. This one worked better on my tests.
for i in 0..<dots.count {
let dot = dots[i]
let angle = rotationAngle + CGFloat(M_PI_2 * Double(i))
let new_x = rotationRadius * cos(angle) + centralDot!.position.x
let new_y = rotationRadius * sin(angle) + centralDot!.position.y
let new_point = CGPoint(x: new_x, y: new_y)
let action = SKAction.moveTo(new_point, duration: 1/60)
dot.runAction(action)
}
rotationRadius is a constant, the distance you want between the center and the green node.

make object move from 1 point to another at a custom speed

I made an app which there is an object that moves towards a moving point all the time - that is why I didn't use any animated function. The problem is that I made this function:
CGPoint center = self.im.center; // "i" is a CGPoint, im is an imageview.
if (!CGPointEqualToPoint(self.im.center, i))
{
a = (i.y-center.y)/(i.x-center.x);
//Y = a*X+b - this is a linear function in math
b = (center.y-(a*center.x));
if (i.y>center.y) {
self.im.center = CGPointMake(((center.y+1)-b)/a, center.y+1);
}
else
{
self.im.center = CGPointMake(((center.y-1)-b)/a, center.y-1);
}
}
The problem is that the closer the functions is becoming a straight horizontal line its faster because the change is mostly to the X axis which means that if I add 1 to Y the change to X is bigger which means it will move faster..
If there is another way to do this i will be glad to try it so if you know other ways tell me!
Managed to find a different solution
CGPoint center = self.im.center;//im = the image view
x = center.x;//starting point
y = center.y;//starting point
double distance = sqrtf(powf(i.x - x, 2) + powf(i.y - y, 2));// i = cgpoint (ending point)
float speedX = (2 * (i.x - x)) / distance;//(the 2 is the speed)
float speedY = (2 * (i.y - y)) / distance;//(the 2 is the speed)
self.im.center = CGPointMake(center.x+speedX, center.y+speedY);//im = the image view