diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index 9d2c16d..896fe87 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1273,41 +1273,41 @@ proc strokeShapes( @[a, b, c, d, a] + proc makeJoin(prevPos, pos, nextPos: Vec2): seq[Vec2] = + let angle = fixAngle(angle(nextPos - pos) - angle(prevPos - pos)) + if abs(abs(angle) - PI) > epsilon: + var + a = (pos - prevPos).normalize() * halfStroke + b = (pos - nextPos).normalize() * halfStroke + if angle >= 0: + a = vec2(-a.y, a.x) + b = vec2(b.y, -b.x) + else: + a = vec2(a.y, -a.x) + b = vec2(-b.y, b.x) + + var lineJoin = lineJoin + if lineJoin == ljMiter and abs(angle) < miterAngleLimit: + lineJoin = ljBevel + + case lineJoin: + of ljMiter: + let + la = line(prevPos + a, pos + a) + lb = line(nextPos + b, pos + b) + var at: Vec2 + if la.intersects(lb, at): + return @[pos + a, at, pos + b, pos, pos + a] + + of ljBevel: + return @[a + pos, b + pos, pos, a + pos] + + of ljRound: + return makeCircle(prevPos) + for shape in shapes: var shapeStroke: seq[seq[Vec2]] - proc makeJoin(prevPos, pos, nextPos: Vec2): seq[Vec2] = - let angle = fixAngle(-angle(prevPos - pos) - -angle(nextPos - pos)) - if abs(abs(angle) - PI) > epsilon: - var - a = (pos - prevPos).normalize() * halfStroke - b = (pos - nextPos).normalize() * halfStroke - if angle >= 0: - a = vec2(-a.y, a.x) - b = vec2(b.y, -b.x) - else: - a = vec2(a.y, -a.x) - b = vec2(-b.y, b.x) - - var lineJoin = lineJoin - if lineJoin == ljMiter and abs(angle) < miterAngleLimit: - lineJoin = ljBevel - - case lineJoin: - of ljMiter: - let - la = line(prevPos + a, pos + a) - lb = line(nextPos + b, pos + b) - var at: Vec2 - if la.intersects(lb, at): - return @[pos + a, at, pos + b, pos, pos + a] - - of ljBevel: - return @[a + pos, b + pos, pos, a + pos] - - of ljRound: - return makeCircle(prevPos) - if shape[0] != shape[^1]: # This shape does not end at the same point it starts so draw the # first line cap. diff --git a/tests/test_paths.nim b/tests/test_paths.nim index 3a2dab0..27126b6 100644 --- a/tests/test_paths.nim +++ b/tests/test_paths.nim @@ -268,3 +268,33 @@ block: image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljBevel) image.writeFile("tests/images/paths/lcSquare.png") + +# Potential error cases, ensure they do not crash + +block: + let + image = newImage(60, 60) + path = parsePath("M 3 3 L 3 3 L 3 3") + image.fill(rgba(255, 255, 255, 255)) + image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter) + +block: + let + image = newImage(60, 60) + path = parsePath("L 0 0 L 0 0") + image.fill(rgba(255, 255, 255, 255)) + image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter) + +block: + let + image = newImage(60, 60) + path = parsePath("L 1 1") + image.fill(rgba(255, 255, 255, 255)) + image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter) + +block: + let + image = newImage(60, 60) + path = parsePath("L 0 0") + image.fill(rgba(255, 255, 255, 255)) + image.strokePath(path, rgba(0, 0, 0, 255), vec2(10, 10), 10, lcSquare, ljMiter)