Skip to content

Commit 418675e

Browse files
committed
Add boolean operations debugging and progress docs
Added BOOLEAN_OPERATIONS_DEBUGGING.md and BOOLEAN_OPERATIONS_PROGRESS.md to document the debugging process and implementation status of boolean operations in Two.js. These guides provide step-by-step debugging instructions, current issues, design decisions, and next steps for developers working on or maintaining the boolean operations feature.
1 parent f99c980 commit 418675e

File tree

2 files changed

+646
-0
lines changed

2 files changed

+646
-0
lines changed

BOOLEAN_OPERATIONS_DEBUGGING.md

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
# Boolean Operations Debugging Guide
2+
3+
## Quick Start
4+
5+
When you return to debug this implementation, start here:
6+
7+
### 1. Open the Test Page
8+
```bash
9+
cd /Users/jonobrandel/Documents/two.js
10+
open tests/boolean-operations.html
11+
```
12+
13+
Open browser console (F12) to see any logs.
14+
15+
### 2. Current Issue
16+
17+
**Problem:** Paths are generated but incomplete - only showing small arc segments instead of full shapes.
18+
19+
**Expected Results:**
20+
- Union of two circles → Full Venn diagram outline
21+
- Subtract → Full donut/ring shape
22+
- Intersect → Full lens shape
23+
- Exclude → Two full crescent shapes
24+
25+
**Actual Results:**
26+
- Union → Small arc segment only
27+
- Subtract → Small arc segment only
28+
- Intersect → Single line segment only
29+
- Exclude → Small arc segment only
30+
31+
## Debugging Strategy
32+
33+
### Step 1: Add Logging to `constructBooleanResult()`
34+
35+
Edit `src/utils/boolean-result.js` around line 615:
36+
37+
```javascript
38+
export function constructBooleanResult(paths, operation, allIntersections) {
39+
console.log('=== BOOLEAN OPERATION START ===');
40+
console.log('Operation:', operation);
41+
console.log('Number of paths:', paths.length);
42+
console.log('Number of intersections:', allIntersections ? allIntersections.length : 0);
43+
44+
// ... existing code ...
45+
46+
// After splitting
47+
console.log('Split paths:', splitPaths.length);
48+
49+
// After creating segments
50+
console.log('Total segments created:', segments.length);
51+
console.log('Segments by path:', splitPaths.map((p, i) => ({
52+
pathIndex: i,
53+
vertices: p.vertices.length,
54+
closed: p.closed
55+
})));
56+
57+
// After classification
58+
const keptSegments = segments.filter(s => s.keep);
59+
console.log('Segments marked to keep:', keptSegments.length, '/', segments.length);
60+
console.log('Keep ratio:', (keptSegments.length / segments.length * 100).toFixed(1) + '%');
61+
62+
// After tracing
63+
console.log('Contours traced:', contours.length);
64+
contours.forEach((c, i) => {
65+
console.log(`Contour ${i}:`, {
66+
segments: c.segments.length,
67+
closed: c.closed
68+
});
69+
});
70+
71+
// After building result
72+
if (resultPath) {
73+
console.log('Result path vertices:', resultPath.vertices.length);
74+
console.log('Result path closed:', resultPath.closed);
75+
} else {
76+
console.log('Result path is NULL');
77+
}
78+
console.log('=== BOOLEAN OPERATION END ===\n');
79+
80+
return resultPath;
81+
}
82+
```
83+
84+
### Step 2: Rebuild and Test
85+
86+
```bash
87+
npm run build
88+
# Refresh browser and check console
89+
```
90+
91+
### Step 3: Analyze the Logs
92+
93+
Look for these red flags:
94+
95+
#### Issue: Too Few Segments Kept
96+
```
97+
Segments marked to keep: 2 / 40
98+
Keep ratio: 5.0%
99+
```
100+
**Diagnosis:** Segment classification is wrong
101+
**Fix Location:** `classifySegments()` function (line 338)
102+
**Possible causes:**
103+
- `pointInAnyPath()` returning incorrect results
104+
- Transformation matrix issues
105+
- Operation rules in `shouldKeepSegment()` are inverted
106+
107+
#### Issue: Contours Have Few Segments
108+
```
109+
Contour 0: { segments: 2, closed: false }
110+
```
111+
**Diagnosis:** Contour tracing stopping too early
112+
**Fix Location:** `traceContours()` function (line 452)
113+
**Possible causes:**
114+
- Adjacency map not built correctly
115+
- Anchor keys not matching due to precision issues
116+
- `findNextSegment()` not finding connected segments
117+
118+
#### Issue: Many Segments Kept But Few Vertices
119+
```
120+
Segments marked to keep: 35 / 40
121+
Contours traced: 1
122+
Contour 0: { segments: 35, closed: true }
123+
Result path vertices: 3
124+
```
125+
**Diagnosis:** Path construction failing
126+
**Fix Location:** `buildResultPath()` function (line 534)
127+
**Possible causes:**
128+
- Vertices not being added properly
129+
- Commands not set correctly
130+
- Clone operation failing
131+
132+
### Step 4: Targeted Fixes
133+
134+
Based on the diagnosis from Step 3, try these fixes:
135+
136+
#### Fix 1: If Segment Classification is Wrong
137+
138+
Check if `pointInPath()` is working:
139+
140+
```javascript
141+
// Add to classifySegments() before line 358
142+
console.log('Testing segment', i, 'midpoint:', midpoint, 'pathIndex:', pathIndex);
143+
console.log(' World coords:', worldX, worldY);
144+
console.log(' Inside other?', isInsideOther);
145+
console.log(' Keep?', segment.keep);
146+
```
147+
148+
Try inverting the logic:
149+
```javascript
150+
// In shouldKeepSegment() - line 301
151+
case 'union':
152+
return !isInsideOther; // Try: return isInsideOther;
153+
```
154+
155+
#### Fix 2: If Contour Tracing is Wrong
156+
157+
Check adjacency map:
158+
159+
```javascript
160+
// Add to traceContours() after line 453
161+
console.log('Adjacency map size:', adjacencyMap.size);
162+
console.log('Adjacency map entries:');
163+
adjacencyMap.forEach((segments, key) => {
164+
console.log(` ${key}: ${segments.length} segments`);
165+
});
166+
```
167+
168+
Check if segments are connecting:
169+
170+
```javascript
171+
// Add inside the tracing loop (around line 505)
172+
console.log('Current segment end:', currentEnd);
173+
console.log('Looking for next segment...');
174+
const nextSegment = findNextSegment(adjacencyMap, currentEnd, usedSegments);
175+
console.log('Found next segment:', !!nextSegment);
176+
```
177+
178+
#### Fix 3: If Path Construction is Wrong
179+
180+
Check vertex addition:
181+
182+
```javascript
183+
// Add to buildResultPath() inside loop (around line 564)
184+
console.log('Adding segment', s, 'end anchor:', segment.endAnchor);
185+
console.log(' Command:', endAnchor.command);
186+
console.log(' Position:', endAnchor.x, endAnchor.y);
187+
allVertices.push(endAnchor);
188+
console.log(' Total vertices now:', allVertices.length);
189+
```
190+
191+
### Step 5: Test with Simple Case
192+
193+
Create a minimal test with two rectangles (easier to debug than circles):
194+
195+
```javascript
196+
// Add to boolean-operations.html
197+
function testSimpleRectangles() {
198+
const two = new Two({ width: 350, height: 250 })
199+
.appendTo(document.getElementById('test-simple'));
200+
201+
const rect1 = two.makeRectangle(120, 125, 80, 80);
202+
rect1.fill = 'rgba(255, 0, 0, 0.3)';
203+
rect1.stroke = '#ccc';
204+
205+
const rect2 = two.makeRectangle(180, 125, 80, 80);
206+
rect2.fill = 'rgba(0, 0, 255, 0.3)';
207+
rect2.stroke = '#ccc';
208+
209+
const boolGroup = two.makeBooleanGroup([rect1, rect2], 'union');
210+
const result = boolGroup.getResultPath();
211+
212+
console.log('SIMPLE TEST - Result:', result);
213+
214+
if (result) {
215+
result.stroke = '#00aa00';
216+
result.linewidth = 3;
217+
two.add(result);
218+
}
219+
220+
two.update();
221+
}
222+
```
223+
224+
Rectangles have:
225+
- Only 4 vertices each
226+
- Only straight lines (no curves)
227+
- Easier to visualize and debug
228+
229+
### Step 6: Visual Debugging
230+
231+
Add visual markers to see what's happening:
232+
233+
```javascript
234+
// After classifySegments() in constructBooleanResult()
235+
// Draw kept segment midpoints
236+
keptSegments.forEach(seg => {
237+
const circle = two.makeCircle(seg.midpoint.x, seg.midpoint.y, 3);
238+
circle.fill = '#00ff00';
239+
circle.noStroke();
240+
});
241+
242+
// Draw discarded segment midpoints
243+
segments.filter(s => !s.keep).forEach(seg => {
244+
const circle = two.makeCircle(seg.midpoint.x, seg.midpoint.y, 3);
245+
circle.fill = '#ff0000';
246+
circle.noStroke();
247+
});
248+
```
249+
250+
This will show green dots for kept segments and red dots for discarded segments.
251+
252+
## Common Issues and Solutions
253+
254+
### Issue: "Cannot read properties of undefined"
255+
**Cause:** Array bounds issues or null checks missing
256+
**Fix:** Add more defensive checks in `insertAnchorAtIntersection()`
257+
258+
### Issue: Segments not connecting
259+
**Cause:** Anchor key precision mismatch
260+
**Fix:** Adjust `anchorKey()` precision (try 2 or 4 decimal places instead of 3)
261+
262+
### Issue: Wrong segments kept for operation
263+
**Cause:** Logic inverted or transformation issues
264+
**Fix:** Double-check `shouldKeepSegment()` rules match the operation semantics
265+
266+
### Issue: Empty result path
267+
**Cause:** All segments discarded or contour tracing failed
268+
**Fix:** Check that at least some segments are marked `keep: true`
269+
270+
## Quick Reference: Operation Rules
271+
272+
For a segment on path A being tested against path B:
273+
274+
| Operation | Segment Inside B | Keep Segment? |
275+
|-----------|------------------|---------------|
276+
| Union | No | ✓ Yes |
277+
| Union | Yes | ✗ No |
278+
| Subtract | No (A only) | ✓ Yes |
279+
| Subtract | Yes (A only) | ✗ No |
280+
| Subtract | Any (B segments) | ✗ No |
281+
| Intersect | Yes | ✓ Yes |
282+
| Intersect | No | ✗ No |
283+
| Exclude | No | ✓ Yes |
284+
| Exclude | Yes | ✗ No |
285+
286+
## Files to Check
287+
288+
Main implementation:
289+
- `src/utils/boolean-result.js` - All the logic
290+
- `src/boolean-group.js` - Entry point
291+
- `tests/boolean-operations.html` - Test page
292+
293+
Supporting files:
294+
- `src/utils/boolean-operations.js` - Intersection detection (working)
295+
- `src/utils/hit-test.js` - Ray casting (used for point-in-path)
296+
297+
## Rebuild Commands
298+
299+
```bash
300+
# Full rebuild
301+
npm run build && npm run lint
302+
303+
# Quick rebuild (skip lint)
304+
npm run build
305+
306+
# Watch mode (if available)
307+
npm run dev
308+
```
309+
310+
## Success Criteria
311+
312+
When fixed, you should see:
313+
- Union: Full outline combining both shapes
314+
- Subtract: Full shape with hole(s) removed
315+
- Intersect: Full overlapping region
316+
- Exclude: Full non-overlapping regions
317+
318+
Each should have 20-50+ vertices for smooth curves.

0 commit comments

Comments
 (0)