UNPKG

holes-in

Version:

Generates a 3D mesh from a 2D outer path and 2D inner paths

195 lines (156 loc) 6.18 kB
"use strict"; const pathTracer = { tracePath(ctx, path, transform, strokeStyle = "black", fillStyle = "",arrow = false) { if(path.length === 0) return; const scale = transform.scale; const translation = transform.translation; const transformed = path.map(pt => ({X: (pt.X * scale) + translation.X, Y: (pt.Y * scale) + translation.Y }) ); let lineWidth = 1; if(strokeStyle === "") { lineWidth = 0; } ctx.lineWidth = lineWidth; ctx.fillStyle = fillStyle; if(fillStyle.startsWith("hatch")){ const pattern = ctx.createPattern(pathTracer.getHatchPattern(strokeStyle, fillStyle === "hatch2"),"repeat"); ctx.fillStyle = pattern; } ctx.strokeStyle = strokeStyle; if(arrow) { for(let i = 0 ; i < transformed.length; i++) { pathTracer.traceArrow(ctx, transformed[i], transformed[(i+1)%transformed.length]); } } else { ctx.beginPath(); ctx.moveTo(transformed[0].X -1, transformed[0].Y ); transformed.forEach(pt => { ctx.lineTo(pt.X, pt.Y); }); ctx.lineTo(transformed[0].X, transformed[0].Y); if(fillStyle !== ""){ ctx.fill(); } ctx.stroke(); } }, traceTriangulation(ctx, triangulation, transform, strokeStyle = "black", fillStyle = "") { if(triangulation.points.length === 0) return; const scale = transform.scale; const translation = transform.translation; const transformed = triangulation.points.map(arr => ({X: (arr[0] * scale) + translation.X, Y: (arr[1] * scale)+ translation.Y }) ); let lineWidth = 1; if(strokeStyle === "") { lineWidth = 0; } ctx.strokeStyle = strokeStyle; ctx.fillStyle = fillStyle; ctx.lineWidth = lineWidth; triangulation.triangles.forEach(triangle => { ctx.beginPath(); let pt = transformed[triangle[0]]; ctx.moveTo(pt.X -1, pt.Y); pt = transformed[triangle[1]]; ctx.lineTo(pt.X, pt.Y); pt = transformed[triangle[2]]; ctx.lineTo(pt.X, pt.Y); pt = transformed[triangle[0]]; ctx.lineTo(pt.X, pt.Y); ctx.stroke(); if(fillStyle !== ""){ ctx.fill(); } }); }, getFitTransformation(path, width, height) { const minMax = path.reduce( (res, pt) => { pt.X > res.max.X ? res.max.X = pt.X : res.max.X = res.max.X; pt.Y > res.max.Y ? res.max.Y = pt.Y : res.max.Y = res.max.Y; pt.X < res.min.X ? res.min.X = pt.X : res.min.X = res.min.X; pt.Y < res.min.Y ? res.min.Y = pt.Y : res.min.Y = res.min.Y; return res; }, {min:{X:Infinity,Y:Infinity}, max:{X:-Infinity,Y:-Infinity}}); const widthPath = minMax.max.X - minMax.min.X; const heightPath = minMax.max.Y - minMax.min.Y; width = 0.90 * width; height = 0.90 * height; const scale = Math.min(width / widthPath, height / heightPath); const translation = {X: -minMax.min.X * scale +20, Y : -minMax.min.Y * scale + 20}; const baseTranslation = {X: -minMax.min.X * scale +20, Y : -minMax.min.Y * scale }; return {scale, translation, width, height, baseTranslation}; }, getMaxFitTransform(paths, width, height) { const minScale = Infinity; let res = null; const allPaths = paths.reduce( (acc, path ) => { return acc.concat(path) }, []); return pathTracer.getFitTransformation(allPaths, width, height);; }, tracePathsInRow(canvas, paths, transform, strokeStyle = "black", fillStyle = "white") { if(paths.length === 0) return; if(!transform){ transform = pathTracer.getMaxFitTransform([paths[0]], width, height); } const ctx = canvas.getContext("2d"); paths.forEach( (path,index) => { let color = strokeStyle; if(strokeStyle == "shades"){ color = 'rgb(' + Math.floor(50 + 100* index / paths.length) +','+ Math.floor(50 + 100* index / paths.length) +','+ Math.floor(50 + 100* index / paths.length) +')'; } pathTracer.tracePath(ctx, path, transform, color, fillStyle); }); }, traceArrow(ctx, begin, end) { ctx.save(); ctx.moveTo(begin.X, begin.Y); ctx.lineTo(end.X, end.Y); ctx.stroke(); let vect = { X: end.X - begin.X, Y: end.Y - begin.Y, }; const norm = Math.sqrt(vect.X * vect.X + vect.Y * vect.Y); vect = { X: vect.X / norm, Y: vect.Y / norm, }; const angle = Math.PI / 2 + Math.atan2(vect.Y, vect.X); ctx.translate(begin.X + vect.X * norm * 0.75, begin.Y + vect.Y * norm * 0.75); ctx.rotate(angle); const sizeA = 5; const branch1 = { X: sizeA, Y: sizeA, }; const branch2 = { X: -sizeA, Y: sizeA, }; ctx.beginPath(); ctx.moveTo(branch1.X, branch1.Y); ctx.lineTo(0, 0); ctx.lineTo(branch2.X, branch2.Y); ctx.stroke(); ctx.restore(); }, getHatchPattern(color = "black", reversed) { const canvasPattern = document.createElement("canvas"); canvasPattern.width = 10; canvasPattern.height = 10; const contextPattern = canvasPattern.getContext("2d"); contextPattern.strokeStyle = color; contextPattern.beginPath(); if (reversed) { contextPattern.moveTo(10,0); contextPattern.lineTo(0,10); } else { contextPattern.moveTo(0,0); contextPattern.lineTo(10,10); } contextPattern.stroke(); return canvasPattern; } }; module.exports = pathTracer;