260 lines
6.7 KiB
Nim
260 lines
6.7 KiB
Nim
|
|
import algorithm, bumpy, chroma, pixie, pixie/images, pixie/paths, print,
|
|
sequtils, vmath
|
|
|
|
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.commandsToShapes()
|
|
|
|
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: Path
|
|
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("experiments/trapezoids/rect.png")
|
|
|
|
block:
|
|
# Rhombus
|
|
print "rhombus"
|
|
var image = newImage(200, 200)
|
|
image.fill(rgba(255, 255, 255, 255))
|
|
|
|
var p: Path
|
|
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("experiments/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("experiments/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("experiments/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("experiments/trapezoids/g.png")
|