0

我正在尝试计算“addCurveToPoint:”或“addQuadCurveToPoint:”的控制点,但我不知道该怎么做。

我尝试了一些代码示例,但没有...有人可以帮助我吗?

谢谢!

4

2 回答 2

4

检查平滑绘图,一个很棒的教程,向您展示如何绘制平滑线。一步步。易于理解,易于实施。但它只是向您展示如何绘制平滑线,没有其他功能,如橡皮擦等。

如果你想做一个绘图应用程序,请检查Smooth-Line-View,一个简单的绘图应用程序。

如果你熟悉c++,你应该检查GLPaint,它是Apple提供的绘图示例应用程序,GLPaint

于 2013-10-12T08:38:19.490 回答
0

虽然我知道这个问题 a) 非常古老,并且 b) 基本上已经回答了,但我认为值得在这里分享一些代码,以防万一它对来自 Google 的人有所帮助。

可以在此处找到此解决方案所基于的原始绘图代码。

我最近不得不在 Cocoa 应用程序中做类似的事情,并实现了一个不需要太多代码的解决方案。此方法在前一个点值和当前点值之间进行调整,但也会考虑移动速度,以便线条跟上鼠标。我添加了注释来解释该过程的步骤。

(我知道这个问题是针对 iOS 的,但是代码在 iOS 上修改起来并不困难,因为它使用了 Core Graphics。从 Objective-C++ 转换成纯 Objective-C 也不是很困难。)

// Somewhere in another file...
@interface CIDDrawView : NSView {
    NSMutableArray *brushStrokes;
    NSMutableArray *strokePoints;
}

@end

// Implementation
#include <QuartzCore/QuartzCore.h>
#include <chrono>
#include <cmath>
#include <algorithm>

float lerp(float a, float b, float f) {
    return a + f * (b - a);
}

NSPoint lerpPoint(NSPoint a, NSPoint b, float f) {
    return {
        lerp(a.x, b.x, f),
        lerp(a.y, b.y, f)
    };
}

float pointDistance(NSPoint a, NSPoint b) {
    return std::sqrt(std::pow(b.x - a.x, 2) + std::pow(b.y - a.y, 2));
}

inline auto msTime() {
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

@implementation CIDDrawView

-(id)initWithFrame:(NSRect)frame {
    if((self = [super initWithFrame:frame])) {
        brushStrokes = [NSMutableArray array];
    }
    
    return self;
}

-(NSPoint)mousePointForEvent:(NSEvent *)e {
    return [self convertPoint:e.locationInWindow fromView:nil];
}

-(void)mouseDown:(NSEvent *)event {
    // Begin new stroke.
    strokePoints = [NSMutableArray array];
    
    // Add the new stroke to the strokes array.
    [brushStrokes addObject:strokePoints];
    
    // Add the first point to the new stroke.
    NSPoint mousePoint = [self mousePointForEvent:event];
    [strokePoints addObject:@(mousePoint)];
}

-(void)mouseDragged:(NSEvent *)event {
    static auto lastPointTime = msTime();
    
    // The reference speed used to normalise the mouse movement speed.
    // Unit is px/s, but that doesn't matter much.
    const float refSpeed = 600.f;
    
    // How long it takes for the lerped points to reach the user's mouse location.
    // Lower values smooth the line less, but higher values make the line catch up more slowly.
    const float lerpAmount = 3.f;
    
    NSPoint mousePoint = [self mousePointForEvent:event];
    
    // Only modify the point value if this is not a new stroke.
    if([strokePoints count] > 1) {
        NSPoint lastPoint = [[strokePoints lastObject] pointValue];
        
        // Calculate the time since the last point was added.
        auto timeNow = msTime();
        float secs = float(timeNow - lastPointTime) / 1000.f;
        
        // Normalise the mouse speed.
        float movementSpeed = std::min(1.0f, (pointDistance(mousePoint, lastPoint) / secs) / refSpeed);
        
        // Lerp between the last point and the current one by the lerp amount (factoring in the speed).
        mousePoint = lerpPoint(lastPoint, mousePoint, movementSpeed / lerpAmount);
        
        lastPointTime = timeNow;
    }
    
    // Add the point to the stroke.
    [strokePoints addObject:@(mousePoint)];
    [self setNeedsDisplay:YES];
}

-(void)mouseUp:(NSEvent *)event {
    NSPoint mousePoint = [self mousePointForEvent:event];
    [strokePoints addObject:@(mousePoint)];
    
    [self setNeedsDisplay:YES];
}

-(void)drawRect:(NSRect)rect {
    const CGColorRef lineColor = [[NSColor blackColor] CGColor];
    const float lineWidth = 1.f;
    
    [[NSColor whiteColor] setFill];
    NSRectFill(rect);
    
    if(![brushStrokes count]) {
        // No strokes to draw.
        return;
    }
    
    CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
    
    for(NSArray *stroke in brushStrokes) {
        CGContextSetLineWidth(context, lineWidth);
        CGContextSetStrokeColorWithColor(context, lineColor);
        
        unsigned long strokePointCount = [stroke count];
        NSPoint startPoint = [[stroke firstObject] pointValue];
        
        CGContextBeginPath(context);
        CGContextMoveToPoint(context, startPoint.x, startPoint.y);
        
        // Add lines to the points, skipping the first mouse point.
        for(unsigned long i = 1; i < strokePointCount; ++i) {
            NSPoint point = [stroke[i] pointValue];
            
            CGContextAddLineToPoint(context, point.x, point.y);
        }
        
        // Stroke the path.
        CGContextDrawPath(context, kCGPathStroke);
    }
}

@end

比较

我试图保持绘图相同,但仍然存在一些差异。这部分归结于这样一个事实,即在没有平滑的情况下绘制会稍微困难一些。

包含平滑绘图的窗口的屏幕截图

没有

未平滑绘图的图像

于 2020-07-11T17:09:25.223 回答