From 272c94348f1fa27629391d1807be0607083bad96 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 29 Jan 2021 22:42:49 -0800 Subject: [PATCH] add trapezoid experiment. --- experiments/trapezoid.nim | 261 +++++++++++++++++++++++++++++ experiments/trapezoids/g.png | Bin 0 -> 10980 bytes experiments/trapezoids/heart.png | Bin 0 -> 4329 bytes experiments/trapezoids/l.png | Bin 0 -> 7765 bytes experiments/trapezoids/rect.png | Bin 0 -> 921 bytes experiments/trapezoids/rhombus.png | Bin 0 -> 1260 bytes 6 files changed, 261 insertions(+) create mode 100644 experiments/trapezoid.nim create mode 100644 experiments/trapezoids/g.png create mode 100644 experiments/trapezoids/heart.png create mode 100644 experiments/trapezoids/l.png create mode 100644 experiments/trapezoids/rect.png create mode 100644 experiments/trapezoids/rhombus.png 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 0000000000000000000000000000000000000000..c63be150ddbe446da082cb13322f8ec66d45fc9b GIT binary patch literal 10980 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 U-BWuBUa!JV8=4pt>p4aK2fi4R*Z=?k literal 0 HcmV?d00001 diff --git a/experiments/trapezoids/heart.png b/experiments/trapezoids/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5f69b70fee238e797e618ec5a771d687748479 GIT binary patch literal 4329 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 literal 0 HcmV?d00001 diff --git a/experiments/trapezoids/l.png b/experiments/trapezoids/l.png new file mode 100644 index 0000000000000000000000000000000000000000..0507a7fbcdc53d64cccf19a86aec688d238cbf8f GIT binary patch literal 7765 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 DoJS3< literal 0 HcmV?d00001 diff --git a/experiments/trapezoids/rhombus.png b/experiments/trapezoids/rhombus.png new file mode 100644 index 0000000000000000000000000000000000000000..5f7e184311673bfeaf92b1eeb164f833da0420df GIT binary patch literal 1260 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST~P>fn~F&i(^OyQIi9$_7XTW1o%0Fn zdytc_bEGUT?*ZCd#&q++T#(vchj}JWKo`wZpP=3YQu3VRNuwZ8=FVpP zmHRB4bRO++RGGhw|H!Gr`5x1DG7BD0-t2DlmP@Jc`B|BhQyM#xcSkDE)DtM&lXuKx z8JCO9@3UejO&U8E&zY$-Q%Rt3in+JSVRogys%%;@$ zxL4|A0Z{c^L1jrTfx<23Q7VU7l=|v=L{1tsE{S)&+`Mq_>#w)!gj#uoJ(MPWbL#@C zo8Y4Iwnxm}C`O^vqt84^=H!G%6(!4aD+*N(vrXz)vTx#Jk2XFPK~LMo$0zd01GRku zdVZ!7P}|cpLheSsKyBYT|7KVUX&ky@$BOYfY%t$%mk>8G3O7x2Zdzs{F_RlLzA@rcKfk1~xm znM;7QO=FIMlggtTZVJ+Si-EMCf_}wuptw}yo)>eK1dqRF*>u?2MX9frWmE9^i59jZ{=;3G!l-|S=6mi<)NZ>pNtqj3Vi&p|3s_t&U&}Rv{C^)fW zv0h_Hn6Xgd74-?sS2|o|jtiV{^a4sh=6K?u3bbS+Fpb+z?08(kl=)z)$B~O|4pKLT zIxTJqdN`j48orI?QUuVhTf!dfd9F%*b{tED?Ue=Hfq7}7i07>r?S!+r+O`iDfZ_4= mVW5E~j!cR@>yjaA$FSn!qrD58?B4@RF$PapKbLh*2~7a5W6zxc literal 0 HcmV?d00001