Getting issue in CGPath Scale - ios

I am drawing polygon using CGPath and adding to CAShapeLayer.I want to scale my CGPath when user click on it. I know how to scale CGPath. But when i click my CGPath, my CGPath drawing far from centre while i am drawing polygon in centre.
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scaleFactor, scaleFactor);
CGPathRef oldPath = polygonLayer.path;
CGPathRef scaledPath = CGPathCreateCopyByTransformingPath(oldPath, &scaleTransform);
polygonLayer.path = scaledPath;

The problem is that you're used to UIView transformation, which is done from the center of the view.
CGPath transformation is done on the points (imagine CGPointZero as the center of the path).
My solution: translate to CGPointZero , scale , and then back to your original coordinates.
CGPathRef CGPath_NGCreateCopyByScalingPathAroundCentre(CGPathRef path,
const float scale)
{
CGRect bounding = CGPathGetPathBoundingBox(path);
CGPoint pathCenterPoint = CGPointMake(CGRectGetMidX(bounding), CGRectGetMidY(bounding));
CGAffineTransform translateAndScale = CGAffineTransformTranslate( CGAffineTransformMakeScale(scale, scale), - pathCenterPoint.x, -pathCenterPoint.y) ;
CGAffineTransform translateBack = CGAffineTransformMakeTranslation(pathCenterPoint.x, pathCenterPoint.y);
CGPathRef centeredAndScaled = CGPathCreateCopyByTransformingPath(path, &translateAndScale);
CGPathRef translatedPathRef = CGPathCreateCopyByTransformingPath(centeredAndScaled, &translateBack);
CGPathRelease(centeredAndScaled);
return translatedPathRef;
}

Related

How to do transform CALayer without changing it's shape

I've a rectangle shaped CALayer, which I've scaled as below:
CATransform3D currentTransform = layer.transform;
CATransform3D newTransform = CATransform3DScale(currentTransform, xscale, yscale, 1.0);
[layer setTransform:newTransform];
After applying CATransform3DScale, transformed CALayer is also a rectangle.
But when I apply CATransform3DRotate, transformed CALayer becomes a rhombus.
CATransform3D currentTransform = layer.transform;
CATransform3D newTransform = CATransform3DRotate(currentTransform, rotation, 0.0, 0.0, 1.0);
[layer setTransform:newTransform];
How can I get the rectangle shape after applying rotate transform?
I didn't change anchor point of the layer. Sometimes I need to change the position of the layer.

iOS UIBezierPath is upside down when rendered using SpriteKit?

A track path is defined using data points with an origin (0,0) at top/left.
From these points a UIBezierPath is created.
The cars movement along the path is given as distance, from which percent is calculated.
A category on UIBezierPath provides the coordinates for the car along the path.
#import "UIBezierPath-Points.h"
CGPoint currCoordinates = [self.trackPath pointAtPercent:percent withSlope:nil];
The problem is that SpriteKit renders the track path upside-down.
In a SKScene class …
SKShapeNode *shapeNode = [[SKShapeNode alloc] init];
[shapeNode setPath:self.trackPath.CGPath]; <== path is rendered upside-down?
[shapeNode setPath:cgPath];
[shapeNode setStrokeColor:[UIColor blueColor]];
[self addChild:shapeNode];
I have attempted various transforms on the UiBezierPath, but cannot get the coordinates to convert to SpriteKit coordinates which I believe are centre based.
Does anyone know how to convert from UIBezierPath coordinates to SpriteKit (SKScene) coordinates?
*don't have enuf reputation to post image.
Excuse me was thinking that it was self-explanatory.
from Source
void ApplyCenteredPathTransform(UIBezierPath *path, CGAffineTransform transform)
{
CGRect rect = CGPathGetPathBoundingBox(path.CGPath);
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGAffineTransform t = CGAffineTransformIdentity;
t = CGAffineTransformTranslate(t, center.x, center.y);
t = CGAffineTransformConcat(transform, t);
t = CGAffineTransformTranslate(t, -center.x, -center.y);
[path applyTransform:t];
}
void ScalePath(UIBezierPath *path, CGFloat sx, CGFloat sy)
{
CGAffineTransform t = CGAffineTransformMakeScale(sx, sy);
ApplyCenteredPathTransform(path, t);
}
then
ScalePath(path, 1.0, -1.0);

Drawing dashed line in Sprite Kit using SKShapeNode and CGPath

