I have a image view with positions ( x:138 and y:107 ) which isn't in the center of the screen. Now I wan't to calculate the angle between these points and the horizontal line but I don't know how to do this.
Can anyone tell me more about this?
You can do something like this, where Start- and endpoint is the images positions.
Example:
CGPoint endPoint = CGPointMake(50, 100);
CGPoint startPoint = CGPointMake(100, 100);
float angleVal = (((atan2((endPoint.x - startPoint.x) , (endPoint.y - startPoint.y)))*180)/M_PI);
Related
I been working on an iOS app which should display indoor blueprints. You should be able to switch between floors and each floor image is controlled by gesture recognisers to handle pan, rotate and scale.
I have been using this example for the gesture recognisers: https://github.com/GreenvilleCocoa/UIGestures/blob/master/UIGestures/RPSimultaneousViewController.m
So now to the problem. Whenever the user switch floor I want to keep the transformation of the image as well as the corresponding center lat/lng. However, the new image can have another rotation offset and aspect ratio.
I have been able to update the new frame of the image with the new size and update the transform with the new rotation offset and verified it. It is when I try to calculate the new center point I can not get it to work. The following code is how I currently do it and it works as long as the view is not rotated:
-(void)changeFromFloor:(int)oldFloorNr toFloor:(int)newFloorNr
{
CGPoint centerPoint = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
// This is the old non transformed center point.
CGPoint oldCenterOnImage = [self.layer convertPoint:centerPoint toLayer:self.mapOverlayView.layer]; // Actual non transformed point
// This point is verified to be the corresponding non transformed center point
CGPoint newCenterOnImage = [self calculateNewCenterFor:oldCenterOnImage fromFloor:oldFloorNr toFloor:newFloorNr];
// Change image, sets a new image and change the fram of mapOverlayView
[self changeImageFromFloor:oldFloorNr toFloor:newFloorNr]
// Adjust transformed rotation on map if new map have different rotation
[self adjustRotationFromFloorNr:oldFloorNr toFloorNr:newFloorNr];
CGPoint centerOfMapOverlay = CGPointMake((self.mapOverlayView.frame.size.width / 2), (self.mapOverlayView.frame.size.height / 2));
CGPoint newCenterOnImageTransformed = CGPointApplyAffineTransform(newCenterOnImage, self.mapOverlayView.transform);
CGFloat newCenterX = centerPoint.x + centerOfMapOverlay.x - newCenterOnImageTransformed.x;
CGFloat newCenterY = centerPoint.y + centerOfMapOverlay.y - newCenterOnImageTransformed.y;
// This only works without any rotation
self.mapOverlayView.center = CGPointMake(newCenterX, newCenterY);
}
Any idea where I go wrong? I have been working with this problem some days now and I can not seem to figure it out.
Please let me know if I need to add something or if something is unclear.
Thanks!
Code added after help was given:
CGPoint centerOfMapOverlay = CGPointMake(
(self.mapOverlayView.bounds.size.width / 2,
(self.mapOverlayView.bounds.size.height / 2)
);
centerOfMapOverlay = CGPointApplyAffineTransform(
centerOfMapOverlay,
self.mapOverlayView.transform
);
If you change the transform on it's view then the frame property becomes undefined. You should instead use the center property to change the view's position and bounds.size to change the view's size.
I'm trying to zoom to fit annotations on a map while locking the center and providing some insets.
- (void)fitAnnotations:(NSArray *)annotations edgeInsets:(UIEdgeInsets)insets
{
CLLocationCoordinate2D originalCenter = self.centerCoordinate;
MKMapRect mapRect = MKMapRectNull;
for (id<MKAnnotation> annotation in annotations) {
MKMapPoint p = MKMapPointForCoordinate([annotation coordinate]);
mapRect = MKMapRectUnion(mapRect, MKMapRectMake(p.x, p.y, 0, 0));
}
mapRect = [self mapRectThatFits:mapRect edgePadding:insets];
MKCoordinateRegion mapRegion = MKCoordinateRegionForMapRect(mapRect);
// we now try to go back to the original center, while increasing the span by neccessary amount
MKCoordinateSpan centeringDelta = MKCoordinateSpanMake(fabs(mapRegion.center.latitude - originalCenter.latitude), fabs(mapRegion.center.longitude - originalCenter.longitude));
mapRegion.center = originalCenter;
mapRegion.span.latitudeDelta += centeringDelta.latitudeDelta * 2.0;
mapRegion.span.longitudeDelta += centeringDelta.longitudeDelta * 2.0;
mapRegion = [self regionThatFits:mapRegion];
[self setRegion:mapRegion animated:YES];
}
The first part of the code here works as expected: it zooms to fit while respecting the insets. However, it shifts the center.
I try to re-adjust the center after that, but it fails. I'm not sure if my math on the re-centering is correct.
The first part of your code that calculates the bounding map rect that fits the annotations is OK.
Only the adjustment of that "minimal" map rect so that the "locked" center is actually in the center needs to be corrected.
The main problem, I believe, is that the code in the question is adjusting the span to both re-center the region and account for the insets after calling mapRectThatFits: (which will itself already give a slightly modified version of the rect you actually requested).
Instead, your code should only calculate the actual, minimal rect it wants and then finally call setVisibleMapRect:edgePadding:animated: and let the map view figure out both the "rect that fits" and the insets.
Please try the following:
- (void)fitAnnotations:(NSArray *)annotations edgeInsets:(UIEdgeInsets)insets
{
MKMapPoint centerMapPoint = MKMapPointForCoordinate(originalCenter);
//--- First create minimal bounding map rect to tightly fit annotations...
MKMapRect minimalMapRect = MKMapRectNull;
for (id<MKAnnotation> annotation in annotations) {
MKMapPoint annMapPoint = MKMapPointForCoordinate(annotation.coordinate);
minimalMapRect = MKMapRectUnion(minimalMapRect, MKMapRectMake(annMapPoint.x, annMapPoint.y, 0, 0));
}
//--- Now create adjusted map rect so center coordinate is in the center...
//distance of desired center from minimal left edge...
double centerXOffset = centerMapPoint.x - minimalMapRect.origin.x;
//raw amount width needs to be adjusted to get center in center...
//negative/positive indicates whether center is in left/right half
double widthOffset = 2.0 * centerXOffset - minimalMapRect.size.width;
//add absolute value of raw width offset to minimal width...
double adjustedWidth = minimalMapRect.size.width + fabs(widthOffset);
//distance of desired center from minimal top edge...
double centerYOffset = centerMapPoint.y - minimalMapRect.origin.y;
//raw amount height needs to be adjusted to get center in center...
//negative/positive indicates whether center is in top/bottom half
double heightOffset = 2.0 * centerYOffset - minimalMapRect.size.height;
//add absolute value of raw height offset to minimal height...
double adjustedHeight = minimalMapRect.size.height + fabs(heightOffset);
//adjust origin if necessary (if center is in top/left half)...
MKMapPoint adjustedOrigin = minimalMapRect.origin;
if ((centerXOffset / minimalMapRect.size.width) < 0.5)
{
adjustedOrigin.x = adjustedOrigin.x + widthOffset;
}
if ((centerYOffset / minimalMapRect.size.height) < 0.5)
{
adjustedOrigin.y = adjustedOrigin.y + heightOffset;
}
//create adjusted MKMapRect...
MKMapRect adjustedMapRect = MKMapRectMake(adjustedOrigin.x, adjustedOrigin.y, adjustedWidth, adjustedHeight);
//--- Apply the adjusted map rect with insets to map view...
[mapView setVisibleMapRect:adjustedMapRect edgePadding:insets animated:YES];
}
Try something like this, where you use your calculated mapRect to create the new region with your originalCenter via the MKCoordinateRegionMake method
MKCoordinateRegion mapRegion = MKCoordinateRegionForMapRect(mapRect);
mapRegion = MKCoordinateRegionMake(originalCenter, mapRegion.span);
mapView.region = mapRegion;
Try this.
MKMapPoint center = MKMapPointForCoordinate(self.mapView.centerCoordinate);
double maxX = 0;
double maxY = 0;
for (MKPointAnnotation *a in self.mapView.annotations)
{
MKMapPoint p = MKMapPointForCoordinate(a.coordinate);
double deltaX = fabs(center.x - p.x);
double deltaY = fabs(center.y - p.y);
maxX = MAX(maxX, deltaX);
maxY = MAX(maxY, deltaY);
}
MKMapRect rect = MKMapRectMake(center.x - maxX, center.y - maxY, maxX * 2, maxY * 2);
rect = [self.mapView mapRectThatFits:rect edgePadding:UIEdgeInsetsMake(20, 20, 20, 20)];
[self.mapView setVisibleMapRect:rect animated:1];
#moby I am thinking of a different approach. How about taking the maps centre location as you already did. Now calculate distance to each annotation from this centre coordinate till you find the longest annotation (say 'requiredDistance' ).
Get a new map rect with all your annotations plotted with same centre using below code:
MKCircle *circleLine = [MKCircle circleWithCenterCoordinate:self.centerCoordinate radius:requiredDistance];
[self.mapView setVisibleMapRect:circleLine.boundingMapRect];
Coming to your insets what ever insets you wanted to apply should be applied to your 'requiredDistace' variable in such a way that your 'requiredDistance' variable has a value always greater than or equal to the distance between your centre coordinate and your longest annotation to make sure all the annotations are always visible.
I'm making a photo hunt style app. I've got a number of X-Rays and I need to set specific areas of the uiimage to process touch events as correct and others as incorrect.
I understand that I can use the code below to get the tap location in the image view but how do I declare an area on the image view as correct and compare it to the tap location value?
CGPoint tapLocation = [gesture locationInView:self.imagePlateA];
Any help much appreciated!
So you have to programmatically create "regions" and test to see whether or not they're in that region after you get that point. For example:
//Get the tap location
CGPoint tapLocation = [gesture locationInView:self.imagePlateA];
if ([self checkIfTap:tapLocation inRegionWithCenter:CGPointMake(someX, someY) radius:radius]) {
//YAY WE'RE WITHIN THE BOUNDS OF A CIRCLE AT POINT (someX, someY)
//THAT HAS A RADIUS OF radius
}
and the method of checkIfTap: inRegionWithCenter: radius: can be defined like this:
- (BOOL)checkIfTap:(CGPoint)tapLocation inRegionWithCenter:(CGPoint)center radius:(CGFloat)radius {
CGFloat dx = tapLocation.x - center.x;
CGFloat dy = tapLocation.y - center.y;
//Pythagorean theorem
if (sqrt(dx * dx + dy * dy) < radius) {
return YES;
} else {
return NO;
}
}
If the correct locations of the image is a CGRect rather than points, you could use CGRectContainsPoint()
CGGeometry Reference
I'm working on an app that lets the user resize and rotate a photo using UIGestureRecognizers. I have this code which adjusts the anchorPoint based on where the user is applying touches (to make it look like they're scaling the image at the point where their fingers actually are):
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
UIView *gestureRecognizerView = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:gestureRecognizerView];
CGPoint locationInSuperview = [gestureRecognizer locationInView:gestureRecognizerView.superview];
gestureRecognizerView.layer.anchorPoint = CGPointMake(locationInView.x / gestureRecognizerView.bounds.size.width, locationInView.y / gestureRecognizerView.bounds.size.height);
gestureRecognizerView.center = locationInSuperview;
}
Later on, I'm simply wanting to calculate the origin based on the center and bounds with this code:
CGRect transformedBounds = CGRectApplyAffineTransform(view.bounds, view.transform);
CGPoint origin = CGPointMake(view.center.x - (transformedBounds.size.width * view.layer.anchorPoint.x), view.center.y - (transformedBounds.size.height * view.layer.anchorPoint.y));
And it's coming out incorrectly (I'm comparing against the frame value which ironically is supposed to be invalidated but actually does have the correct value).
So all in all I'm wondering, what am I not taking into account here? How is the anchorPoint influencing the center in a way I'm not able to determine?
I think the problem is that the origin you are calculating is not really an origin, but rather an offset of the origin of your transformedBounds rect.
I haven't fully tested it, but if you do something like this you should get the correct frame:
CGRect transformedBounds =
CGRectApplyAffineTransform(view.bounds, view.transform);
CGSize originOffset = CGSizeMake(
view.center.x - (transformedBounds.size.width * view.layer.anchorPoint.x),
view.center.y -
(transformedBounds.size.height * view.layer.anchorPoint.y));
transformedBounds.origin.x += originOffset.width;
transformedBounds.origin.y += originOffset.height;
In this wheel we have 6 pieces. The top of the wheel is the specific point. The specific point gives information of pieces. Now it gives information of the blue piece. So if i click on one of the piece for example purple, i need that purple piece to goes to the specific point and automatically go into the given information about the purple piece.
CGFloat topPositionAngle = radiansToDegrees(atan2(view.transform.a, view.transform.b));
-180 - pink
-120 - blue
-60 - orange
0 - purple
60 - yellow
120 - green
Now the topPositionAngle shows -120 = blue, when purple comes to the specific point it shows 0.
UITouch *touch = [touches anyObject];
CGPoint currentTouchPoint = [touch locationInView:view];
CGFloat currentAngle = radiansToDegrees(atan2(currentTouchPoint.x, currentTouchPoint.y));
CGFloat angleTransform = ???
CGAffineTransform current = view.transform;
[UIView animateWithDuration:0.2f animations:^{
[view setTransform:CGAffineTransformRotate(current, angleTransform)];
}];
How can we get the automatically rotation to the specific point? Just like Dansk Bank app (see the following youtube link) something similar to the video from 0:21 - 0:25 min.
http://www.youtube.com/watch?v=hulBh_KNGjE
float fromAngle = atan2(m_locationBegan.y-img.center.y, m_locationBegan.x-img.center.x);
float toAngle = atan2(_location.y-imgDigits.center.y, _location.x-img.center.x);
float newAngle = wrapd(m_currentAngle + (toAngle - fromAngle), 0, 2*3.14);
angle = newAngle;
CGAffineTransform transform = CGAffineTransformMakeRotation(newAngle);
img.transform = transform;
Use something like this. Hope this helps
Here u should know touch point and your specific point
You Need to Know touched Point Angle:
CGFloat touchedPointAngle = atan2f(touchPoint.y - centerSuper.y, touchPoint.x - centerSuper.x) + M_PI;
if ((touchedPointAngle < sliceInRadians) && (touchedPointAngle > 0)) {
angleTransform = sliceInRadians;
} else if ...
Then you can transform.