问题
I'm trying to take a bezier curve (any arbitrary curve in Core Graphics) and shrink (or expand) it proportionally given another two end points. I have an approach that sort of works, but it ends up 'flattening' out the curves, and not retaining the shape exactly. Maybe I've messed up the code or logic, but I have the two original points along with the control point(s). Given another set of end points I want to calculate the appropriate control points to produce the same shape between the new end points.
Here's the main code that will calculate 1 control point:
CGPoint (^ScaledCtrlPoint)(CGPoint, CGPoint, CGPoint, CGPoint, CGPoint) = ^CGPoint (CGPoint refPoint1, CGPoint refPoint2, CGPoint bevPoint1, CGPoint bevPoint2, CGPoint ctrlPoint){
//Normalize points to refPoint1
refPoint2.x -= refPoint1.x; refPoint2.y -= refPoint1.y;
ctrlPoint.x -= refPoint1.x; ctrlPoint.y -= refPoint1.y;
//Normalize bevPoints to bevPoint1
bevPoint2.x -= bevPoint1.x; bevPoint2.y -= bevPoint1.y;
//Calculate control point angle
CGFloat theta = PointTheta(refPoint2);
CGFloat refHyp = (refPoint2.y != 0.0f) ? refPoint2.y / sinf(theta) : refPoint2.x / cosf(theta);
theta = PointTheta(bevPoint2);
CGFloat bevHyp = (bevPoint2.y != 0.0f) ? bevPoint2.y / sinf(theta) : bevPoint2.x / cosf(theta);
theta = PointTheta(ctrlPoint);
CGFloat ctrlHyp = (ctrlPoint.y != 0.0f) ? ctrlPoint.y / sinf(theta) : ctrlPoint.x / cosf(theta);
ctrlHyp *= (bevHyp / refHyp);
return CGPointMake(bevPoint1.x + cosf(theta) * ctrlHyp, bevPoint1.y + sinf(theta) * ctrlHyp);
};
The bevPoints
are the new points I'm using to calculate the new control point. The refPoints
and ctrlPoint
are the original points of the bezier curve. As you can see, I'm trying to scale the ctrlPoint down (could also work up) by the same ratio as the the original end points are to the new end points.
I also use another function, which I use to calculate incident angles. It's pretty simple:
CGFloat PointTheta(CGPoint point){
//This assumes an origin of {0, 0} and returns a theta for the given point
CGFloat theta = atanf(point.y / point.x);
//Using arc tan requires some adjustment depending on the point quadrant
if (point.x == 0.0f) theta = (point.y >= 0.0f) ? M_PI_2 : M_PI + M_PI_2;
else if (point.x < 0.0f) theta += M_PI;
else if (point.x > 0.0f && point.y < 0.0f) theta += (M_PI * 2);
return theta;
}
回答1:
I would compute the CGAffineTransform
with parameters
(a, b, -b, a, tx, ty)
(i.e. a transform without skewing) that maps the old endpoints to the new endpoints, and then apply this transform to the old control point to get the new control point.
The condition that the 2 old endpoints are mapped to the 2 new endpoints gives 4 equations for a, b, tx, ty, and these equations can even be solved without trigonometric functions.
来源:https://stackoverflow.com/questions/12264741/scale-a-bezier-curve-proportionally-calculate-control-points