Draw line at center of circle with angle in objective-c - objective-c

I want to draw line at the center of circle with fixed size. Please check the image, it will explain everything. I have P1 center of the circle and P2 somewhere at the border of the circle. P1 will always remain fixed and P2 will keep changing with mouse move and it will be any point at circle's border. Now, I want to draw line with fixed length shown in green color. I want to find the (x1,y1) and (x2,y2) with fixed distance, every time P2 get changed, i want to draw green line facing towards P2.
Hope, image explains everything, in short i need following.
angle between P1 and P2.
Draw green line with fixed distance with angle at the center of the circle.
Thanks

Since you (apparently) want the green line to be perpendicular to the red line, you don't need to compute the angle between p1 and p2. Rotating a vector by 90˚ is quite trivial.
I assume you have p1 and p2 as CGPoint (or NSPoint) variables.
First let's compute the vector from p1 to p2:
CGPoint p1p2 = CGPointMake(p2.x - p1.x, p2.y - p1.y);
Next we'll compute a vector that is perpendicular to the p1->p2 vector:
CGPoint p1p2Perp = CGPointMake(p1p2.y, -p1p2.x);
Now let's find the length of the perpendicular vector:
CGFloat radius = hypot(p1p2Perp.x, p1p2Perp.y);
Note that if the radius of the circle is constant, you can just use that instead of computing it. If you're on iOS, use hypotf instead of hypot.
So now we can “normalize” the perpendicular vector, making it have length 1, by dividing its coordinates by its length:
p1p2Perp.x /= radius;
p1p2Perp.y /= radius;
Next we can multiply it by half of the desired length of the green line. I assume you have the desired length of the green line in a constant or variable named greenLineLength.
p1p2Perp.x *= greenLineLength / 2;
p1p2Perp.y *= greenLineLength / 2;
Now we can create a path for the green line. I assume your CGContextRef is in a variable named gc:
CGContextBeginPath(gc);
CGContextMoveToPoint(gc, p1.x - p1p2Perp.x, p1.y - p1p2Perp.y);
CGContextAddLineToPoint(gc, p1.x + p1p2Perp.x, p1.y + p1p2Perp.y);
You can stroke the path however you like. For example:
CGContextSetRGBStrokeColor(gc, 0, 1, 0, 1);
CGContextSetLineWidth(gc, 2);
CGContextSetLineCap(gc, kCGLineCapRound);
CGContextStrokePath(gc);

The angle between the two, according to your question, is always perpendicular -- 90˚ or π/2 radians. You can get the angle of the red segment using atan2(); then subtract M_PI_2 for the angle of the green one.
From there, you are trying to find two points on the circumference of a circle whose diameter is the length of the segment. You can think about this in polar coordinates (r, theta) and convert to Cartesian (x, y):
x = (segment_length / 2) * cos(theta)
y = (segment_length / 2) * sin(theta)
Add M_PI to theta to get the other endpoint.

Related

How can I implement degrees round the drawn compass

