Calculating point coordinates from user tap with constraints - ios

I am trying to calculate the coordinates along a circle corresponding to the tap location. The coordinates should be on the border of the circle nearest to the tap location (e.g. the border that is less distant from the radius). To facilitate this I am detecting only taps that are distant by 80% of the radius from the circle center.
Input:
P (GPPoint) - center of the circle
P1 (GPPoint) current position of an image displayed
r (float) radius of circle
P3 (CGPoint) user tap coordinate
Desired output:
P2 (CGPoint) - new coordinates for the image corresponding to P3 but along the circle. Sorry for the bad explanation, I try to explain it in other words: once the user taps on the screen I would like to move the image in P2. P2 should be derived by moving P2 to the border of the circle. It should be possible to do so by using the radius information.
The idea is to create from P3 coordinates a new coordinate called P2 as described above - the key is that P2 distance from the centre should correspond exactly to the radius and the ANGLE should be the same as tapPoint.
Would anyome be able to suggest a formula to calculate the corresponding coordinate given a tap? I simply need to calculate P3 using the input I have.
Code so far:
-(void)tapInImageView:(UITapGestureRecognizer *)tap
{
CGPoint tapPoint = [tap locationInView:tap.view];
if ([self isInOuternCircle:tapPoint]) {
// then create from tapPoint coordinates a new coordinate P2 as described above - but have no idea how.. the key is that P2 distance from the centre should correspond exactly to the radius and the ANGLE should be the same as tapPoint.
}
}
-(BOOL)isInOuternCircle:(CGPoint)point
{
double distanceToCenter = sqrt((point.x - _timerView.center.x)*(point.x - _timerView.center.x) + (point.y - _timerView.center.y)*(point.y - _timerView.center.y));
if (distanceToCenter < _innerCircleRadius) {
return false;
}
return true;
}

I've done this once before, but the math usually depends on how you've set up your coordinate system, so I'll just outline what I did. You'll need a bit of geometry, and a few formulae to determine the new coordinate along the circle.
Calculate the formula of a line passing through the center (P) and your tap point (P3) using this: http://en.wikipedia.org/wiki/Linear_equation#Two-point_form
Determine the equation for your circle: http://en.wikipedia.org/wiki/Circle#Equations
Using the above two equations, you'll have a system of a linear and a quadratic equation: http://www.mathsisfun.com/algebra/systems-linear-quadratic-equations.html
Once you have the equation above, you need to solve it. The result will yield two possible points (the line will intersect the circle in two places), and the point you are looking for is the point closer the tap point. In this case, just compare the distances to P3 between the two solutions, and the shorter distance will show your required solution - P2.

Related

Get vector in SCNNode environment from touch location swift

I have the position and orientation of my camera, the CGPoint touch location on the screen, I need the line (preferably vector) in the direction that I touched on the screen in my 3d SCNNode environment, how can I get this?
A code snippet would be very helpful.
You can use the SCNSceneRenderer.unprojectPoint(_:) method for this.
This method, which is implemented by SCNView, takes the coordinates of your point as a SCNVector3. Set the first two elements in the coordinate space of your view. Apple describes the use of the third element:
The z-coordinate of the point parameter describes the depth at which to unproject the point relative to the near and far clipping planes of the renderer’s viewing frustum (defined by its
pointOfView
node). Unprojecting a point whose z-coordinate is 0.0 returns a point on the near clipping plane; unprojecting a point whose z-coordinate is 1.0 returns a point on the far clipping plane.
You are not looking for the location of these points, but for the line that connects them. Just subtract both to get the line.
func getDirection(for point: CGPoint, in view: SCNView) -> SCNVector3 {
let farPoint = view.unprojectPoint(SCNVector3Make(point.x, point.y, 1))
let nearPoint = view.unprojectPoint(SCNVector3Make(point.x, point.y, 0))
return SCNVector3Make(farPoint.x - nearPoint.x, farPoint.y - nearPoint.y, farPoint.z - nearPoint.z)
}

