2

As a user traces along a line, a SKShapeNode path is built from line segments (which come from underlying 'tiles'). The actual path is built by locking in lines the user has previously dragged over, and finding the closest point on the closest line to the latest touch point, then building a path like so:

var activatedPointPath: UIBezierPath {
    let activatedPointPath = UIBezierPath()

    // self.state.activatedPoints contains the end point
    // of each full line segment the user has traced through
    if let firstActivatedPoint = self.state.activatedPoints.first {
        activatedPointPath.move(to: firstActivatedPoint)
        for activatedPoint in self.state.activatedPoints {
            activatedPointPath.addLine(to: activatedPoint)
        }
        // self.state.endOfLine contains the last point the user
        // has touched, which may or may not be at the end of a line
        // segment
        if let lastPoint = self.state.endOfLine {
            activatedPointPath.addLine(to: lastPoint)
        }
    }

    return activatedPointPath
}

this is then assigned to the SKShapeNode as:

self.lineNode.path = self.activatedPointPath.cgPath

however when the user is tracing the line, previous segments flicker with triangles missing while being drawn to the screen, like so:

solid line     line with tear     line with tear

The images above are from a simple 3x3 tile grid allowing the user to trace a simple straight line:

wireframe of tiles

Here is a more complex example, where the line starts on the bottom left and ends at the top right:

more complex example with corners

What could be causing these artifacts?

edit while I have found a solution to this issue, I would still welcome any explanation for why the original code didn't work and the new code does.

ReactiveRaven
  • 7,203
  • 2
  • 29
  • 38
  • 1
    SKShapeNode is a poorly implemented, half finished, WIP, wrapper around CAShapeLayer (I think) that's recalcitrant, immutable, inflexible, inanimate and not worth your time. Draw these line segments with stretched and skewed SKSpriteNodes. You'll get better performance when animating them, too. The original purpose of SKShapeNode was that it be used as a physics debug drawing tool, for the physics shapes... only. Only in iOS 10 have they got the outline to draw in a performant manner, if it's not changed and treated like a texture. It might be finished in iOS 16. – Confused Jan 24 '17 at 08:29

1 Answers1

1

My fix for this issue was to change the activatedPointPath calculated variable to build the line out of segments combined together, rather than drawing the line in one pass.

The new code looked like this:

var previousPoint = firstActivatedPoint
for activatedPoint in self.state.activatedPoints {
    let newSegment = UIBezierPath()
    newSegment.move(to: previousPoint)
    newSegment.addLine(to: activatedPoint)

    activatedPointPath.append(newSegment)

    previousPoint = activatedPoint
}
if let lastPoint = self.state.endOfLine {
    let newSegment = UIBezierPath()
    newSegment.move(to: previousPoint)
    newSegment.addLine(to: lastPoint)
    activatedPointPath.append(newSegment)
} else {
    print("No last point")
}

which now produces smooth lines.

ReactiveRaven
  • 7,203
  • 2
  • 29
  • 38
  • ... And while it works, it was horribly inefficient. In the end I took Confused's advice and rendered the shapes with a bunch of `CALayer`s down into a texture in stead. This had the side effect of the original, long-line-drawn-at-once, rendering correctly. – ReactiveRaven Feb 07 '17 at 14:39