I am developing a GPS waypoint application. I have started by drawing my compass but am finding it difficult to implement degree text around the circle. Can anyone help me with a solution? The compass image am working on] 1 here shows the circle of the compass I have drawn.
This image here shows what I want to achieve, that is implementing degree text round the compass [Image of what I want to achieve] 2
Assuming you're doing this in a custom view, you need to use one of the drawText methods on the Canvas passed in to onDraw.
You'll have to do a little trigonometry to get the x, y position of the text - basically if there's a circle with radius r you're placing the text origins on (i.e. how far out from the centre they are), and you're placing one at angle θ:
x = r * cosθ
y = r * sinθ
The sin and cos functions take a value in radians, so you'll have to convert that if you're using degrees:
val radians = (degrees.toDouble() / 360.0) * (2.0 * Math.PI)
and 0 degrees is at 3 o'clock on the circle, not 12, so you'll have to subtract 90 degrees from your usual compass positions (e.g. 90 degrees on the compass is 0 degrees in the local coordinates). The negative values you get are fine, -90 is the same as 270. If you're trying to replicate the image you posted (where the numbers and everything else are rotating while the needle stays at the top) you'll have to apply an angle offset anyway!
These x and y values are distance from the centre of the circle, which probably needs to be the centre of your view (which you've probably already calculated to draw your circle). You'll also need to account for the extra space you need to draw those labels, scaling everything so it all fits in the View

Draw a line at a canvas

Currently I have this code:
sample.beginPath();
sample.moveTo(X1.x,Y1.x );
sample.lineTo(X2.x,Y2.y);
sample.stroke();
sample.beginPath();
sample.arc(X2.x, Y2.y, 4, 0, 2 * Math.PI, false);
sample.fill();
sample.lineWidth = 1;
sample.stroke();
This will create this :
This will point in any direction.
What I want is this:
Note:
1.There will only be one line, either Line A or B.
2.They will always point from left to right.
3. They are in 45 degrees.
Take a look at the following figure:
Point (x1,y1) is the starting point of the mouse. Assuming that mouse has moved to the right (you'll have to handle the case when it moves left), new mouse coordinates will be (x2,y2). However, we don't want to draw the line between (x1,y1) and (x2,y2), because the slop of this line (the angle) won't be the desired one. So we must calculate the coordinates of the new point P, that stands on our line. Note: I assumed that x-coordinate of this point will be equal to new mouse-x coordinate x2!
With this assumption and with help of some basic 2D geometry we get:
a = x2 - x1
tan(alpha) = b / a => b = a * tan(alpha)
P.x = x2
Value of the P.y coordinate depends on whether mouse has moved up or down from the start position.
IF (y1 > y2)
P.y = y1 - b // Mouse has moved up (drawing shows this scenario)
ELSE
P.y = y1 + b // Mouse has moved down (not shown in the drawing)
So we have a new point P, and now you can simply draw the line between (x1,y1) and P. You also have to handle some special case such as what if mouse moves to the left of starting point.
In order to get your point P, you should plug-in your desired angle as well (it can be different than 45 degrees, but it has to be a positive angle - you could derive formula that will work fine with negative angles as well).

Relation between horizontal, vertical and diagonal Field-of-View

Is there a mathematical relation between those values? If I know hFOV and vFOV can I calculate the diagonal FOV without involving other values like focal lengths etc?
My first thought was to use Pythagorean theorem but maybe it's wrong.
The physical quantities of interest are the sensor size and the focal length. The latter, in the pinhole camera model, is the the distance between the camera center and the image plane. Therefore, if you denote with f the focal length (in mm), W and H respectively the image sensor width and height (in mm), and assume the focal axis is orthogonal to the image plane, by simple trigonometry it is:
FOV_Horizontal = 2 * atan(W/2/f) = 2 * atan2(W/2, f) radians
FOV_Vertical = 2 * atan(H/2/f) = 2 * atan2(H/2, f) radians
FOV_Diagonal = 2 * atan2(sqrt(W^2 + H^2)/2, f) radians
Note that, if you have the sensor size and horizontal or vertical fov's, you can solve one of the first two equations for f and plug it into the third one to get the diagonal fov.
When, as is usual, the focal length is estimated through camera calibration, and is expressed in pixels, the above expressions need some adapting.
Denote with K the 3x3 camera matrix, with the camera frame having its origin at the camera center (focal point), X axis oriented left-to-right, Y axis top-to-bottom and Z axis toward the scene. Let Wp and Hp respectively be the width and height of the image in pixels.
In the simplest case the focal axis is orthogonal to the image plane (K12 = 0), the pixels are square (K11 = K22), and the principal point is at the image center (K13 = Wp/2; K23 = Hp/2). Then the same equations as above apply, replacing W with Wp, H with Hp and f with K11.
A lil more complex is the case just as above, but with the principal point off-center. Then one simply adds the two sides of each FOV angle. So, for example:
FOV_Horizontal = atan2(Wp/2 - K13, K11) + atan2(Wp/2 + K13, K11)
If the pixels are not square the same expressions apply for FOV_vertical, but using K22 and Hp, etc. The diagonal is a tad trickier, since you need to "convert" the image height into the same units as the width. Use the "pixel aspect ratio" PAR=K22/K11 for this purpose, so that:
FOV_Diagonal = 2 * atan2(sqrt(Wp^2 + (Hp/PAR)^2) / 2, K11)

Random movement using physics, CGPoint, vectors, a little confused on the physics/math side of things

I've managed to get Chipmunk physics and some other stuff to lay down a ball on my screen, and I can affect the gravity with some buttons / accelerometer. Yay me!
Next up, I'd like to turn off the gravity, and simulate a top-down view, where that ball moves around the screen of its own volition. I can apply forces to the ball using body -> f = cpv(dx, dy), but I'm not quite up on my physics and mathematics, so I'm trying to understand how the two values I feed it cause the movement.
I understand that positive values will move it right or down, and negative values will move it left or up, but that's about all I'm understanding at this point.
If I wanted to, say, pick a random compass bearing (0 - 359 degrees) and move it on that bearing, how would such a value translate into a vector?
I've created this method, but it's not working as expected and I'm unsure what I'm doing wrong:
- (CGPoint) getVectorFromAngle: (float) angle AndMagnitude: (float) magnitude
{
float x = magnitude * cos(angle);
float y = magnitude * sin(angle);
CGPoint point = CGPointMake(x, y);
NSLog(#"Made a CGPoint of X: %f and Y: %f.", point.x, point.y);
return point;
}
If I feed it an angle of 45 and a magnitude of 10, it creates X as 5.253220 and 8.509035. However, the calculator found here shows that it should create X and Y as 7.0711.
What do I have wrong here?
sin and cos take angles in radians, multiply your angles by π/180.
It's also good to point out that Chipmunk already contains a functions that do exactly what you want.
cpvmult(cpvforangle(radians), magnitude)

CGContext - "modulo" drawing?

Imagine I want to draw a custom view in a given rectangle (e.g. 100 x 100 pixels). My custom view's contents might be bigger than 100 x 100. Instead having some content not drawn, I'd like to draw all content inside the 100 x 100 area. For example, a point that would normally be located at (125, 140) would now be drawn at point (25, 40).
Is there any way to do this without having to (majorly) modify the drawing code? Keep in mind that I also draw more complex shapes, like bezier paths.
Perhaps you could scale your drawing space via CGContextScaleCTM(...).
e.x.
CGFloat sx, sy;
sx = self.frame.size.width / desiredWidth;
sy = self.frame.size.height / desiredHeight;
CGContextScaleCTM(context, sx, sy);
EDIT:
As Codo suggests below, you may be looking for CGContextTranslateCTM(...) which will offset your context's coordinate space by some x/y value.