#Anton I stucked this flipping.
I downloaded the project from here.
I have four buttons each button set the title as right,left,bottom,top to the str variable.
In rendering the initial rotation axis is teapotNode_.rotationAxis = CC3VectorMake(0.1, 1, 0.3);
- (void)update:(float)dt
{
if ([str isEqualToString:#"right"])
{
teapotNode_.rotationAxis = CC3VectorMake(0.1, 1, 0.3);
angle +=2.0;
}
else if ([str isEqualToString:#"left"])
{
teapotNode_.rotationAxis = CC3VectorMake(0.1, 1, 0.3);
angle -=2.0;
}
else if ([str isEqualToString:#"bottom"])
{
teapotNode_.rotationAxis = CC3VectorMake(1,0,0);
angle +=2.0;
}
else if ([str isEqualToString:#"top"])
{
teapotNode_.rotationAxis = CC3VectorMake(1,0,0);
angle -=2.0;
}
else{
angle +=2.0;
}
teapotNode_.rotationAngle = angle;
}
I touch the top(or)down rotation button change the axis like
teapotNode_.rotationAxis = CC3VectorMake(1,0,0); object flipped.
I want the same axis position to rotate the object top to bottom and bottom to top.
The solution is:
You itself says the rotation axis value as (0.1,1,0.3) initial one. so don't change that value.
This project itself having separate function for rotation. that is teapotNode_.rotation
Use like this. Its working fine for me. Try this.
Globally declared:
float x=0.0;
float y=0.0;
float z=0.0;
float angle;
float rangle=2.0;
float xangle;
float yangle;
- (void)update:(float)dt
{
if ([str isEqualToString:#"right"])
{
x=0; y=1; z=0; //ROTATE OBJECT IN Y-AXIS(Y=1 & X=0)
yAngle+=rAngle; //GET ANGLE OF OBJECT ROTATION IN Y-AXIS (ANTI CLOCKWISE)
angle +=rAngle; //INCREMENT ANGLE OF OBJECT ROTATION IN ANTICLOCLWISE
}
else if ([str isEqualToString:#"left"])
{
x=0; y=1; z=0; //ROTATE OBJECT IN Y-AXIS(Y=1 & X=0)
yAngle-=rAngle; //GET ANGLE OF OBJECT ROTATION IN Y-AXIS (CLOCLWISE)
angle -= rAngle; //INC ANGLE OF OBJECT ROTATION IN CLOCKWISE
}
else if ([str isEqualToString:#"bottom"])
{
x=1; y=0; z=0; //ROTATE OBJECT IN X-AXIS(X=1 & Y=0)
xAngle+=rAngle; //GET ANGLE OF OBJECT ROTATION IN X-AXIS (ANTI CLOCKWISE)
angle += rAngle; //INC ANGLE OF OBJECT ROTATION
}
else if ([str isEqualToString:#"top"])
{
x=1; y=0; z=0 ; //ROTATE OBJECT IN Y-AXIS(X=1 & Y=0)
xAngle-=rAngle; //GET ANGLE OF OBJECT ROTATION IN X-AXIS (CLOCLWISE)
angle -= rAngle; //INC ANGLE OF OBJECT ROTATION
}
else
{
angle +=rAngle;
}
// teapotNode_.rotationAxis = CC3VectorMake(x, y, z);
// teapotNode_.rotationAngle = angle;
teapotNode_.rotation = CC3VectorMake(xAngle, yAngle, 0);
}
Related
I have a problem with My code , I need to handle pan or pinch on screen.
but sometimes the handlePanl method call when My two finger on screen.
What Am I doing wrong ?
-(void)addGestureRecognizer
{
// handle Pinch
imageViewForCrop.userInteractionEnabled = YES;
UIPinchGestureRecognizer *PinchOnScreen = [[UIPinchGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePinch:)];
[imageViewForCrop addGestureRecognizer:PinchOnScreen];
// handle Pan
UIPanGestureRecognizer *panOnScreen = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[imageViewForCrop addGestureRecognizer:panOnScreen];
}
This is Pan method :
-(void)handlePan:(UIPanGestureRecognizer *)recognizer
{
switch (recognizer.state)
{
case UIGestureRecognizerStateChanged:
{
NSLog(#"%lu",(unsigned long)recognizer.numberOfTouches);
CGPoint translation = [recognizer translationInView:imageViewForCrop];
if (image.size.width > image.size.height) // weight image
{
// after user pinch in the screen
if (imageViewForCrop.frame.size.height > self.view.frame.size.height - bottomImageCropView.frame.size.height)
{
// allow dragging only in Y coordinates by only updating the Y coordenates with translation position
recognizer.view.center = CGPointMake(recognizer.view.center.x, recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:imageViewForCrop];
// get the top edge coordinate for the top left corner of crop frame
float topEdgeImagePosition = CGRectGetMinY(imageViewForCrop.frame);
float topEdgeCropEreaPosition = CGRectGetMinY(cropErea.frame);
//get the buttom edge coordinate for bottom left corner of crop frame
float bottomEdgeImagePosition = CGRectGetMaxY(imageViewForCrop.frame);
float bottomEdgeCropEreaPosition = CGRectGetMaxY(cropErea.frame);
//
// if the top edge coordinate is less than or equal to Crop Erea frame
if (topEdgeImagePosition > topEdgeCropEreaPosition)
{
// draw drag view in max top position
imageViewForCrop.frame = CGRectMake(imageViewForCrop.frame.origin.x,topEdgeCropEreaPosition,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
// if bottom edge coordinate is greater than or equal to 480
if (bottomEdgeImagePosition < bottomEdgeCropEreaPosition)
{
float ratio = (imageViewForCrop.frame.size.height - cropErea.frame.size.height) * -1;
// draw drag view in max bottom position
imageViewForCrop.frame = CGRectMake(imageViewForCrop.frame.origin.x,ratio,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
}
// allow dragging only in X coordinates by only updating the X coordenates with translation position
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:imageViewForCrop];
// get the top edge coordinate for the top left corner of crop frame
float leftEdgeImagePosition = CGRectGetMinX(imageViewForCrop.frame);
float leftEdgeCropEreaPosition = CGRectGetMinX(cropErea.frame);
//get the buttom edge coordinate for bottom left corner of crop frame
float rightEdgeImagePosition = CGRectGetMaxX(imageViewForCrop.frame);
float rightEdgeCropEreaPosition = CGRectGetMaxX(cropErea.frame);
//
// if the top edge coordinate is less than or equal to Crop Erea frame
if (leftEdgeImagePosition > leftEdgeCropEreaPosition)
{
// draw drag view in max top position
imageViewForCrop.frame = CGRectMake(leftEdgeCropEreaPosition,0,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
// if bottom edge coordinate is greater than or equal to 480
if (rightEdgeImagePosition < rightEdgeCropEreaPosition)
{
float ratio = (imageViewForCrop.frame.size.width - cropErea.frame.size.width) * -1;
// draw drag view in max bottom position
imageViewForCrop.frame = CGRectMake(ratio,0,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
}
else if (image.size.width < image.size.height) // Height image
{
// after user pinch in the screen
if (imageViewForCrop.frame.size.width > self.view.frame.size.width)
{
// allow dragging only in X coordinates by only updating the X coordenates with translation position
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:imageViewForCrop];
// get the top edge coordinate for the top left corner of crop frame
float leftEdgeImagePosition = CGRectGetMinX(imageViewForCrop.frame);
float leftEdgeCropEreaPosition = CGRectGetMinX(cropErea.frame);
//get the buttom edge coordinate for bottom left corner of crop frame
float rightEdgeImagePosition = CGRectGetMaxX(imageViewForCrop.frame);
float rightEdgeCropEreaPosition = CGRectGetMaxX(cropErea.frame);
//
// if the top edge coordinate is less than or equal to Crop Erea frame
if (leftEdgeImagePosition > leftEdgeCropEreaPosition)
{
// draw drag view in max top position
imageViewForCrop.frame = CGRectMake(leftEdgeCropEreaPosition,imageViewForCrop.frame.origin.y,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
// if bottom edge coordinate is greater than or equal to 480
if (rightEdgeImagePosition < rightEdgeCropEreaPosition)
{
float ratio = (imageViewForCrop.frame.size.width - cropErea.frame.size.width) * -1;
// draw drag view in max bottom position
imageViewForCrop.frame = CGRectMake(ratio,imageViewForCrop.frame.origin.y,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
}
// allow dragging only in Y coordinates by only updating the Y coordenates with translation position
recognizer.view.center = CGPointMake(recognizer.view.center.x, recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:imageViewForCrop];
// get the top edge coordinate for the top left corner of crop frame
float topEdgeImagePosition = CGRectGetMinY(imageViewForCrop.frame);
float topEdgeCropEreaPosition = CGRectGetMinY(cropErea.frame);
//get the buttom edge coordinate for bottom left corner of crop frame
float bottomEdgeImagePosition = CGRectGetMaxY(imageViewForCrop.frame);
float bottomEdgeCropEreaPosition = CGRectGetMaxY(cropErea.frame);
//
// if the top edge coordinate is less than or equal to Crop Erea frame
if (topEdgeImagePosition > topEdgeCropEreaPosition)
{
// draw drag view in max top position
imageViewForCrop.frame = CGRectMake(0,topEdgeCropEreaPosition,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
// if bottom edge coordinate is greater than or equal to 480
if (bottomEdgeImagePosition < bottomEdgeCropEreaPosition)
{
float ratio = (imageViewForCrop.frame.size.height - cropErea.frame.size.height) * -1;
// draw drag view in max bottom position
imageViewForCrop.frame = CGRectMake(0,ratio,imageViewForCrop.frame.size.width,imageViewForCrop.frame.size.height);
}
}
else
{
imageViewForCrop.frame = CGRectMake(cropErea.frame.origin.x, cropErea.frame.origin.y, 320, 320);
}
}
default:
break;
}
}
This is pinch method :
- (void)handlePinch:(UIPinchGestureRecognizer *)pinchGestureRecognizer
{
NSLog(#" X %i " ,(int)imageViewForCrop.frame.origin.x );
if ((int)imageViewForCrop.frame.origin.x > 0 )
{
[UIView animateWithDuration:0.4 animations:^
{
[imageViewForCrop setFrame:CGRectMake( 0 , imageViewForCrop.frame.origin.y , imageViewForCrop.frame.size.width , imageViewForCrop.frame.size.height )];
}];
}
NSLog(#" Y %i " , (int)imageViewForCrop.frame.origin.y );
if ((int)imageViewForCrop.frame.origin.y > 0)
{
[UIView animateWithDuration:0.4 animations:^
{
[imageViewForCrop setFrame:CGRectMake( imageViewForCrop.frame.origin.x , 0 , imageViewForCrop.frame.size.width , imageViewForCrop.frame.size.height )];
}];
}
NSLog(#" Width %i and %i " , (int)imageViewForCrop.frame.size.width , (int)originalSizeForIphoneWidth );
if ((int)imageViewForCrop.frame.size.width < (int)originalSizeForIphoneWidth)
{
[UIView animateWithDuration:0.4 animations:^
{
[imageViewForCrop setFrame:CGRectMake( imageViewForCrop.frame.origin.x , imageViewForCrop.frame.origin.y , originalSizeForIphoneWidth+1, imageViewForCrop.frame.size.height)];
}];
}
NSLog(#"Hight %i and %i " , (int)imageViewForCrop.frame.size.height , (int)originalSizeForIphoneHight );
if ((int)imageViewForCrop.frame.size.height < (int)originalSizeForIphoneHight)
{
[UIView animateWithDuration:0.4 animations:^
{
[imageViewForCrop setFrame:CGRectMake(imageViewForCrop.frame.origin.x , imageViewForCrop.frame.origin.y , imageViewForCrop.frame.size.width, originalSizeForIphoneHight+1)];
}];
//return;
}
CGFloat factor = [(UIPinchGestureRecognizer *) pinchGestureRecognizer scale];
if(factor >1)
{
// for the first time bug when pinch in or out
if (lastScaleFactor == 0 )
{
lastScaleFactor = 1;
}
imageViewForCrop.transform =CGAffineTransformMakeScale(lastScaleFactor +(factor-1), lastScaleFactor +(factor-1));
}
else // pinch in
{
imageViewForCrop.transform=CGAffineTransformMakeScale(lastScaleFactor * factor, lastScaleFactor*factor);
}
if (pinchGestureRecognizer.state == UIGestureRecognizerStateEnded)
{
if (factor>1) {
lastScaleFactor +=(factor-1);
}
else {
lastScaleFactor*= factor;
}
}
}
I solved the problem with limit maximumNumberOfTouches:
// handle Pan
UIPanGestureRecognizer *panOnScreen = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
panOnScreen.maximumNumberOfTouches = 1;
[imageViewForCrop addGestureRecognizer:panOnScreen];
I hope this help someone (-: .
I am using a UILongPressGestureRecognizer which works perfectly but the data I get is not precise enough for my use case. CGPoints that I get are rounded off I think.
Example points that I get: 100.5, 103.0 etc. The decimal part is either .5 or .0 . Is there a way to get more precise points? I was hoping for something like .xxxx as in '100.8745' but .xx would do to.
The reason I need this is because I have a circular UIBezierPath, I want to restrict a drag gesture to only that circular path. The item should only be draggable along the circumference of this circle. To do this I calculated 720 points on the circle's boundary using it's radius. Now these points are .xxxx numbers. If I round them off, the drag is not as smooth around the middle section of the circle.This is because in the middle section, the equator, the points on the x-coordinate are very close together. So when I rounded of the y-coordinate, I lost a lot of points and hence the "not so smooth" drag action.
Here is how I calculate the points
for (CGFloat i = -154;i<154;i++) {
CGPoint point = [self pointAroundCircumferenceFromCenter:center forX:i];
[bezierPoints addObject:[NSValue valueWithCGPoint:point]];
i = i - .5;
}
- (CGPoint)pointAroundCircumferenceFromCenter:(CGPoint)center forX:(CGFloat)x
{
CGFloat radius = 154;
CGPoint upperPoint = CGPointZero;
CGPoint lowerPoint = CGPointZero;
//theta used to be the x variable. was first calculating points using the angle
/* point.x = center.x + radius * cosf(theta);
point.y = center.y + radius * sinf(theta);*/
CGFloat y = (radius*radius) - (theta*theta);
upperPoint.x = x+156;
upperPoint.y = 230-sqrtf(y);
lowerPoint.x = x+156;
lowerPoint.y = sqrtf(y)+230;
NSLog(#"x = %f, y = %f",upperPoint.x, upperPoint.y);
[lowerPoints addObject:[NSValue valueWithCGPoint:lowerPoint]];
[upperPoints addObject:[NSValue valueWithCGPoint:upperPoint]];
return upperPoint;
}
I know the code is weird I mean why would I add the points into arrays and return one point back.
Here is how I restrict the movement
-(void)handleLongPress:(UILongPressGestureRecognizer *)recognizer{
CGPoint finalpoint;
CGPoint initialpoint;
CGFloat y;
CGFloat x;
CGPoint tempPoint;
if(recognizer.state == UIGestureRecognizerStateBegan){
initialpoint = [recognizer locationInView:self.view];
CGRect rect = CGRectMake(initialpoint.x, initialpoint.y, 40, 40);
self.hourHand.frame = rect;
self.hourHand.center = initialpoint;
NSLog(#"Long Press Activated at %f,%f",initialpoint.x, initialpoint.y );
}
else if (recognizer.state == UIGestureRecognizerStateChanged){
CGPoint currentPoint = [recognizer locationInView:self.view];
x = currentPoint.x-initialpoint.x;
y = currentPoint.y-initialpoint.y;
tempPoint = CGPointMake( currentPoint.x, currentPoint.y);
NSLog(#"temp point ::%f, %f", tempPoint.x, tempPoint.y);
tempPoint = [self givePointOnCircleForPoint:tempPoint];
self.hourHand.center = tempPoint;
}
else if (recognizer.state == UIGestureRecognizerStateEnded){
// finalpoint = [recognizer locationInView:self.view];
CGRect rect = CGRectMake(tempPoint.x, tempPoint.y, 20, 20);
self.hourHand.frame = rect;
self.hourHand.center = tempPoint;
NSLog(#"Long Press DeActivated at %f,%f",tempPoint.x, tempPoint.y );
}
}
-(CGPoint)givePointOnCircleForPoint:(CGPoint) point{
CGPoint resultingPoint;
for (NSValue *pointValue in allPoints){
CGPoint pointFromArray = [pointValue CGPointValue];
if (point.x == pointFromArray.x) {
// if(point.y > 230.0){
resultingPoint = pointFromArray;
break;
// }
}
}
Basically, I taking the x-coordinate of the "touched point" and returning the y by comparing it to the array of points I calculated earlier.
Currently this code works for half a circle only because, each x has 2 y values because it's a circle, Ignore this because I think this can be easily dealt with.
In the picture, the white circle is the original circle, the black circle is the circle of the points I have from the code+formatting it to remove precision to fit the input I get. If you look around the equator(red highlighted part) you will see a gap between the next points. This gap is my problem.
To answer your original question: On a device with a Retina display, one pixel is 0.5 points, so 0.5 is the best resolution you can get on this hardware.
(On non-Retina devices, 1 pixel == 1 point.)
But it seems to me that you don't need that points array at all. If understand the problem correctly, you can use the following code to
"restrict" (or "project") an arbitrary point to the circumference of the circle:
CGPoint center = ...; // Center of the circle
CGFloat radius = ...; // Radius of the circle
CGPoint point = ...; // The touched point
CGPoint resultingPoint; // Resulting point on the circumference
// Distance from center to point:
CGFloat dist = hypot(point.x - center.x, point.y - center.y);
if (dist == 0) {
// The touched point is the circle center.
// Choose any point on the circumference:
resultingPoint = CGPointMake(center.x + radius, center.y);
} else {
// Project point to circle circumference:
resultingPoint = CGPointMake(center.x + (point.x - center.x)*radius/dist,
center.y + (point.y - center.y)*radius/dist);
}
I am Using the below code to scroll my core plot along X-axis Y-axis and zoom. Its working fine. But I when i zoom my core plot, it zooms in both the directions. I want the plot to zoom along X if I pinch along x-direction and zoom Y if pinch along Y-direction. Can someone help me with this please.
-(CGPoint)plotSpace:(CPTPlotSpace *)space willDisplaceBy:(CGPoint)displacement
{
return CGPointMake(displacement.x, displacement.y);
}
-(CPTPlotRange *)plotSpace:(CPTPlotSpace *)space willChangePlotRangeTo:(CPTPlotRange *)newRange forCoordinate:(CPTCoordinate)coordinate
{
// Adjust axis to keep them in view at the left and bottom;
// adjust scale-labels to match the scroll.
CPTXYAxisSet *axisSet = (CPTXYAxisSet *)self.hostView.hostedGraph.axisSet;
if (coordinate == CPTCoordinateX) {
axisSet.yAxis.orthogonalCoordinateDecimal = newRange.location;
}
else {
axisSet.yAxis.titleLocation = CPTDecimalFromFloat(newRange.locationDouble + (newRange.lengthDouble / 2.0F));
}
return newRange;
}
The easiest way to keep the axes and title in the correct position is to use the axisConstraints for the axes and leave the titleLocation at it's default of NAN. This will relieve your delegate of responsibility for updating those items and you can focus on the zooming.
Of those two delegate methods, you only need -plotSpace:willChangePlotRangeTo:forCoordinate:. The other one is only called when scrolling.
Decide whether you want to allow the zoom to happen in x or y (see the links in the comments on the original question). Check the coordinate parameter in the delegate method; return the newRange to allow the zoom to happen or [space plotRangeForCoordinate:coordinate] to restore the original range and prevent zooming.
If you need to use your own gesture recognizer to detect the pinch angle, set allowPinchScaling to NO on the hosting view to disable the built-in recognizer. Add your own recognizer to the hosting view. In the handler method, decide which axis to scale (if any) and adjust the appropriate plot range accordingly. If you do it this way, you don't need a plot space delegate at all.
I am calculating the change in X and Y coordinates using UIPinchGestureRecognizer and then determining which direction the plot range should change(either X or Y). Its working fine but It is not as smooth as regular zoom, and it responds late. Can someone suggest me a better way to do this
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan){
CGPoint translation = [gestureRecognizer locationInView:hostView];
NSLog(#"Sender value %f %f", translation.x,translation.y );
initialX = translation.x;
initialY = translation.y;
return;
}
else if ([gestureRecognizer state] == UIGestureRecognizerStateBegan || [gestureRecognizer state] == UIGestureRecognizerStateChanged){
NSLog(#"inside else");
CGPoint currentTouchLocation = [gestureRecognizer locationInView:hostView];
NSLog(#"currentTouchLocation = %f and %f and ",currentTouchLocation.x, currentTouchLocation.y);
finalX = currentTouchLocation.x;
finalY = currentTouchLocation.y;
}
}
-(CPTPlotRange *)plotSpace:(CPTPlotSpace *)space willChangePlotRangeTo:(CPTPlotRange *)newRange forCoordinate:(CPTCoordinate)coordinate
{
float x = fabsf(finalX - initialX) ;
float y = fabsf(finalY - initialY);
NSLog(#"pinch x = %f pinch y = %f", x, y);
CPTPlotRange *updatedRange = nil;
if (x > y) {
switch ( coordinate ) {
case CPTCoordinateX:
NSLog(#"x is greater than y change x-range");
if (newRange.locationDouble < 0.0F ) {
CPTMutablePlotRange *mutableRange = [[newRange mutableCopy] autorelease];
mutableRange.location = CPTDecimalFromFloat(0.0);
updatedRange = mutableRange;
}
else {
updatedRange = newRange;
}
break;
case CPTCoordinateY:
NSLog(#"x is greater than y keep y range constant");
updatedRange = ((CPTXYPlotSpace *)space).yRange;
break;
}
}
if (x < y) {
switch ( coordinate ) {
case CPTCoordinateX:
NSLog(#"y is greater than x keep x-range constant");
updatedRange = ((CPTXYPlotSpace *)space).xRange;
break;
case CPTCoordinateY:
if (newRange.locationDouble < 0.0F) {
NSLog(#"y is greater than x increase y range");
CPTMutablePlotRange *mutableRange = [[newRange mutableCopy] autorelease];
// mutableRange.location = CPTDecimalFromFloat(0.0);
updatedRange = mutableRange;
}
else {
updatedRange = newRange;
}
break;
}
}
if (x == y) {
switch ( coordinate ) {
case CPTCoordinateX:
NSLog(#"y is equal to x keep x-range constant");
updatedRange = ((CPTXYPlotSpace *)space).xRange;
break;
case CPTCoordinateY:
NSLog(#"y is equal to x keep y-range constant");
//NSLog(#"%d", CPTCoordinateY);
updatedRange = ((CPTXYPlotSpace *)space).yRange;
break;
}
}
return updatedRange;
}
I am trying to detect the collision of two circles. The process works when a user creates circles (button click) and then mouse clicks on one of the circles to activate it. Mouse clicking anywhere outside the active circle will move the circle to the x,y of the mouse click. What I want to do is stop the movement if there is another circle within the path of the active circle.
NSPoint P = the mouse click (passed in through a notification center)
activeView = the selected circle (subclass of NSView)
My process is to get the point to move to, determine the direction of movement based on the start and end points and also the number of increments for the x start and end points, loop through the increments, use the animator method of NSView to move the view, loop through all non-active views (all the other circles) and compare their position to the current position of the active view using the formula ((x1*x2)+(x1*x2)) + (y1*y2)+(y1*y2) and comparing to the square of the radius of both circles (the circles are in a 30x30 rect with a 4 pixel stroke on the circles, so I used 26 for my radius (26*26) check.
I have a couple of issues (aside from my desire to reinvent the wheel : D - this is a learning exercise for me):
1: The movement code part actually works great. However, I'd like to get the center point of the circle on the mouse click, right now the circle is stopping so that the NSRect that contains it (30x30) x,y coordinate is at the mouse click (this is not flipped, so its lower left).
2: I am getting some collision detection but this seems to be only when the x,y of both containing rects intersect, not the circle edges.
3: Unrelated to all this, I have the NSColor for the containing rects set to clearColor and they initially start out that way but then when I move them they revert back to the default color (that gray color). Do I have to assign the clearColor each time the view is redrawn?
Thanks for the help.
hasCollision = NO;
//get point clicked value
NSValue* pVal = [[notification userInfo] objectForKey:#"dataPoint"];
//convert value to point (end point)
NSPoint p = [pVal pointValue];
//get activeView frame
NSRect avFrame = [activeView frame];
float startX = avFrame.origin.x; //x1
float startY = avFrame.origin.y; //y1
float endX = p.x; //x2
float endY = p.y; //y2
//set direction of activeView movement
if (endX > startX && endY > startY) {
//NSLog(#"endXY > startXY");
cStart = startX;
cCheck =endX;
distanceX = endX - startX;
incrementY = (endY - startY)/distanceX;
} else if (endX < startX && endY > startY) {
//NSLog(#"endX<startX, endY>startY");
cStart = endX;
cCheck =startX;
distanceX = startX - endX;
incrementY = (endY - startY)/distanceX;
} else if (endX < startX && endY < startY) {
//NSLog(#"endX<startX, endY<startY");
cStart = endX;
cCheck =startX;
distanceX = startX - endX;
incrementY = (startY - endY)/distanceX;
} else {
//NSLog(#"endX>startX, endY<startY");
cStart = startX;
cCheck =endX;
distanceX = endX - startX;
incrementY = (startY - endY)/distanceX;
}
//NSLog(#"Start:%f:%f:End%f:%f:::distance=%f:::Y Increment=%f", startX, startY, endX, endY, distanceX, incrementY);
float currentX = startX;
float currentY = startY;
for(int c=cStart; c<cCheck; c++) {
//get move coordinates
if (endX > startX && endY > startY) {
currentX++;
currentY = currentY + incrementY;
} else if (endX < startX && endY > startY) {
currentX--;
currentY = currentY + incrementY;
} else if (endX < startX && endY < startY) {
currentX--;
currentY = currentY - incrementY;
} else {
currentX++;
currentY = currentY - incrementY;
}
//move view
[[activeView animator] setFrame:NSMakeRect(currentX, currentY, avFrame.size.width, avFrame.size.height)];
//make a rect for each object
NSRect avRect = [activeView frame];
//store previous positions
prevPos = [activeView frame];
//check for collisions
for(int v=0; v<[createdViewsDict count]; v++) {
//get the id of the view in the dict
NSString* nextNum = [NSString stringWithFormat:#"circle_%i", v+1];
//init a view and assign it to the next view in the dict
NSView* thisView = [createdViewsDict objectForKey:nextNum];
//make sure this view is not our active view
if (thisView != activeView) {
NSRect tvRect = [thisView frame];
//get the xy coordinates to compare
float x1 = avRect.origin.x;
float y1 = avRect.origin.y;
float x2 = tvRect.origin.x;
float y2 = tvRect.origin.y;
float radius = 26.0;
float dx = x2-x1;
float dy = y2-y1;
float radii = radius + radius;
if ( ( dx * dx ) + ( dy * dy ) < radii * radii ) {
hasCollision = YES;
//[[activeView animator] setFrame:NSMakeRect(prevPos.origin.x, prevPos.origin.y, avFrame.size.width, avFrame.size.height)];
NSLog(#"Collision!!Moving object coords:x=%f,y=%f,:::::::::Stationary object coords:x=%f,y=%f", avRect.origin.x, avRect.origin.y, tvRect.origin.x, tvRect.origin.y);
}
/*
//check for collision
NSRect collisionRect = NSIntersectionRect(avRect, tvRect);
//move circle to previous
if ( !NSIsEmptyRect(collisionRect) ) {
hasCollision = YES;
[[activeView animator] setFrame:NSMakeRect(prevPos.origin.x, prevPos.origin.y, avFrame.size.width, avFrame.size.height)];
NSLog(#"Collision!!Moving object coords:x=%f,y=%f,:::::::::Stationary object coords:x=%f,y=%f", avRect.origin.x, avRect.origin.y, tvRect.origin.x, tvRect.origin.y);
}
*/
}
}
if(hasCollision == YES) {
[[activeView animator] setFrame:NSMakeRect(prevPos.origin.x - 20.0, prevPos.origin.y - 20.0, avFrame.size.width, avFrame.size.height)];
hasCollision = NO;
break;
}
}
I've got an NSImage being drawn on a subclass of NSView. In a previous question, I was helped to draw it upright in the upper left corner. Now, I want to be able to rotate the image. I have a button that increments a rotation variable %4, and then I multiply this by -90 to find the rotation angle. I then use an NSAffineTransform to rotate the image, and translate it back onto the screen. However, it doesn't seem to be working the way I expect it to. I have two problems.
1) When I rotate, the portion of the image that is in an area that wasn't in the previous frame gets drawn correctly. However, the potion that was previously there remains as the original image. This means that after several rotations, there's a square of the original upright image and then a rectangle below or to the left of rotate image.
2) When I re-size the window, the image redraws (as it should) in the original upright orientation (as it should not).
Basically, I'm coming to the conclusion that NSAffineTransform doesn't work the way I think it does. Is there some other way to rotate (and translate) the image? thanks
large code chunk: (code between "WORKS" and "end works" is working code to just draw the image. It came from my previous question).
[OLD CODE DELETED, replaced lower with new code]
thanks
EDIT: A little more research finds that instead of doing an NSAffineTtransform I can rotate the view. This seems to work better. However, I can't get the translation to work quite right. new code below (original code deleted to save space)
- (void)drawRect:(NSRect)rect
{
//WORKS
NSRect frame;
frame.origin = NSZeroPoint;
frame.size = [image size];
// end works
float deltaX, deltaY, height, width;
// if the rotate button was just clicked, we need to rotate by 90 deg, otherwise not
double rotateDeg = justRot ? -90.0 : 0;
justRot = NO;
// rotate
deltaX = 0;
deltaY = 0;
// translate to account for rotation
height = [image size].height;
width = [image size].width;
switch (rotation)
{
case 0:
NSLog(#"No rotation ");
break;
case 1:
deltaX += width;
break;
case 2:
deltaX += width;
deltaY += height;
break;
case 3:
deltaY += height;
break;
}
NSPoint orig;
if (rotation != 0)
{
orig.x = -100;
orig.y = -100;
}
[self rotateByAngle: rotateDeg];
NSLog(#"orig %f %f", orig.x, orig.y);
// WORKS
[self setFrame: frame];
[image drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1];
// end works
[self translateOriginToPoint: orig];
}
Ok, for history, in case anyone else has this question here's the answer I've come up with:
The frame rotation stays in the drawRect, everything else moves to the rotate method:
-(void)rotate
{
float deltaX, deltaY, height, width;
rotation = (rotation +1) % 4 ;
deltaX = 0;
deltaY = 0;
// translate to account for rotation
height = [image size].height;
width = [image size].width;
switch (rotation)
{
case 0:
NSLog(#"No rotation ");
deltaY -= width;
break;
case 1:
deltaY -= height;
break;
case 2:
deltaX += height-width;
deltaY -= height ;
break;
case 3:
deltaX += height-width;
deltaY -= width;
break;
}
NSPoint orig;
orig.x = deltaX;
orig.y = deltaY;
[self rotateByAngle: 90.0];
[self translateOriginToPoint: orig];
[self setNeedsDisplay:YES];
}
I'm guessing that you'd like to rotate the image about it's center. If so you need to translate the origin of the affine transform to the center of the image, then rotate, then translate back. Now if you have artifacts of the previos position left over, it's probably because you didn't call -[NSView setNeedsDisplayInRect:] with the correct rectangles. Remember that you need to invalidate both the images previous and new positions.
Update: I just noticed that you're changing the view's frame from within drawRect:. That's too late to change the frame. Instead, draw the image in the correct location without changing the view's frame.
Have a look at my old Transformed Image sample code at: http://developer.apple.com/mac/library/samplecode/Transformed_Image/index.html