I am reading iOS Core Animation Advanced Techniques.
In this book there is an example code that gets control points from a CAMediaTimingFunction:
//create timing function
CAMediaTimingFunction *function = [CAMediaTimingFunction functionWithControlPoints:1 :0 :0.75 :1];
//get control points
CGPoint controlPoint1, controlPoint2;
[function getControlPointAtIndex:1 values:(float *)&controlPoint1];
[function getControlPointAtIndex:2 values:(float *)&controlPoint2];
The controlPoint value that I get seems to be wrong:
controlPoint1 CGPoint (x = 5.2635442471208903E-315, y = 4.9406564584124654E-324)
controlPoint2 CGPoint (x = 0.0078125018408172764, y = 2.8421186829703262E-314)
However, if I use an float array as type of the parameter values, I get the right values of controlPoint:
float c1p[2], c2p[2];
[function getControlPointAtIndex:1 values:c1p];
[function getControlPointAtIndex:2 values:c2p];
The values are:
controlPoint1 CGPoint (x = 1, y = 0)
controlPoint2 CGPoint (x = 0.75, y = 1)
The question is, how can I use CGPoint as the first code snippet shows? Why isn't it getting the right values?
Thanks.
If what you are showing is the book code, the book code is wrong. functionWithControlPoints: takes float values. CGPoint is made up of CGFloat values. They are not the same - as you have discovered. You can get these values using an array of float, as you are doing.
(I suppose the first code would work on a 32-bit device, where CGFloat and float are the same. But that is sort of an accident. To try to treat two floats as a CGPoint is just silly.)
Related
For Android world, we could tap any locations in by:
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
device.click(x, y)
And the x and y above could be get from Pixelmator like:
For example, the coordinate of key a should be x=100 and y=1800 (Ruler from Pixelmator).
According to tapCoordinate , we might do something similar in iOS:
func tapCoordinate(at xCoordinate: Double, and yCoordinate: Double) {
let normalized = app.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
let coordinate = normalized.withOffset(CGVector(dx: xCoordinate, dy: yCoordinate))
coordinate.tap()
}
But it didn't work as expected, I was wondering if maybe we could tap some points by the x and y of the global screen?
Your question doesn't make a lot of sense. The function debugDescription() generates a string from various data types, suitable for logging to the console. iOS doesn't "calculate the coordinates from debugDescription".
You don't tell us what you are logging. You should edit your question to tell us what it is that you are logging, show the code that captures the value, and also show the print statement that logs it.
Based on the format of the output, it looks like you are logging a rectangle (CGRect data type.)
A CGRect is in the form (origin, size), where origin is a CGPoint and size is a CGSize.
It sounds like tapCoordinate is giving your the center of the rectangle, which would be:
x = rect.origin.x + size.width/2
y = rect.origin.y + size.width/2
That gives
x = 23+41/2 = 43.5
y = 573+49/2 = 597.5
Assuming your x value is a typo and should be 33, that is quite close to the values you give in your question.
Is there an easy way to do this.
I have a CGPoint pointA (10, 10) and another CGPoint pointB (15, 8). I need to get a CGPoint which is on the same line as the one connecting A and B and at a certain distance (say 2) before point A.
I tried looking around for any vector based struct. There is something called CGVector but that seems to be pretty useless here.
It can be done like this:
Assumption: The direction of line is from head:(point2) tail:(point1)
- (CGPoint)getPointFromLineConnecting:(CGPoint)point1 andPoint2:(CGPoint)point2 withDistanceFromPoint1:(CGFloat)dist {
// distance between connecting points
CGFloat distance = sqrtf(powf(point1.x-point2.x, 2) + powf(point1.y-point2.y, 2));
// unit vector point: v = (x1-x0)i/distance + (y1-y0)j/distance
CGPoint unitVectorPoint = CGPointMake((point2.x - point1.x)/distance, (point2.y - point1.y)/distance);
// resultant point at a distance d from p1
CGPoint resultPoint = CGPointMake((point1.x+dist*unitVectorPoint.x), (point1.y+dist*unitVectorPoint.y));
return resultPoint;
}
I'm trying to figure out how to construct a limited range of floating point values based on a UIPanGestureRecognizer's velocity. I have a minimum, or starting value of 1.0, and a maximum of 3.0 to provide a limited range for the UIBezierPath's lineWidth property.
I'm trying to figure out how to build an exponential range from 1.0 to 3.0 based accordingly on the UIPanGestureRecognizer's velocity, but am having a difficult time where I should start for mapping the values. The faster the combined x and y velocity, the smaller (down to 1.0) the lineWidth should be, and respectively the opposite up to 3.0 if the combined velocity is slower. I'm also trying to taper/smooth the line width in progress by storing a lastWidth property so the transitions aren't noticeable between subpaths.
I'd appreciate any help offered.
Working and final Code based on answer:
#property (nonatomic, assign) CGFloat lastWidth;
if (recognizer.state == UIGestureRecognizerStateChanged)
{
CGPoint velocity = [recognizer velocityInView:self.view];
CGFloat absoluteVelocity = 1000.0 / sqrtf(pow(velocity.x, 2) + pow(velocity.y, 2));
CGFloat clampedVel = MAX(MIN(absoluteVelocity, 3.0), 1.0);
if (clampedVel > self.lastWidth)
{
clampedVel = self.lastWidth + 0.15;
}
else if (clampedVel < self.lastWidth)
{
clampedVel = self.lastWidth - 0.15;
}
self.lastWidth = clampedVel;
UIBezierPath *path = [UIBezierPath bezierPath];
path.lineCapStyle = kCGLineCapRound;
path.lineWidth = self.lastWidth;
}
So I'd use an inverted exponential function.
Start with your velocity, V(x,y). Your absolute velocity is obviously:
sqrt(pow(x, 2) + pow(y, 2));
We'll call this value "v."
Next, we want a value that is between 1 and 3 where 1 is the width where "v" is very high and 3 is the width where "v" is very low.
We can calculate that using the following exponential function:
- (CGFloat)getExponentialWidthForVeloctity(CGFloat)v {
if (v <= 1 / 3.0)
return 3;
CGFloat inverse = 1 / v;
return 1 + inverse;
}
Or this function that smooths it out a little bit
- (CGFloat)getExponentialRootWidthForVeloctity(CGFloat)v {
//play with this value to get the feel right
//The higher it is, the faster you'll have to go to get a thinner line
CGFloat rootConstantYouCanAdjust = 2;
if (pow(v, rootConstantYouCanAdjust) <= 1 / 3.0)
return 3;
CGFloat inverse = 1 / pow(v, rootConstantYouCanAdjust);
return 1 + inverse;
}
If that doesn't feel right, try a linear solution:
- (CGFloat)getLinearWidthForVelocity(CGFloat)v {
//Find this value by swiping your finger really quickly and seeing what the fastest velocity you can get is
CGFloat myExpectedMaximumVelocity = 1000;
if (v > myExpectedMaximumVelocity)
v = myExpectedMaximumVelocity;
return 3 - 2 * (v / myExpectedMaximumVelocity);
}
And finally, as a bonus, try this sqrt based function that you might find works nicely:
- (CGFloat)getSqrtWidthForVelocity(CGFloat)v {
//find the same way as above
CGFloat myExpectedMaximumVelocity = 1000;
if (v > myExpectedMaximumVelocity)
return 1;
return 3 - 2 * sqrt(v) / sqrt(myExpectedMaximumVelocity);
}
I'd be curious to know which works best! Let me know. I have a lot more functions up my sleeve, these are just some really simple ones that should get you started.
This is my method to move squares around a circle like satelites does on planets:
-(CGPoint)circularMovement:(float)degrees radius:(CGFloat)radius{
float x = (planet.position.x + planet.radius) *cos(degrees);
float y = (planet.position.y + planet.radius) *sin(degrees);
CGPoint posicion = CGPointMake(x, y);
return posicion;
}
As you can see, I get an x and y position of my satelite, and calling this method with degrees++ I got a circular movement around planets.
But my problem with this movement sistem is I need the degrees of satelite.position.x+satelite.size.width/2 to detect collisions with another object moving around with the same movement-sistem.
Anybody knows how to get this value??
Just do same calculations, but backwards.
In your example you knew: planet.position, planet.radius, degrees and you had to find x and y for that CGPoint.
Now you know: planet.position, planet.radius and that CGPoint and you need to find degrees.
From your formula:
float x = (planet.position.x + planet.radius) *cos(degrees);
you can find your degrees:
cos(degrees) = x / (planet.position.x + planet.radius);
For example:
cos(x) = 1 / 2;
then
x = acos(1/2);
x = 60 degrees or Pi/3 rads
I calculate angle between two CGPoints :
//calculate radian and degree
CGPoint diff = ccpSub(center, location);//return ccp(v1.x - v2.x, v1.y - v2.y);
float rads = atan2f( diff.y, diff.x);
float degs = -CC_RADIANS_TO_DEGREES(rads);
NSLog(#"Rad %.2f Degs %.2f",rads,degs);
Now In another function where I have a pre known CGPoint and the degree of above function, I want to calculate closest point that satisfies the degree.
I was thinking about maybe below code would help me but in below code start point and rotation point is known, in my situation I only know start point.
-(void) rotateAroundPoint:(CGPoint)rotationPoint angle:(CGFloat)angle {
CGFloat x = cos(CC_DEGREES_TO_RADIANS(-angle)) * (self.position.x-rotationPoint.x) - sin(CC_DEGREES_TO_RADIANS(-angle)) * (self.position.y-rotationPoint.y) + rotationPoint.x;
CGFloat y = sin(CC_DEGREES_TO_RADIANS(-angle)) * (self.position.x-rotationPoint.x) + cos(CC_DEGREES_TO_RADIANS(-angle)) * (self.position.y-rotationPoint.y) + rotationPoint.y;
Lets say I have a point 800,600 and I have a degree of 70, how can I calculate closest point with that point and that degree?
EDIT:::
Normally in my game sprites are moved with a button therefore all rotation,movement,speed etc are handled when button pressed [sprite moveToPreGivenPostion:CGPoint]
But now a compass is added and when user choose an angle on the compass I need to move the sprite in the direction of degree on compass, since [sprite moveToPreGivenPostion:CGPoint] already handles rotation and other stuff I just want to determine that what CGPoint should I send to moveToPreGivenPostion function.
As #trumpetlicks said you cant find the closest point like that, but I guess I understood what you want and that function -(void) rotateAroundPoint:(CGPoint)rotationPoint angle:(CGFloat)angle you are trying to use is perfectly fine to achieve what you want.
all you need to do is choose float radius.
you know your current point and lets say your radius is 1, basically you can calculate your previous point without a degree, assuming 0 degrees is left of your point and lets say your point is 200,200 with 1 radius 0 degree your previous point automatically becomes 199,200.
So now you have a reference point so now calculate the point you want to move your sprite:
//choose a feasable radius
float radius = 0.5;
//position_ is your preknown position as you said
//find a the point to roate
//position_.x-radius is always 0 degrees of your current point
CGFloat x = cos(rads) * ((position_.x-radius)-position_.x) - sin(rads) * ((position_.y)-position_.y) + position_.x;
CGFloat y = sin(rads) * ((position_.x-radius)-position_.x) + cos(rads) * ((position_.y)-position_.y) + position_.y;
//get the new point
CGPoint newLocation = ccp(x, y);