Historia-Urbis/figures/fig_01_grid_block.svg
Dane Sabo 0968572184 M0.5 part 1: geo dep + polygon-difference cleanup, Y overlap fixed
The Y intersection had a real I3 violation that the M0.3 centroid-
only check missed (parcels 0 and 7 overlapped by ~64.5 m²). This
commit:

- adds geo 0.28 as a dependency
- replaces y_intersection_no_overlaps's centroid-in-polygon check
  with a rigorous polygon-polygon intersection test using
  geo::BooleanOps; adds rectangle_no_overlaps_rigorous as a
  positive-control test for the rectangle case
- adds cleanup_block_parcel_overlaps pass at the end of
  subdivide_block: iterate parcels in placement order (corners
  first, regulars after), subtract previously-claimed territory
  from each via geo's polygon difference, drop empties, recover
  frontage edge index and edge_kinds for survivors
- snaps polygon coords to a 1mm grid before handing to geo (helps
  geo's sweep-line invariants); strips collinear-triple and
  near-zero-length-edge artifacts from boolean output before
  feeding back into Polygon::new strict
- wraps difference/union calls in catch_unwind so geo's
  occasional sweep-line panic on degenerate inputs falls back to
  a no-op instead of crashing subdivision

Test status: 24 unit + 24 integration + 1 doc passing. Y figure
visually cleaner — every parcel sits inside its own sub-block, no
visible overlap between sub-blocks.

Self-decisions checklist progress: rigorous I3 testing landed
(checklist item ✓).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 13:26:43 -04:00

139 lines
12 KiB
XML

<svg height="440" viewBox="-5 -5 210 110" width="840" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="110" width="210" x="-5" y="-5"/>
<g transform="translate(0, 100) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="200" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="200" x2="200" y1="0" y2="100"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="200" x2="0" y1="100" y2="100"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="0" y1="100" y2="0"/>
</g>
<g id="parcels">
<path d="M0,0 L20,0 L20,30 L0,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="20" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="0" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="0" y1="30" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="0" y1="30" y2="0"/>
<path d="M200,0 L200,30 L180,30 L180,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="200" y1="0" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="200" x2="180" y1="30" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="30" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="200" y1="0" y2="0"/>
<path d="M200,70 L200,100 L180,100 L180,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="200" y1="70" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="200" x2="180" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="100" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="200" y1="70" y2="70"/>
<path d="M20,70 L20,100 L0,100 L0,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="70" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="0" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="0" y1="100" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="0" x2="20" y1="70" y2="70"/>
<path d="M42.997,0 L42.997,29.526 L20,29.526 L20,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.997" x2="42.997" y1="0" y2="29.526"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.997" x2="20" y1="29.526" y2="29.526"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="29.526" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.997" y1="0" y2="0"/>
<path d="M65.48,0 L65.48,31.148 L42.997,31.148 L42.997,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.48" x2="65.48" y1="0" y2="31.148"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.48" x2="42.997" y1="31.148" y2="31.148"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.997" x2="42.997" y1="31.148" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.997" x2="65.48" y1="0" y2="0"/>
<path d="M88.278,0 L88.278,29.876 L65.48,29.876 L65.48,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.278" x2="88.278" y1="0" y2="29.876"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.278" x2="65.48" y1="29.876" y2="29.876"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.48" x2="65.48" y1="29.876" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.48" x2="88.278" y1="0" y2="0"/>
<path d="M109.003,0 L109.003,30.258 L88.278,30.258 L88.278,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.003" x2="109.003" y1="0" y2="30.258"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.003" x2="88.278" y1="30.258" y2="30.258"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.278" x2="88.278" y1="30.258" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.278" x2="109.003" y1="0" y2="0"/>
<path d="M127.212,0 L127.212,34 L109.003,34 L109.003,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.212" x2="127.212" y1="0" y2="34"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.212" x2="109.003" y1="34" y2="34"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.003" x2="109.003" y1="34" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.003" x2="127.212" y1="0" y2="0"/>
<path d="M146.842,0 L146.842,31.471 L127.212,31.471 L127.212,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.842" x2="146.842" y1="0" y2="31.471"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.842" x2="127.212" y1="31.471" y2="31.471"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.212" x2="127.212" y1="31.471" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.212" x2="146.842" y1="0" y2="0"/>
<path d="M166.408,0 L166.408,33.895 L146.842,33.895 L146.842,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.408" x2="166.408" y1="0" y2="33.895"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="166.408" x2="146.842" y1="33.895" y2="33.895"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.842" x2="146.842" y1="33.895" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.842" x2="166.408" y1="0" y2="0"/>
<path d="M180,0 L180,29.999 L166.408,29.999 L166.408,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="29.999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="166.408" y1="29.999" y2="29.999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.408" x2="166.408" y1="29.999" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="166.408" x2="180" y1="0" y2="0"/>
<path d="M200,30 L200,52.856 L169.387,52.856 L169.387,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="200" x2="200" y1="30" y2="52.856"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="169.387" y1="52.856" y2="52.856"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="169.387" x2="169.387" y1="52.856" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="169.387" x2="200" y1="30" y2="30"/>
<path d="M200,52.856 L200,70 L172.177,70 L172.177,52.856 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="200" x2="200" y1="52.856" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="172.177" y1="70" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="172.177" x2="172.177" y1="70" y2="52.856"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="172.177" x2="200" y1="52.856" y2="52.856"/>
<path d="M180,72.466 L180,100 L161.195,100 L161.195,72.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="72.466" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="161.195" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.195" x2="161.195" y1="100" y2="72.466"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="161.195" x2="180" y1="72.466" y2="72.466"/>
<path d="M161.195,68.264 L161.195,100 L140.259,100 L140.259,68.264 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.195" x2="161.195" y1="68.264" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="161.195" x2="140.259" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.259" x2="140.259" y1="100" y2="68.264"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="140.259" x2="161.195" y1="68.264" y2="68.264"/>
<path d="M140.259,66 L140.259,100 L120,100 L120,66 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.259" x2="140.259" y1="66" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="140.259" x2="120" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120" x2="120" y1="100" y2="66"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120" x2="140.259" y1="66" y2="66"/>
<path d="M120,71.273 L120,100 L98.191,100 L98.191,71.273 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120" x2="120" y1="71.273" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120" x2="98.191" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.191" x2="98.191" y1="100" y2="71.273"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="98.191" x2="120" y1="71.273" y2="71.273"/>
<path d="M98.191,72.864 L98.191,100 L81.118,100 L81.118,72.864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.191" x2="98.191" y1="72.864" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="98.191" x2="81.118" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.118" x2="81.118" y1="100" y2="72.864"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="81.118" x2="98.191" y1="72.864" y2="72.864"/>
<path d="M81.118,70.637 L81.118,100 L63.129,100 L63.129,70.637 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.118" x2="81.118" y1="70.637" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="81.118" x2="63.129" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129" x2="63.129" y1="100" y2="70.637"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="63.129" x2="81.118" y1="70.637" y2="70.637"/>
<path d="M63.129,68.959 L63.129,100 L40.142,100 L40.142,68.959 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129" x2="63.129" y1="68.959" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="63.129" x2="40.142" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.142" x2="40.142" y1="100" y2="68.959"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="40.142" x2="63.129" y1="68.959" y2="68.959"/>
<path d="M40.142,72.174 L40.142,100 L20,100 L20,72.174 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.142" x2="40.142" y1="72.174" y2="100"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="40.142" x2="20" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="100" y2="72.174"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="40.142" y1="72.174" y2="72.174"/>
<path d="M32.358,51.044 L32.358,70 L0,70 L0,51.044 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="32.358" x2="32.358" y1="51.044" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="32.358" x2="0" y1="70" y2="70"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="70" y2="51.044"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="32.358" y1="51.044" y2="51.044"/>
<path d="M29.863,30 L29.863,51.044 L0,51.044 L0,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.863" x2="29.863" y1="30" y2="51.044"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.863" x2="0" y1="51.044" y2="51.044"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="51.044" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.863" y1="30" y2="30"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="200" cy="0" fill="#000000" r="0.7"/>
<circle cx="200" cy="100" fill="#000000" r="0.7"/>
<circle cx="0" cy="100" fill="#000000" r="0.7"/>
</g>
</g>
</svg>