Objective c NSBezierPath:创建一个;“弯曲的”;广场

Objective c NSBezierPath:创建一个;“弯曲的”;广场,objective-c,macos,cocoa,nsbezierpath,Objective C,Macos,Cocoa,Nsbezierpath,我想创建一个NSBezierPath,它表示一个“弯曲”或“膨胀”的正方形,如下所示: 我正在努力想出正确的数学方法,使这个形状完全正确。我已经在互联网上搜索过了,但在谷歌上搜索这个主题时,大多数人都会发现“这是你画圆角的方式”,这不是我需要的 有人能告诉我一个公式,我可以用来放置这些曲线的控制点吗?谢谢 好的;经过多次尝试和错误,我得到了一些有用的东西。下面的代码将绘制类似于此的形状。(此图对代码中的#define语句使用以下值): 给定500x500像素的视图,这些值将绘制此形状: 绘图

我想创建一个NSBezierPath,它表示一个“弯曲”或“膨胀”的正方形,如下所示:

我正在努力想出正确的数学方法,使这个形状完全正确。我已经在互联网上搜索过了,但在谷歌上搜索这个主题时,大多数人都会发现“这是你画圆角的方式”,这不是我需要的


有人能告诉我一个公式,我可以用来放置这些曲线的控制点吗?谢谢

好的;经过多次尝试和错误,我得到了一些有用的东西。下面的代码将绘制类似于此的形状。(此图对代码中的#define语句使用以下值):

给定500x500像素的视图,这些值将绘制此形状:

绘图代码 在我的例子中,我在NSButtonCell子类中绘制这个形状。因为我知道这个单元格的框架在我的应用程序中永远不会调整大小,所以我可以做一些优化。具体来说,我将NSBezierPath存储为iVar,这样我就不必每次都通过-drawImage重新创建它。。。此外,我将NSShadow和NSColor存储为IVAR,这样就不必重新创建它们

如果要在调整大小的视图中绘制此形状,则需要稍微调整代码。如果正方形的大小发生了巨大的变化,则需要立即手动调整#define语句的值(否则,角看起来不太合适。在侧曲线过渡到圆角曲线的地方有一些凹痕,等等)

下面的代码配置为在52x52像素的正方形中绘制相同的形状。通常的方法是设置所需的“侧偏移”和“角半径”,然后调整两个“偏移”定义语句(即百分比),直到角看起来完美为止。“kappa”的值永远不能改变——这个值取自我发现的一篇数学论文

以下是优化的图形代码:

    #import "LPProjectIconButtonCell.h"


#define SIDE_DEFLECTION 1.0f
#define CORNER_RAD 4.0f

// Distance (%) control points should be from curve start/end to form perfectly circular rounded corners
#define KAPPA 0.55238f

// Percentage offset (from perfectly circular rounded corner location) that the corner control points use to
// compensate for the fact that our sides are rounded. Without this, we get a rough transition between the
// curve of the side and the start of the corner curve
#define CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION .85

// As the curve approaches each side-center point, this is the percentage of the distance between the side endpoints
// and the side centerpoint where the control point for approaching the centerpoint is located. You are not expected
// to understand the preceeding sentence.
#define SIDE_CENTER_CP_OFFSET 0.60



@implementation LPProjectIconButtonCell

- (void) awakeFromNib
{
    _shadow = [[NSShadow alloc] init];
    [_shadow setShadowBlurRadius:1.0f];
    [_shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0f alpha:0.2f]];
    [_shadow setShadowOffset:NSMakeSize(0.0f, -1.0f)];

    _borderColor = [[NSColor colorWithCalibratedWhite:0.13 alpha:1.0f] retain];
}

- (void) dealloc
{
    [_path release];
    _path = nil;

    [_shadow release];
    _shadow = nil;

    [_borderColor release];
    _borderColor = nil;

    [super dealloc];
}


- (void) drawImage:(NSImage *)image withFrame:(NSRect)frame inView:(NSView *)controlView
{
    NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];

    // The path never changes because this view never resizes. So we'll save it to be efficient
    if (!_path)
    {
        NSRect rect = NSInsetRect(frame, 2.0f, 2.0f);

        // Create the primary points -- 3 per side
        NSPoint TCenter = NSMakePoint(rect.size.width/2.0f, rect.origin.y);
        NSPoint TLeft = NSMakePoint(rect.origin.x + CORNER_RAD + SIDE_DEFLECTION, rect.origin.y + SIDE_DEFLECTION);
        NSPoint TRight = NSMakePoint(rect.origin.x + rect.size.width - (CORNER_RAD + SIDE_DEFLECTION), rect.origin.y + SIDE_DEFLECTION);

        NSPoint LTop = NSMakePoint(rect.origin.x + SIDE_DEFLECTION, rect.origin.y + CORNER_RAD + SIDE_DEFLECTION);
        NSPoint LCenter = NSMakePoint(rect.origin.x, rect.size.height/2.0f);
        NSPoint LBottom = NSMakePoint(rect.origin.x + SIDE_DEFLECTION, rect.origin.y + rect.size.height - CORNER_RAD - SIDE_DEFLECTION);

        NSPoint BLeft = NSMakePoint(TLeft.x, rect.origin.y + rect.size.height - SIDE_DEFLECTION);
        NSPoint BCenter = NSMakePoint(TCenter.x, rect.origin.y + rect.size.height);
        NSPoint BRight = NSMakePoint(TRight.x, BLeft.y);

        NSPoint RTop = NSMakePoint(rect.origin.x + rect.size.width - SIDE_DEFLECTION, LTop.y);
        NSPoint RCenter = NSMakePoint(rect.origin.x + rect.size.width, LCenter.y);
        NSPoint RBottom = NSMakePoint(RTop.x, LBottom.y);


        // Create corner control points for rounded corners
        // We don't want them to be perfectly circular, because our sides are curved. So we adjust them slightly to compensate for that.
        NSPoint CP_TLeft = NSMakePoint(TLeft.x - (TLeft.x - LTop.x) * KAPPA, TLeft.y + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);
        NSPoint CP_LTop = NSMakePoint(LTop.x + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, LTop.y - (LTop.y - TLeft.y) * KAPPA);

        NSPoint CP_LBottom = NSMakePoint(LBottom.x + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, LBottom.y + (BLeft.y - LBottom.y) * KAPPA);
        NSPoint CP_BLeft = NSMakePoint(BLeft.x - (BLeft.x - LBottom.x) * KAPPA, BLeft.y - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);

        NSPoint CP_BRight = NSMakePoint(BRight.x + (RBottom.x - BRight.x) * KAPPA, BRight.y - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);
        NSPoint CP_RBottom = NSMakePoint(RBottom.x - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, RBottom.y + (BRight.y - RBottom.y) * KAPPA);

        NSPoint CP_RTop = NSMakePoint(RTop.x - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, RTop.y - (RTop.y - TRight.y) * KAPPA);
        NSPoint CP_TRight = NSMakePoint(TRight.x + (RTop.x - TRight.x) * KAPPA, TRight.y + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);


        // Create control points for the rounded sides. (The "duplicate" control points are here in case I ever tweak this in the future.)
        NSPoint CP_DepartingTCenterForTLeft = NSMakePoint(TCenter.x - (TCenter.x - TLeft.x) * SIDE_CENTER_CP_OFFSET, TCenter.y);
        NSPoint CP_ApproachingTLeft = TLeft;

        NSPoint CP_DepartingLTopForLCenter = LTop;
        NSPoint CP_ApproachingLCenterFromLTop = NSMakePoint(LCenter.x, LCenter.y - (LCenter.y - LTop.y) * SIDE_CENTER_CP_OFFSET);

        NSPoint CP_DepartingLCenterForLBottom = NSMakePoint(LCenter.x, LCenter.y + (LBottom.y - LCenter.y) * SIDE_CENTER_CP_OFFSET);
        NSPoint CP_ApproachingLBottom = LBottom;

        NSPoint CP_DepartingBLeftForBCenter = BLeft;
        NSPoint CP_ApproachingBCenter = NSMakePoint(BCenter.x - (BCenter.x - BLeft.x) * SIDE_CENTER_CP_OFFSET, BCenter.y);

        NSPoint CP_DepartingBCenterForBRight = NSMakePoint(BCenter.x + (BRight.x - BCenter.x) * SIDE_CENTER_CP_OFFSET, BCenter.y);
        NSPoint CP_ApproachingBRight = BRight;

        NSPoint CP_DepartingRBottomForRCenter = RBottom;
        NSPoint CP_ApproachingRCenterFromRBottom = NSMakePoint(RCenter.x, RCenter.y + (RBottom.y - RCenter.y) * SIDE_CENTER_CP_OFFSET);

        NSPoint CP_DepartingRCenterForRTop = NSMakePoint(RCenter.x, RCenter.y - (RCenter.y - RTop.y) * SIDE_CENTER_CP_OFFSET);
        NSPoint CP_ApproachingRTopFromRCenter = RTop;

        NSPoint CP_DepartingTRightForTCenter = TRight;
        NSPoint CP_ApproachingTCenterFromTRight = NSMakePoint(TCenter.x + (TRight.x - TCenter.x) * SIDE_CENTER_CP_OFFSET, TCenter.y);


        // Draw the bloody square
        NSBezierPath *p = [[NSBezierPath alloc] init];

        [p moveToPoint:TCenter];
        [p curveToPoint:TLeft controlPoint1:CP_DepartingTCenterForTLeft controlPoint2:CP_ApproachingTLeft];
        [p curveToPoint:LTop controlPoint1:CP_TLeft controlPoint2:CP_LTop];
        [p curveToPoint:LCenter controlPoint1:CP_DepartingLTopForLCenter controlPoint2:CP_ApproachingLCenterFromLTop];
        [p curveToPoint:LBottom controlPoint1:CP_DepartingLCenterForLBottom controlPoint2:CP_ApproachingLBottom];
        [p curveToPoint:BLeft controlPoint1:CP_LBottom controlPoint2:CP_BLeft];
        [p curveToPoint:BCenter controlPoint1:CP_DepartingBLeftForBCenter controlPoint2:CP_ApproachingBCenter];
        [p curveToPoint:BRight controlPoint1:CP_DepartingBCenterForBRight controlPoint2:CP_ApproachingBRight];
        [p curveToPoint:RBottom controlPoint1:CP_BRight controlPoint2:CP_RBottom];
        [p curveToPoint:RCenter controlPoint1:CP_DepartingRBottomForRCenter controlPoint2:CP_ApproachingRCenterFromRBottom];
        [p curveToPoint:RTop controlPoint1:CP_DepartingRCenterForRTop controlPoint2:CP_ApproachingRTopFromRCenter];
        [p curveToPoint:TRight controlPoint1:CP_RTop controlPoint2:CP_TRight];
        [p curveToPoint:TCenter controlPoint1:CP_DepartingTRightForTCenter controlPoint2:CP_ApproachingTCenterFromTRight];

        [p closePath];

        _path = p;
    }

    //  We want a slightly white drop shadow on the stroke and fill, giving our square some sense of depth.
    [_shadow set];

    [[NSColor blackColor] set];
    [_path fill];


    // Clip to the bezier path and draw a fill image inside of it.
    [currentContext saveGraphicsState];

        [_path addClip];
        [image drawInRect:frame fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0f respectFlipped:YES hints:nil];

        if (self.isHighlighted)
        {
            // If we're clicked, draw a 50% black overlay to show that
            NSColor *overColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.5];
            [overColor set];
            [_path fill];
        }

    [currentContext restoreGraphicsState];


    // Stroke the square to create a nice border with a drop shadow at top and bottom.
    [_borderColor set];
    [_path stroke];
}

