import algorithm, bumpy, chroma, pixie/images, print,
    sequtils, vmath, benchy

import pixie, pixie/paths {.all.}


printColors = false

proc intersects*(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

proc pixelCover(a0, b0: Vec2): float32 =
  ## Returns the amount of area a given segment sweeps to the right
  ## in a [0,0 to 1,1] box.
  var
    a = a0
    b = b0
    aI: Vec2
    bI: Vec2
    area: float32 = 0.0

  # # Sort A on top.
  # if a.y > b.y:
  #   let tmp = a
  #   a = b
  #   b = tmp

  # if (b.y < 0 or a.y > 1) or # Above or bellow, no effect.
  #   (a.x >= 1 and b.x >= 1) or # To the right, no effect.
  #   (a.y == b.y): # Horizontal line, no effect.
  #   return 0

  if (a.x < 0 and b.x < 0) or # Both to the left.
    (a.x == b.x): # Vertical line
    # Area of the rectangle:
    return (1 - clamp(a.x, 0, 1)) * (min(b.y, 1) - max(a.y, 0))

  else:
    # y = mm*x + bb
    let
      mm: float32 = (b.y - a.y) / (b.x - a.x)
      bb: float32 = a.y - mm * a.x

    if a.x >= 0 and a.x <= 1 and a.y >= 0 and a.y <= 1:
      # A is in pixel bounds.
      aI = a
    else:
      aI = vec2((0 - bb) / mm, 0)
      if aI.x < 0:
        let y = mm * 0 + bb
        # Area of the extra rectangle.
        area += (min(bb, 1) - max(a.y, 0)).clamp(0, 1)
        aI = vec2(0, y.clamp(0, 1))
      elif aI.x > 1:
        let y = mm * 1 + bb
        aI = vec2(1, y.clamp(0, 1))

    if b.x >= 0 and b.x <= 1 and b.y >= 0 and b.y <= 1:
      # B is in pixel bounds.
      bI = b
    else:
      bI = vec2((1 - bb) / mm, 1)
      if bI.x < 0:
        let y = mm * 0 + bb
        # Area of the extra rectangle.
        area += (min(b.y, 1) - max(bb, 0)).clamp(0, 1)
        bI = vec2(0, y.clamp(0, 1))
      elif bI.x > 1:
        let y = mm * 1 + bb
        bI = vec2(1, y.clamp(0, 1))

  area += ((1 - aI.x) + (1 - bI.x)) / 2 * (bI.y - aI.y)
  return area

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

  Line = object
    #m, x, b: float32
    atx, tox: float32

proc toLine(s: Segment): Line =
  var line = Line()
  # y = mx + b
  line.atx = s.at.x
  line.tox = s.to.x
  # line.m = (s.at.y - s.to.y) / (s.at.x - s.to.x)
  # line.b = s.at.y - line.m * s.at.x
  return line

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 fillPath2(mask: Mask, p: Path) =

  var polygons = p.commandsToShapes()

  const q = 1/256.0

  # Creates segment q, quantize and remove horizontal lines.
  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:
        if s.at.y > s.to.y:
          # make sure segments always are at.y higher
          swap(s.at, s.to)
        segments1.add(s)
  segments1.sort(proc(a, b: Segment): int = cmp(a.at.y, b.at.y))

  # Compute cutLines
  var
    cutLines: seq[float32]
    last = segments1[0].at.y
    bottom = segments1[0].to.y
  cutLines.add(last)
  for s in segments1:
    if s.at.y != last:
      last = s.at.y
      cutLines.add(last)
    if bottom < s.to.y:
      bottom = s.to.y
  cutLines.add(bottom)
  #print cutLines

  var
    sweeps = newSeq[seq[Line]](cutLines.len - 1) # dont add bottom cutLine
    lastSeg = 0
  for i, sweep in sweeps.mpairs:
    #print "sweep", i, cutLines[i]
    while segments1[lastSeg].at.y == cutLines[i]:
      let s = segments1[lastSeg]
      #print s
      if s.to.y != cutLines[i + 1]:
        #print "needs cut?"
        quit()
      sweep.add(toLine(segments1[lastSeg]))
      inc lastSeg
      if lastSeg >= segments1.len:
        # Sort the last sweep by X
        break
    # Sort the sweep by X
    sweep.sort proc(a, b: Line): int =
      result = cmp(a.atx, b.atx)
      if result == 0:
        result = cmp(a.tox, b.tox)

  #print sweeps

  proc fillCoverage(y: int, currCutLine: int, sweep: seq[Line]) =

    let
      sweepHeight = cutLines[currCutLine + 1] - cutLines[currCutLine]
      yFracTop = (y.float32 - cutLines[currCutLine]) / sweepHeight
      yFracBottom = (y.float32 + 1 - cutLines[currCutLine]) / sweepHeight
    var i = 0
    while i < sweep.len:
      let
        nwX = mix(sweep[i+0].atx, sweep[i+0].tox, yFracTop)
        neX = mix(sweep[i+1].atx, sweep[i+1].tox, yFracTop)

        swX = mix(sweep[i+0].atx, sweep[i+0].tox, yFracBottom)
        seX = mix(sweep[i+1].atx, sweep[i+1].tox, yFracBottom)

        minWi = min(nwX, swX).int
        maxWi = max(nwX, swX).ceil.int

        minEi = min(neX, seX).int
        maxEi = max(neX, seX).ceil.int

      template write(x, y: int, alpha: float32) =
        let backdrop = mask.getValueUnsafe(x, y)
        mask.setValueUnsafe(x, y, backdrop + (alpha * 255).uint8)

      let
        nw = vec2(sweep[i+0].atx, cutLines[currCutLine])
        sw = vec2(sweep[i+0].tox, cutLines[currCutLine + 1])
      for x in minWi ..< maxWi:
        var area = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(x.float32, y.float32))
        write(x, y, area)

      let x = maxWi
      var midArea = pixelCover(nw - vec2(x.float32, y.float32), sw - vec2(x.float32, y.float32))
      for x in maxWi ..< minEi:
        write(x, y, midArea)

      let
        ne = vec2(sweep[i+1].atx, cutLines[currCutLine])
        se = vec2(sweep[i+1].tox, cutLines[currCutLine + 1])
      for x in minEi ..< maxEi:

        var area = midArea - pixelCover(ne - vec2(x.float32, y.float32), se - vec2(x.float32, y.float32))
        write(x, y, area)

      i += 2


    # ## x10 slower
    # for x in 0 ..< mask.width:
    #   for i, line in sweep:
    #     let
    #       segat = vec2(line.atx, cutLines[currCutLine])
    #       segto = vec2(line.tox, cutLines[currCutLine + 1])
    #     var area = pixelCover(segat - vec2(x.float32, y.float32), segto - vec2(x.float32, y.float32))
    #     if i mod 2 == 1:
    #       area = -area
    #     let backdrop = mask.getValueUnsafe(x, y)
    #     mask.setValueUnsafe(x, y, backdrop + (area * 255).uint8)


    # var i = 0
    # let
    #   sweepHeight = cutLines[currCutLine + 1] - cutLines[currCutLine]
    #   yFracTop = (y.float32 - cutLines[currCutLine]) / sweepHeight
    #   yFracBottom = (y.float32 + 1 - cutLines[currCutLine]) / sweepHeight
    # #print "cover", y, sweepHeight, yFrac
    # while i < sweep.len:
    #   #print "fill", sweep[i].at.x, "..", sweep[i+1].at.x


    #   var ay = 1.0.float32
    #   if y.float32 < cutLines[currCutLine]:
    #     let a2 = cutLines[currCutLine] - y.float32
    #     #print "cut from top", y.float, cutLines[currCutLine], a2
    #     ay *= a2
    #   if y.float32 + 1 > cutLines[currCutLine + 1]:
    #     let a2 = (y.float32 + 1) - cutLines[currCutLine + 1]
    #     #print "cut from bottom", y.float, cutLines[currCutLine], a2
    #     ay *= a2

    #   #if y == 20:
    #   # print "--", y

    #   template write(x, y: int, alpha: float32) =
    #     let backdrop = mask.getValueUnsafe(x, y)
    #     mask.setValueUnsafe(x, y, backdrop + (ay * alpha * 255).uint8)

    #   let
    #     nwX = mix(sweep[i+0].atx, sweep[i+0].tox, yFracTop)
    #     neX = mix(sweep[i+1].atx, sweep[i+1].tox, yFracTop)

    #     swX = mix(sweep[i+0].atx, sweep[i+0].tox, yFracBottom)
    #     seX = mix(sweep[i+1].atx, sweep[i+1].tox, yFracBottom)

    #     # minW = min(nwX, swX)
    #     # maxW = max(nwX, swX)
    #     # minE = min(neX, seX)
    #     # maxE = max(neX, seX)



    #   var
    #     endWAt: float32 = 0
    #     endWTo: float32 = 0
    #     endWArea: float32 = 0

    #     slopeWUp = swX < nwX
    #     slopeWAt: float32 = 0
    #     slopeWTo: float32 = 0
    #     slopeWArea: float32 = 0
    #     slopeWRate: float32 = 0

    #     transitionWAt: float32 = 0
    #     transitionWTo: float32 = 0
    #     transitionWArea: float32 = 0

    #     fillAt: float32 = 0
    #     fillTo: float32 = 0

    #     transitionEAt: float32 = 0
    #     transitionETo: float32 = 0
    #     transitionEArea: float32 = 0

    #     slopeEUp = seX < neX
    #     slopeEAt: float32 = 0
    #     slopeETo: float32 = 0
    #     slopeEArea: float32 = 0
    #     slopeERate: float32 = 0

    #     endEAt: float32 = 0
    #     endETo: float32 = 0
    #     endEArea: float32 = 0

    #   if slopeWUp:
    #     endWAt = swX.floor
    #     endWTo = swX.ceil

    #     slopeWAt = endWTo
    #     slopeWTo = nwX.floor

    #     transitionWAt = slopeWAt
    #     transitionWTo = nwX.ceil

    #     fillAt = transitionWTo

    #   else:
    #     endWAt = nwX.floor
    #     endWTo = nwX.ceil

    #     slopeWAt = endWTo
    #     slopeWTo = swX.floor

    #     transitionWAt = slopeWAt
    #     transitionWTo = swX.ceil

    #     fillAt = transitionWTo


    #   if slopeEUp:

    #     fillTo = seX.floor

    #     transitionEAt = fillTo
    #     transitionETo = seX.ceil

    #     slopeEAt = transitionETo
    #     slopeETo = neX.floor

    #     endEAt = slopeETo
    #     endETo = neX.ceil

    #   else:

    #     fillTo = neX.floor

    #     transitionEAt = fillTo
    #     transitionETo = neX.ceil

    #     slopeEAt = transitionETo
    #     slopeETo = seX.floor

    #     endEAt = slopeETo
    #     endETo = seX.ceil


    #   let
    #     segat = vec2(line.atx, cutLines[currCutLine])
    #     segto = vec2(line.tox, cutLines[currCutLine + 1])


    #     var area = pixelCover(segat - vec2(x.float32, y.float32), segto - vec2(x.float32, y.float32))
    #     if i mod 2 == 1:
    #       area = -area


    #   #print endWAt, slopeWAt, transitionWAt, fillAt, fillTo, transitionEAt, slopeEAt, endEAt

    #   # if nwX < swX:
    #   #   print nwX.int, swX.int
    #   #   for x in nwX.int ..< swX.int:
    #   #     var a = 0.5 # ((x.float32 - nwX) / (swX - nwX))
    #   #     #print a
    #   #     write(x, y, a.clamp(0, a))
    #   # else:
    #   #   for x in swX.int ..< nwX.int:
    #   #     var a = 0.5 #((x.float32 - swX) / (nwX - swX))
    #   #     #print a
    #   #     write(x, y, a.clamp(0, a))

    #   #write(wX.int, y, 1 - (wX - wX.floor))

    #   # for x in endWAt.int ..< endWTo.int:
    #   #   write(x, y, 0.1)

    #   # for x in slopeWAt.int ..< slopeWTo.int:
    #   #   write(x, y, 0.25)

    #   # for x in transitionWAt.int ..< transitionWTo.int:
    #   #   write(x, y, 0.50)

    #   for x in fillAt.int ..< fillTo.int:
    #     write(x, y, 1)

    #   # for x in transitionEAt.int ..< transitionETo.int:
    #   #   write(x, y, 0.50)

    #   # for x in slopeEAt.int ..< slopeETo.int:
    #   #   write(x, y, 0.25)

    #   # for x in endEAt.int ..< endETo.int:
    #   #   write(x, y, 0.1)

    #   # print 1 - (eX - eX.floor)
    #   # write(eX.int - 1, y, 1 - (eX - eX.floor))

    #   # if neX < seX:
    #   #   for x in neX.int ..< seX.int:
    #   #     var a = 0.5 # ((neX - x.float32) / (seX - neX))
    #   #     #print a
    #   #     write(x, y, a.clamp(0, a))
    #   # else:
    #   #   for x in seX.int ..< neX.int:
    #   #     var a = 0.5 # ((seX - x.float32) / (neX - seX))
    #   #     #print a
    #   #     write(x, y, a.clamp(0, a))

    #   i += 2

    # let quality = 5
    # for m in 0 ..< quality:
    #   let
    #     sweepHeight = cutLines[currCutLine + 1] - cutLines[currCutLine]
    #     yFrac = (y.float32 + (m.float32 / quality.float32) - cutLines[currCutLine]) / sweepHeight
    #   if yFrac < 0.0 or yFrac >= 1.0:
    #     continue
    #   var i = 0
    #   while i < sweep.len:
    #     let
    #       minXf1 = mix(sweep[i+0].at.x, sweep[i+0].to.x, yFrac)
    #       maxXf1 = mix(sweep[i+1].at.x, sweep[i+1].to.x, yFrac)
    #       minXi1 = minXf1.int
    #       maxXi1 = maxXf1.int
    #     for x in minXi1 ..< maxXi1:
    #       let backdrop = mask.getValueUnsafe(x, y)
    #       mask.setValueUnsafe(x, y, backdrop + (255 div quality).uint8)
    #       # if x == 100 and y == 165:
    #       #   print backdrop, 255 div quality
    #       #   print mask.getValueUnsafe(x, y)
    #     i += 2

    # let
    #   sweepHeight = cutLines[currCutLine + 1] - cutLines[currCutLine]
    #   yFrac = (y.float32 - cutLines[currCutLine]) / sweepHeight
    # var i = 0
    # while i < sweep.len:
    #   let
    #     minXf1 = mix(sweep[i+0].atx, sweep[i+0].tox, yFrac)
    #     maxXf1 = mix(sweep[i+1].atx, sweep[i+1].tox, yFrac)
    #     minXi1 = minXf1.floor.int
    #     maxXi1 = maxXf1.floor.int
    #   for x in minXi1 .. maxXi1:
    #     mask.setValueUnsafe(x, y, 255)
    #   i += 2

  var
    currCutLine = 0
  for scanLine in cutLines[0].int ..< cutLines[^1].ceil.int:
    #print scanLine, "..<", scanLine + 1
    #print "  ", currCutLine, cutLines[currCutLine], "..<", cutLines[currCutLine + 1]
    fillCoverage(scanLine, currCutLine, sweeps[currCutLine])

    while cutLines[currCutLine + 1] < scanLine.float + 1.0:
      inc currCutLine
      if currCutLine == sweeps.len:
        break
      #print "  ", currCutLine, cutLines[currCutLine], "..<", cutLines[currCutLine + 1]
      fillCoverage(scanLine, currCutLine, sweeps[currCutLine])

  # print sweeps[^1]
  # print cutLines




  # var segments: seq[Segment]
  # while segments1.len > 0:
  #   #print segments1.len, segments.len
  #   var s = segments1.pop()
  #   var collision = false
  #   for y in cutLines:
  #     let scanLine = line(vec2(0, y), vec2(1, y))
  #     var at: Vec2
  #     if intersects(scanLine, 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:
  #     # means its touching, not intersecting
  #     segments.add(s)

  # # sort at/to in segments
  # # for s in segments.mitems:
  # #   if s.at.y > s.to.y:
  # #     swap(s.at, s.to)


  # #let blender = blendMode.blender()

  # for yScanLine in cutLines[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()

  #   # TODO: winding rules will go here

  #   var trapezoids: seq[Trapezoid]
  #   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

  #     trapezoids.add(Trapezoid(
  #       nw: a.at,
  #       ne: b.at,
  #       se: b.to, # + vec2(0,0.7),
  #       sw: a.to # + vec2(0,0.7)
  #     ))

  #   var i = 0
  #   while i < trapezoids.len:

  #     let t = trapezoids[i]
  #     # print t
  #     let
  #       nw = t.nw
  #       ne = t.ne
  #       se = t.se
  #       sw = t.sw

  #     let
  #       height = sw.y - nw.y
  #       minYf = nw.y
  #       maxYf = sw.y
  #       minYi = minYf.floor.int
  #       maxYi = maxYf.floor.int

  #     # print t

  #     for y in minYi .. maxYi:
  #       let
  #         yFrac = (y.float - nw.y) / height
  #         minXf = mix(nw.x, sw.x, yFrac)
  #         maxXf = mix(ne.x, se.x, yFrac)
  #         minXi = minXf.floor.int
  #         maxXi = maxXf.floor.int
  #       #print yFrac
  #       # if not(minY.int == 58 or maxY.int == 58) or minX > 100:
  #       #   continue

  #       var ay: float32
  #       if y == minYi and y == maxYi:
  #         ay = maxYf - minYf
  #         # print "middle", maxYf, minYf, a
  #         #print "double", y, a, minY, maxY, round(a * 255)
  #       elif y == minYi:
  #         ay = (1 - (minYf - float32(minYi)))
  #         # print "min y", minYf, minYi, a
  #         #print "s", y, a, minY, round(a * 255)
  #       elif y == maxYi:
  #         ay = (maxYf - float32(maxYi))
  #         #print "max y", maxYf, maxYi, a
  #         # print "e", y, a, maxY, round(a * 255)
  #       else:
  #         ay = 1.0

  #       for x in minXi .. maxXi:
  #         var ax: float32
  #         # if x == minXi:
  #         #   a2 = (1 - (minXf - float32(minXi)))
  #         #   #a2 = 1.0
  #         # elif x == maxXi:
  #         #   a2 = (maxXf - float32(maxXi))
  #         #   #a2 = 1.0
  #         # else:
  #         #   a2 = 1.0

  #         if x.float32 < max(nw.x, sw.x):
  #           ax = 0.5
  #         elif x.float32 > min(ne.x, se.x):
  #           ax = 0.25
  #         else:
  #           ax = 1.0

  #         let backdrop = mask.getValueUnsafe(x, y)
  #         mask.setValueUnsafe(x, y, backdrop + floor(255 * ay * ax).uint8)
  #         # if x == 100 and y == 172:
  #         #   print backdrop, round(255 * a * a2).uint8
  #         #   print mask.getValueUnsafe(x, y)

  #     inc i