Determine if image being dragged is inside a specific area

I have a game where I'm moving square Blocks on a top layer overtop circles underneath, which are non-moveable. So when the dragging of a block ceases, I want to run a check or an if statement to see if the block I'm moving (myBlocks[objectDragging]) is within x amount of pixels of the center of my circle (myCircles[objectDragging]). objectDragging is just getting the tag of the image clicked. The matchable circle will have the same tag. Everything is working fine, I just cannot figure out how to check if the block I'm dropping (it's center point) is within so many pixels of the circles center point.
Some of what I'm working with:
var myBlocks = [UIImageView]()
var myCircles = [UIImageView]()
let objectDragging = recognizer.view?.tag
if myBlocks[objectDragging!].center.x == myCircles[objectDragging!].center.x {
...
} //this checks for an exact match of center.x where-as I want to check
//if the center.x for myBlocks[objectDragging!] is <= we'll say,
//25, pixels of the myCircles[objectDragging!].center.x
Discussion here to find distance between two CGPoints:
How to find the distance between two CG points?
per Lucius (answer 2)
You can use the hypot() or hypotf() function to calculate the
hypotenuse. Given two points p1 and p2:
CGFloat distance = hypotf(p1.x - p2.x, p1.y - p2.y);
sub in your myBlocks.center and myCircles.center for p1 and p2 and then
if distance < 25 {
...
}

Plot a CGPoint given origin, angle and distance

I am having trouble plotting a new CGPoint when given an origin, distance and angle. The task is pretty simple: I have a line with three edit handles attached to it - one on each end and one in the center. When the the end handles are dragged, the line is moved relative to the handle being dragged. That functionality is working properly. When the center handle is dragged, the two endpoint handles should maintain their relationship to one another as shown in the image below. So when dragging the center handle the two other handles should move with it.
Here is my current code to plot the points:
func pointFromPoint(origin:CGPoint, distance:Double, degrees:Double) -> CGPoint {
var endPoint = CGPoint()
endPoint.x = CGFloat(distance * cos(degrees) + Double(origin.x))
endPoint.y = CGFloat(distance * sin(degrees) + Double(origin.y))
return endPoint
}
When using this function, the new CGPoint locations seems to fall in random locations. Can anyone spot anything wrong in my math? Thanks!
Most trigonometry functions need radians, not degrees.
Also, as #Putz1103 pointed out, you can probably use the delta x and delta y of the UITouch of the movement instead of calculating the new position of the ends based on the center point's movement.

Make a line thicker in 3D?