@end

好,;经过多次尝试和错误,我得到了一些有用的东西。下面的代码将绘制类似于此的形状。(此图对代码中的#define语句使用以下值):

给定500x500像素的视图,这些值将绘制此形状:

绘图代码 在我的例子中,我在NSButtonCell子类中绘制这个形状。因为我知道这个单元格的框架在我的应用程序中永远不会调整大小,所以我可以做一些优化。具体来说,我将NSBezierPath存储为iVar,这样我就不必每次都通过-drawImage重新创建它。。。此外,我将NSShadow和NSColor存储为IVAR,这样就不必重新创建它们

如果要在调整大小的视图中绘制此形状,则需要稍微调整代码。如果正方形的大小发生了巨大的变化,则需要立即手动调整#define语句的值(否则,角看起来不太合适。在侧曲线过渡到圆角曲线的地方有一些凹痕,等等)

下面的代码配置为在52x52像素的正方形中绘制相同的形状。通常的方法是设置所需的“侧偏移”和“角半径”,然后调整两个“偏移”定义语句(即百分比),直到角看起来完美为止。“kappa”的值永远不能改变——这个值取自我发现的一篇数学论文

以下是优化的图形代码:

    #import "LPProjectIconButtonCell.h"


#define SIDE_DEFLECTION 1.0f
#define CORNER_RAD 4.0f

// Distance (%) control points should be from curve start/end to form perfectly circular rounded corners
#define KAPPA 0.55238f

// Percentage offset (from perfectly circular rounded corner location) that the corner control points use to
// compensate for the fact that our sides are rounded. Without this, we get a rough transition between the
// curve of the side and the start of the corner curve
#define CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION .85

// As the curve approaches each side-center point, this is the percentage of the distance between the side endpoints
// and the side centerpoint where the control point for approaching the centerpoint is located. You are not expected
// to understand the preceeding sentence.
#define SIDE_CENTER_CP_OFFSET 0.60



@implementation LPProjectIconButtonCell

- (void) awakeFromNib
{
    _shadow = [[NSShadow alloc] init];
    [_shadow setShadowBlurRadius:1.0f];
    [_shadow setShadowColor:[NSColor colorWithCalibratedWhite:1.0f alpha:0.2f]];
    [_shadow setShadowOffset:NSMakeSize(0.0f, -1.0f)];

    _borderColor = [[NSColor colorWithCalibratedWhite:0.13 alpha:1.0f] retain];
}

- (void) dealloc
{
    [_path release];
    _path = nil;

    [_shadow release];
    _shadow = nil;

    [_borderColor release];
    _borderColor = nil;

    [super dealloc];
}


- (void) drawImage:(NSImage *)image withFrame:(NSRect)frame inView:(NSView *)controlView
{
    NSGraphicsContext *currentContext = [NSGraphicsContext currentContext];

    // The path never changes because this view never resizes. So we'll save it to be efficient
    if (!_path)
    {
        NSRect rect = NSInsetRect(frame, 2.0f, 2.0f);

        // Create the primary points -- 3 per side
        NSPoint TCenter = NSMakePoint(rect.size.width/2.0f, rect.origin.y);
        NSPoint TLeft = NSMakePoint(rect.origin.x + CORNER_RAD + SIDE_DEFLECTION, rect.origin.y + SIDE_DEFLECTION);
        NSPoint TRight = NSMakePoint(rect.origin.x + rect.size.width - (CORNER_RAD + SIDE_DEFLECTION), rect.origin.y + SIDE_DEFLECTION);

        NSPoint LTop = NSMakePoint(rect.origin.x + SIDE_DEFLECTION, rect.origin.y + CORNER_RAD + SIDE_DEFLECTION);
        NSPoint LCenter = NSMakePoint(rect.origin.x, rect.size.height/2.0f);
        NSPoint LBottom = NSMakePoint(rect.origin.x + SIDE_DEFLECTION, rect.origin.y + rect.size.height - CORNER_RAD - SIDE_DEFLECTION);

        NSPoint BLeft = NSMakePoint(TLeft.x, rect.origin.y + rect.size.height - SIDE_DEFLECTION);
        NSPoint BCenter = NSMakePoint(TCenter.x, rect.origin.y + rect.size.height);
        NSPoint BRight = NSMakePoint(TRight.x, BLeft.y);

        NSPoint RTop = NSMakePoint(rect.origin.x + rect.size.width - SIDE_DEFLECTION, LTop.y);
        NSPoint RCenter = NSMakePoint(rect.origin.x + rect.size.width, LCenter.y);
        NSPoint RBottom = NSMakePoint(RTop.x, LBottom.y);


        // Create corner control points for rounded corners
        // We don't want them to be perfectly circular, because our sides are curved. So we adjust them slightly to compensate for that.
        NSPoint CP_TLeft = NSMakePoint(TLeft.x - (TLeft.x - LTop.x) * KAPPA, TLeft.y + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);
        NSPoint CP_LTop = NSMakePoint(LTop.x + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, LTop.y - (LTop.y - TLeft.y) * KAPPA);

        NSPoint CP_LBottom = NSMakePoint(LBottom.x + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, LBottom.y + (BLeft.y - LBottom.y) * KAPPA);
        NSPoint CP_BLeft = NSMakePoint(BLeft.x - (BLeft.x - LBottom.x) * KAPPA, BLeft.y - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);

        NSPoint CP_BRight = NSMakePoint(BRight.x + (RBottom.x - BRight.x) * KAPPA, BRight.y - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);
        NSPoint CP_RBottom = NSMakePoint(RBottom.x - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, RBottom.y + (BRight.y - RBottom.y) * KAPPA);

        NSPoint CP_RTop = NSMakePoint(RTop.x - SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION, RTop.y - (RTop.y - TRight.y) * KAPPA);
        NSPoint CP_TRight = NSMakePoint(TRight.x + (RTop.x - TRight.x) * KAPPA, TRight.y + SIDE_DEFLECTION * CORNER_CP_OFFSET_FOR_SIDE_DEFLECTION);


        // Create control points for the rounded sides. (The "duplicate" control points are here in case I ever tweak this in the future.)
        NSPoint CP_DepartingTCenterForTLeft = NSMakePoint(TCenter.x - (TCenter.x - TLeft.x) * SIDE_CENTER_CP_OFFSET, TCenter.y);
        NSPoint CP_ApproachingTLeft = TLeft;

        NSPoint CP_DepartingLTopForLCenter = LTop;
        NSPoint CP_ApproachingLCenterFromLTop = NSMakePoint(LCenter.x, LCenter.y - (LCenter.y - LTop.y) * SIDE_CENTER_CP_OFFSET);

        NSPoint CP_DepartingLCenterForLBottom = NSMakePoint(LCenter.x, LCenter.y + (LBottom.y - LCenter.y) * SIDE_CENTER_CP_OFFSET);
        NSPoint CP_ApproachingLBottom = LBottom;

        NSPoint CP_DepartingBLeftForBCenter = BLeft;
        NSPoint CP_ApproachingBCenter = NSMakePoint(BCenter.x - (BCenter.x - BLeft.x) * SIDE_CENTER_CP_OFFSET, BCenter.y);

        NSPoint CP_DepartingBCenterForBRight = NSMakePoint(BCenter.x + (BRight.x - BCenter.x) * SIDE_CENTER_CP_OFFSET, BCenter.y);
        NSPoint CP_ApproachingBRight = BRight;

        NSPoint CP_DepartingRBottomForRCenter = RBottom;
        NSPoint CP_ApproachingRCenterFromRBottom = NSMakePoint(RCenter.x, RCenter.y + (RBottom.y - RCenter.y) * SIDE_CENTER_CP_OFFSET);

        NSPoint CP_DepartingRCenterForRTop = NSMakePoint(RCenter.x, RCenter.y - (RCenter.y - RTop.y) * SIDE_CENTER_CP_OFFSET);
        NSPoint CP_ApproachingRTopFromRCenter = RTop;

        NSPoint CP_DepartingTRightForTCenter = TRight;
        NSPoint CP_ApproachingTCenterFromTRight = NSMakePoint(TCenter.x + (TRight.x - TCenter.x) * SIDE_CENTER_CP_OFFSET, TCenter.y);


        // Draw the bloody square
        NSBezierPath *p = [[NSBezierPath alloc] init];

        [p moveToPoint:TCenter];
        [p curveToPoint:TLeft controlPoint1:CP_DepartingTCenterForTLeft controlPoint2:CP_ApproachingTLeft];
        [p curveToPoint:LTop controlPoint1:CP_TLeft controlPoint2:CP_LTop];
        [p curveToPoint:LCenter controlPoint1:CP_DepartingLTopForLCenter controlPoint2:CP_ApproachingLCenterFromLTop];
        [p curveToPoint:LBottom controlPoint1:CP_DepartingLCenterForLBottom controlPoint2:CP_ApproachingLBottom];
        [p curveToPoint:BLeft controlPoint1:CP_LBottom controlPoint2:CP_BLeft];
        [p curveToPoint:BCenter controlPoint1:CP_DepartingBLeftForBCenter controlPoint2:CP_ApproachingBCenter];
        [p curveToPoint:BRight controlPoint1:CP_DepartingBCenterForBRight controlPoint2:CP_ApproachingBRight];
        [p curveToPoint:RBottom controlPoint1:CP_BRight controlPoint2:CP_RBottom];
        [p curveToPoint:RCenter controlPoint1:CP_DepartingRBottomForRCenter controlPoint2:CP_ApproachingRCenterFromRBottom];
        [p curveToPoint:RTop controlPoint1:CP_DepartingRCenterForRTop controlPoint2:CP_ApproachingRTopFromRCenter];
        [p curveToPoint:TRight controlPoint1:CP_RTop controlPoint2:CP_TRight];
        [p curveToPoint:TCenter controlPoint1:CP_DepartingTRightForTCenter controlPoint2:CP_ApproachingTCenterFromTRight];

        [p closePath];

        _path = p;
    }

    //  We want a slightly white drop shadow on the stroke and fill, giving our square some sense of depth.
    [_shadow set];

    [[NSColor blackColor] set];
    [_path fill];


    // Clip to the bezier path and draw a fill image inside of it.
    [currentContext saveGraphicsState];

        [_path addClip];
        [image drawInRect:frame fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0f respectFlipped:YES hints:nil];

        if (self.isHighlighted)
        {
            // If we're clicked, draw a 50% black overlay to show that
            NSColor *overColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.5];
            [overColor set];
            [_path fill];
        }

    [currentContext restoreGraphicsState];


    // Stroke the square to create a nice border with a drop shadow at top and bottom.
    [_borderColor set];
    [_path stroke];
}

@end

你有没有试过用矢量图形程序来追踪那个图像?或者,例如,可以让您绘制一个形状,然后导出为Objective-C代码。即使你不这么做,只要玩一个贝塞尔路径工具就可以让你对控制点的工作方式有一些直觉;我听说过这些应用程序,但从未尝试过。它们确实很贵!没错,但免费试用可以让你走得更远。或者使用任何其他矢量绘图应用程序——几乎所有的应用程序都有贝塞尔路径工具,甚至是免费的和开源的。你有没有试过用矢量图形程序来追踪那个图像?或者,例如,可以让您绘制一个形状,然后导出为Objective-C代码。即使你不这么做,只要玩一个贝塞尔路径工具就可以让你对控制点的工作方式有一些直觉;我听说过这些应用程序,但从未尝试过。它们确实很贵!没错,但免费试用可以让你走得更远。或者使用任何其他矢量绘图应用程序——几乎所有的应用程序都有贝塞尔路径工具,甚至是免费的开源工具。