block:
  # Rect
  print "rect"
  #var image = newImage(200, 200)

  # rect
  # var p = Path()
  # p.moveTo(50.5, 50.5)
  # p.lineTo(50.5, 150.5)
  # p.lineTo(150.5, 150.5)
  # p.lineTo(150.5, 50.5)
  # p.closePath()

  ## rhobus
  var p = Path()
  p.moveTo(100, 50)
  p.lineTo(150, 100)
  p.lineTo(100, 150)
  p.lineTo(50, 100)
  p.closePath()

  # ## heart
  # var p = parsePath("""
  #   M 20 60
  #   A 40 40 90 0 1 100 60
  #   A 40 40 90 0 1 180 60
  #   Q 180 120 100 180
  #   Q 20 120 20 60
  #   z
  # """)

  ## cricle
  # var p = Path()
  # p.arc(100, 100, 50, 0, PI * 2, true)
  # p.closePath()

  # image.fill(rgba(255, 255, 255, 255))
  #image.fillPath2(p, color(0, 0, 0, 1))

  var mask = newMask(200, 200)
  timeIt "rect sweeps", 100:
    for i in 0 ..< 100:
      mask.fill(0)
      mask.fillPath2(p)
      #image.fillPath2(p, color(0, 0, 0, 1))
  mask.writeFile("experiments/trapezoids/output_sweep.png")

  var mask2 = newMask(200, 200)
  timeIt "rect scanline", 10:
    for i in 0 ..< 100:
      mask2.fill(0)
      mask2.fillPath(p)
  mask2.writeFile("experiments/trapezoids/output_scanline.png")

  let (score, image) = diff(mask.newImage, mask2.newImage)
  print score
  image.writeFile("experiments/trapezoids/output_diff.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()

#   image.fillPath2(p, color(0, 0, 0, 1))

#   image.writeFile("experiments/trapezoids/rhombus.png")

# block:
#   # heart
#   print "heart"
#   var image = newImage(400, 400)
#   image.fill(rgba(0, 0, 0, 0))

#   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 mask = newMask(image)
#   mask.fillPath2(p)

#   image.draw(mask, blendMode = OverwriteBlend)

#   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.fillPath2(p, color(0, 0, 0, 1))

#   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.fillPath2(p, color(0, 0, 0, 1))

#   image.writeFile("experiments/trapezoids/g.png")