Search code examples
iosmacoscore-graphics

Flipping CGContext inside drawrect method draws in wrong posotion


I tried to draw a circle flipped vertically by its center. (Flipping a circle by its center looks same so that I can check visually). But the drawn position is entirely wrong. I expect that the fill has to be occurred entirely inside the stroked region enter image description here

circleRect = CGRect(x:100, y:100, width:100, height: 100)

override func draw(_ dirtyRect: CGRect) {
    let ctx = NSGraphicsContext.current()!.cgContext
    if(drawCircle) {
        let path = getOvalPath(circleRect) //returns a oval cgpath for the rect, here the rect is a square so it gives a circle

        //stroked without flipping
        ctx.addPath(path)
        ctx.setStrokeColor(CGColor.black)
        ctx.strokePath()

        //filled with flipping
        ctx.saveGState
        ctx.translateBy(x: 0, y: circleRect.size.height)
        ctx.scaleBy(x: 1, y: -1)

        ctx.addPath(path)
        ctx.setFillColor(fillColor)
        ctx.fillPath()
        ctx.restoreGState()
}

drawCircle = true
setNeedsDisplay(circleRect)

Solution

  • Your circle's center is at (100, 100). Consider the effects of your transforms on that point:

    var point = CGPoint(x: 100, y: 100)
    // point = (100, 100)
    
    point = point.applying(CGAffineTransform(scaleX: 1, y: -1))
    // point = (100, -100)
    
    point = point.applying(CGAffineTransform(translationX: 0, y: 100))
    // point = (100, 0)
    

    So your transforms move the center of the circle.

    You're thinking about “flipping” the y axis, moving it from the bottom left corner of some “canvas” to the top left corner. Your circle will stay put if it is centered vertically on that canvas. Your canvas's height is implicitly defined by the y translation you apply, which is 100. So your canvas has height 100, which means the circle's center would need to be at y = 100/2 = 50 to stay put.

    Since your circle's center is at y = 100, you need to use a canvas height of 2*100 = 200 to make the circle stay put.