| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <style>
- body { font-family: monospace; background: #1a1a2e; color: #e0e0e0; display: flex; flex-direction: column; align-items: center; padding: 30px; }
- h2 { color: #e94560; }
- .legend { margin-top: 20px; font-size: 13px; line-height: 2.2; }
- .legend span { display: inline-block; width: 14px; height: 14px; vertical-align: middle; margin-right: 6px; border-radius: 2px; }
- </style>
- </head>
- <body>
- <h2>Door 21:Projection Fallback 案例 — 线段端点在 bbox 内部</h2>
- <canvas id="c" width="700" height="700"></canvas>
- <div class="legend">
- <span style="background:#4a90d9"></span> 门 bbox (12×38px)<br>
- <span style="background:#e94560"></span> 墙线段 seg50 (x=521, y=424→575)<br>
- <span style="background:#50fa7b"></span> 交点 (仅1个, 顶边 y=437)<br>
- <span style="background:#f1fa8c"></span> expand +10px 后仍只有1个交点<br>
- <span style="background:#ff79c6"></span> Fallback 门线 (取 bbox 长边 = 38px ≈ 0.76m)<br>
- <span style="background:#666"></span> 相邻墙线段
- </div>
- <script>
- const c = document.getElementById('c');
- const ctx = c.getContext('2d');
- const S = 8;
- const OX = 80, OY = 80;
- function px(x, y) { return [OX + x * S, OY + y * S]; }
- // Draw all wall segments
- const segments = [
- [[420,266],[420,360]], [[420,360],[537,360]], [[537,360],[537,266]], [[537,266],[420,266]],
- [[458,424],[509,424]], [[509,424],[698,424]], [[698,424],[698,379]], [[698,379],[740,379]],
- [[740,379],[740,266]], [[740,266],[560,266]], [[560,266],[560,304]], [[560,304],[537,304]],
- [[537,304],[537,360]], [[537,360],[458,360]], [[458,360],[458,424]],
- [[336,326],[336,360]], [[336,360],[399,360]], [[399,360],[399,326]], [[399,326],[336,326]],
- [[458,360],[336,360]], [[336,360],[336,514]], [[336,514],[458,514]], [[458,514],[458,360]],
- [[336,404],[230,404]], [[230,404],[230,426]], [[230,426],[230,559]], [[230,559],[336,559]], [[336,559],[336,404]],
- [[521,575],[698,575]], [[698,575],[698,480]], [[698,480],[742,480]], [[698,480],[698,424]], [[698,424],[521,424]], [[521,424],[521,575]],
- [[458,424],[458,575]], [[458,575],[521,575]], [[521,575],[521,424]], [[521,424],[458,424]],
- [[336,514],[336,575]], [[336,575],[379,575]], [[379,575],[458,575]], [[458,575],[458,514]], [[458,514],[336,514]],
- [[230,559],[230,647]], [[230,647],[263,647]], [[263,647],[263,669]], [[263,669],[336,669]], [[336,669],[336,606]], [[336,606],[336,559]], [[336,559],[230,559]],
- [[336,675],[336,742]], [[336,742],[672,742]], [[672,742],[672,575]], [[672,575],[458,575]], [[458,575],[458,606]], [[458,606],[336,606]], [[336,606],[336,675]],
- [[788,575],[672,575]], [[672,575],[672,742]], [[672,742],[716,742]], [[716,742],[716,704]], [[716,704],[788,704]], [[788,704],[788,575]],
- ];
- segments.forEach(s => {
- const [x1,y1] = px(s[0][0], s[0][1]);
- const [x2,y2] = px(s[1][0], s[1][1]);
- ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2);
- ctx.strokeStyle = '#555'; ctx.lineWidth = 2; ctx.stroke();
- });
- // Draw target segment (seg 50 in pixel: 521,424 -> 521,575)
- const [sx1,sy1] = px(521, 424);
- const [sx2,sy2] = px(521, 575);
- ctx.beginPath(); ctx.moveTo(sx1, sy1); ctx.lineTo(sx2, sy2);
- ctx.strokeStyle = '#e94560'; ctx.lineWidth = 3; ctx.stroke();
- // Draw door bbox (pixel: 515,399 -> 527,437, size 12x38)
- const bx = 515, by = 399, bw = 12, bh = 38;
- const [bx1,by1] = px(bx, by);
- const [bx2,by2] = px(bx+bw, by+bh);
- ctx.fillStyle = 'rgba(74,144,217,0.3)';
- ctx.fillRect(bx1, by1, bx2-bx1, by2-by1);
- ctx.strokeStyle = '#4a90d9'; ctx.lineWidth = 2;
- ctx.strokeRect(bx1, by1, bx2-bx1, by2-by1);
- // Draw intersection point (only 1: wall x=521 crosses bbox top edge at y=437)
- const wallX = 521;
- const ip1 = px(wallX, 437); // bbox bottom edge
- ctx.beginPath(); ctx.arc(ip1[0], ip1[1], 6, 0, Math.PI*2);
- ctx.fillStyle = '#50fa7b'; ctx.fill();
- // Label intersection point
- ctx.font = 'bold 13px monospace';
- ctx.fillStyle = '#50fa7b';
- ctx.fillText('交点 (仅1个)', ip1[0] + 10, ip1[1] - 10);
- ctx.fillText('(x=521, y=437)', ip1[0] + 10, ip1[1] + 8);
- // Draw segment endpoint (inside bbox)
- ctx.beginPath(); ctx.arc(sx1, sy1, 6, 0, Math.PI*2);
- ctx.fillStyle = '#ff79c6'; ctx.fill();
- ctx.font = 'bold 13px monospace';
- ctx.fillStyle = '#ff79c6';
- ctx.fillText('线段端点 (在bbox内)', sx1 + 10, sy1 + 20);
- ctx.fillText('(y=424)', sx1 + 10, sy1 + 38);
- // Draw bbox edges
- ctx.font = '12px monospace';
- ctx.fillStyle = '#aaa';
- ctx.fillText('bbox顶边 y=399', bx1 - 5, by1 - 8);
- ctx.fillText('bbox底边 y=437', bx1 - 5, by2 + 15);
- ctx.fillText('x=515', bx1 - 30, by1 - 5);
- ctx.fillText('x=527', bx2 + 5, by1 - 5);
- // Show expand +10px bbox
- const expBx = bx - 10, expBy = by - 10, expBw = bw + 20, expBh = bh + 20;
- const [ebx1,eby1] = px(expBx, expBy);
- const [ebx2,eby2] = px(expBx+expBw, expBy+expBh);
- ctx.fillStyle = 'rgba(241,250,140,0.1)';
- ctx.fillRect(ebx1, eby1, ebx2-ebx1, eby2-eby1);
- ctx.strokeStyle = 'rgba(241,250,140,0.5)'; ctx.lineWidth = 1;
- ctx.setLineDash([4,4]);
- ctx.strokeRect(ebx1, eby1, ebx2-ebx1, eby2-eby1);
- ctx.setLineDash([]);
- ctx.font = '11px monospace';
- ctx.fillStyle = '#f1fa8c';
- ctx.fillText('expand +10px: 底边到 y=447', ebx1 + 5, eby1 - 8);
- ctx.fillText('仍 < 437,仍只有1个交点', ebx1 + 5, eby1 + 8);
- // Arrow from segment endpoint to bbox bottom
- const arrowX = bx1 + 30;
- ctx.strokeStyle = '#ff79c6'; ctx.lineWidth = 1;
- ctx.beginPath(); ctx.moveTo(arrowX, sy1); ctx.lineTo(arrowX, by2);
- ctx.stroke();
- ctx.font = '11px monospace';
- ctx.fillStyle = '#ff79c6';
- ctx.fillText('端点y=424', arrowX - 50, (sy1+by2)/2 - 5);
- ctx.fillText('底边y=437', arrowX - 50, (sy1+by2)/2 + 10);
- ctx.fillText('差13px未触及底边', arrowX - 50, (sy1+by2)/2 + 25);
- // Show fallback door line
- const doorCx = 0.37, doorCy = (1.36 + 2.12) / 2;
- const doorLen = 0.76; // bbox longer side in world coords
- // Convert to pixel for drawing
- const doorHalfPx = (doorLen / 0.01) / 2 / S; // in canvas pixels
- // The door line goes along segment direction (vertical)
- // centered on segment midpoint, with length = 38px = 0.76m
- const doorMidY = (437 + 399) / 2;
- const fd1 = px(wallX, doorMidY - 19);
- const fd2 = px(wallX, doorMidY + 19);
- ctx.beginPath(); ctx.moveTo(fd1[0], fd1[1]); ctx.lineTo(fd2[0], fd2[1]);
- ctx.strokeStyle = '#ff79c6'; ctx.lineWidth = 4;
- ctx.setLineDash([6, 4]);
- ctx.stroke();
- ctx.setLineDash([]);
- ctx.font = 'bold 14px monospace';
- ctx.fillStyle = '#ff79c6';
- ctx.fillText('Fallback 门线 (38px = 0.76m)', fd2[0] + 10, fd2[1]);
- // Dimension on bbox
- ctx.strokeStyle = '#f1fa8c'; ctx.lineWidth = 1;
- const dimX = bx1 - 25;
- ctx.beginPath(); ctx.moveTo(dimX, by1); ctx.lineTo(dimX, by2); ctx.stroke();
- ctx.fillStyle = '#f1fa8c';
- ctx.fillText('38px', dimX - 22, (by1+by2)/2 + 5);
- const dimY = by2 + 20;
- ctx.beginPath(); ctx.moveTo(bx1, dimY); ctx.lineTo(bx2, dimY); ctx.stroke();
- ctx.fillText('12px', (bx1+bx2)/2 - 15, dimY + 15);
- // Summary
- ctx.font = 'bold 13px monospace';
- ctx.fillStyle = '#e94560';
- ctx.fillText('总结:线段从 bbox 顶边进入,端点在 bbox 内部', 20, 650);
- ctx.fillText('贯穿检测只能找到1个交点(底边未触及)', 20, 670);
- ctx.fillText('expand 也无法找到第2个交点 → 进入 projection fallback', 20, 690);
- </script>
- </body>
- </html>
|