fallback_analysis.html 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <style>
  6. body { font-family: monospace; background: #1a1a2e; color: #e0e0e0; display: flex; flex-direction: column; align-items: center; padding: 30px; }
  7. h2 { color: #e94560; }
  8. .legend { margin-top: 20px; font-size: 13px; line-height: 2.2; }
  9. .legend span { display: inline-block; width: 14px; height: 14px; vertical-align: middle; margin-right: 6px; border-radius: 2px; }
  10. </style>
  11. </head>
  12. <body>
  13. <h2>Door 21:Projection Fallback 案例 — 线段端点在 bbox 内部</h2>
  14. <canvas id="c" width="700" height="700"></canvas>
  15. <div class="legend">
  16. <span style="background:#4a90d9"></span> 门 bbox (12×38px)<br>
  17. <span style="background:#e94560"></span> 墙线段 seg50 (x=521, y=424→575)<br>
  18. <span style="background:#50fa7b"></span> 交点 (仅1个, 顶边 y=437)<br>
  19. <span style="background:#f1fa8c"></span> expand +10px 后仍只有1个交点<br>
  20. <span style="background:#ff79c6"></span> Fallback 门线 (取 bbox 长边 = 38px ≈ 0.76m)<br>
  21. <span style="background:#666"></span> 相邻墙线段
  22. </div>
  23. <script>
  24. const c = document.getElementById('c');
  25. const ctx = c.getContext('2d');
  26. const S = 8;
  27. const OX = 80, OY = 80;
  28. function px(x, y) { return [OX + x * S, OY + y * S]; }
  29. // Draw all wall segments
  30. const segments = [
  31. [[420,266],[420,360]], [[420,360],[537,360]], [[537,360],[537,266]], [[537,266],[420,266]],
  32. [[458,424],[509,424]], [[509,424],[698,424]], [[698,424],[698,379]], [[698,379],[740,379]],
  33. [[740,379],[740,266]], [[740,266],[560,266]], [[560,266],[560,304]], [[560,304],[537,304]],
  34. [[537,304],[537,360]], [[537,360],[458,360]], [[458,360],[458,424]],
  35. [[336,326],[336,360]], [[336,360],[399,360]], [[399,360],[399,326]], [[399,326],[336,326]],
  36. [[458,360],[336,360]], [[336,360],[336,514]], [[336,514],[458,514]], [[458,514],[458,360]],
  37. [[336,404],[230,404]], [[230,404],[230,426]], [[230,426],[230,559]], [[230,559],[336,559]], [[336,559],[336,404]],
  38. [[521,575],[698,575]], [[698,575],[698,480]], [[698,480],[742,480]], [[698,480],[698,424]], [[698,424],[521,424]], [[521,424],[521,575]],
  39. [[458,424],[458,575]], [[458,575],[521,575]], [[521,575],[521,424]], [[521,424],[458,424]],
  40. [[336,514],[336,575]], [[336,575],[379,575]], [[379,575],[458,575]], [[458,575],[458,514]], [[458,514],[336,514]],
  41. [[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]],
  42. [[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]],
  43. [[788,575],[672,575]], [[672,575],[672,742]], [[672,742],[716,742]], [[716,742],[716,704]], [[716,704],[788,704]], [[788,704],[788,575]],
  44. ];
  45. segments.forEach(s => {
  46. const [x1,y1] = px(s[0][0], s[0][1]);
  47. const [x2,y2] = px(s[1][0], s[1][1]);
  48. ctx.beginPath(); ctx.moveTo(x1,y1); ctx.lineTo(x2,y2);
  49. ctx.strokeStyle = '#555'; ctx.lineWidth = 2; ctx.stroke();
  50. });
  51. // Draw target segment (seg 50 in pixel: 521,424 -> 521,575)
  52. const [sx1,sy1] = px(521, 424);
  53. const [sx2,sy2] = px(521, 575);
  54. ctx.beginPath(); ctx.moveTo(sx1, sy1); ctx.lineTo(sx2, sy2);
  55. ctx.strokeStyle = '#e94560'; ctx.lineWidth = 3; ctx.stroke();
  56. // Draw door bbox (pixel: 515,399 -> 527,437, size 12x38)
  57. const bx = 515, by = 399, bw = 12, bh = 38;
  58. const [bx1,by1] = px(bx, by);
  59. const [bx2,by2] = px(bx+bw, by+bh);
  60. ctx.fillStyle = 'rgba(74,144,217,0.3)';
  61. ctx.fillRect(bx1, by1, bx2-bx1, by2-by1);
  62. ctx.strokeStyle = '#4a90d9'; ctx.lineWidth = 2;
  63. ctx.strokeRect(bx1, by1, bx2-bx1, by2-by1);
  64. // Draw intersection point (only 1: wall x=521 crosses bbox top edge at y=437)
  65. const wallX = 521;
  66. const ip1 = px(wallX, 437); // bbox bottom edge
  67. ctx.beginPath(); ctx.arc(ip1[0], ip1[1], 6, 0, Math.PI*2);
  68. ctx.fillStyle = '#50fa7b'; ctx.fill();
  69. // Label intersection point
  70. ctx.font = 'bold 13px monospace';
  71. ctx.fillStyle = '#50fa7b';
  72. ctx.fillText('交点 (仅1个)', ip1[0] + 10, ip1[1] - 10);
  73. ctx.fillText('(x=521, y=437)', ip1[0] + 10, ip1[1] + 8);
  74. // Draw segment endpoint (inside bbox)
  75. ctx.beginPath(); ctx.arc(sx1, sy1, 6, 0, Math.PI*2);
  76. ctx.fillStyle = '#ff79c6'; ctx.fill();
  77. ctx.font = 'bold 13px monospace';
  78. ctx.fillStyle = '#ff79c6';
  79. ctx.fillText('线段端点 (在bbox内)', sx1 + 10, sy1 + 20);
  80. ctx.fillText('(y=424)', sx1 + 10, sy1 + 38);
  81. // Draw bbox edges
  82. ctx.font = '12px monospace';
  83. ctx.fillStyle = '#aaa';
  84. ctx.fillText('bbox顶边 y=399', bx1 - 5, by1 - 8);
  85. ctx.fillText('bbox底边 y=437', bx1 - 5, by2 + 15);
  86. ctx.fillText('x=515', bx1 - 30, by1 - 5);
  87. ctx.fillText('x=527', bx2 + 5, by1 - 5);
  88. // Show expand +10px bbox
  89. const expBx = bx - 10, expBy = by - 10, expBw = bw + 20, expBh = bh + 20;
  90. const [ebx1,eby1] = px(expBx, expBy);
  91. const [ebx2,eby2] = px(expBx+expBw, expBy+expBh);
  92. ctx.fillStyle = 'rgba(241,250,140,0.1)';
  93. ctx.fillRect(ebx1, eby1, ebx2-ebx1, eby2-eby1);
  94. ctx.strokeStyle = 'rgba(241,250,140,0.5)'; ctx.lineWidth = 1;
  95. ctx.setLineDash([4,4]);
  96. ctx.strokeRect(ebx1, eby1, ebx2-ebx1, eby2-eby1);
  97. ctx.setLineDash([]);
  98. ctx.font = '11px monospace';
  99. ctx.fillStyle = '#f1fa8c';
  100. ctx.fillText('expand +10px: 底边到 y=447', ebx1 + 5, eby1 - 8);
  101. ctx.fillText('仍 < 437,仍只有1个交点', ebx1 + 5, eby1 + 8);
  102. // Arrow from segment endpoint to bbox bottom
  103. const arrowX = bx1 + 30;
  104. ctx.strokeStyle = '#ff79c6'; ctx.lineWidth = 1;
  105. ctx.beginPath(); ctx.moveTo(arrowX, sy1); ctx.lineTo(arrowX, by2);
  106. ctx.stroke();
  107. ctx.font = '11px monospace';
  108. ctx.fillStyle = '#ff79c6';
  109. ctx.fillText('端点y=424', arrowX - 50, (sy1+by2)/2 - 5);
  110. ctx.fillText('底边y=437', arrowX - 50, (sy1+by2)/2 + 10);
  111. ctx.fillText('差13px未触及底边', arrowX - 50, (sy1+by2)/2 + 25);
  112. // Show fallback door line
  113. const doorCx = 0.37, doorCy = (1.36 + 2.12) / 2;
  114. const doorLen = 0.76; // bbox longer side in world coords
  115. // Convert to pixel for drawing
  116. const doorHalfPx = (doorLen / 0.01) / 2 / S; // in canvas pixels
  117. // The door line goes along segment direction (vertical)
  118. // centered on segment midpoint, with length = 38px = 0.76m
  119. const doorMidY = (437 + 399) / 2;
  120. const fd1 = px(wallX, doorMidY - 19);
  121. const fd2 = px(wallX, doorMidY + 19);
  122. ctx.beginPath(); ctx.moveTo(fd1[0], fd1[1]); ctx.lineTo(fd2[0], fd2[1]);
  123. ctx.strokeStyle = '#ff79c6'; ctx.lineWidth = 4;
  124. ctx.setLineDash([6, 4]);
  125. ctx.stroke();
  126. ctx.setLineDash([]);
  127. ctx.font = 'bold 14px monospace';
  128. ctx.fillStyle = '#ff79c6';
  129. ctx.fillText('Fallback 门线 (38px = 0.76m)', fd2[0] + 10, fd2[1]);
  130. // Dimension on bbox
  131. ctx.strokeStyle = '#f1fa8c'; ctx.lineWidth = 1;
  132. const dimX = bx1 - 25;
  133. ctx.beginPath(); ctx.moveTo(dimX, by1); ctx.lineTo(dimX, by2); ctx.stroke();
  134. ctx.fillStyle = '#f1fa8c';
  135. ctx.fillText('38px', dimX - 22, (by1+by2)/2 + 5);
  136. const dimY = by2 + 20;
  137. ctx.beginPath(); ctx.moveTo(bx1, dimY); ctx.lineTo(bx2, dimY); ctx.stroke();
  138. ctx.fillText('12px', (bx1+bx2)/2 - 15, dimY + 15);
  139. // Summary
  140. ctx.font = 'bold 13px monospace';
  141. ctx.fillStyle = '#e94560';
  142. ctx.fillText('总结:线段从 bbox 顶边进入,端点在 bbox 内部', 20, 650);
  143. ctx.fillText('贯穿检测只能找到1个交点(底边未触及)', 20, 670);
  144. ctx.fillText('expand 也无法找到第2个交点 → 进入 projection fallback', 20, 690);
  145. </script>
  146. </body>
  147. </html>