The Fill Rule Demo

The HTML file:

<h1>The Fill Rule Demo</h1>


<table>
  <tr><td>
     <canvas id="rule"  style="border:1px solid #000000;" width=200 height=200>   </td><td>
     <table>
       <tr></td>
          <label for="fillSelect">Select a Fill Style</label>
          <select id="fillSelect" >
             <option value="none">No Fill</option>
             <option value="evenodd">Even Odd</option>
             <option value="nonzero">Non-Zero</option>
           </select>
       </td></tr> <tr><td>
           <label for="dataSelect">Select a Dataset</label>
           <select id="dataSelect" >
              <option value="ll">Line Loop</option>
              <option value="bd">Broken Diamond</option>
           </select>
       </td></tr>
     </table>
   </tr></td>
</table>
<script src="winding.js"> </script>

winding.js

"use strict"

const ruleCTX = document.getElementById("rule").getContext("2d")
const ruleWidth = document.getElementById("rule").width
const ruleHeight = document.getElementById("rule").height

const fillSelect = document.getElementById("fillSelect")
fillSelect.addEventListener("change", RedoFill)

const dataSelect = document.getElementById("dataSelect")
dataSelect.addEventListener("change", ChangeDataset)

const points1 = [[80,20], [80,180], [180,180], [180,60], [20,60], [20,160], 
                 [60,160], [60,110], [160,110], [160,140], [120,140], 
                 [120,20], [80,20]]

const points2  = [[100,20], [180,100], [20,130], [100,100], 
                  [180,180], [20,100], [100,20]]

let arrows = true
let points = points1

function ChangeDataset() {
   switch(dataSelect.value) {
      case 'll':
         points = points1
         arrows = true
         break;
      case 'bd':
         points = points2
         arrows = true
         break;
   }

   RedoFill()
}

function RedoFill() {
   switch(fillSelect.value) {
      case "none":
          DrawShape('nonzero', false)
          break
      case "evenodd":
          DrawShape('evenodd')
          break
      case "nonzero":
          DrawShape('nonzero')
          break
   }
}

// note this is broken, we will fix it later.
// it only really works for horizontal and vertical lines.
// I added the other shape too late to fix it.
function DrawArrow(a,b) {
    let x1 = a[0]
    let y1 = a[1]
    let x2 = b[0]
    let y2 = b[1]

    let mpx = (x1+x2)/2
    let mpy = (y1+y2)/2

    let dx = 5 
    let dy = 5 

    let tmpStyle = ruleCTX.strokeStyle

    ruleCTX.strokeStyle = "red"
    ruleCTX.beginPath()

    if (x1 == x2 ) {
        if(y1 < y2) {
            dy = -5
        }
        ruleCTX.moveTo(mpx-dx, mpy+dy) 
        ruleCTX.lineTo(mpx, mpy)
        ruleCTX.lineTo(mpx+dx, mpy+dy) 
    } else {
        if (x1 < x2) {
           dx = -5
        }
        ruleCTX.moveTo(mpx+dx, mpy-dy) 
        ruleCTX.lineTo(mpx, mpy)
        ruleCTX.lineTo(mpx+dx, mpy+dy) 
    }
    ruleCTX.stroke()

    ruleCTX.strokeStyle = tmpStyle
}

function DrawShape(rule,  filled = true) {
    let i;
    ruleCTX.fillStyle = "white"
    ruleCTX.fillRect(0,0, ruleWidth, ruleHeight)

    ruleCTX.fillStyle = "green"
    ruleCTX.beginPath()
    ruleCTX.moveTo(points[0][0], points[0][1]) 
    for(i=1;i<points.length; ++i) {
       ruleCTX.lineTo(points[i][0], points[i][1])
    }

    if (filled) {
    ruleCTX.fill(rule)
    }
    ruleCTX.stroke()

    if (arrows) {
        for(i = 0; i < points.length-1; ++i) {
           DrawArrow(points[i], points[i+1])
        }
    }
}

DrawShape('nonzero', false)