I want to draw a dashed line in my sprite kit game, I can use SKShapeNode node to draw a normal line like the following:
UIBezierPath *_path=[UIBezierPath bezierPath];
//1
CGPoint point1 = CGPointMake(100,100);
CGPoint point2 = CGPointMake(150,150);
[_path moveToPoint:point1];
[_path addLineToPoint:point2];
//2
SKShapeNode *line = [SKShapeNode node];
line.path = _path.CGPath;
I tried to set a dashed pattern to UIBezierPath like this:
// adding this code at location 1 or 2 above but no effect
CGFloat dashes[] = {6, 2};
[_path setLineDash:dashes count:2 phase:0];
but the dashed pattern is not applied.
I also tried to create a dashed copy of CGPath directly from UIBezierpath.CGPath property as:
CGFloat dashes[] = {6, 2};
CGPathRef aCGPath= CGPathCreateCopyByDashingPath(_path.CGPath,NULL,0,dashes,2);
line.path = aCGPath;
but also the same.
I really appreciate if someone could explain what is the problem and how can I draw a dashed line between two points by applying dashed cgpath to skshapenode.
Edit: I know for this simple example I could divide the distance between these two points to small fixed distances and moving and drawing dashed line by bezeirpath but consider a free hand path with points came from touches, it is a very complex and inefficient to redraw the path with fixed length points then draw dashes. I wonder if there is a way to apply the dashed pattern to the path and making skshapenode to use it that is my question.
If anyone is still interested in a simple answer to this question:
Use CGPathCreateCopyByDashingPath to create a dashed copy of - [UIBezierCurve CGPath]
CGPathRef CGPathCreateCopyByDashingPath(
CGPathRef path,
const CGAffineTransform *transform,
CGFloat phase,
const CGFloat *lengths,
size_t count
);
and add it to the SKShapeNode's path property.
Example:
// creates a dashed pattern
CGFloat pattern[2];
pattern[0] = 10.0;
pattern[1] = 10.0;
CGPathRef dashed =
CGPathCreateCopyByDashingPath([bezierPath CGPath],
NULL,
0,
pattern,
2);
self.myShapeNode.path = dashed;
CGPathRelease(dashed);
EDIT: For performance, you can add an SKShapeNode to an SKEffectNode and set the shouldRasterize property to YES.
Change the code at location 1 to something like this:
UIBezierPath *_path=[UIBezierPath bezierPath];
CGPoint point1 = CGPointMake(100,100);
CGPoint point2 = CGPointMake(150,150);
CGFloat deltaX = 1;
CGFloat deltaY = 1;
CGPoint tmpPoint = point1;
[_path moveToPoint:point1];
while(tmpPoint.x<point2.x && tmpPoint.y<point2.y){
tmpPoint.x+=deltaX;
tmpPoint.y+=deltaY;
if((tmpPoint.y-point1.y)%2==1){
[_path addLineToPoint:tmpPoint];
}else{
[_path moveToPoint:tmpPoint];
}
}
// If the line is not a 45 degree straight line
// Please modify the while loop accordingly
[_path addLineToPoint:point2];

UIImageView & CGAffineTransformScale, getting wrong path

I'm trying to add a border to image (UIImageView), everything works fine except one thing: when i'm resizing image i'm getting larger ( or smaller) shape. Here's some code:
CGPoint center = view.center; // view is a UIImageView
CGAffineTransform transform = CGAffineTransformIdentity;
CGFloat angle = [(NSNumber *)[view valueForKeyPath:#"layer.transform.rotation.z"] floatValue];
CGFloat scale = [(NSNumber *)[view valueForKeyPath:#"layer.transform.scale"] floatValue];
transform = CGAffineTransformTranslate(transform, center.x, center.y);
transform = CGAffineTransformScale(transform, scale, scale);
transform = CGAffineTransformRotate(transform, angle);
transform = CGAffineTransformTranslate(transform, -center.x, -center.y);
CGPathRef path = CGPathCreateWithRect(view.frame, &transform); // looks like the problem is here
shape.path = path; // shape is a CAShapeLayer
CGPathRelease(path);
Solved by replacing
CGPathRef path = CGPathCreateWithRect(view.frame, &transform);
with
CGPathAddLines(path, nil, points, 5);

Draw a pie chart with UIBezierPath

I'm trying to draw a PieChart using UIBezierPath, and I'm pretty close to do so, however, I've got a problem, as you can see on the screenshot attached
Here's the code I'm using :
-(void)drawRect:(CGRect)rect
{
CGRect bounds = self.bounds;
CGPoint center = CGPointMake((bounds.size.width/2.0), (bounds.size.height/2.0));
NSManagedObject *gameObject = [SCGameManager sharedInstance].gameObject;
int playerNumber = 0;
int totalOfPlayers = [(NSSet*)[gameObject valueForKey:#"playerColors"] count];
float anglePerPlayer = M_PI*2 / totalOfPlayers;
for (NSManagedObject *aPlayerColor in [gameObject valueForKey:#"playerColors"]){
//Draw the progress
CGFloat startAngle = anglePerPlayer * playerNumber;
CGFloat endAngle = startAngle + anglePerPlayer;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:self.frame. size.width/2 startAngle:startAngle endAngle:endAngle clockwise:YES];
UIColor *playerColor = [SCConstants getUIColorForPlayer:[[aPlayerColor valueForKey:#"colorIndex"] intValue]];
[playerColor set];
[path fill];
playerNumber++;
}
}
Apparently, I just need to move my Path to the center of the circle, and then close it, but when I'm adding the following line of code :
[path addLineToPoint:self.center];
[path closePath];
It draws something weird :
Do you have any idea of what is going wrong with my code? I'm not a Bezier expert at all, so any help is welcome!
Thanks!
It looks like the center point you're using is the problem. Indeed, if you look at the documentation for the center property of UIView, you'll find:
The center is specified within the coordinate system of its superview and is measured in points.
You want the center point of the view specified in its own coordinate system, not that of its superview. You've already determined the center of the view in its own coordinates here:
CGPoint center = CGPointMake((bounds.size.width/2.0), (bounds.size.height/2.0));
So just change the point you're using as the center point from self.center to center, like this:
[path addLineToPoint:center];

Resources