Differences between addCurveToPoint and addQuadCurveToPoint draw smooth curved lines in Swift


Note. In the question below, I use the term "lag" when I probably mean "latency" when drawing using a function addCurveToPoint.


Problem:

Both functions of the Bezier curve, addQuadCurveToPointand addCurveToPointhave one strength and one weakness. The goal is to achieve a perfect combination of both, a perfect continuous smooth curved line that is free to draw. The figures below show that touching the screen usually matches the updated image.

The image below uses the function addQuadCurveToPoint. It quickly draws without delay when drawing during touch events, but the end result is a less perfect smooth curved line that looks more segmented.

Drawing with addQuadCurveToPoint

The image below uses the function addCurveToPoint. It approaches an ideal continuous smooth curve but slower when there is some lag when drawing during the touch event.

enter image description here


Question:

Can someone help explain or give a solution, please:

  • How to get perfect addQuadCurveToPointcurved lines or without delayed addCurveToPointcurved lines?

. addCurveToPoint, , addQuadCurveToPoint.


addCurveToPoint:

// Swift 2 code below tested using Xcode 7.0.1.

class drawView: UIView {

var path:UIBezierPath?
var incrementalImage:UIImage?

var points = [CGPoint?](count: 5, repeatedValue: nil)
var counter:Int?

var infoView:UIView = UIView()
var strokeColor:UIColor?


required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.multipleTouchEnabled = false
    self.backgroundColor = UIColor.whiteColor()
    path = UIBezierPath()
    path?.lineWidth = 20.0
    strokeColor = UIColor.darkGrayColor()
    path?.lineCapStyle = CGLineCap.Round
}

override init(frame: CGRect) {
    super.init(frame: frame)
    self.multipleTouchEnabled = false
    path = UIBezierPath()
    path?.lineWidth = 20.0
}


override func drawRect(rect: CGRect) {
    incrementalImage?.drawInRect(rect)
    strokeColor?.setStroke()
    path?.stroke()
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    counter = 0
    let touch: AnyObject? = touches.first
    points[0] = touch!.locationInView(self)
    infoView.removeFromSuperview()
}

override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch: AnyObject? = touches.first
    let point = touch!.locationInView(self)
    counter = counter! + 1
    points[counter!] = point

    if counter == 4{

        points[3]! = CGPointMake((points[2]!.x + points[4]!.x)/2.0, (points[2]!.y + points[4]!.y)/2.0)
        path?.moveToPoint(points[0]!)
        path?.addCurveToPoint(points[3]!, controlPoint1: points[1]!, controlPoint2: points[2]!)

        self.setNeedsDisplay()

        points[0]! = points[3]!
        points[1]! = points[4]!
        counter = 1

    }
}

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {

    self.drawBitmap()
    self.setNeedsDisplay()
    path?.removeAllPoints()
    counter = 0
}

override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    self.touchesEnded(touches!, withEvent: event)
}

func drawBitmap(){
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, true, 0.0)
        strokeColor?.setStroke()
        if((incrementalImage) == nil){
            let rectPath:UIBezierPath = UIBezierPath(rect: self.bounds)
            UIColor.whiteColor().setFill()
            rectPath.fill()
        }

        incrementalImage?.drawAtPoint(CGPointZero)
        path?.stroke()
        incrementalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
}
}

- addQuadCurveToPoint:

// Swift 2 code below tested using Xcode 7.0.1.

class DrawableView: UIView {

    let path=UIBezierPath()
    var previousPoint:CGPoint
    var lineWidth:CGFloat=20.0
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override init(frame: CGRect) {
        previousPoint=CGPoint.zero
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        previousPoint=CGPoint.zero
        super.init(coder: aDecoder)
        let panGestureRecognizer=UIPanGestureRecognizer(target: self, action: "pan:")
        panGestureRecognizer.maximumNumberOfTouches=1
        self.addGestureRecognizer(panGestureRecognizer)

    }

    override func drawRect(rect: CGRect) {
        // Drawing code
        UIColor.darkGrayColor().setStroke()
        path.stroke()
        path.lineWidth=lineWidth
        path.lineCapStyle = .Round
    }
    func pan(panGestureRecognizer:UIPanGestureRecognizer)->Void
    {
        let currentPoint=panGestureRecognizer.locationInView(self)
        let midPoint=self.midPoint(previousPoint, p1: currentPoint)

        if panGestureRecognizer.state == .Began
        {
            path.moveToPoint(currentPoint)
        }
        else if panGestureRecognizer.state == .Changed
        {
            path.addQuadCurveToPoint(midPoint,controlPoint: previousPoint)
        }

        previousPoint=currentPoint
        self.setNeedsDisplay()
    }
    func midPoint(p0:CGPoint,p1:CGPoint)->CGPoint
    {
        let x=(p0.x+p1.x)/2
        let y=(p0.y+p1.y)/2
        return CGPoint(x: x, y: y)
    }

}
+4

Source: https://habr.com/ru/post/1614565/


All Articles