In reference to this question
Drawing a line between two points using SceneKit
I'm drawing a line in 3D and want to make it thicker by using this code
func renderer(aRenderer: SCNSceneRenderer, willRenderScene scene: SCNScene, atTime time: NSTimeInterval) {
//Makes the lines thicker
glLineWidth(20)
}
but it doesn't work, iOS 8.2.
Is there another way?
Update
From the docs
https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNSceneRendererDelegate_Protocol/index.html#//apple_ref/occ/intfm/SCNSceneRendererDelegate/renderer:updateAtTime:
I did add SCNSceneRendererDelegate and a valid line width but still could not get the line width to increase.
You cannot assign any number to glLineWidth().
You can check the range of possible values of glLineWidth()by:
glGetFloatv(GL_LINE_WIDTH_RANGE,sizes);
One crazy idea is to use a cylinder for drawing lines ;). I use it when I want to have nice and controllable lines but I am not aware of a handy OpenGl function to do so.
#G Alexander: here you go my implementation of cylinder. It is a bit tedious but it is what I have at the moment.
If you give me points p0 and p1, Vector normal=(p1-p0).normalize() would be the axis of the cylinder.
pick point p2 that is not on the vector Normal.
q=(p2-p0).normalize();
normal.crossproduct(q)=v0;
normal.crossproduct(v0)=v1;
Having these two vectors you can have circles with any radius that are stacked along the axis of the cylinder using the following function (A cylinder is a stack of circles):
public Circle make_circle(Point center, Vector v0, Vector v1, double radius)
{
Circle c;
for (double i = 0; i < 2 * Math.PI; i += 0.05)
{
Point p = new Point(center + radius * Math.Cos(i) * v0 + radius * Math.Sin(i) * v1);
c.Add(p);
}
return c;
}
You only need to make circles using this function along the axis of the cylinder:
List<Circle> Cylinder = new List<Circle>();
for(double i=0;i<1;i+=0.1)
{
Cylinder.add( make_circle(P0+i*normal, v0, v1,radius);
}
Now you should take two consecutive circles and connect them with quads by sampling uniformly.
I have implemented it this way since I had circles implemented already.
A simpler way to implement is make the circle along the x axis and then rotate and translate it to p0 and make it align with normal or to use gluCylinder if you are the fan of Glu....
Hopefully it works for you.

Best practice for using lat/long within a UIView (not MKMapView)

Basically i have a list of POI's (name,lat,long) and i want to draw them on the UIView, relative to my current lat/long. I'm looking for some best practice for mapping these POI (lat/long) to a UIView.
I don't want to use MKMapView (no need for displaying map-data).
I was reading:
http://developer.apple.com/library/ios/#documentation/general/conceptual/Devpedia-CocoaApp/CoordinateSystem.html
But I'm clueless how i get from a CLLocation to a (x,y) on my UIView. I only want to draw those POI's around my current location. So, for example if my screen would represent a 20 by 30 KM region, how do i map my POI's to their corresponding (x,y) coordinates?
Thanks.
What you're doing is a little strange, but you can convert latitude/longitude to a CGPoint-like struct called an MKMapPoint. An MKMapPoint has an x and y value which correspond to points on a map. Imagine if you laid out a flat map of the world, and 0,0 was the top left. MKMapPoint is a point on that map using that coordinate system.
Use the function MKMapPointForCoordinate() to convert a CLLocationCoordinate2D to an MKMapPoint
MKMapPoint myMapPoint = MKMapPointForCoordinate(myLocationCoordinate);
When you get the list of points, you'll have to do something like finding the max and min x and y values, then fitting all the points into your view using those values, otherwise you'll end up with a load of very close points in one place in your view.
My guess is that, for a 20KM by 30KM region, you can consider the earth to be flat and there fore linearly extrapolate the coordinates. I am sure you can google and find out as to how much distance is a difference in 0.00001 in latitude and longitude.
So if you have 20Km to be represented on X axis, and your current location is 30.1234567 in latitude, and 0.0000001 is 1 km then you can put your coordinate in the center of the screen and 30.1234557 as the left most X coordinate and so on.
I am not trying to provide an answer here, but just trying to think out loud, because I wanted to do some thing similar as well and did it as an Internet based app (without display though), where given two coordinates, I had to find the distance between them.
There are many (many) different approaches to modelling the planet and translating 3D coordinates onto a 2D surface, and the errors introduced by the various methods vary depending on what part of the globe you are. This question seems to cover most of what you are after though:
Converting Longitude & Latitude to X Y on a map with Calibration points
I think its best way (correctly work for Mercator projection map):
extension UIView
{
func addLocation(coordinate: CLLocationCoordinate2D)
{
// max MKMapPoint values
let maxY = Double(267995781)
let maxX = Double(268435456)
let mapPoint = MKMapPointForCoordinate(coordinate)
let normalizatePointX = CGFloat(mapPoint.x / maxX)
let normalizatePointY = CGFloat(mapPoint.y / maxY)
let pointView = UIView(frame: CGRectMake(0, 0, 5, 5))
pointView.center = CGPointMake(normalizatePointX * frame.width, normalizatePointY * frame.height)
pointView.backgroundColor = UIColor.blueColor()
addSubview(pointView)
}
}
My simple project for adding coordinate on UIView: https://github.com/Glechik/MapCoordinateDrawer

Resources