diff --git a/experiments/trapezoid.nim b/experiments/trapezoid.nim new file mode 100644 index 0000000..e9146de --- /dev/null +++ b/experiments/trapezoid.nim @@ -0,0 +1,261 @@ + + +import pixie, pixie/paths, pixie/images, chroma, print, vmath, algorithm, sequtils, bumpy + +printColors = false + + +proc intersectsInner*(a, b: Segment, at: var Vec2): bool {.inline.} = + ## Checks if the a segment intersects b segment. + ## If it returns true, at will have point of intersection + let + s1 = a.to - a.at + s2 = b.to - b.at + denominator = (-s2.x * s1.y + s1.x * s2.y) + s = (-s1.y * (a.at.x - b.at.x) + s1.x * (a.at.y - b.at.y)) / denominator + t = (s2.x * (a.at.y - b.at.y) - s2.y * (a.at.x - b.at.x)) / denominator + + if s > 0 and s < 1 and t >= 0 and t <= 1: + at = a.at + (t * s1) + return true + +type Trapezoid = object + nw, ne, se, sw: Vec2 + +proc roundBy*(v: Vec2, n: float32): Vec2 {.inline.} = + result.x = sign(v.x) * round(abs(v.x) / n) * n + result.y = sign(v.y) * round(abs(v.y) / n) * n + +proc pathToTrapezoids(p: Path): seq[Trapezoid] = + + var polygons = p.commands.commandsToPolygons() + + const q = 1/256.0 + + # Creates segment q, quantize and remove verticals. + var segments1: seq[Segment] + for shape in polygons: + for s in shape.segments: + var s = s + s.at = s.at.roundBy(q) + s.to = s.to.roundBy(q) + if s.at.y != s.to.y: + segments1.add(s) + #print segments1 + + # Handle segments overlapping each other: + # var segments1: seq[Segment] + # while segments0.len > 0: + # var a = segments0.pop() + # var collision = false + # for b in segments0: + # if a != b: + # var at: Vec2 + # if a.intersectsInner(b, at): + # print "seg2seg intersects!", a, b, at + # quit() + # if not collision: + # segments1.add(a) + + # There is probably a clever way to insert-sort them. + var yScanLines: seq[float32] + for s in segments1: + if s.at.y notin yScanLines: + yScanLines.add s.at.y + if s.to.y notin yScanLines: + yScanLines.add s.to.y + yScanLines.sort() + + var segments: seq[Segment] + while segments1.len > 0: + #print segments1.len, segments.len + var s = segments1.pop() + var collision = false + for y in yScanLines: + var at: Vec2 + if intersects(line(vec2(0,y), vec2(1,y)), s, at): + at = at.roundBy(q) + at.y = y + if s.at.y != at.y and s.to.y != at.y: + #print "seg2yline intersects!", a, y, at + collision = true + var s1 = segment(s.at, at) + var s2 = segment(at, s.to) + #print s.length, "->", s1.length, s2.length + segments1.add(s1) + segments1.add(s2) + break + + if not collision: + segments.add(s) + + #print segments + + # sort at/to in segments + for s in segments.mitems: + if s.at.y > s.to.y: + swap(s.at, s.to) + + #print segments + #print yScanLines + + for yScanLine in yScanLines[0..^2]: + + var scanSegments: seq[Segment] + for s in segments: + if s.at.y == yScanLine: + scanSegments.add(s) + scanSegments.sort(proc(a, b: Segment): int = + cmp(a.at.x, b.at.x)) + + if scanSegments.len mod 2 != 0: + print "error???" + print yScanLine + print scanSegments + quit() + + # if scanSegments.len == 0: + # print "error???" + # print yScanLine + # print scanSegments + # quit() + + # TODO: winding rules will go here + + for i in 0 ..< scanSegments.len div 2: + let + a = scanSegments[i*2+0] + b = scanSegments[i*2+1] + + assert a.at.y == b.at.y + assert a.to.y == b.to.y + #assert a.at.x < b.at.x + #assert a.to.x < b.to.x + + result.add( + Trapezoid( + nw: a.at, + ne: b.at, + se: b.to,# + vec2(0,0.7), + sw: a.to# + vec2(0,0.7) + ) + ) + +proc trapFill(image: Image, t: Trapezoid, color: ColorRGBA) = + # assert t.nw.y == t.ne.y + # assert t.sw.y == t.se.y + + let + height = t.sw.y - t.nw.y + minY = clamp(t.nw.y, 0, image.height.float) + maxY = clamp(t.sw.y, 0, image.height.float) + for y in minY.int ..< maxY.int: + var yRate, minX, maxX: float32 + + yRate = clamp((y.float - t.nw.y) / height, 0, 1) + minX = clamp(lerp(t.nw.x, t.sw.x, yRate).round, 0, image.width.float) + maxX = clamp(lerp(t.ne.x, t.se.x, yRate).round, 0, image.width.float) + + for x in minX.int ..< maxX.int: + image.setRgbaUnsafe(x, y, color) + +proc drawTrapezoids(image: Image, trapezoids: seq[Trapezoid]) = + + for trapezoid in trapezoids: + image.trapFill(trapezoid, rgba(0, 0, 0, 255)) + + # for trapezoid in trapezoids: + # var p = newPath() + # p.moveTo(trapezoid.nw) + # p.lineTo(trapezoid.ne) + # p.lineTo(trapezoid.se) + # p.lineTo(trapezoid.sw) + # p.closePath() + # image.fillPath(p, rgba(0, 0, 0, 255)) + # image.strokePath(p, rgba(255, 0, 0, 255)) + +block: + # Rect + print "rect" + var image = newImage(200, 200) + image.fill(rgba(255, 255, 255, 255)) + + var p = newPath() + p.moveTo(50, 50) + p.lineTo(50, 150) + p.lineTo(150, 150) + p.lineTo(150, 50) + p.closePath() + + var trapezoids = p.pathToTrapezoids() + image.drawTrapezoids(trapezoids) + + image.writeFile("trapezoids/rect.png") + +block: + # Rhombus + print "rhombus" + var image = newImage(200, 200) + image.fill(rgba(255, 255, 255, 255)) + + var p = newPath() + p.moveTo(100, 50) + p.lineTo(150, 100) + p.lineTo(100, 150) + p.lineTo(50, 100) + p.closePath() + + var trapezoids = p.pathToTrapezoids() + image.drawTrapezoids(trapezoids) + + image.writeFile("trapezoids/rhombus.png") + +block: + # heart + print "heart" + var image = newImage(400, 400) + image.fill(rgba(255, 255, 255, 255)) + + var p = parsePath(""" + M 40 120 A 80 80 90 0 1 200 120 A 80 80 90 0 1 360 120 + Q 360 240 200 360 Q 40 240 40 120 z + """) + + var trapezoids = p.pathToTrapezoids() + image.drawTrapezoids(trapezoids) + + image.writeFile("trapezoids/heart.png") + +block: + # l + print "l" + var image = newImage(500, 800) + image.fill(rgba(255, 255, 255, 255)) + + var p = parsePath(""" + M 236 20 Q 150 22 114 57 T 78 166 V 790 L 171 806 V 181 Q 171 158 175 143 T 188 119 T 212 105.5 T 249 98 Z + """) + + #image.strokePath(p, rgba(0, 0, 0, 255)) + + var trapezoids = p.pathToTrapezoids() + image.drawTrapezoids(trapezoids) + + image.writeFile("trapezoids/l.png") + +block: + # g + print "g" + var image = newImage(500, 800) + image.fill(rgba(255, 255, 255, 255)) + + var p = parsePath(""" + M 406 538 Q 394 546 359.5 558.5 T 279 571 Q 232 571 190.5 556 T 118 509.5 T 69 431 T 51 319 Q 51 262 68 214.5 T 117.5 132.5 T 197 78.5 T 303 59 Q 368 59 416.5 68.5 T 498 86 V 550 Q 498 670 436 724 T 248 778 Q 199 778 155.5 770 T 80 751 L 97 670 Q 125 681 165.5 689.5 T 250 698 Q 333 698 369.5 665 T 406 560 V 538 Z M 405 152 Q 391 148 367.5 144.5 T 304 141 Q 229 141 188.5 190 T 148 320 Q 148 365 159.5 397 T 190.5 450 T 235.5 481 T 288 491 Q 325 491 356 480.5 T 405 456 V 152 Z + """) + + #image.strokePath(p, rgba(0, 0, 0, 255)) + + var trapezoids = p.pathToTrapezoids() + image.drawTrapezoids(trapezoids) + + image.writeFile("trapezoids/g.png") diff --git a/experiments/trapezoids/g.png b/experiments/trapezoids/g.png new file mode 100644 index 0000000..c63be15 Binary files /dev/null and b/experiments/trapezoids/g.png differ diff --git a/experiments/trapezoids/heart.png b/experiments/trapezoids/heart.png new file mode 100644 index 0000000..2a5f69b Binary files /dev/null and b/experiments/trapezoids/heart.png differ diff --git a/experiments/trapezoids/l.png b/experiments/trapezoids/l.png new file mode 100644 index 0000000..0507a7f Binary files /dev/null and b/experiments/trapezoids/l.png differ diff --git a/experiments/trapezoids/rect.png b/experiments/trapezoids/rect.png new file mode 100644 index 0000000..36f2913 Binary files /dev/null and b/experiments/trapezoids/rect.png differ diff --git a/experiments/trapezoids/rhombus.png b/experiments/trapezoids/rhombus.png new file mode 100644 index 0000000..5f7e184 Binary files /dev/null and b/experiments/trapezoids/rhombus.png differ