381 lines
17 KiB
HTML
381 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>NodeSlicer - Path Operations Visual Test Suite</title>
|
|
<style>
|
|
:root {
|
|
--bg-color: #0f141c;
|
|
--card-bg: #1a2230;
|
|
--text-color: #e2e8f0;
|
|
--text-muted: #94a3b8;
|
|
--accent-color: #38bdf8;
|
|
--border-color: #334155;
|
|
--pass-color: #22c55e;
|
|
--fail-color: #ef4444;
|
|
}
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
background-color: var(--bg-color);
|
|
color: var(--text-color);
|
|
margin: 0;
|
|
padding: 24px;
|
|
}
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
header {
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding-bottom: 16px;
|
|
margin-bottom: 24px;
|
|
}
|
|
h1 {
|
|
color: var(--accent-color);
|
|
margin: 0 0 8px 0;
|
|
}
|
|
p.subtitle {
|
|
color: var(--text-muted);
|
|
margin: 0;
|
|
font-size: 1.1rem;
|
|
}
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(650px, 1fr));
|
|
gap: 24px;
|
|
}
|
|
.test-card {
|
|
background: var(--card-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.test-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 8px;
|
|
}
|
|
.test-header h3 {
|
|
margin: 0;
|
|
font-size: 1.2rem;
|
|
color: #f8fafc;
|
|
}
|
|
.badge {
|
|
padding: 4px 10px;
|
|
border-radius: 12px;
|
|
font-size: 0.8rem;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
.badge-pass {
|
|
background: rgba(34, 197, 94, 0.2);
|
|
color: var(--pass-color);
|
|
border: 1px solid var(--pass-color);
|
|
}
|
|
.badge-fail {
|
|
background: rgba(239, 68, 68, 0.2);
|
|
color: var(--fail-color);
|
|
border: 1px solid var(--fail-color);
|
|
}
|
|
.test-desc {
|
|
color: var(--text-muted);
|
|
margin: 0 0 12px 0;
|
|
font-size: 0.95rem;
|
|
}
|
|
.test-meta {
|
|
display: flex;
|
|
gap: 16px;
|
|
font-size: 0.85rem;
|
|
color: var(--text-muted);
|
|
margin-bottom: 16px;
|
|
padding: 8px 12px;
|
|
background: rgba(0, 0, 0, 0.2);
|
|
border-radius: 4px;
|
|
}
|
|
code {
|
|
background: #0f172a;
|
|
color: var(--accent-color);
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
}
|
|
.test-image img {
|
|
width: 100%;
|
|
height: auto;
|
|
border-radius: 6px;
|
|
border: 1px solid var(--border-color);
|
|
transition: transform 0.2s;
|
|
}
|
|
.test-image img:hover {
|
|
transform: scale(1.01);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header>
|
|
<h1>NodeSlicer - Path Operations Visual Test Suite</h1>
|
|
<p class="subtitle">Side-by-side verification gallery for Boolean Union, Multi-layer Minimum Coverage, Difference, XOR, Zip, and Extend operations.</p>
|
|
</header>
|
|
<div class="grid">
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC01: Maximum Coverage: Overlapping Squares</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Calculates the boolean union of two overlapping polygons into a single continuous boundary.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MAX_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/01_max_coverage_overlapping_squares.png" target="_blank">
|
|
<img src="images/01_max_coverage_overlapping_squares.png" alt="Maximum Coverage: Overlapping Squares" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC02: Maximum Coverage: Donut with Hole + Solid Box</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Correctly preserves hole topology during union, partially clipping the interior hole.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MAX_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 2</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/02_max_coverage_donut_and_box.png" target="_blank">
|
|
<img src="images/02_max_coverage_donut_and_box.png" alt="Maximum Coverage: Donut with Hole + Solid Box" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC03: Minimum Coverage: Multi-Layer Staggered Objects</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Verifies that empty paths on layers where only 1 object exists do NOT zero out the layer, while overlapping layers are properly intersected.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MIN_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 3</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/03_min_coverage_staggered_layers.png" target="_blank">
|
|
<img src="images/03_min_coverage_staggered_layers.png" alt="Minimum Coverage: Multi-Layer Staggered Objects" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC04: Minimum Coverage: Two Overlapping Squares</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Calculates the intersection region [10, 20] x [10, 20] between two active inputs.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MIN_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/04_min_coverage_overlapping_squares.png" target="_blank">
|
|
<img src="images/04_min_coverage_overlapping_squares.png" alt="Minimum Coverage: Two Overlapping Squares" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC05: Minimum Coverage: Single Object with Multiple Islands</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Ensures disjoint shapes within a single object are preserved intact rather than annihilated by self-intersection.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MIN_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 1</span>
|
|
<span><strong>Output Paths:</strong> 2</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/05_min_coverage_single_object_islands.png" target="_blank">
|
|
<img src="images/05_min_coverage_single_object_islands.png" alt="Minimum Coverage: Single Object with Multiple Islands" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC06: Boolean Intersection: Multi-Layer Staggered</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Standard intersection requires all operands to be present on each layer, producing output only on layer 2.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>INTERSECTION</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/06_intersection_staggered_layers.png" target="_blank">
|
|
<img src="images/06_intersection_staggered_layers.png" alt="Boolean Intersection: Multi-Layer Staggered" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC07: Subtract: Difference Creating Interior Hole</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Performs geometric subtraction producing outer boundary + interior hole contour.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>SUBTRACT</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 2</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/07_subtract_hole_creation.png" target="_blank">
|
|
<img src="images/07_subtract_hole_creation.png" alt="Subtract: Difference Creating Interior Hole" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC08: Exclusive Or (XOR): Symmetric Difference</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Calculates symmetric difference, removing the overlapping intersection.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>XOR</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 2</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/08_xor_overlapping_squares.png" target="_blank">
|
|
<img src="images/08_xor_overlapping_squares.png" alt="Exclusive Or (XOR): Symmetric Difference" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC09: Zip Paths: Layer-by-Layer Interleaving</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Interleaves toolpath streams layer-by-layer conserving metadata and extrusion attributes.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>ZIP</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 4</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/09_zip_paths_layer_interleaving.png" target="_blank">
|
|
<img src="images/09_zip_paths_layer_interleaving.png" alt="Zip Paths: Layer-by-Layer Interleaving" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC10: Extend Paths: Sequential Concatenation</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Concatenates multiple toolpath streams sequentially preserving all group IDs and attributes.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>EXTEND</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 4</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/10_extend_paths_sequential.png" target="_blank">
|
|
<img src="images/10_extend_paths_sequential.png" alt="Extend Paths: Sequential Concatenation" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC11: Maximum Coverage: Concentric Perimeters (2 Inputs)</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Calculates boolean union across concentric shells yielding the outsidemost perimeter boundary.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MAX_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/11_max_coverage_concentric_perimeters.png" target="_blank">
|
|
<img src="images/11_max_coverage_concentric_perimeters.png" alt="Maximum Coverage: Concentric Perimeters (2 Inputs)" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC12: Minimum Coverage: Concentric Perimeters (2 Inputs)</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Calculates boolean intersection across concentric shells yielding the insidemost perimeter boundary.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MIN_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 2</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/12_min_coverage_concentric_perimeters.png" target="_blank">
|
|
<img src="images/12_min_coverage_concentric_perimeters.png" alt="Minimum Coverage: Concentric Perimeters (2 Inputs)" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC13: Maximum Coverage: Single Input with Concentric Shells</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Unions concentric perimeter loops within a single input into the outermost boundary.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MAX_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 1</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/13_max_coverage_single_input_perimeters.png" target="_blank">
|
|
<img src="images/13_max_coverage_single_input_perimeters.png" alt="Maximum Coverage: Single Input with Concentric Shells" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="test-card">
|
|
<div class="test-header">
|
|
<h3>TC14: Minimum Coverage: Single Input with Concentric Shells</h3>
|
|
<span class="badge badge-pass">PASS</span>
|
|
</div>
|
|
<p class="test-desc">Intersects concentric perimeter loops within a single input into the innermost boundary.</p>
|
|
<div class="test-meta">
|
|
<span><strong>Operation:</strong> <code>MIN_COVERAGE</code></span>
|
|
<span><strong>Inputs:</strong> 1</span>
|
|
<span><strong>Output Paths:</strong> 1</span>
|
|
</div>
|
|
<div class="test-image">
|
|
<a href="images/14_min_coverage_single_input_perimeters.png" target="_blank">
|
|
<img src="images/14_min_coverage_single_input_perimeters.png" alt="Minimum Coverage: Single Input with Concentric Shells" loading="lazy" />
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|