nodeslicer/tests/visual/index.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>