### Curve + Line 5 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Performs clipping between a shape with mixed line and curve segments and a Bezier curve shape. This example tests clipping with curves that have different start and end points. ```javascript { name: 'Curve + Line 5', shape1: [ [ [600,60],[600,150],[560,190],[560,110],[500,180],[380,120] ] ], shape2: [ [ [400,50],[400,150,200,200,200,50] ] ], } ``` -------------------------------- ### Simple Boxes Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Demonstrates clipping between two simple rectangular polygons. This case tests the intersection of two basic box shapes. ```javascript { name: 'Simple Boxes', poly1: { regions: [ [ [500,60],[500,150],[200,150],[200,60] ] ], inverted: false }, poly2: { regions: [ [ [500,60],[500,150],[380,150],[380,60] ] ], inverted: false } } ``` -------------------------------- ### Curved Shapes 1 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two polygons defined with Bezier curve segments. This example focuses on the interaction between curved boundaries. ```javascript { name: 'Curved Shapes 1', poly1: { regions: [ [ [450,150],[200,150,200,60,450,60] ] ], inverted: false }, poly2: { regions: [ [ [500,150],[380,150,380,60,500,60] ] ], inverted: false }, } ``` -------------------------------- ### Curved Shapes 2 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example tests clipping with different curved boundary configurations. ```javascript { name: 'Curved Shapes 2', shape1: [ [ [410,160],[200,150,200,60,450,60] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Curve + Line 2 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips a shape with mixed line and curve segments against a shape defined by Bezier curves. This example tests different configurations of mixed geometries. ```javascript { name: 'Curve + Line 2', shape1: [ [ [600,60],[600,150],[560,190],[560,110],[500,180],[360,110] ] ], shape2: [ [ [400,50],[400,150,200,200,200,50] ] ], } ``` -------------------------------- ### Simple Rectangles Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Demonstrates clipping between two simple rectangular shapes. This serves as a basic test case for polygon intersection. ```javascript { name: 'Simple Rectangles', shape1: [ [ [200,50],[600,50],[600,150],[200,150] ] ], shape2: [ [ [300,150],[500,150],[500,200],[300,200] ] ], } ``` -------------------------------- ### Open Paths Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Demonstrates clipping with shapes that include open paths, indicated by the `shapeXOpen` arrays. This tests the library's ability to process geometries that do not form closed loops. ```javascript { name: 'Open Paths', shape1: [ [ [540,60],[540,180],[420,180],[420,60] ], [ [130,160],[350,60],[560,160] ] ], shape1Open: [1], shape2: [ [ [280,60],[280,180],[150,180],[150,60] ], [ [130,80],[280,180,420,180,560,80] ] ], shape2Open: [1], } ``` -------------------------------- ### Curved Shapes 4 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example tests clipping with different curve start and end points. ```javascript { name: 'Curved Shapes 4', shape1: [ [ [340,150],[200,150,200,60,450,60] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Curved Shapes 3 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example explores clipping with varying curve parameters. ```javascript { name: 'Curved Shapes 3', shape1: [ [ [450,100],[200,150,200,60,450,60] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Curve + Line 4 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips a shape composed of lines and curves against a Bezier curve shape. This example tests clipping with different curve control points. ```javascript { name: 'Curve + Line 4', shape1: [ [ [600,60],[600,150],[560,190],[560,110],[500,180],[240,160] ] ], shape2: [ [ [400,50],[400,150,200,200,200,50] ] ], } ``` -------------------------------- ### Assorted Polygons Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Demonstrates clipping operations on two polygons with complex, non-convex shapes. This case highlights the library's ability to handle intricate polygon definitions. ```javascript var polyCases = [ { name: 'Assorted Polygons', poly1: { regions: [ [ [500,60],[500,150],[320,150],[260,210],[200,150],[200,60] ] ], inverted: false }, poly2: { regions: [ [ [500,60],[500,150],[460,190],[460,110],[400,180],[160,90] ], [ [220,170],[260,30],[310,160],[310,210],[260,170],[240,190] ] ], inverted: false } } ``` -------------------------------- ### Curved Shapes 8 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two polygons defined with Bezier curve segments. This example tests clipping with polygons that have identical curved boundaries. ```javascript { name: 'Curved Shapes 8', poly1: { regions: [ [ [450,150],[200,150,200,60,450,60] ] ], inverted: false }, poly2: { regions: [ [ [500,150],[380,150,380,60,380,60] ] ], inverted: false }, } ``` -------------------------------- ### Curved Shapes 10 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two polygons defined with Bezier curve segments. This example tests clipping with polygons where one has a more complex curved boundary. ```javascript { name: 'Curved Shapes 10', poly1: { regions: [ [ [450,150],[200,150,200,60,450,60] ] ], inverted: false }, poly2: { regions: [ [ [510,140],[380,150,290,130,240,20] ] ], inverted: false }, } ``` -------------------------------- ### Get Start Point of a Segment Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Recursively retrieves the starting point of a segment, handling nested segments or segments with associated data. ```javascript function segStart(seg) { if ('seg' in seg) { return segStart(seg.seg); } return 'data' in seg ? seg.data.p0 : seg.p0; } ``` -------------------------------- ### Curved Shapes 5 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example demonstrates clipping with curves positioned differently relative to each other. ```javascript { name: 'Curved Shapes 5', shape1: [ [ [460,70],[200,150,200,60,450,60] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Curve + Line 1 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Demonstrates clipping between a shape defined by a series of line segments and Bezier curves, and another shape defined solely by Bezier curves. This tests mixed geometry clipping. ```javascript { name: 'Curve + Line 1', shape1: [ [ [600,60],[600,150],[560,190],[560,110],[500,180],[260,90] ] ], shape2: [ [ [400,50],[400,150,200,200,200,50] ] ], } ``` -------------------------------- ### Curved Shapes 7 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example explores clipping with curves that have varying heights and widths. ```javascript { name: 'Curved Shapes 7', shape1: [ [ [450,120],[200,150,200,60,450,60] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Curved Leg Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Illustrates clipping with shapes that include Bezier curve segments, defined using a specific format for shape points. This case tests the handling of curved boundaries. ```javascript { name: 'Curved Leg', shape1: [ [ [190,50],[530,50],[530,170],[190,170] ] ], shape2: [ [ [330,130],[230,130,290,170,190,170],[470,170],[470,130] ] ] } ``` -------------------------------- ### Curved Shapes 9 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two polygons defined with Bezier curve segments. This example demonstrates clipping with polygons that have overlapping curved boundaries. ```javascript { name: 'Curved Shapes 9', poly1: { regions: [ [ [290,150],[200,150,200,60,270,60] ] ], inverted: false }, poly2: { regions: [ [ [290,150],[380,150,380,60,290,60] ] ], inverted: false }, } ``` -------------------------------- ### Curve + Line 3 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Performs clipping between a shape with line and curve segments and a Bezier curve shape. This case explores clipping with varying curve endpoints. ```javascript { name: 'Curve + Line 3', shape1: [ [ [600,60],[600,150],[560,190],[560,110],[500,180],[270,130] ] ], shape2: [ [ [400,50],[400,150,200,200,200,50] ] ], } ``` -------------------------------- ### M Shape Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html This snippet is incomplete and likely intended to define a polygon with an 'M' shape for clipping operations. ```javascript { name: 'M Shape', poly1: { regions: [ ``` -------------------------------- ### Curved Shapes 6 Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two shapes defined with Bezier curve segments. This example tests clipping with curves that have different control points and endpoints. ```javascript { name: 'Curved Shapes 6', shape1: [ [ [450,80],[200,150,200,60,450,10] ] ], shape2: [ [ [500,150],[380,150,380,60,500,60] ] ] } ``` -------------------------------- ### Simple Self-Overlap Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips a polygon against itself, resulting in self-overlap. This tests the library's handling of degenerate cases and self-intersecting polygons. ```javascript { name: 'Simple Self-Overlap', poly1: { regions: [ [ [200,50],[400,50],[400,150],[200,150] ] ], inverted: false }, poly2: { regions: [ [ [400,150],[500,150],[300,50],[400,50] ] ], inverted: false } } ``` -------------------------------- ### Initialize Chain Tracking Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Stores the last segment for chain tracking and resets the match flag. Used at the start of a new chain segment. ```javascript bl_last_seg_keep = bl; bl_last_seg_keep_match = false; break; ``` -------------------------------- ### Shared Right Edge Example Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clips two polygons where they share a common edge on their right side. This tests the precision of the clipping algorithm when boundaries align. ```javascript { name: 'Shared Right Edge', poly1: { regions: [ [ [500,60],[500,150],[200,150],[200,60] ] ], inverted: false }, poly2: { regions: [ [ [500,60],[500,150],[450,230],[400,180],[590,60] ] ], inverted: false } } ``` -------------------------------- ### Create Shapes with Bezier Curves Source: https://context7.com/velipso/polybool/llms.txt Use `bezierCurveTo` to define shapes with smooth, curved boundaries. This example demonstrates creating a shape with a cubic bezier curve and performing an intersection with a rectangle. ```typescript import polybool from '@velipso/polybool'; const curvedShape = polybool.shape() .beginPath() .moveTo(50, 50) .bezierCurveTo(100, 0, 150, 100, 200, 50) // cp1x, cp1y, cp2x, cp2y, x, y .lineTo(200, 150) .lineTo(50, 150) .closePath(); const straightShape = polybool.shape() .beginPath() .rect(75, 75, 100, 100); const result = curvedShape.combine(straightShape).intersect(); const paths: string[] = []; result.output({ beginPath: () => {}, moveTo: (x, y) => paths.push(`M${x},${y}`), lineTo: (x, y) => paths.push(`L${x},${y}`), bezierCurveTo: (cp1x, cp1y, cp2x, cp2y, x, y) => { paths.push(`C${cp1x},${cp1y} ${cp2x},${cp2y} ${x},${y}`); }, closePath: () => paths.push('Z') }); console.log(paths.join(' ')); ``` -------------------------------- ### Add New Chain Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Adds a new chain to the list with its starting segment and assigns a new chain ID. Resets the last segment tracker. ```javascript setChainClosed(bl.closed); bl_chains_x.push([bl.sf]); bl_chainids_x.push(bl_nextchainid++); bl_last_seg_keep = false; break; ``` -------------------------------- ### Navigate Between Demos Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Controls for cycling through different polygon demonstration cases. Use `nextDemo(1)` to advance and `nextDemo(-1)` to go back. ```javascript var nextDemoIndex = -1; var caseName, demo, polyBox; var scaleToFit = false; function nextDemo(d){ nextDemoIndex = (((nextDemoIndex + d) % polyCases.length) + polyCases.length) % polyCases.length; demo = polyCases[nextDemoIndex]; caseName = (nextDemoIndex + 1) + '. ' + demo.name; window.demo = demo; polyBox = { min: [false, false], max: [false, false] }; const polyBoxPoint = (x, y) => { if (polyBox.min[0] === false || x < polyBox.min[0]) polyBox.min[0] = x; if (polyBox.min[1] === false || y < polyBox.min[1]) polyBox.min[1] = y; if (polyBox.max[0] === false || x > polyBox.max[0]) polyBox.max[0] = x; if (polyBox.max[1] === false || y > polyBox.max[1]) polyBox.max[1] = y; }; const calcBox = (regions) => { for (const region of regions) { for (const pt of region) { for (let i = 0; i < pt.length; i += 2) { polyBoxPoint(pt[i], pt[i + 1]); } } } }; if ('poly1' in demo) { calcBox(demo.poly1.regions); calcBox(demo.poly2.regions); } else { calcBox(demo.shape1); calcBox(demo.shape2); } recalc(); } ``` -------------------------------- ### Build Log Playback Controls Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Manages the playback of a build log, allowing stepping forward, backward, and playing/pausing. Used for visualizing the steps of a polygon clipping operation. ```javascript var buildLogMax = 0; var buildLogTimer = false; function buildLogNext(delta){ buildLogMax += delta; if (buildLogMax < 0) buildLogMax = 0; if (buildLogMax > clipResult.build_log.length) buildLogMax = clipResult.build_log.length; redraw(); return buildLogMax >= clipResult.build_log.length; } function buildLogNextWrap(delta){ buildLogStop(); if (buildLogMax <= 0 && delta < 0){ buildLogMax = clipResult.build_log.length; redraw(); } else if (buildLogMax >= clipResult.build_log.length && delta > 0){ buildLogMax = 0; redraw(); } else buildLogNext(delta); } function buildLogStop(){ if (buildLogTimer === false) return; clearInterval(buildLogTimer); buildLogTimer = false; document.getElementById('bl_play').innerHTML = 'Play'; } function buildLogPlay(){ if (buildLogTimer === false){ if (buildLogNext(1)) buildLogMax = -1; buildLogTimer = setInterval(function(){ if (buildLogNext(1)){ buildLogStop(); buildLogTimer = setInterval(function(){ buildLogNextWrap(1); clearInterval(buildLogTimer); }, 100); } }, 100); } else{ clearInterval(buildLogTimer); buildLogTimer = false; } document.getEle ``` -------------------------------- ### Initialize PolyBool Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Initializes the PolyBool library with a GeometryEpsilon for precision. This is a prerequisite for performing any clipping operations. ```javascript import { PolyBool, GeometryEpsilon } from '../dist/polybool.js'; const polybool = new PolyBool(new GeometryEpsilon()); ``` -------------------------------- ### Instructional API - Shape Creation Source: https://github.com/velipso/polybool/blob/main/README.md Explains how to create shapes using the Instructional API, similar to CanvasRenderingContext2D. ```APIDOC ## Instructional API - Shape Creation ### Description Shapes are created using a chain of methods mimicking the CanvasRenderingContext2D API. Supports multiple regions and open paths. ### Method `polybool.shape()` followed by methods like `beginPath`, `moveTo`, `lineTo`, `bezierCurveTo`, `closePath`. ### Parameters - **x, y** (number) - Coordinates for points. - **cp1x, cp1y, cp2x, cp2y** (number) - Control points for bezier curves. ### Request Example ```typescript const shape = polybool.shape() .beginPath() .moveTo(50, 50) .lineTo(150, 150) .lineTo(190, 50) .closePath() .moveTo(130, 50) .lineTo(290, 150) .lineTo(290, 50) .closePath(); ``` ### Response - **Shape** (object) - A shape object that can be further combined or outputted. ``` -------------------------------- ### Instructional API - Combining Shapes Source: https://github.com/velipso/polybool/blob/main/README.md Details how to combine multiple shapes before performing boolean operations. ```APIDOC ## Instructional API - Combining Shapes ### Description Combine multiple shapes into a single object for subsequent boolean operations. Once a shape is used in a combine operation, it cannot be modified further. ### Method `shape1.combine(shape2)` ### Parameters - **shape1** (Shape) - The first shape. - **shape2** (Shape) - The second shape. ### Request Example ```typescript const combinedShape = shape1.combine(shape2); ``` ### Response - **ShapeCombined** (object) - An object representing the combination of shapes, ready for boolean operations. ``` -------------------------------- ### Initialize PolyBool with Custom Epsilon Source: https://github.com/velipso/polybool/blob/main/README.md This TypeScript code shows how to initialize the PolyBool class with a custom epsilon value for floating-point comparisons. This is useful when dealing with polygons of very large or very small dimensions. ```typescript import { PolyBool, GeometryEpsilon } from '@velipso/polybool'; const polybool = new PolyBool(new GeometryEpsilon(newEpsilonValue)); ``` -------------------------------- ### Instructional API - Outputting Results Source: https://github.com/velipso/polybool/blob/main/README.md Explains how to output a shape (original or result of an operation) to a receiver. ```APIDOC ## Instructional API - Outputting Results ### Description Output any shape object to a receiver object, which must implement the `IPolyBoolReceiver` interface. ### Method `shape.output(receiver)` ### Parameters - **shape** (Shape) - The shape object to output. - **receiver** (T extends IPolyBoolReceiver) - An object with methods like `beginPath`, `moveTo`, `lineTo`, `bezierCurveTo`, `closePath`. ### Request Example ```typescript const receiver = { ... }; // An object implementing IPolyBoolReceiver shape.output(receiver); ``` ### Response - **T** (object) - The receiver object is returned after the output operation. ``` -------------------------------- ### Enable Debug Logging in Polybool Source: https://context7.com/velipso/polybool/llms.txt Shows how to enable and inspect the build log for debugging polygon operations. Remember to disable logging when it's no longer needed. ```typescript import { PolyBool, GeometryEpsilon } from '@velipso/polybool'; const polybool = new PolyBool(new GeometryEpsilon()); // Enable logging const log = polybool.buildLog(true); const poly1 = { regions: [[[0, 0], [100, 0], [100, 100], [0, 100]]], inverted: false }; const poly2 = { regions: [[[50, 50], [150, 50], [150, 150], [50, 150]]], inverted: false }; const result = polybool.union(poly1, poly2); // Inspect the build log for debugging console.log('Build log entries:', log?.length); if (log) { log.forEach((entry, i) => console.log(`${i}: ${JSON.stringify(entry)}`)); } // Disable logging when done polybool.buildLog(false); ``` -------------------------------- ### Get End Point of a Segment Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Recursively retrieves the ending point of a segment, handling nested segments, segments with data, or different endpoint properties. ```javascript function segEnd(seg) { if ('seg' in seg) { return segEnd(seg.seg); } if ('data' in seg) { if ('p3' in seg.data) { return seg.data.p3; } return seg.data.p1; } if ('p3' in seg) { return seg.p3; } return seg.p1; } ``` -------------------------------- ### Create a Shape with Multiple Regions Source: https://github.com/velipso/polybool/blob/main/README.md Demonstrates creating a shape with multiple disjoint regions using moveTo calls. Supports open and closed paths; closePath is required for filled paths. ```typescript const shape = polybool.shape() .beginPath() .moveTo(50, 50) .lineTo(150, 150) .lineTo(190, 50) .closePath() .moveTo(130, 50) .lineTo(290, 150) .lineTo(290, 50) .closePath(); ``` -------------------------------- ### Polygon Simplification Source: https://github.com/velipso/polybool/blob/main/README.md Demonstrates an efficient method for cleaning up polygon data by converting it to segments and back. ```APIDOC ## Polygon Simplification ### Description This method efficiently simplifies polygon data by converting it to segments and then reconstructing the polygon. ### Method `polybool.polygon(polybool.segments(polygon))` ### Parameters - **polygon** (object) - The input polygon data to be simplified. ### Request Example ```typescript const polygon = { regions: [[ [0,0], [1,0], [1,1], [0,1] ]], inverted: false }; const cleaned = polybool.polygon(polybool.segments(polygon)); ``` ### Response - **cleaned** (object) - The simplified polygon object. ``` -------------------------------- ### Initialize Canvas and Transformations Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Initializes the canvas element, sets its display size, and applies transformations for high-DPI screens and inverted Y-axis. This function is essential for setting up the drawing environment. ```javascript function init(){ cnv = document.getElementById('cnv'); ctx = cnv.getContext('2d'); cnv.style.width = cnv.width / wscale + 'px'; cnv.style.height = cnv.height / wscale + 'px'; // make y go up and scale by 2 (for high DPI screens) ctx.transform(rscale, 0, 0, -rscale, 0, cnv.height); cnvWidth = cnv.width / rscale; cnvHeight = cnv.height / rscale; nextDemo(1); function mousePos(e){ var rect = cnv.getBoundingClientRect(); return [ e.clientX - rect.left, cnvHeight - e.clientY + rect.top ]; } function closestPoint(regions, x, y){ x = unscaleX(x); y = unscaleY(y); var reg = false; var vert = false; var len2 = false; var j = false; regions.forEach(function(region){ for (var i = 0; i < region.length; i++){ const pt = region[i]; for (var jj = 0; jj < pt.length; jj += 2) { var dx = scaleX(pt[jj] - x); var dy = scaleY(pt[jj + 1] - y); var thisLen2 = dx * dx + dy * dy; if (len2 === false || thisLen2 < len2){ reg = region; vert = i; j = jj; len2 = thisLen2; } } } }); return { region: reg, vert: vert, j: j, len: Math.sqrt(len2) }; } function mouseTrackHover(mp){ // look for the closest node let regions1, regions2; if ('poly1' in demo) { regions1 = demo.poly1.regions; regions2 = demo.poly2.regions; } else { regions1 = demo.shape1; regions2 = demo.shape2; } var p1 = closestPoint(regions1, mp[0], mp[1] - cnvHeight / 2); var p2 = closestPoint(regions2, mp[0], mp[1] - cnvHeight / 2); if (p2.len < p1.len) p1 = p2; if (p1.len > 10) { if (hover !== false) { hover = false; redraw(); } return; } if ( hover === false || hover.region !== p1.region || hover.vert !== p1.vert || hover.j !== p1.j ) { hover = p1; redraw(); } } var dragging = false; cnv.addEventListener('mousemove', function(e){ var mp = mousePos(e); if (dragging){ var dx = mp[0] - dragging[0]; var dy = mp[1] - dragging[1]; var pt = hover.region[hover.vert]; const ix = hover.j; const iy = hover.j + 1; if (pt[iy] + dy < 0) dy = -pt[iy]; if (document.getElementById('snap').checked){ var tx = pt[ix] + dx; var ty = pt[iy] + dy; tx = Math.round(tx / 10) * 10; ty = Math.round(ty / 10) * 10; dx = tx - pt[ix]; dy = ty - pt[iy]; } if (dx !== 0 || dy !== 0){ dragging = [dragging[0] + dx, dragging[1] + dy]; pt[ix] = unscaleX(scaleX(pt[ix]) + dx); pt[iy] = unscaleY(scaleY(pt[iy]) + dy); recalc(); } } else mouseTrackHover(mp); if (window.debugMousePos === true){ ctx.save(); var mx = mp[0], my = mp[1] - cnvHeight / 2; if (hover !== false){ mx = hover.region[hover.vert][hover.j]; my = hover.region[hover.vert][hover.j + 1]; } ctx.setTransform(rscale, 0, 0, rscale, 0, 0); ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, 100, 20); ctx.fillStyle = '#000'; ctx.textAlign = 'left' ctx.textBaseline = 'top'; ctx.fillText('(' + mx + ', ' + my + ')', 0, 0); ctx.restore(); } }); cnv.addEventListener('mouseup', function(e){ var mp = mousePos(e); if (dragging){ dragging = false; mouseTrackHover(mp); redraw(); } else mouseTrackHover(mp); }); cnv.addEventListener('mouseleave', function(e){ if (dragging){ dragging = false; hover = false; redraw(); } }); cnv.addEventListener('mousedown', function(e){ var mp = mousePos(e); mouseTrackHover(mp); if (hover !== false){ dragging = mp; // begin dragging e.preventDefault(); } }); document.addEventListener('keydown', function(e){ if (e.keyCode === 37){ // left buildLogNextWrap(e.shiftKey ? -10 : -1); e.preventDefault(); } else if (e.keyCode === 39){ // right buildLogNextWrap(e.shiftKey ? 10 : 1); e.preventDefault(); } else if (e.keyCode === 32){ // space buildLogPlay(); e.preventDefault(); } }); } window.demo = demo; window.init = init; window.recalc = recalc; window.setMode = setMode; window.nextDemo = nextDemo; window.buildLogNextWrap = buildLogNextWrap; window.buildLogPlay = buildLogPlay; ``` -------------------------------- ### Create Rectangles with Instructional API Source: https://context7.com/velipso/polybool/llms.txt Quickly create rectangular shapes using the `rect` method. The output can be directed to a receiver for further processing, such as generating SVG path data. ```typescript import polybool from '@velipso/polybool'; const shape = polybool.shape() .beginPath() .rect(10, 10, 80, 80) // x, y, width, height .closePath(); const pathData: string[] = []; const svgReceiver = { beginPath: () => {}, moveTo: (x: number, y: number) => pathData.push(`M ${x} ${y}`), lineTo: (x: number, y: number) => pathData.push(`L ${x} ${y}`), bezierCurveTo: (cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number) => { pathData.push(`C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${x} ${y}`); }, closePath: () => pathData.push('Z') }; shape.output(svgReceiver); console.log(pathData.join(' ')); // Output: "M 10 10 L 90 10 L 90 90 L 10 90 Z" ``` -------------------------------- ### Instructional API - Performing Operations Source: https://github.com/velipso/polybool/blob/main/README.md Shows how to perform boolean operations (union, intersect, difference) on combined shapes. ```APIDOC ## Instructional API - Performing Operations ### Description Perform boolean operations on a combined shape to generate new shapes. ### Method - `combinedShape.union()` - `combinedShape.intersect()` - `combinedShape.difference()` (shape1 - shape2) - `combinedShape.differenceRev()` (shape2 - shape1) - `combinedShape.xor()` ### Parameters - **combinedShape** (ShapeCombined) - The combined shape object. ### Request Example ```typescript const unionResult = combinedShape1.union(); const intersectionResult = combinedShape1.intersect(); ``` ### Response - **Shape** (object) - The resulting shape after the boolean operation. ``` -------------------------------- ### Efficient Polygon Union using Polygonal API Source: https://github.com/velipso/polybool/blob/main/README.md Demonstrates an efficient method for unioning multiple polygons by directly using the Polygonal API, avoiding repeated conversions between polygon and segment formats. ```typescript // works AND efficient let segments = polybool.segments(polygons[0]); for (let i = 1; i < polygons.length; i++){ const seg2 = polybool.segments(polygons[i]); const comb = polybool.combine(segments, seg2); segments = polybool.selectUnion(comb); } return polybool.polygon(segments); ``` -------------------------------- ### Shape Class Output Method (Partial) Source: https://github.com/velipso/polybool/blob/main/README.md Shows the output method signature for the Shape class, which accepts a generic receiver implementing IPolyBoolReceiver and returns the receiver. ```typescript class Shape { // ...continued from above output(receiver: T): T; } ``` -------------------------------- ### Redraw Canvas and Process Build Log Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Clears the canvas, sets up drawing styles, and processes a build log to update segment data and status. Handles various log entry types like 'info', 'vert', 'new_seg', 'check', and 'pop_seg'. ```javascript function redraw(){ ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, cnvWidth, cnvHeight); ctx.lineWidth = 2; var labels = []; // this is quite the mess... sorry... var bl_oldsegs = []; var bl_segs = []; var bl_segid = {}; var bl_vert = 0; var bl_last_check = false; var bl_last_check_i1 = false; var bl_last_check_i2 = false; var bl_last_div_seg = false; var bl_last_pop_seg = false; var bl_last_pop_seg_i = false; var bl_last_status = false; var bl_last_temp_status = false; var bl_last_temp_status_i = false; var bl_last_chop = false; var bl_last_seg_keep = false; var bl_last_seg_keep_match = false; var bl_last_done = false; var bl_finish = false; var bl_status = []; var bl_chains_o = []; var bl_chainids_o = []; var bl_oldchains_o = []; var bl_oldchainids_o = []; var bl_chains_c = []; var bl_chainids_c = []; var bl_oldchains_c = []; var bl_oldchainids_c = []; var bl_chains_x = bl_chains_o; var bl_chainids_x = bl_chainids_o; var bl_oldchains_x = bl_oldchains_o; var bl_oldchainids_x = bl_oldchainids_o; const setChainClosed = (closed) => { bl_chains_x = closed ? bl_chains_c : bl_chains_o; bl_chainids_x = closed ? bl_chainids_c : bl_chainids_o; bl_oldchains_x = closed ? bl_oldchains_c : bl_oldchains_o; bl_oldchainids_x = closed ? bl_oldchainids_c : bl_oldchainids_o; }; var bl_nextchainid = 0; var bl_phase = 0; var bl_selected = false; var bl_info = []; window.buildLogMax = buildLogMax; clipResult.build_log.forEach(function(blw, i){ if (i >= buildLogMax) { if (i === buildLogMax) console.log(clipResult.build_log[i - 1]); return; } var bl = blw.data; bl_last_check = false; bl_last_div_seg = false; bl_last_pop_seg = false; bl_last_status = false; bl_last_chop = false; if (bl_last_done) { bl_oldsegs = []; } bl_last_done = false; switch (blw.type){ case 'info': bl_info.unshift(blw.data); break; case 'vert': bl_vert = bl.x; break; case 'new_seg': for (var i = 0; i < bl_segs.length; i++){ if (bl_segs[i].id === bl.seg.id){ bl_segs.splice(i, 1); break; } } for (var i = 0; i < bl_oldsegs.length; i++){ if (bl_oldsegs[i].id === bl.seg.id){ bl_oldsegs.splice(i, 1); break; } } bl_segs.push(bl.seg); bl_segid[bl.seg.id] = { phase: bl_phase, primary: bl.primary }; break; case 'check': bl_last_check = bl; bl_last_check_i1 = false; bl_last_check_i2 = false; for (var i = 0; i < bl_status.length; i++){ if (bl_status[i].id === bl.seg1.id) bl_last_check_i1 = i; if (bl_status[i].id === bl.seg2.id) bl_last_check_i2 = i; } break; case 'pop_seg': bl_last_pop_seg = bl; for (var i = 0; i < ``` -------------------------------- ### Compare Floating Point Numbers with Epsilon Source: https://github.com/velipso/polybool/blob/main/README.md This JavaScript code demonstrates the standard method for comparing floating-point numbers for equality using an epsilon value to account for potential inaccuracies in floating-point arithmetic. ```javascript if (Math.abs(A - B) < epsilon) /* A and B are equal */; else /* A and B are not equal */; ``` -------------------------------- ### Polygon Operations using Instructional API Source: https://github.com/velipso/polybool/blob/main/README.md Utilizes a chainable instructional API to define shapes, combine them, perform boolean operations (like intersection), and output the results to a receiver object. This API allows for a more declarative approach to polygon manipulation. ```typescript import polybool from '@velipso/polybool'; const shape1 = polybool.shape() .beginPath() .moveTo(50, 50) .lineTo(150, 150) .lineTo(190, 50) .closePath() .moveTo(130, 50) .lineTo(290, 150) .lineTo(290, 50) .closePath(); const shape2 = polybool.shape() .beginPath() .moveTo(110, 20) .lineTo(110, 110) .lineTo(20, 20) .closePath() .moveTo(130, 170) .lineTo(130, 20) .lineTo(260, 20) .lineTo(260, 170) .closePath(); const receiver = { beginPath: () => { console.log('beginPath'); }, moveTo: (x: number, y: number) => { console.log('moveTo', x, y); }, lineTo: (x: number, y: number) => { console.log('lineTo', x, y); }, bezierCurveTo: ( cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number, ) => { console.log('bezierCurveTo', cp1x, cp1y, cp2x, cp2y, x, y); }, closePath: () => { console.log('closePath'); } } // start with the first shape shape1 // combine it with the second shape .combine(shape2) // perform the operation .intersect() // output results to the receiver object .output(receiver); ``` -------------------------------- ### Coordinate Scaling Utilities Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Functions for scaling and unscaling coordinates based on a bounding box and canvas dimensions. Useful for fitting and positioning polygons within a view. ```javascript function scaleX(x) { if (!scaleToFit) return x; return (x - polyBox.min[0]) * 650 / (polyBox.max[0] - polyBox.min[0]) + 25; } function scaleY(y) { if (!scaleToFit) return y; return (y - polyBox.min[1]) * 200 / (polyBox.max[1] - polyBox.min[1]) + 25; } function unscaleX(x) { if (!scaleToFit) return x; return (x - 25) * (polyBox.max[0] - polyBox.min[0]) / 650 + polyBox.min[0]; } function unscaleY(y) { if (!scaleToFit) return y; return (y - 25) * (polyBox.max[1] - polyBox.min[1]) / 200 + polyBox.min[1]; } ``` -------------------------------- ### Handle Inverted Polygons with Polybool Source: https://context7.com/velipso/polybool/llms.txt Demonstrates how to define and use inverted polygons, which represent the area outside a given region. Intersecting with an inverted polygon effectively creates a hole. ```typescript import polybool from '@velipso/polybool'; // A normal square const square = { regions: [[[0, 0], [100, 0], [100, 100], [0, 100]]], inverted: false }; // An inverted circle (everything except the circle area) const invertedHole = { regions: [[[25, 25], [75, 25], [75, 75], [25, 75]]], inverted: true // This represents "everything except this region" }; // Intersecting with an inverted polygon creates a hole const result = polybool.intersect(square, invertedHole); console.log(result); // Creates a square with a rectangular hole in the middle ``` -------------------------------- ### Shape Class Definition (Partial) Source: https://github.com/velipso/polybool/blob/main/README.md Provides the method signatures for the Shape class, including path drawing and curve methods. Note that bezierCurveTo support is experimental. ```typescript class Shape { beginPath(): Shape; moveTo(x: number, y: number): Shape; lineTo(x: number, y: number): Shape; bezierCurveTo( cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number, ): Shape; closePath(): Shape; // ...continued below } ``` -------------------------------- ### Simplified Polygonal API Operations Source: https://github.com/velipso/polybool/blob/main/README.md Perform union, intersection, difference, and XOR operations using the simplified API. These are convenient wrappers for the Polygonal API. ```typescript import polybool from '@velipso/polybool'; const poly = polybool.union(poly1, poly2); const poly = polybool.intersect(poly1, poly2); const poly = polybool.difference(poly1, poly2); // poly1 - poly2 const poly = polybool.differenceRev(poly1, poly2); // poly2 - poly1 const poly = polybool.xor(poly1, poly2); ``` -------------------------------- ### Configure Custom Epsilon for Precision Source: https://context7.com/velipso/polybool/llms.txt Instantiate PolyBool with a custom epsilon value for precise floating-point comparisons, especially useful for very large or very small coordinate systems. The default epsilon is 0.0000000001. ```typescript import { PolyBool, GeometryEpsilon } from '@velipso/polybool'; // Create a custom PolyBool instance with different epsilon const customPolybool = new PolyBool(new GeometryEpsilon(0.001)); const poly1 = { regions: [[[0, 0], [1000000, 0], [1000000, 1000000], [0, 1000000]]], inverted: false }; const poly2 = { regions: [[[500000, 500000], [1500000, 500000], [1500000, 1500000], [500000, 1500000]]], inverted: false }; const result = customPolybool.intersect(poly1, poly2); console.log(result); // Default epsilon is 0.0000000001 // Increase for large coordinates, decrease for tiny coordinates ``` -------------------------------- ### Canvas Drawing and Information Display Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Draws phase information, a progress bar, and log messages onto the canvas. It uses transformations for scaling and positioning text and shapes. ```javascript ctx.save(); ctx.setTransform(rscale, 0, 0, rscale, 0, 0); ctx.font = '13px sans-serif'; ctx.textAlign = 'right'; ctx.fillStyle = '#000'; ctx.fillText(phase, cnvWidth - 4, cnvHeight / 2 + 16); ctx.restore(); ctx.fillStyle = '#999'; ctx.fillRect(0, 0, cnvWidth * buildLogMax / clipResult.build_log.length, 3); ctx.beginPath(); for (var x = 0; x < cnvWidth; x += 10){ ctx.moveTo(x, cnvHeight / 2); ctx.lineTo(x + 5, cnvHeight / 2); } ctx.strokeStyle = 'rgba(0, 0, 0, 0.3)'; ctx.lineWidth = 1; ctx.stroke(); // draw info log ctx.save(); ctx.setTransform(rscale, 0, 0, rscale, 0, 0); ctx.font = '13px monospace'; ctx.textAlign = 'left'; ctx.fillStyle = '#000'; for (let y = 210, i = 0; y >= 0 && i < bl_info.length; y -= 20, i++) { const { msg, data } = bl_info[i]; ctx.fillText(msg + ( typeof data === 'undefined' ? '' : ' ' + JSON.stringify(data) ), 10, y); } ctx.restore(); ``` -------------------------------- ### Join and Simplify Chains Source: https://github.com/velipso/polybool/blob/main/demo/demo.html Joins two chains by removing the last segment of the first and the first segment of the second, then appends the modified second chain to the first. Updates closed status. ```javascript setChainClosed(bl.closed); bl_chains_x[bl.index1].pop(); bl_chains_x[bl.index2].shift(); bl_chains_x[bl.index1].push(bl.sf); break; ```