【问题标题】:iOS - How to draw smooth line with finger in "touchesMoved:" methodiOS - 如何在“touchesMoved:”方法中用手指绘制平滑线
【发布时间】:2013-09-11 14:55:37
【问题描述】:

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

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

谢谢!

【问题讨论】:

标签: ios objective-c drawrect uibezierpath smooth


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    (我知道这个问题是针对 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
    

    比较

    我试图保持绘图不变,但仍有一些差异。部分原因在于,如果不进行平滑处理,绘制起来会稍微困难一些。

    没有

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2023-03-11
      • 2012-01-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多