initial commit

This commit is contained in:
Dane Sabo 2026-04-25 14:33:11 -04:00
commit 86b246ead2
11756 changed files with 22633 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,139 @@
<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,100 L180,100 L180,70 L200,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="200" y1="70" y2="100"/>
<path d="M0,100 L0,70 L20,70 L20,100 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<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"/>
<path d="M20,0 L42.996502,0 L42.996502,29.52622 L20,29.52622 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="29.526220147024308" y2="0"/>
<path d="M42.996502,0 L65.47971,0 L65.47971,31.147902 L42.996502,31.147902 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="31.1479024543553" y2="0"/>
<path d="M65.47971,0 L88.27847,0 L88.27847,29.876335 L65.47971,29.876335 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="88.27847104373568" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="29.87633532437634" y2="0"/>
<path d="M88.27847,0 L109.00264,0 L109.00264,30.25849 L88.27847,30.25849 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.27847104373568" x2="109.00264275540263" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="30.25848985257909" y2="0"/>
<path d="M109.00264,0 L127.212425,0 L127.212425,34 L109.00264,34 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.00264275540263" x2="127.2124223685276" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="34.000001" y2="0"/>
<path d="M127.212425,0 L146.8415,0 L146.8415,31.470558 L127.212425,31.470558 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.2124223685276" x2="146.84151010926075" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="31.47055834801727" y2="0"/>
<path d="M146.8415,0 L166.40808,0 L166.40808,33.89513 L146.8415,33.89513 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.84151010926075" x2="166.4080836096857" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.4080836096857" x2="166.4080836096857" y1="0" y2="33.89512833223243"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="166.4080836096857" x2="146.84151010926075" y1="33.89512833223243" y2="33.89512833223243"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="33.89512833223243" y2="0"/>
<path d="M166.40808,0 L180,0 L180,29.999052 L166.40808,29.999052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="166.4080836096857" x2="180" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="29.999051502429545"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="166.4080836096857" y1="29.999051502429545" y2="29.999051502429545"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.4080836096857" x2="166.4080836096857" y1="29.999051502429545" y2="0"/>
<path d="M200,30 L200,52.855724 L169.38701,52.855724 L169.38701,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.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="169.3870078110646" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="169.3870078110646" x2="169.3870078110646" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="169.3870078110646" x2="200" y1="30" y2="30"/>
<path d="M200,52.855724 L200,70 L172.17726,70 L172.17726,52.855724 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="200" x2="200" y1="52.855722506414395" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="172.17726330629023" y1="70" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="172.17726330629023" x2="172.17726330629023" y1="70" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="172.17726330629023" x2="200" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M180,100 L161.1955,100 L161.1955,72.466 L180,72.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="161.19549058892116" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="100" y2="72.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="161.19549058892116" x2="180" y1="72.46600595599794" y2="72.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="72.46600595599794" y2="100"/>
<path d="M161.1955,100 L140.25922,100 L140.25922,68.26361 L161.1955,68.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="161.19549058892116" x2="140.25920977847343" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="100" y2="68.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="140.25920977847343" x2="161.19549058892116" y1="68.26360853604555" y2="68.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="68.26360853604555" y2="100"/>
<path d="M140.25922,100 L120.000404,100 L120.000404,66 L140.25922,66 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="140.25920977847343" x2="120.0004046814901" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="100" y2="65.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.0004046814901" x2="140.25920977847343" y1="65.999999" y2="65.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="65.999999" y2="100"/>
<path d="M120.000404,100 L98.191284,100 L98.191284,71.2734 L120.000404,71.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.0004046814901" x2="98.19128506754218" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="100" y2="71.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="98.19128506754218" x2="120.0004046814901" y1="71.27339761286972" y2="71.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="71.27339761286972" y2="100"/>
<path d="M98.191284,100 L81.11824,100 L81.11824,72.8639 L98.191284,72.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="98.19128506754218" x2="81.11823933536908" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="100" y2="72.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="81.11823933536908" x2="98.19128506754218" y1="72.86389925174167" y2="72.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="72.86389925174167" y2="100"/>
<path d="M81.11824,100 L63.12904,100 L63.12904,70.637054 L81.11824,70.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="81.11823933536908" x2="63.129039299600635" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="100" y2="70.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="63.129039299600635" x2="81.11823933536908" y1="70.63705247970552" y2="70.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="70.63705247970552" y2="100"/>
<path d="M63.12904,100 L40.141674,100 L40.141674,68.958885 L63.12904,68.958885 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="63.129039299600635" x2="40.14167365815044" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="100" y2="68.95888431009364"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="40.14167365815044" x2="63.129039299600635" y1="68.95888431009364" y2="68.95888431009364"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="68.95888431009364" y2="100"/>
<path d="M40.141674,100 L20,100 L20,72.17421 L40.141674,72.17421 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="40.14167365815044" x2="20" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="100" y2="72.17420690790317"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="40.14167365815044" y1="72.17420690790317" y2="72.17420690790317"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="72.17420690790317" y2="100"/>
<path d="M0,70 L0,51.044052 L32.357586,51.044052 L32.357586,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="70" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="32.357584460743425" y1="51.04405176448549" y2="51.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="32.357584460743425" x2="32.357584460743425" y1="51.04405176448549" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="32.357584460743425" x2="0" y1="70" y2="70"/>
<path d="M0,51.044052 L0,30 L29.8626,30 L29.8626,51.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="51.04405176448549" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="30" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="51.04405176448549" y2="51.04405176448549"/>
</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>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,183 @@
<svg height="474.31457505076196" viewBox="-146.42135623730948 -5 292.84271247461896 118.57864376269049" width="1171.3708498984759" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="118.57864376269049" width="292.84271247461896" x="-146.42135623730948" y="-5"/>
<g transform="translate(0, 108.57864376269049) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-141.42135623730948" x2="-111.11404660392043" y1="58.57864376269049" y2="33.70607753949096"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-111.11404660392043" x2="-76.53668647301795" y1="33.70607753949096" y2="15.224093497742643"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-76.53668647301795" x2="-39.01806440322565" y1="15.224093497742643" y2="3.8429439193538997"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-39.01806440322565" x2="0" y1="3.8429439193538997" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="39.01806440322565" y1="0" y2="3.8429439193538997"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="39.01806440322565" x2="76.53668647301795" y1="3.8429439193538997" y2="15.224093497742643"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="76.53668647301795" x2="111.11404660392043" y1="15.224093497742643" y2="33.70607753949096"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="111.11404660392043" x2="141.42135623730948" y1="33.70607753949096" y2="58.57864376269049"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="141.42135623730948" x2="141.42135623730948" y1="58.57864376269049" y2="108.57864376269049"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="141.42135623730948" x2="-141.42135623730948" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-141.42135623730948" x2="-141.42135623730948" y1="108.57864376269049" y2="58.57864376269049"/>
</g>
<g id="parcels">
<path d="M-141.42136,78.182076 L-141.42136,58.578644 L-126.2677,46.14236 L-113.83142,61.296017 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-141.42135623730948" x2="-141.42135623730948" y1="78.1820718286026" y2="58.57864376269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-141.42135623730948" x2="-126.26770142061496" y1="58.57864376269049" y2="46.142360651090726"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-126.26770142061496" x2="-113.83141830901519" y1="46.142360651090726" y2="61.29601546778525"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-113.83141830901519" x2="-141.42135623730948" y1="61.29601546778525" y2="78.1820718286026"/>
<path d="M126.2677,46.14236 L141.42136,58.578644 L141.42136,78.182076 L121.817924,78.182076 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="126.26770142061496" x2="141.42135623730948" y1="46.142360651090726" y2="58.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.42135623730948" x2="141.42135623730948" y1="58.57864376269049" y2="78.1820718286026"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="141.42135623730948" x2="121.81792817139737" y1="78.1820718286026" y2="78.1820718286026"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="121.81792817139737" x2="126.26770142061496" y1="78.1820718286026" y2="46.142360651090726"/>
<path d="M141.42136,83.57864 L141.42136,108.57864 L121.42136,108.57864 L121.42136,83.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="141.42135623730948" x2="141.42135623730948" y1="83.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.42135623730948" x2="121.42135623730948" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="121.42135623730948" x2="121.42135623730948" y1="108.57864376269049" y2="83.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="121.42135623730948" x2="141.42135623730948" y1="83.57864376269049" y2="83.57864376269049"/>
<path d="M-121.42136,108.57864 L-141.42136,108.57864 L-141.42136,83.57864 L-121.42136,83.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-121.42135623730948" x2="-141.42135623730948" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-141.42135623730948" x2="-141.42135623730948" y1="108.57864376269049" y2="83.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-141.42135623730948" x2="-121.42135623730948" y1="83.57864376269049" y2="83.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-121.42135623730948" x2="-121.42135623730948" y1="83.57864376269049" y2="108.57864376269049"/>
<path d="M-104.698326,72.42471 L-126.2677,46.14236 L-111.114044,33.706078 L-89.54468,59.988434 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-104.69832912465773" x2="-126.26770142061496" y1="72.42471683843424" y2="46.142360651090726"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-126.26770142061496" x2="-111.11404660392043" y1="46.142360651090726" y2="33.70607753949096"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-111.11404660392043" x2="-89.5446743079632" y1="33.70607753949096" y2="59.98843372683447"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-89.5446743079632" x2="-104.69832912465773" y1="59.98843372683447" y2="72.42471683843424"/>
<path d="M-96.68318,60.704327 L-111.114044,33.706078 L-90.9571,22.931965 L-76.52624,49.930214 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-96.6831819815765" x2="-111.11404660392043" y1="60.7043263162432" y2="33.70607753949096"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-111.11404660392043" x2="-90.95709891346829" y1="33.70607753949096" y2="22.931964532166692"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-90.95709891346829" x2="-76.52623429112435" y1="22.931964532166692" y2="49.93021330891892"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-76.52623429112435" x2="-96.6831819815765" y1="49.93021330891892" y2="60.7043263162432"/>
<path d="M-77.84155,47.46943 L-90.9571,22.931965 L-76.53669,15.224093 L-63.42114,39.761555 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-77.84155162648455" x2="-90.95709891346829" y1="47.46942765471458" y2="22.931964532166692"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-90.95709891346829" x2="-76.53668647301795" y1="22.931964532166692" y2="15.224093497742643"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-76.53668647301795" x2="-63.42113918603421" y1="15.224093497742643" y2="39.761556620290534"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-63.42113918603421" x2="-77.84155162648455" y1="39.761556620290534" y2="47.46942765471458"/>
<path d="M-67.43769,45.21946 L-76.53669,15.224093 L-58.541893,9.765432 L-49.442898,39.7608 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-67.43769193939679" x2="-76.53668647301795" y1="45.21945862063639" y2="15.224093497742643"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-76.53668647301795" x2="-58.54189292390069" y1="15.224093497742643" y2="9.76543255241912"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-58.54189292390069" x2="-49.442898390279524" y1="9.76543255241912" y2="39.76079767531286"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-49.442898390279524" x2="-67.43769193939679" y1="39.76079767531286" y2="45.21945862063639"/>
<path d="M-48.842854,41.738876 L-58.541893,9.765432 L-39.018063,3.842944 L-29.319027,35.816387 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-48.84285499134623" x2="-58.54189292390069" y1="41.73887566778566" y2="9.76543255241912"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-58.54189292390069" x2="-39.01806440322565" y1="9.76543255241912" y2="3.8429439193538997"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-39.01806440322565" x2="-29.319026470671194" y1="3.8429439193538997" y2="35.816387034720435"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-29.319026470671194" x2="-48.84285499134623" y1="35.816387034720435" y2="41.73887566778566"/>
<path d="M-35.846466,36.044716 L-39.018063,3.842944 L-20.153395,1.9849361 L-16.981796,34.18671 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.84646650641136" x2="-39.01806440322565" y1="36.04471776669137" y2="3.8429439193538997"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-39.01806440322565" x2="-20.153394239652826" y1="3.8429439193538997" y2="1.9849360810735917"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-20.153394239652826" x2="-16.981796342838535" y1="1.9849360810735917" y2="34.18670992841106"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-16.981796342838535" x2="-35.84646650641136" y1="34.18670992841106" y2="36.04471776669137"/>
<path d="M-17.226347,31.703741 L-20.153395,1.9849361 L0,0 L2.9270468,29.718805 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-17.22634748873851" x2="-20.153394239652826" y1="31.70374047560363" y2="1.9849360810735917"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-20.153394239652826" x2="0" y1="1.9849360810735917" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="2.927046750914316" y1="0" y2="29.71880439453004"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="2.927046750914316" x2="-17.22634748873851" y1="29.71880439453004" y2="31.70374047560363"/>
<path d="M-2.7981045,28.409632 L0,0 L21.350157,2.1028068 L18.552052,30.512438 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-2.7981044921361704" x2="0" y1="28.40963167099237" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="21.350155898545456" y1="0" y2="2.1028068163419746"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="21.350155898545456" x2="18.552051406409284" y1="2.1028068163419746" y2="30.512438487334343"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="18.552051406409284" x2="-2.7981044921361704" y1="30.512438487334343" y2="28.40963167099237"/>
<path d="M18.473928,31.305632 L21.350157,2.1028068 L39.018063,3.842944 L36.141838,33.04577 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="18.473928778980437" x2="21.350155898545456" y1="31.30563083474714" y2="2.1028068163419746"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="21.350155898545456" x2="39.01806440322565" y1="2.1028068163419746" y2="3.8429439193538997"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="39.01806440322565" x2="36.14183728366063" y1="3.8429439193538997" y2="33.04576793775907"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="36.14183728366063" x2="18.473928778980437" y1="33.04576793775907" y2="31.30563083474714"/>
<path d="M29.676126,34.639187 L39.018063,3.842944 L55.306503,8.783988 L45.964565,39.58023 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.676126141626234" x2="39.01806440322565" y1="34.63918718302443" y2="3.8429439193538997"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="39.01806440322565" x2="55.30650452687427" y1="3.8429439193538997" y2="8.783988211999482"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="55.30650452687427" x2="45.964566265274854" y1="8.783988211999482" y2="39.58023147567001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="45.964566265274854" x2="29.676126141626234" y1="39.58023147567001" y2="34.63918718302443"/>
<path d="M45.436825,41.31996 L55.306503,8.783988 L76.53669,15.224093 L66.66701,47.760067 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="45.436825209937865" x2="55.30650452687427" y1="41.31996058383491" y2="8.783988211999482"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="55.30650452687427" x2="76.53668647301795" y1="8.783988211999482" y2="15.224093497742643"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="76.53668647301795" x2="66.66700715608155" y1="15.224093497742643" y2="47.76006586957808"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="66.66700715608155" x2="45.436825209937865" y1="47.76006586957808" y2="41.31996058383491"/>
<path d="M63.186447,40.200634 L76.53669,15.224093 L93.47632,24.278517 L80.12608,49.25506 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.18644721838215" x2="76.53668647301795" y1="40.20063440907146" y2="15.224093497742643"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="76.53668647301795" x2="93.47631974740862" y1="15.224093497742643" y2="24.27851612182704"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="93.47631974740862" x2="80.12608049277281" y1="24.27851612182704" y2="49.25505703315586"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="80.12608049277281" x2="63.18644721838215" y1="49.25505703315586" y2="40.20063440907146"/>
<path d="M78.17772,52.90018 L93.47632,24.278517 L111.114044,33.706078 L95.815445,62.327744 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.1777208090943" x2="93.47631974740862" y1="52.90018162015397" y2="24.27851612182704"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="93.47631974740862" x2="111.11404660392043" y1="24.27851612182704" y2="33.70607753949096"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="111.11404660392043" x2="95.8154476656061" y1="33.70607753949096" y2="62.327743037817896"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="95.8154476656061" x2="78.1777208090943" y1="62.327743037817896" y2="52.90018162015397"/>
<path d="M90.74086,58.53088 L111.114044,33.706078 L126.2677,46.14236 L105.894516,70.96716 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="90.7408578741026" x2="111.11404660392043" y1="58.53087983424322" y2="33.70607753949096"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="111.11404660392043" x2="126.26770142061496" y1="33.70607753949096" y2="46.142360651090726"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="126.26770142061496" x2="105.89451269079713" y1="46.142360651090726" y2="70.96716294584299"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="105.89451269079713" x2="90.7408578741026" y1="70.96716294584299" y2="58.53087983424322"/>
<path d="M121.42136,78.98262 L121.42136,108.57864 L99.49015,108.57864 L99.49015,78.98262 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="121.42135623730948" x2="121.42135623730948" y1="78.98261674535236" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="121.42135623730948" x2="99.49015362260259" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="99.49015362260259" x2="99.49015362260259" y1="108.57864376269049" y2="78.98261674535236"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="99.49015362260259" x2="121.42135623730948" y1="78.98261674535236" y2="78.98261674535236"/>
<path d="M99.49015,80.18517 L99.49015,108.57864 L80.528275,108.57864 L80.528275,80.18517 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="99.49015362260259" x2="99.49015362260259" y1="80.18517400362686" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="99.49015362260259" x2="80.52827179289598" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="80.52827179289598" x2="80.52827179289598" y1="108.57864376269049" y2="80.18517400362686"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="80.52827179289598" x2="99.49015362260259" y1="80.18517400362686" y2="80.18517400362686"/>
<path d="M80.528275,80.378174 L80.528275,108.57864 L61.465908,108.57864 L61.465908,80.378174 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="80.52827179289598" x2="80.52827179289598" y1="80.37817743225405" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="80.52827179289598" x2="61.46590754151808" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.46590754151808" x2="61.46590754151808" y1="108.57864376269049" y2="80.37817743225405"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="61.46590754151808" x2="80.52827179289598" y1="80.37817743225405" y2="80.37817743225405"/>
<path d="M61.465908,74.57864 L61.465908,108.57864 L44.11051,108.57864 L44.11051,74.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.46590754151808" x2="61.46590754151808" y1="74.57864276269049" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="61.46590754151808" x2="44.11051097877353" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.11051097877353" x2="44.11051097877353" y1="108.57864376269049" y2="74.57864276269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="44.11051097877353" x2="61.46590754151808" y1="74.57864276269049" y2="74.57864276269049"/>
<path d="M44.11051,78.17331 L44.11051,108.57864 L25.038666,108.57864 L25.038666,78.17331 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.11051097877353" x2="44.11051097877353" y1="78.17331141636416" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="44.11051097877353" x2="25.03866594691874" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="25.03866594691874" x2="25.03866594691874" y1="108.57864376269049" y2="78.17331141636416"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="25.03866594691874" x2="44.11051097877353" y1="78.17331141636416" y2="78.17331141636416"/>
<path d="M25.038666,79.174446 L25.038666,108.57864 L2.420242,108.57864 L2.420242,79.174446 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="25.03866594691874" x2="25.03866594691874" y1="79.17444777206498" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="25.03866594691874" x2="2.4202421168091632" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="2.4202421168091632" x2="2.4202421168091632" y1="108.57864376269049" y2="79.17444777206498"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="2.4202421168091632" x2="25.03866594691874" y1="79.17444777206498" y2="79.17444777206498"/>
<path d="M2.420242,74.57864 L2.420242,108.57864 L-20.558802,108.57864 L-20.558802,74.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="2.4202421168091632" x2="2.4202421168091632" y1="74.57864276269049" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="2.4202421168091632" x2="-20.558801380058355" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-20.558801380058355" x2="-20.558801380058355" y1="108.57864376269049" y2="74.57864276269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-20.558801380058355" x2="2.4202421168091632" y1="74.57864276269049" y2="74.57864276269049"/>
<path d="M-20.558802,79.67424 L-20.558802,108.57864 L-39.86155,108.57864 L-39.86155,79.67424 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-20.558801380058355" x2="-20.558801380058355" y1="79.67424164828014" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-20.558801380058355" x2="-39.86154910109951" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-39.86154910109951" x2="-39.86154910109951" y1="108.57864376269049" y2="79.67424164828014"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-39.86154910109951" x2="-20.558801380058355" y1="79.67424164828014" y2="79.67424164828014"/>
<path d="M-39.86155,76.30053 L-39.86155,108.57864 L-58.38272,108.57864 L-58.38272,76.30053 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-39.86154910109951" x2="-39.86154910109951" y1="76.30053248116458" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-39.86154910109951" x2="-58.38272096776976" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-58.38272096776976" x2="-58.38272096776976" y1="108.57864376269049" y2="76.30053248116458"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-58.38272096776976" x2="-39.86154910109951" y1="76.30053248116458" y2="76.30053248116458"/>
<path d="M-58.38272,79.101776 L-58.38272,108.57864 L-80.86967,108.57864 L-80.86967,79.101776 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-58.38272096776976" x2="-58.38272096776976" y1="79.1017752456556" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-58.38272096776976" x2="-80.86966768798288" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-80.86966768798288" x2="-80.86966768798288" y1="108.57864376269049" y2="79.1017752456556"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-80.86966768798288" x2="-58.38272096776976" y1="79.1017752456556" y2="79.1017752456556"/>
<path d="M-80.86967,74.57864 L-80.86967,108.57864 L-98.31728,108.57864 L-98.31728,74.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-80.86966768798288" x2="-80.86966768798288" y1="74.57864276269049" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-80.86966768798288" x2="-98.3172806944311" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-98.3172806944311" x2="-98.3172806944311" y1="108.57864376269049" y2="74.57864276269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-98.3172806944311" x2="-80.86966768798288" y1="74.57864276269049" y2="74.57864276269049"/>
<path d="M-98.31728,74.57864 L-98.31728,108.57864 L-121.42136,108.57864 L-121.42136,74.57864 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-98.3172806944311" x2="-98.3172806944311" y1="74.57864276269049" y2="108.57864376269049"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-98.3172806944311" x2="-121.42135623730948" y1="108.57864376269049" y2="108.57864376269049"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-121.42135623730948" x2="-121.42135623730948" y1="108.57864376269049" y2="74.57864276269049"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-121.42135623730948" x2="-98.3172806944311" y1="74.57864276269049" y2="74.57864276269049"/>
</g>
<g id="nodes">
<circle cx="141.42135623730948" cy="108.57864376269049" fill="#000000" r="0.7"/>
<circle cx="-141.42135623730948" cy="108.57864376269049" fill="#000000" r="0.7"/>
<circle cx="-141.42135623730948" cy="58.57864376269049" fill="#000000" r="0.7"/>
<circle cx="-111.11404660392043" cy="33.70607753949096" fill="#000000" r="0.7"/>
<circle cx="-76.53668647301795" cy="15.224093497742643" fill="#000000" r="0.7"/>
<circle cx="-39.01806440322565" cy="3.8429439193538997" fill="#000000" r="0.7"/>
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="39.01806440322565" cy="3.8429439193538997" fill="#000000" r="0.7"/>
<circle cx="76.53668647301795" cy="15.224093497742643" fill="#000000" r="0.7"/>
<circle cx="111.11404660392043" cy="33.70607753949096" fill="#000000" r="0.7"/>
<circle cx="141.42135623730948" cy="58.57864376269049" fill="#000000" r="0.7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

View File

@ -0,0 +1,243 @@
<svg height="663.5382907247957" viewBox="-50.00000000000004 -82.94228634059945 145.00000000000006 165.88457268119893" width="580.0000000000002" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="165.88457268119893" width="145.00000000000006" x="-50.00000000000004" y="-82.94228634059945"/>
<g transform="translate(0, 0.00000000000002842170943040401) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="90" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="-44.999999999999986" y1="0" y2="77.94228634059948"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="-45.00000000000004" y1="0" y2="-77.94228634059945"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="90" x2="-44.999999999999986" y1="0" y2="77.94228634059948"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-44.999999999999986" x2="-45.00000000000004" y1="77.94228634059948" y2="-77.94228634059945"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="-45.00000000000004" x2="90" y1="-77.94228634059945" y2="0"/>
</g>
<g id="parcels">
<path d="M-15,25.980762 L0,0 L20,0 L20,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-14.999999999999998" x2="0" y1="25.980762113533164" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="20" y1="0" y2="0"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="20" y1="0" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="-14.999999999999998" y1="30" y2="25.980762113533164"/>
<path d="M20,12.990381 L20,0 L42.996502,0 L42.996502,12.990381 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="12.99038105676658" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="12.99038105676658"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="12.99038105676658" y2="12.99038105676658"/>
<path d="M42.996502,12.990381 L42.996502,0 L65.47971,0 L65.47971,12.990381 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="12.99038105676658" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="12.99038105676658"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="12.99038105676658" y2="12.99038105676658"/>
<path d="M67.5,12.990381 L65.47971,12.990381 L65.47971,0 L90,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="67.5" x2="65.47971305349805" y1="12.99038105676658" y2="12.99038105676658"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="12.99038105676658" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="90" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="90" x2="67.5" y1="0" y2="12.99038105676658"/>
<path d="M90,0 L73.583664,9.477974 L68.11156,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="90" x2="73.58366727522163" y1="0" y2="9.477974117757258"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="73.58366727522163" x2="68.11155636696218" y1="9.477974117757258" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="68.11155636696218" x2="90" y1="0" y2="0"/>
<path d="M45,0 L68.11156,0 L73.583664,9.477974 L55.575096,19.875227 L44.325096,0.38965538 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="45" x2="68.11155636696218" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="68.11155636696218" x2="73.58366727522163" y1="0" y2="9.477974117757258"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="73.58366727522163" x2="55.57509710703826" y1="9.477974117757258" y2="19.875226952078187"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="55.57509710703826" x2="44.32509710703826" y1="19.875226952078187" y2="0.3896553669283165"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="44.32509710703826" x2="45" y1="0.3896553669283165" y2="0"/>
<path d="M44.325096,0.38965538 L55.575096,19.875227 L37.37281,30.384321 L26.122812,10.898749 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.32509710703826" x2="55.57509710703826" y1="0.3896553669283165" y2="19.875226952078187"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="55.57509710703826" x2="37.37281205825939" y1="19.875226952078187" y2="30.384321124856967"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="37.37281205825939" x2="26.12281205825939" y1="30.384321124856967" y2="10.898749539707097"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="26.12281205825939" x2="44.32509710703826" y1="10.898749539707097" y2="0.3896553669283165"/>
<path d="M26.122812,10.898749 L37.37281,30.384321 L20.791067,39.957798 L9.541066,20.472225 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="26.12281205825939" x2="37.37281205825939" y1="10.898749539707097" y2="30.384321124856967"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="37.37281205825939" x2="20.791066316395757" y1="30.384321124856967" y2="39.95779649255587"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20.791066316395757" x2="9.541066316395757" y1="39.95779649255587" y2="20.472224907406"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="9.541066316395757" x2="26.12281205825939" y1="20.472224907406" y2="10.898749539707097"/>
<path d="M9.541066,20.472225 L20.791067,39.957798 L5.2092485,48.953964 L-6.0407515,29.468391 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="9.541066316395757" x2="20.791066316395757" y1="20.472224907406" y2="39.95779649255587"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20.791066316395757" x2="5.2092487340263744" y1="39.95779649255587" y2="48.95396306820048"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="5.2092487340263744" x2="-6.0407512659736256" y1="48.95396306820048" y2="29.46839148305061"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-6.0407512659736256" x2="9.541066316395757" y1="29.46839148305061" y2="20.472224907406"/>
<path d="M-22.5,38.97114 L-6.0407515,29.468391 L5.2092485,48.953964 L-12.245153,59.031265 L-23.163435,40.120247 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.499999999999993" x2="-6.0407512659736256" y1="38.97114317029974" y2="29.46839148305061"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-6.0407512659736256" x2="5.2092487340263744" y1="29.46839148305061" y2="48.95396306820048"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="5.2092487340263744" x2="-12.245153214380935" y1="48.95396306820048" y2="59.03126673165737"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-12.245153214380935" x2="-23.16343547625395" y1="59.03126673165737" y2="40.120247122715256"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-23.16343547625395" x2="-22.499999999999993" y1="40.120247122715256" y2="38.97114317029974"/>
<path d="M-23.163435,40.120247 L-12.245153,59.031265 L-30.50305,69.57247 L-35.335365,61.202652 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-23.163435476253955" x2="-12.245153214380935" y1="40.120247122715256" y2="59.03126673165737"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-12.245153214380935" x2="-30.503050418986206" y1="59.03126673165737" y2="69.57246859757274"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-30.503050418986206" x2="-35.3353669459908" y1="69.57246859757274" y2="61.20265085454599"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-35.3353669459908" x2="-23.163435476253955" y1="61.20265085454599" y2="40.120247122715256"/>
<path d="M-35.335365,61.202652 L-30.50305,69.57247 L-45,77.94228 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.3353669459908" x2="-30.503050418986206" y1="61.202650854546" y2="69.57246859757274"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-30.503050418986206" x2="-45" y1="69.57246859757274" y2="77.94228634059948"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45" x2="-35.3353669459908" y1="77.94228634059948" y2="61.202650854546"/>
<path d="M-22.5,64.951904 L-45,77.94228 L-33.57214,58.14865 L-22.322138,64.64384 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.499999999999986" x2="-44.999999999999986" y1="64.9519052838329" y2="77.94228634059948"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-44.999999999999986" x2="-33.57213874679279" y1="77.94228634059948" y2="58.14865002819687"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-33.57213874679279" x2="-22.32213874679279" y1="58.14865002819687" y2="64.64384055658016"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.32213874679279" x2="-22.499999999999986" y1="64.64384055658016" y2="64.9519052838329"/>
<path d="M-22.322138,64.64384 L-33.57214,58.14865 L-22.822025,39.528908 L-11.572025,46.024097 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.32213874679279" x2="-33.57213874679279" y1="64.64384055658016" y2="58.14865002819687"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-33.57213874679279" x2="-22.82202535134675" y1="58.14865002819687" y2="39.52890744015754"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.82202535134675" x2="-11.572025351346747" y1="39.52890744015754" y2="46.02409796854083"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-11.572025351346747" x2="-22.32213874679279" y1="46.02409796854083" y2="64.64384055658016"/>
<path d="M-11.572025,46.024097 L-22.822025,39.528908 L-15,25.980762 L-3.75,32.475952 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-11.572025351346747" x2="-22.82202535134675" y1="46.02409796854083" y2="39.52890744015754"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-22.82202535134675" x2="-14.999999999999996" y1="39.52890744015754" y2="25.98076211353316"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-14.999999999999996" x2="-3.7499999999999947" y1="25.98076211353316" y2="32.47595264191645"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-3.7499999999999947" x2="-11.572025351346747" y1="32.47595264191645" y2="46.02409796854083"/>
<path d="M20,0 L0,0 L-15,-25.980762 L2.320508,-35.980762 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="0" y1="0" y2="0"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="0" x2="-15.000000000000014" y1="0" y2="-25.980762113533153"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-15.000000000000014" x2="2.3205080756887533" y1="-25.980762113533153" y2="-35.98076211353316"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="2.3205080756887533" x2="20" y1="-35.98076211353316" y2="0"/>
<path d="M67.5,-12.990381 L90,0 L67.003494,0 L67.003494,-12.990381 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="67.49999999999999" x2="90" y1="-12.990381056766575" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="90" x2="67.00349662162836" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="67.00349662162836" x2="67.00349662162836" y1="0" y2="-12.990381056766575"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="67.00349662162836" x2="67.49999999999999" y1="-12.990381056766575" y2="-12.990381056766575"/>
<path d="M67.003494,-12.990381 L67.003494,0 L44.520287,0 L44.520287,-12.990381 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="67.00349662162836" x2="67.00349662162836" y1="-12.990381056766575" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="67.00349662162836" x2="44.52028694650196" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.52028694650196" x2="44.52028694650196" y1="0" y2="-12.990381056766575"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="44.52028694650196" x2="67.00349662162836" y1="-12.990381056766575" y2="-12.990381056766575"/>
<path d="M44.520287,-12.990381 L44.520287,0 L20,0 L20,-12.990381 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.52028694650196" x2="44.52028694650196" y1="-12.990381056766575" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="44.52028694650196" x2="20" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="0" y2="-12.990381056766575"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="44.52028694650196" y1="-12.990381056766575" y2="-12.990381056766575"/>
<path d="M-3.75,-32.475952 L-15,-25.980762 L-24.402254,-42.265945 L-13.152255,-48.761135 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-3.750000000000025" x2="-15.000000000000014" y1="-32.47595264191644" y2="-25.980762113533153"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-15.000000000000014" x2="-24.402254705539438" y1="-25.980762113533153" y2="-42.26594496923097"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-24.402254705539438" x2="-13.152254705539448" y1="-42.26594496923097" y2="-48.76113549761426"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-13.152254705539448" x2="-3.750000000000025" y1="-48.76113549761426" y2="-32.47595264191644"/>
<path d="M-22.5,-64.951904 L-13.152255,-48.761135 L-24.402254,-42.265945 L-34.870396,-60.397297 L-24.74079,-66.24563 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.50000000000003" x2="-13.152254705539448" y1="-64.95190528383287" y2="-48.76113549761426"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-13.152254705539448" x2="-24.402254705539438" y1="-48.76113549761426" y2="-42.26594496923097"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-24.402254705539438" x2="-34.87039511076331" y1="-42.26594496923097" y2="-60.39729601184336"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-34.87039511076331" x2="-24.740790221526616" y1="-60.39729601184336" y2="-66.24562612142873"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-24.740790221526616" x2="-22.50000000000003" y1="-66.24562612142873" y2="-64.95190528383287"/>
<path d="M-24.74079,-66.24563 L-34.870396,-60.397297 L-45,-77.94228 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-24.740790221526613" x2="-34.87039511076331" y1="-66.24562612142873" y2="-60.39729601184336"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-34.87039511076331" x2="-45.00000000000004" y1="-60.39729601184336" y2="-77.94228634059945"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.00000000000004" x2="-24.740790221526613" y1="-77.94228634059945" y2="-66.24562612142873"/>
<path d="M-45,-77.94228 L-30.259056,-69.4316 L-35.172703,-60.92091 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.00000000000004" x2="-30.25905700870268" y1="-77.94228634059945" y2="-69.431598936465"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-30.25905700870268" x2="-35.172704672468456" y1="-69.431598936465" y2="-60.92091153233055"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.172704672468456" x2="-45.00000000000004" y1="-60.92091153233055" y2="-77.94228634059945"/>
<path d="M-22.5,-38.97114 L-35.172703,-60.92091 L-30.259056,-69.4316 L-10.41208,-57.972942 L-21.662079,-38.48737 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.500000000000032" x2="-35.172704672468456" y1="-38.97114317029975" y2="-60.92091153233055"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.172704672468456" x2="-30.25905700870268" y1="-60.92091153233055" y2="-69.431598936465"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-30.25905700870268" x2="-10.41207961667945" y1="-69.431598936465" y2="-57.97294119657998"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-10.41207961667945" x2="-21.662079616679435" y1="-57.97294119657998" y2="-38.487369611430125"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-21.662079616679435" x2="-22.500000000000032" y1="-38.487369611430125" y2="-38.97114317029975"/>
<path d="M-21.662079,-38.48737 L-10.41208,-57.972942 L7.6761527,-47.529694 L-3.5738473,-28.044125 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-21.662079616679435" x2="-10.41207961667945" y1="-38.487369611430125" y2="-57.97294119657998"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-10.41207961667945" x2="7.676152770599806" y1="-57.97294119657998" y2="-47.529695358619804"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="7.676152770599806" x2="-3.5738472294001795" y1="-47.529695358619804" y2="-28.044123773469945"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-3.5738472294001795" x2="-21.662079616679435" y1="-28.044123773469945" y2="-38.487369611430125"/>
<path d="M-3.5738473,-28.044125 L7.6761527,-47.529694 L27.363934,-36.16295 L16.113934,-16.677378 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-3.5738472294001795" x2="7.676152770599806" y1="-28.044123773469945" y2="-47.529695358619804"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="7.676152770599806" x2="27.3639339040313" y1="-47.529695358619804" y2="-36.162949621486696"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="27.3639339040313" x2="16.113933904031313" y1="-36.162949621486696" y2="-16.677378036336837"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="16.113933904031313" x2="-3.5738472294001795" y1="-16.677378036336837" y2="-28.044123773469945"/>
<path d="M16.113934,-16.677378 L27.363934,-36.16295 L47.07334,-24.783718 L35.82334,-5.2981467 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="16.113933904031313" x2="27.3639339040313" y1="-16.677378036336837" y2="-36.162949621486696"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="27.3639339040313" x2="47.07334043858461" y1="-36.162949621486696" y2="-24.783718453194588"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="47.07334043858461" x2="35.82334043858462" y1="-24.783718453194588" y2="-5.298146868044729"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="35.82334043858462" x2="16.113933904031313" y1="-5.298146868044729" y2="-16.677378036336837"/>
<path d="M45,0 L35.82334,-5.2981467 L47.07334,-24.783718 L63.216145,-15.463665 L54.288197,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="45.00000000000002" x2="35.82334043858462" y1="0" y2="-5.298146868044729"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="35.82334043858462" x2="47.07334043858461" y1="-5.298146868044729" y2="-24.783718453194588"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="47.07334043858461" x2="63.216146085134696" y1="-24.783718453194588" y2="-15.46366526768309"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.216146085134696" x2="54.2881947801796" y1="-15.46366526768309" y2="0"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="54.2881947801796" x2="45.00000000000002" y1="0" y2="0"/>
<path d="M54.288197,0 L63.216145,-15.463665 L80.78675,-5.319271 L77.71567,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="54.2881947801796" x2="63.216146085134696" y1="0" y2="-15.46366526768309"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="63.216146085134696" x2="80.78675235361686" y1="-15.46366526768309" y2="-5.319271008749993"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="80.78675235361686" x2="77.71566980482248" y1="-5.319271008749993" y2="0"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="77.71566980482248" x2="54.2881947801796" y1="0" y2="0"/>
<path d="M-15,-25.980762 L0,0 L-10,17.320509 L-35.980762,2.320508 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-15.000000000000014" x2="0" y1="-25.980762113533153" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="-9.999999999999998" y1="0" y2="17.320508075688778"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-9.999999999999998" x2="-35.98076211353316" y1="17.320508075688778" y2="2.32050807568878"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.98076211353316" x2="-15.000000000000014" y1="2.32050807568878" y2="-25.980762113533153"/>
<path d="M-21.25,10.825317 L-10,17.320509 L-21.427862,37.114143 L-32.67786,30.618954 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-21.250000000000004" x2="-9.999999999999998" y1="10.825317547305488" y2="17.320508075688778"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-9.999999999999998" x2="-21.427861253207194" y1="17.320508075688778" y2="37.11414438809139"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-21.427861253207194" x2="-32.6778612532072" y1="37.11414438809139" y2="30.6189538597081"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-32.6778612532072" x2="-21.250000000000004" y1="30.6189538597081" y2="10.825317547305488"/>
<path d="M-32.67786,30.618954 L-21.427862,37.114143 L-32.177975,55.733887 L-43.427975,49.238697 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-32.6778612532072" x2="-21.427861253207194" y1="30.6189538597081" y2="37.11414438809139"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-21.427861253207194" x2="-32.177974648653226" y1="37.11414438809139" y2="55.733886976130705"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-32.177974648653226" x2="-43.42797464865323" y1="55.733886976130705" y2="49.238696447747415"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-43.42797464865323" x2="-32.6778612532072" y1="49.238696447747415" y2="30.6189538597081"/>
<path d="M-45,51.961525 L-43.427975,49.238697 L-32.177975,55.733887 L-42.03285,72.803024 L-45,71.08994 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-44.99999999999999" x2="-43.42797464865323" y1="51.96152422706632" y2="49.238696447747415"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-43.42797464865323" x2="-32.177974648653226" y1="49.238696447747415" y2="55.733886976130705"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-32.177974648653226" x2="-42.03284671950401" y1="55.733886976130705" y2="72.80302610493578"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-42.03284671950401" x2="-44.999999999999986" y1="72.80302610493578" y2="71.08993935971455"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-44.999999999999986" x2="-44.99999999999999" y1="71.08993935971455" y2="51.96152422706632"/>
<path d="M-45,77.94228 L-45,56.488827 L-32.61384,56.488827 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-44.999999999999986" x2="-44.99999999999999" y1="77.94228634059948" y2="56.48882616749338"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-44.99999999999999" x2="-32.61383899400827" y1="56.48882616749338" y2="56.48882616749337"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-32.61383899400827" x2="-44.999999999999986" y1="56.48882616749337" y2="77.94228634059948"/>
<path d="M-22.5,38.97114 L-32.61384,56.488827 L-45,56.488827 L-45,34.366146 L-22.5,34.366146 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.499999999999993" x2="-32.61383899400827" y1="38.97114317029974" y2="56.48882616749337"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-32.61383899400827" x2="-44.99999999999999" y1="56.48882616749337" y2="56.48882616749338"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-44.99999999999999" x2="-45" y1="56.48882616749338" y2="34.36614540874733"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45" x2="-22.499999999999993" y1="34.36614540874733" y2="34.36614540874732"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.499999999999993" x2="-22.499999999999993" y1="34.36614540874732" y2="38.97114317029974"/>
<path d="M-22.5,34.366146 L-45,34.366146 L-45,16.205826 L-22.5,16.205826 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.499999999999993" x2="-45" y1="34.36614540874732" y2="34.36614540874733"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45" x2="-45.00000000000001" y1="34.36614540874733" y2="16.205825111234716"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.00000000000001" x2="-22.5" y1="16.205825111234716" y2="16.20582511123471"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.5" x2="-22.499999999999993" y1="16.20582511123471" y2="34.36614540874732"/>
<path d="M-22.5,16.205826 L-45,16.205826 L-45,-2.5522678 L-22.5,-2.5522678 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.5" x2="-45.00000000000001" y1="16.20582511123471" y2="16.205825111234716"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.00000000000001" x2="-45.000000000000014" y1="16.205825111234716" y2="-2.5522678857734604"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.000000000000014" x2="-22.500000000000007" y1="-2.5522678857734604" y2="-2.5522678857734684"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.500000000000007" x2="-22.5" y1="-2.5522678857734684" y2="16.20582511123471"/>
<path d="M-22.5,-2.5522678 L-45,-2.5522678 L-45,-19.88903 L-22.5,-19.88903 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.500000000000007" x2="-45.000000000000014" y1="-2.5522678857734684" y2="-2.5522678857734604"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.000000000000014" x2="-45.00000000000002" y1="-2.5522678857734604" y2="-19.88903112236116"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.00000000000002" x2="-22.500000000000014" y1="-19.88903112236116" y2="-19.889031122361168"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.500000000000014" x2="-22.500000000000007" y1="-19.889031122361168" y2="-2.5522678857734684"/>
<path d="M-22.5,-19.88903 L-45,-19.88903 L-45,-38.21426 L-22.5,-38.21426 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.500000000000014" x2="-45.00000000000002" y1="-19.889031122361168" y2="-19.88903112236116"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.00000000000002" x2="-45.00000000000003" y1="-19.88903112236116" y2="-38.21426049589469"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.00000000000003" x2="-22.50000000000002" y1="-38.21426049589469" y2="-38.2142604958947"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.50000000000002" x2="-22.500000000000014" y1="-38.2142604958947" y2="-19.889031122361168"/>
<path d="M-22.5,-38.97114 L-22.5,-38.21426 L-45,-38.21426 L-45,-57.02484 L-32.92331,-57.02484 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-22.50000000000002" x2="-22.50000000000002" y1="-38.971143170299726" y2="-38.2142604958947"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-22.50000000000002" x2="-45.00000000000003" y1="-38.2142604958947" y2="-38.21426049589469"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.00000000000003" x2="-45.000000000000036" y1="-38.21426049589469" y2="-57.0248414835568"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.000000000000036" x2="-32.9233075810273" y1="-57.0248414835568" y2="-57.024841483556806"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-32.9233075810273" x2="-22.50000000000002" y1="-57.024841483556806" y2="-38.971143170299726"/>
<path d="M-32.92331,-57.02484 L-45,-57.02484 L-45,-77.94228 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-32.9233075810273" x2="-45.000000000000036" y1="-57.024841483556806" y2="-57.0248414835568"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.000000000000036" x2="-45.00000000000004" y1="-57.0248414835568" y2="-77.94228634059945"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.00000000000004" x2="-32.9233075810273" y1="-77.94228634059945" y2="-57.024841483556806"/>
<path d="M-45,-77.94228 L-35.597744,-61.657104 L-45,-56.22871 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-45.00000000000004" x2="-35.597745294460616" y1="-77.94228634059945" y2="-61.65710348490163"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-35.597745294460616" x2="-45.000000000000036" y1="-61.65710348490163" y2="-56.22870919966902"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.000000000000036" x2="-45.00000000000004" y1="-56.22870919966902" y2="-77.94228634059945"/>
<path d="M-45,-51.961525 L-45,-56.22871 L-35.597744,-61.657104 L-25.129604,-43.525753 L-36.379604,-37.030563 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-45.000000000000036" x2="-45.000000000000036" y1="-51.96152422706629" y2="-56.22870919966902"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-45.000000000000036" x2="-35.597745294460616" y1="-56.22870919966902" y2="-61.65710348490163"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-35.597745294460616" x2="-25.12960488923674" y1="-61.65710348490163" y2="-43.525752442289246"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-25.12960488923674" x2="-36.379604889236745" y1="-43.525752442289246" y2="-37.03056191390595"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-36.379604889236745" x2="-45.000000000000036" y1="-37.03056191390595" y2="-51.96152422706629"/>
<path d="M-36.379604,-37.030563 L-25.129604,-43.525753 L-15,-25.980762 L-26.25,-19.48557 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-36.379604889236745" x2="-25.12960488923674" y1="-37.03056191390595" y2="-43.525752442289246"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="-25.12960488923674" x2="-15.000000000000014" y1="-43.525752442289246" y2="-25.980762113533146"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="-15.000000000000014" x2="-26.250000000000014" y1="-25.980762113533146" y2="-19.48557158514985"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="-26.250000000000014" x2="-36.379604889236745" y1="-19.48557158514985" y2="-37.03056191390595"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="90" cy="0" fill="#000000" r="0.7"/>
<circle cx="-44.999999999999986" cy="77.94228634059948" fill="#000000" r="0.7"/>
<circle cx="-45.00000000000004" cy="-77.94228634059945" fill="#000000" r="0.7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

View File

@ -0,0 +1,139 @@
<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,100 L180,100 L180,70 L200,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="200" y1="70" y2="100"/>
<path d="M0,100 L0,70 L20,70 L20,100 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<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"/>
<path d="M20,0 L42.996502,0 L42.996502,29.52622 L20,29.52622 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="29.526220147024308" y2="0"/>
<path d="M42.996502,0 L65.47971,0 L65.47971,31.147902 L42.996502,31.147902 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="31.1479024543553" y2="0"/>
<path d="M65.47971,0 L88.27847,0 L88.27847,29.876335 L65.47971,29.876335 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="88.27847104373568" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="29.87633532437634" y2="0"/>
<path d="M88.27847,0 L109.00264,0 L109.00264,30.25849 L88.27847,30.25849 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.27847104373568" x2="109.00264275540263" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="30.25848985257909" y2="0"/>
<path d="M109.00264,0 L127.212425,0 L127.212425,34 L109.00264,34 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.00264275540263" x2="127.2124223685276" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="34.000001" y2="0"/>
<path d="M127.212425,0 L146.8415,0 L146.8415,31.470558 L127.212425,31.470558 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.2124223685276" x2="146.84151010926075" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="31.47055834801727" y2="0"/>
<path d="M146.8415,0 L166.40808,0 L166.40808,33.89513 L146.8415,33.89513 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.84151010926075" x2="166.4080836096857" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.4080836096857" x2="166.4080836096857" y1="0" y2="33.89512833223243"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="166.4080836096857" x2="146.84151010926075" y1="33.89512833223243" y2="33.89512833223243"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="33.89512833223243" y2="0"/>
<path d="M166.40808,0 L180,0 L180,29.999052 L166.40808,29.999052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="166.4080836096857" x2="180" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="29.999051502429545"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="166.4080836096857" y1="29.999051502429545" y2="29.999051502429545"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.4080836096857" x2="166.4080836096857" y1="29.999051502429545" y2="0"/>
<path d="M200,30 L200,52.855724 L169.38701,52.855724 L169.38701,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.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="169.3870078110646" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="169.3870078110646" x2="169.3870078110646" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="169.3870078110646" x2="200" y1="30" y2="30"/>
<path d="M200,52.855724 L200,70 L172.17726,70 L172.17726,52.855724 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="200" x2="200" y1="52.855722506414395" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="172.17726330629023" y1="70" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="172.17726330629023" x2="172.17726330629023" y1="70" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="172.17726330629023" x2="200" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M180,100 L161.1955,100 L161.1955,72.466 L180,72.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="161.19549058892116" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="100" y2="72.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="161.19549058892116" x2="180" y1="72.46600595599794" y2="72.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="72.46600595599794" y2="100"/>
<path d="M161.1955,100 L140.25922,100 L140.25922,68.26361 L161.1955,68.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="161.19549058892116" x2="140.25920977847343" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="100" y2="68.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="140.25920977847343" x2="161.19549058892116" y1="68.26360853604555" y2="68.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="68.26360853604555" y2="100"/>
<path d="M140.25922,100 L120.000404,100 L120.000404,66 L140.25922,66 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="140.25920977847343" x2="120.0004046814901" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="100" y2="65.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.0004046814901" x2="140.25920977847343" y1="65.999999" y2="65.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="65.999999" y2="100"/>
<path d="M120.000404,100 L98.191284,100 L98.191284,71.2734 L120.000404,71.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.0004046814901" x2="98.19128506754218" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="100" y2="71.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="98.19128506754218" x2="120.0004046814901" y1="71.27339761286972" y2="71.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="71.27339761286972" y2="100"/>
<path d="M98.191284,100 L81.11824,100 L81.11824,72.8639 L98.191284,72.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="98.19128506754218" x2="81.11823933536908" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="100" y2="72.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="81.11823933536908" x2="98.19128506754218" y1="72.86389925174167" y2="72.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="72.86389925174167" y2="100"/>
<path d="M81.11824,100 L63.12904,100 L63.12904,70.637054 L81.11824,70.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="81.11823933536908" x2="63.129039299600635" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="100" y2="70.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="63.129039299600635" x2="81.11823933536908" y1="70.63705247970552" y2="70.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="70.63705247970552" y2="100"/>
<path d="M63.12904,100 L40.141674,100 L40.141674,68.958885 L63.12904,68.958885 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="63.129039299600635" x2="40.14167365815044" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="100" y2="68.95888431009364"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="40.14167365815044" x2="63.129039299600635" y1="68.95888431009364" y2="68.95888431009364"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="68.95888431009364" y2="100"/>
<path d="M40.141674,100 L20,100 L20,72.17421 L40.141674,72.17421 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="40.14167365815044" x2="20" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="100" y2="72.17420690790317"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="40.14167365815044" y1="72.17420690790317" y2="72.17420690790317"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="72.17420690790317" y2="100"/>
<path d="M0,70 L0,51.044052 L32.357586,51.044052 L32.357586,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="70" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="32.357584460743425" y1="51.04405176448549" y2="51.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="32.357584460743425" x2="32.357584460743425" y1="51.04405176448549" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="32.357584460743425" x2="0" y1="70" y2="70"/>
<path d="M0,51.044052 L0,30 L29.8626,30 L29.8626,51.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="51.04405176448549" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="30" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="51.04405176448549" y2="51.04405176448549"/>
</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>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,139 @@
<svg height="440" viewBox="-5 -5 218 110" width="872" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="110" width="218" 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="208" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="208" 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.8,0 L20,30 L0,30 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="20.8" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20.8" 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="M208,0 L200,30 L180,30 L187.2,0 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="208" 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="187.20000000000002" y1="30" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="187.20000000000002" x2="208" y1="0" y2="0"/>
<path d="M200,100 L180,100 L180,70 L200,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="200" x2="200" y1="70" y2="100"/>
<path d="M0,100 L0,70 L20,70 L20,100 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<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"/>
<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"/>
<path d="M20.8,0 L44.716362,0 L42.996502,29.52622 L20,29.52622 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20.8" x2="44.71636351350652" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="44.71636351350652" x2="42.99650337837165" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20.8" y1="29.526220147024308" y2="0"/>
<path d="M44.716362,0 L68.0989,0 L65.47971,31.147902 L42.996502,31.147902 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="44.71636351350652" x2="68.09890157563797" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="68.09890157563797" x2="65.47971305349805" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="44.71636351350652" y1="31.1479024543553" y2="0"/>
<path d="M68.0989,0 L91.80961,0 L88.27847,29.876335 L65.47971,29.876335 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="68.09890157563797" x2="91.80960988548512" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="91.80960988548512" x2="88.27847104373568" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="68.09890157563797" y1="29.87633532437634" y2="0"/>
<path d="M91.80961,0 L113.36275,0 L109.00264,30.25849 L88.27847,30.25849 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="91.80960988548512" x2="113.36274846561874" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="113.36274846561874" x2="109.00264275540263" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="91.80960988548512" y1="30.25848985257909" y2="0"/>
<path d="M113.36275,0 L132.30092,0 L127.212425,34 L109.00264,34 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="113.36274846561874" x2="132.30091926326872" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="132.30091926326872" x2="127.2124223685276" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="113.36274846561874" y1="34.000001" y2="0"/>
<path d="M132.30092,0 L152.71516,0 L146.8415,31.470558 L127.212425,31.470558 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="132.30091926326872" x2="152.71517051363116" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="152.71517051363116" x2="146.84151010926075" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="132.30091926326872" y1="31.47055834801727" y2="0"/>
<path d="M152.71516,0 L173.0644,0 L166.40808,33.89513 L146.8415,33.89513 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="152.71517051363116" x2="173.0644069540731" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="173.0644069540731" x2="166.4080836096857" y1="0" y2="33.89512833223243"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="166.4080836096857" x2="146.84151010926075" y1="33.89512833223243" y2="33.89512833223243"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="152.71517051363116" y1="33.89512833223243" y2="0"/>
<path d="M173.0644,0 L187.2,0 L180,29.999052 L166.40808,29.999052 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="173.0644069540731" x2="187.20000000000002" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="187.20000000000002" x2="180" y1="0" y2="29.999051502429545"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="166.4080836096857" y1="29.999051502429545" y2="29.999051502429545"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="166.4080836096857" x2="173.0644069540731" y1="29.999051502429545" y2="0"/>
<path d="M205.6,30 L203.77155,52.855724 L169.38701,52.855724 L169.38701,30 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="205.6" x2="203.77154219948684" y1="30" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="203.77154219948684" x2="169.3870078110646" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="169.3870078110646" x2="169.3870078110646" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="169.3870078110646" x2="205.6" y1="30" y2="30"/>
<path d="M203.77155,52.855724 L202.4,70 L172.17726,70 L172.17726,52.855724 z" fill="#9fd99a" fill-opacity="0.55" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="203.77154219948684" x2="202.4" y1="52.855722506414395" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="202.4" x2="172.17726330629023" y1="70" y2="70"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="172.17726330629023" x2="172.17726330629023" y1="70" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="172.17726330629023" x2="203.77154219948684" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M180,100 L161.1955,100 L161.1955,72.466 L180,72.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="161.19549058892116" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="100" y2="72.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="161.19549058892116" x2="180" y1="72.46600595599794" y2="72.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="72.46600595599794" y2="100"/>
<path d="M161.1955,100 L140.25922,100 L140.25922,68.26361 L161.1955,68.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="161.19549058892116" x2="140.25920977847343" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="100" y2="68.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="140.25920977847343" x2="161.19549058892116" y1="68.26360853604555" y2="68.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="161.19549058892116" x2="161.19549058892116" y1="68.26360853604555" y2="100"/>
<path d="M140.25922,100 L120.000404,100 L120.000404,66 L140.25922,66 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="140.25920977847343" x2="120.0004046814901" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="100" y2="65.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.0004046814901" x2="140.25920977847343" y1="65.999999" y2="65.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="140.25920977847343" x2="140.25920977847343" y1="65.999999" y2="100"/>
<path d="M120.000404,100 L98.191284,100 L98.191284,71.2734 L120.000404,71.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.0004046814901" x2="98.19128506754218" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="100" y2="71.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="98.19128506754218" x2="120.0004046814901" y1="71.27339761286972" y2="71.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.0004046814901" x2="120.0004046814901" y1="71.27339761286972" y2="100"/>
<path d="M98.191284,100 L81.11824,100 L81.11824,72.8639 L98.191284,72.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="98.19128506754218" x2="81.11823933536908" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="100" y2="72.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="81.11823933536908" x2="98.19128506754218" y1="72.86389925174167" y2="72.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="98.19128506754218" x2="98.19128506754218" y1="72.86389925174167" y2="100"/>
<path d="M81.11824,100 L63.12904,100 L63.12904,70.637054 L81.11824,70.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="81.11823933536908" x2="63.129039299600635" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="100" y2="70.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="63.129039299600635" x2="81.11823933536908" y1="70.63705247970552" y2="70.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="81.11823933536908" x2="81.11823933536908" y1="70.63705247970552" y2="100"/>
<path d="M63.12904,100 L40.141674,100 L40.141674,68.958885 L63.12904,68.958885 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="63.129039299600635" x2="40.14167365815044" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="100" y2="68.95888431009364"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="40.14167365815044" x2="63.129039299600635" y1="68.95888431009364" y2="68.95888431009364"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="63.129039299600635" x2="63.129039299600635" y1="68.95888431009364" y2="100"/>
<path d="M40.141674,100 L20,100 L20,72.17421 L40.141674,72.17421 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="40.14167365815044" x2="20" y1="100" y2="100"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="100" y2="72.17420690790317"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="40.14167365815044" y1="72.17420690790317" y2="72.17420690790317"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="40.14167365815044" x2="40.14167365815044" y1="72.17420690790317" y2="100"/>
<path d="M0,70 L0,51.044052 L32.357586,51.044052 L32.357586,70 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="70" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="32.357584460743425" y1="51.04405176448549" y2="51.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="32.357584460743425" x2="32.357584460743425" y1="51.04405176448549" y2="70"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="32.357584460743425" x2="0" y1="70" y2="70"/>
<path d="M0,51.044052 L0,30 L29.8626,30 L29.8626,51.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="51.04405176448549" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="30" y2="51.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="51.04405176448549" y2="51.04405176448549"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="208" 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>

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

View File

@ -0,0 +1,134 @@
<svg height="480" viewBox="-5 -5 190 120" width="760" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="120" width="190" x="-5" y="-5"/>
<g transform="translate(0, 110) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="180" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="180" y1="0" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="0" y1="110" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="0" y1="110" 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="M180,0 L180,30 L160,30 L160,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="160" y1="30" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="30" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="180" y1="0" y2="0"/>
<path d="M180,110 L160,110 L160,80 L180,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="160" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="180" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="80" y2="110"/>
<path d="M0,110 L0,80 L20,80 L20,110 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="0" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="0" x2="20" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="80" y2="110"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="0" y1="110" y2="110"/>
<path d="M20,0 L42.996502,0 L42.996502,27.409153 L20,27.409153 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="27.409153569625865"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="27.409153569625865" y2="27.409153569625865"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="27.409153569625865" y2="0"/>
<path d="M42.996502,0 L65.47971,0 L65.47971,29.52622 L42.996502,29.52622 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="29.526220147024308" y2="0"/>
<path d="M65.47971,0 L88.27847,0 L88.27847,31.147902 L65.47971,31.147902 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="88.27847104373568" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="31.1479024543553" y2="0"/>
<path d="M88.27847,0 L109.00264,0 L109.00264,29.876335 L88.27847,29.876335 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.27847104373568" x2="109.00264275540263" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="29.87633532437634" y2="0"/>
<path d="M109.00264,0 L127.212425,0 L127.212425,30.25849 L109.00264,30.25849 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.00264275540263" x2="127.2124223685276" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="30.25848985257909" y2="0"/>
<path d="M127.212425,0 L146.8415,0 L146.8415,34 L127.212425,34 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.2124223685276" x2="146.84151010926075" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="34.000001" y2="0"/>
<path d="M146.8415,0 L160,0 L160,31.470558 L146.8415,31.470558 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.84151010926075" x2="160" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="146.84151010926075" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="31.47055834801727" y2="0"/>
<path d="M180,30 L180,52.855724 L152.17726,52.855724 L152.17726,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="30" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="152.17726330629023" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="152.17726330629023" x2="152.17726330629023" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="152.17726330629023" x2="180" y1="30" y2="30"/>
<path d="M180,52.855724 L180,74.35595 L151.5346,74.35595 L151.5346,52.855724 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="52.855722506414395" y2="74.35594929730647"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="151.5346047936249" y1="74.35594929730647" y2="74.35594929730647"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="151.5346047936249" x2="151.5346047936249" y1="74.35594929730647" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="151.5346047936249" x2="180" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M160,110 L141.1955,110 L141.1955,76.1546 L160,76.1546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="141.19549058892116" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="110" y2="76.1546017562395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="141.19549058892116" x2="160" y1="76.1546017562395" y2="76.1546017562395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="76.1546017562395" y2="110"/>
<path d="M141.1955,110 L120.25921,110 L120.25921,82.466 L141.1955,82.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="141.19549058892116" x2="120.25920977847343" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="110" y2="82.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.25920977847343" x2="141.19549058892116" y1="82.46600595599794" y2="82.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="82.46600595599794" y2="110"/>
<path d="M120.25921,110 L100.000404,110 L100.000404,78.26361 L120.25921,78.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.25920977847343" x2="100.0004046814901" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="110" y2="78.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="100.0004046814901" x2="120.25920977847343" y1="78.26360853604555" y2="78.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="78.26360853604555" y2="110"/>
<path d="M100.000404,110 L78.191284,110 L78.191284,76 L100.000404,76 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="100.0004046814901" x2="78.19128506754218" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="110" y2="75.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="78.19128506754218" x2="100.0004046814901" y1="75.999999" y2="75.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="75.999999" y2="110"/>
<path d="M78.191284,110 L61.11824,110 L61.11824,81.2734 L78.191284,81.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="78.19128506754218" x2="61.11823933536908" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="110" y2="81.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="61.11823933536908" x2="78.19128506754218" y1="81.27339761286972" y2="81.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="81.27339761286972" y2="110"/>
<path d="M61.11824,110 L43.12904,110 L43.12904,82.8639 L61.11824,82.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="61.11823933536908" x2="43.129039299600635" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="110" y2="82.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="43.129039299600635" x2="61.11823933536908" y1="82.86389925174167" y2="82.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="82.86389925174167" y2="110"/>
<path d="M43.12904,110 L20,110 L20,80.637054 L43.12904,80.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="43.129039299600635" x2="20" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="110" y2="80.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="43.129039299600635" y1="80.63705247970552" y2="80.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="80.63705247970552" y2="110"/>
<path d="M0,80 L0,61.044052 L29.8626,61.044052 L29.8626,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="80" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="61.04405176448549" y2="61.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="61.04405176448549" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="80" y2="80"/>
<path d="M0,61.044052 L0,40.249546 L28.32311,40.249546 L28.32311,61.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="61.04405176448549" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="28.323110868385612" y1="40.24954609584363" y2="40.24954609584363"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="28.323110868385612" x2="28.323110868385612" y1="40.24954609584363" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="28.323110868385612" x2="0" y1="61.04405176448549" y2="61.04405176448549"/>
<path d="M0,40.249546 L0,30 L31.206142,30 L31.206142,40.249546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="40.24954609584363" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="31.206143102551692" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="31.206143102551692" x2="31.206143102551692" y1="30" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="31.206143102551692" x2="0" y1="40.24954609584363" y2="40.24954609584363"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="110" fill="#000000" r="0.7"/>
<circle cx="0" cy="110" fill="#000000" r="0.7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,134 @@
<svg height="480" viewBox="-5 -5 190 120" width="760" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="120" width="190" x="-5" y="-5"/>
<g transform="translate(0, 110) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="180" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="180" y1="0" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="0" y1="110" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="0" y1="110" 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="M180,0 L180,30 L160,30 L160,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="160" y1="30" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="30" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="180" y1="0" y2="0"/>
<path d="M180,110 L160,110 L160,80 L180,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="160" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="180" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="80" y2="110"/>
<path d="M0,110 L0,80 L20,80 L20,110 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="0" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="0" x2="20" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="80" y2="110"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="0" y1="110" y2="110"/>
<path d="M20,0 L42.996502,0 L42.996502,27.409153 L20,27.409153 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="27.409153569625865"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="27.409153569625865" y2="27.409153569625865"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="27.409153569625865" y2="0"/>
<path d="M42.996502,0 L65.47971,0 L65.47971,29.52622 L42.996502,29.52622 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="29.526220147024308" y2="0"/>
<path d="M65.47971,0 L88.27847,0 L88.27847,31.147902 L65.47971,31.147902 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="88.27847104373568" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="31.1479024543553" y2="0"/>
<path d="M88.27847,0 L109.00264,0 L109.00264,29.876335 L88.27847,29.876335 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.27847104373568" x2="109.00264275540263" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="29.87633532437634" y2="0"/>
<path d="M109.00264,0 L127.212425,0 L127.212425,30.25849 L109.00264,30.25849 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.00264275540263" x2="127.2124223685276" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="30.25848985257909" y2="0"/>
<path d="M127.212425,0 L146.8415,0 L146.8415,34 L127.212425,34 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.2124223685276" x2="146.84151010926075" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="34.000001" y2="0"/>
<path d="M146.8415,0 L160,0 L160,31.470558 L146.8415,31.470558 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.84151010926075" x2="160" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="146.84151010926075" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="31.47055834801727" y2="0"/>
<path d="M180,30 L180,52.855724 L152.17726,52.855724 L152.17726,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="30" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="152.17726330629023" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="152.17726330629023" x2="152.17726330629023" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="152.17726330629023" x2="180" y1="30" y2="30"/>
<path d="M180,52.855724 L180,74.35595 L151.5346,74.35595 L151.5346,52.855724 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="52.855722506414395" y2="74.35594929730647"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="151.5346047936249" y1="74.35594929730647" y2="74.35594929730647"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="151.5346047936249" x2="151.5346047936249" y1="74.35594929730647" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="151.5346047936249" x2="180" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M160,110 L141.1955,110 L141.1955,76.1546 L160,76.1546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="141.19549058892116" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="110" y2="76.1546017562395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="141.19549058892116" x2="160" y1="76.1546017562395" y2="76.1546017562395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="76.1546017562395" y2="110"/>
<path d="M141.1955,110 L120.25921,110 L120.25921,82.466 L141.1955,82.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="141.19549058892116" x2="120.25920977847343" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="110" y2="82.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.25920977847343" x2="141.19549058892116" y1="82.46600595599794" y2="82.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="82.46600595599794" y2="110"/>
<path d="M120.25921,110 L100.000404,110 L100.000404,78.26361 L120.25921,78.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.25920977847343" x2="100.0004046814901" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="110" y2="78.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="100.0004046814901" x2="120.25920977847343" y1="78.26360853604555" y2="78.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="78.26360853604555" y2="110"/>
<path d="M100.000404,110 L78.191284,110 L78.191284,76 L100.000404,76 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="100.0004046814901" x2="78.19128506754218" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="110" y2="75.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="78.19128506754218" x2="100.0004046814901" y1="75.999999" y2="75.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="75.999999" y2="110"/>
<path d="M78.191284,110 L61.11824,110 L61.11824,81.2734 L78.191284,81.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="78.19128506754218" x2="61.11823933536908" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="110" y2="81.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="61.11823933536908" x2="78.19128506754218" y1="81.27339761286972" y2="81.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="81.27339761286972" y2="110"/>
<path d="M61.11824,110 L43.12904,110 L43.12904,82.8639 L61.11824,82.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="61.11823933536908" x2="43.129039299600635" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="110" y2="82.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="43.129039299600635" x2="61.11823933536908" y1="82.86389925174167" y2="82.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="82.86389925174167" y2="110"/>
<path d="M43.12904,110 L20,110 L20,80.637054 L43.12904,80.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="43.129039299600635" x2="20" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="110" y2="80.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="43.129039299600635" y1="80.63705247970552" y2="80.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="80.63705247970552" y2="110"/>
<path d="M0,80 L0,61.044052 L29.8626,61.044052 L29.8626,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="80" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="61.04405176448549" y2="61.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="61.04405176448549" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="80" y2="80"/>
<path d="M0,61.044052 L0,40.249546 L28.32311,40.249546 L28.32311,61.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="61.04405176448549" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="28.323110868385612" y1="40.24954609584363" y2="40.24954609584363"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="28.323110868385612" x2="28.323110868385612" y1="40.24954609584363" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="28.323110868385612" x2="0" y1="61.04405176448549" y2="61.04405176448549"/>
<path d="M0,40.249546 L0,30 L31.206142,30 L31.206142,40.249546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="40.24954609584363" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="31.206143102551692" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="31.206143102551692" x2="31.206143102551692" y1="30" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="31.206143102551692" x2="0" y1="40.24954609584363" y2="40.24954609584363"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="110" fill="#000000" r="0.7"/>
<circle cx="0" cy="110" fill="#000000" r="0.7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,134 @@
<svg height="480" viewBox="-5 -5 190 120" width="760" xmlns="http://www.w3.org/2000/svg">
<rect fill="#ffffff" height="120" width="190" x="-5" y="-5"/>
<g transform="translate(0, 110) scale(1, -1)">
<g id="roads">
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="180" y1="0" y2="0"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="180" y1="0" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="180" x2="0" y1="110" y2="110"/>
<line stroke="#000000" stroke-linecap="round" stroke-width="0.6" x1="0" x2="0" y1="110" 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="M180,0 L180,30 L160,30 L160,0 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="0" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="180" x2="160" y1="30" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="30" y2="0"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="180" y1="0" y2="0"/>
<path d="M180,110 L160,110 L160,80 L180,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="160" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="180" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="180" y1="80" y2="110"/>
<path d="M0,110 L0,80 L20,80 L20,110 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="0" y1="110" y2="80"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="0" x2="20" y1="80" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="80" y2="110"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="0" y1="110" y2="110"/>
<path d="M20,0 L42.996502,0 L42.996502,27.409153 L20,27.409153 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="20" x2="42.99650337837165" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="0" y2="27.409153569625865"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="42.99650337837165" x2="20" y1="27.409153569625865" y2="27.409153569625865"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="27.409153569625865" y2="0"/>
<path d="M42.996502,0 L65.47971,0 L65.47971,29.52622 L42.996502,29.52622 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="42.99650337837165" x2="65.47971305349805" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="0" y2="29.526220147024308"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="65.47971305349805" x2="42.99650337837165" y1="29.526220147024308" y2="29.526220147024308"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="42.99650337837165" x2="42.99650337837165" y1="29.526220147024308" y2="0"/>
<path d="M65.47971,0 L88.27847,0 L88.27847,31.147902 L65.47971,31.147902 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="65.47971305349805" x2="88.27847104373568" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="0" y2="31.1479024543553"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="88.27847104373568" x2="65.47971305349805" y1="31.1479024543553" y2="31.1479024543553"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="65.47971305349805" x2="65.47971305349805" y1="31.1479024543553" y2="0"/>
<path d="M88.27847,0 L109.00264,0 L109.00264,29.876335 L88.27847,29.876335 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="88.27847104373568" x2="109.00264275540263" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="0" y2="29.87633532437634"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="109.00264275540263" x2="88.27847104373568" y1="29.87633532437634" y2="29.87633532437634"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="88.27847104373568" x2="88.27847104373568" y1="29.87633532437634" y2="0"/>
<path d="M109.00264,0 L127.212425,0 L127.212425,30.25849 L109.00264,30.25849 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="109.00264275540263" x2="127.2124223685276" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="0" y2="30.25848985257909"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="127.2124223685276" x2="109.00264275540263" y1="30.25848985257909" y2="30.25848985257909"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="109.00264275540263" x2="109.00264275540263" y1="30.25848985257909" y2="0"/>
<path d="M127.212425,0 L146.8415,0 L146.8415,34 L127.212425,34 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="127.2124223685276" x2="146.84151010926075" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="0" y2="34.000001"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="146.84151010926075" x2="127.2124223685276" y1="34.000001" y2="34.000001"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="127.2124223685276" x2="127.2124223685276" y1="34.000001" y2="0"/>
<path d="M146.8415,0 L160,0 L160,31.470558 L146.8415,31.470558 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="146.84151010926075" x2="160" y1="0" y2="0"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="0" y2="31.47055834801727"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="160" x2="146.84151010926075" y1="31.47055834801727" y2="31.47055834801727"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="146.84151010926075" x2="146.84151010926075" y1="31.47055834801727" y2="0"/>
<path d="M180,30 L180,52.855724 L152.17726,52.855724 L152.17726,30 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="30" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="152.17726330629023" y1="52.855722506414395" y2="52.855722506414395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="152.17726330629023" x2="152.17726330629023" y1="52.855722506414395" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="152.17726330629023" x2="180" y1="30" y2="30"/>
<path d="M180,52.855724 L180,74.35595 L151.5346,74.35595 L151.5346,52.855724 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="180" x2="180" y1="52.855722506414395" y2="74.35594929730647"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="180" x2="151.5346047936249" y1="74.35594929730647" y2="74.35594929730647"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="151.5346047936249" x2="151.5346047936249" y1="74.35594929730647" y2="52.855722506414395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="151.5346047936249" x2="180" y1="52.855722506414395" y2="52.855722506414395"/>
<path d="M160,110 L141.1955,110 L141.1955,76.1546 L160,76.1546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="160" x2="141.19549058892116" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="110" y2="76.1546017562395"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="141.19549058892116" x2="160" y1="76.1546017562395" y2="76.1546017562395"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="160" x2="160" y1="76.1546017562395" y2="110"/>
<path d="M141.1955,110 L120.25921,110 L120.25921,82.466 L141.1955,82.466 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="141.19549058892116" x2="120.25920977847343" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="110" y2="82.46600595599794"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="120.25920977847343" x2="141.19549058892116" y1="82.46600595599794" y2="82.46600595599794"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="141.19549058892116" x2="141.19549058892116" y1="82.46600595599794" y2="110"/>
<path d="M120.25921,110 L100.000404,110 L100.000404,78.26361 L120.25921,78.26361 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="120.25920977847343" x2="100.0004046814901" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="110" y2="78.26360853604555"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="100.0004046814901" x2="120.25920977847343" y1="78.26360853604555" y2="78.26360853604555"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="120.25920977847343" x2="120.25920977847343" y1="78.26360853604555" y2="110"/>
<path d="M100.000404,110 L78.191284,110 L78.191284,76 L100.000404,76 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="100.0004046814901" x2="78.19128506754218" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="110" y2="75.999999"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="78.19128506754218" x2="100.0004046814901" y1="75.999999" y2="75.999999"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="100.0004046814901" x2="100.0004046814901" y1="75.999999" y2="110"/>
<path d="M78.191284,110 L61.11824,110 L61.11824,81.2734 L78.191284,81.2734 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="78.19128506754218" x2="61.11823933536908" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="110" y2="81.27339761286972"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="61.11823933536908" x2="78.19128506754218" y1="81.27339761286972" y2="81.27339761286972"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="78.19128506754218" x2="78.19128506754218" y1="81.27339761286972" y2="110"/>
<path d="M61.11824,110 L43.12904,110 L43.12904,82.8639 L61.11824,82.8639 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="61.11823933536908" x2="43.129039299600635" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="110" y2="82.86389925174167"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="43.129039299600635" x2="61.11823933536908" y1="82.86389925174167" y2="82.86389925174167"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="61.11823933536908" x2="61.11823933536908" y1="82.86389925174167" y2="110"/>
<path d="M43.12904,110 L20,110 L20,80.637054 L43.12904,80.637054 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="43.129039299600635" x2="20" y1="110" y2="110"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="20" x2="20" y1="110" y2="80.63705247970552"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="20" x2="43.129039299600635" y1="80.63705247970552" y2="80.63705247970552"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="43.129039299600635" x2="43.129039299600635" y1="80.63705247970552" y2="110"/>
<path d="M0,80 L0,61.044052 L29.8626,61.044052 L29.8626,80 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="80" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="29.862600980530416" y1="61.04405176448549" y2="61.04405176448549"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="29.862600980530416" x2="29.862600980530416" y1="61.04405176448549" y2="80"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="29.862600980530416" x2="0" y1="80" y2="80"/>
<path d="M0,61.044052 L0,40.249546 L28.32311,40.249546 L28.32311,61.044052 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="61.04405176448549" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="28.323110868385612" y1="40.24954609584363" y2="40.24954609584363"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="28.323110868385612" x2="28.323110868385612" y1="40.24954609584363" y2="61.04405176448549"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="28.323110868385612" x2="0" y1="61.04405176448549" y2="61.04405176448549"/>
<path d="M0,40.249546 L0,30 L31.206142,30 L31.206142,40.249546 z" fill="#fff7c2" fill-opacity="0.3" stroke="none"/>
<line stroke="#1f5fb4" stroke-width="0.9" x1="0" x2="0" y1="40.24954609584363" y2="30"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="0" x2="31.206143102551692" y1="30" y2="30"/>
<line stroke="#bdbdbd" stroke-dasharray="1.6,1.2" stroke-width="0.4" x1="31.206143102551692" x2="31.206143102551692" y1="30" y2="40.24954609584363"/>
<line stroke="#6b6b6b" stroke-width="0.4" x1="31.206143102551692" x2="0" y1="40.24954609584363" y2="40.24954609584363"/>
</g>
<g id="nodes">
<circle cx="0" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="0" fill="#000000" r="0.7"/>
<circle cx="180" cy="110" fill="#000000" r="0.7"/>
<circle cx="0" cy="110" fill="#000000" r="0.7"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

149
journal.aux Normal file
View File

@ -0,0 +1,149 @@
\relax
\providecommand\hyper@newdestlabel[2]{}
\providecommand\HyField@AuxAddToFields[1]{}
\providecommand\HyField@AuxAddToCoFields[2]{}
\@writefile{toc}{\contentsline {section}{\numberline {1}Project Context and Motivation}{3}{section.1}\protected@file@percent }
\newlabel{sec:context}{{1}{3}{Project Context and Motivation}{section.1}{}}
\newlabel{sec:context@cref}{{[section][1][]1}{[1][3][]3}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {1.1}Why Build This}{3}{subsection.1.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {1.2}Scope of This Document}{3}{subsection.1.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {1.3}Audience}{3}{subsection.1.3}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {2}Core Invariants}{3}{section.2}\protected@file@percent }
\newlabel{sec:invariants}{{2}{3}{Core Invariants}{section.2}{}}
\newlabel{sec:invariants@cref}{{[section][2][]2}{[1][3][]3}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {3}Geometric Foundations}{4}{section.3}\protected@file@percent }
\newlabel{sec:geometry}{{3}{4}{Geometric Foundations}{section.3}{}}
\newlabel{sec:geometry@cref}{{[section][3][]3}{[1][4][]4}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {3.1}Coordinate System and Numerical Type}{4}{subsection.3.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.2}Road Network as a Planar Graph}{4}{subsection.3.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.3}Block Extraction}{5}{subsection.3.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {3.4}Inward Offsetting}{5}{subsection.3.4}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {4}Subdivision Algorithm}{5}{section.4}\protected@file@percent }
\newlabel{sec:subdivision}{{4}{5}{Subdivision Algorithm}{section.4}{}}
\newlabel{sec:subdivision@cref}{{[section][4][]4}{[1][5][]5}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {4.1}Frontage-First Subdivision}{5}{subsection.4.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Inputs.}{5}{subsection.4.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Procedure.}{5}{subsection.4.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.2}Edge Classification}{6}{subsection.4.2}\protected@file@percent }
\@writefile{lot}{\contentsline {table}{\numberline {1}{\ignorespaces Edge classification scheme.}}{6}{table.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {4.3}Regularization Pass}{6}{subsection.4.3}\protected@file@percent }
\newlabel{sec:regularization}{{4.3}{6}{Regularization Pass}{subsection.4.3}{}}
\newlabel{sec:regularization@cref}{{[subsection][3][4]4.3}{[1][6][]6}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {5}Road Edit Handling}{6}{section.5}\protected@file@percent }
\newlabel{sec:edit-handling}{{5}{6}{Road Edit Handling}{section.5}{}}
\newlabel{sec:edit-handling@cref}{{[section][5][]5}{[1][6][]6}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.1}Edit Types}{7}{subsection.5.1}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {1}Road edit enum.}{7}{lstlisting.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.2}Deformation Pipeline}{7}{subsection.5.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.3}Regeneration Thresholds}{7}{subsection.5.3}\protected@file@percent }
\@writefile{lot}{\contentsline {table}{\numberline {2}{\ignorespaces Thresholds that trigger regeneration or condemnation. $\alpha _{\max }$ defaults to $30^\circ $.}}{7}{table.2}\protected@file@percent }
\gdef \LT@i {\LT@entry
{1}{140.0374pt}\LT@entry
{1}{182.71652pt}\LT@entry
{1}{111.58464pt}}
\@writefile{toc}{\contentsline {subsection}{\numberline {5.4}Determinism and Idempotence}{8}{subsection.5.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {5.5}Building Footprint Preservation}{8}{subsection.5.5}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {2}Building persistence hook.}{8}{lstlisting.2}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {6}Degenerate Cases}{8}{section.6}\protected@file@percent }
\newlabel{sec:degenerate}{{6}{8}{Degenerate Cases}{section.6}{}}
\newlabel{sec:degenerate@cref}{{[section][6][]6}{[1][8][]8}{}{}{}}
\@writefile{lot}{\contentsline {table}{\numberline {3}{Required degenerate-case tests. Each must exist by name and pass.}}{9}{table.3}\protected@file@percent }
\newlabel{tab:degenerate}{{3}{9}{Required degenerate-case tests. Each must exist by name and pass}{table.3}{}}
\newlabel{tab:degenerate@cref}{{[table][3][]3}{[1][8][]9}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {7}Crate Architecture}{9}{section.7}\protected@file@percent }
\newlabel{sec:architecture}{{7}{9}{Crate Architecture}{section.7}{}}
\newlabel{sec:architecture@cref}{{[section][7][]7}{[1][9][]9}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {7.1}Module Layout}{9}{subsection.7.1}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {3}Crate structure.}{9}{lstlisting.3}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {7.2}Public API Surface}{10}{subsection.7.2}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {4}Public API in \texttt {lib.rs}.}{10}{lstlisting.4}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {7.3}Error Types}{10}{subsection.7.3}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {5}Error enum.}{10}{lstlisting.5}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {7.4}Dependencies}{11}{subsection.7.4}\protected@file@percent }
\@writefile{lot}{\contentsline {table}{\numberline {4}{\ignorespaces Dependency manifest.}}{11}{table.4}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {8}Idiomatic Rust Requirements}{11}{section.8}\protected@file@percent }
\newlabel{sec:idioms}{{8}{11}{Idiomatic Rust Requirements}{section.8}{}}
\newlabel{sec:idioms@cref}{{[section][8][]8}{[1][11][]11}{}{}{}}
\gdef \LT@ii {\LT@entry
{2}{168.13437pt}\LT@entry
{2}{188.11266pt}}
\@writefile{toc}{\contentsline {section}{\numberline {9}Testing Strategy}{12}{section.9}\protected@file@percent }
\newlabel{sec:testing}{{9}{12}{Testing Strategy}{section.9}{}}
\newlabel{sec:testing@cref}{{[section][9][]9}{[1][12][]12}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {9.1}Three Layers}{12}{subsection.9.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Unit tests}{12}{subsection.9.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Integration tests}{12}{subsection.9.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Property tests}{12}{subsection.9.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {9.2}Snapshot Testing}{12}{subsection.9.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {9.3}Coverage Requirement}{12}{subsection.9.3}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {10}Visualization and Figures}{12}{section.10}\protected@file@percent }
\newlabel{sec:figures}{{10}{12}{Visualization and Figures}{section.10}{}}
\newlabel{sec:figures@cref}{{[section][10][]10}{[1][12][]12}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {10.1}Required Figures}{12}{subsection.10.1}\protected@file@percent }
\@writefile{lot}{\contentsline {table}{\numberline {5}{Required figure deliverables.}}{13}{table.5}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {10.2}Color Conventions}{13}{subsection.10.2}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {10.3}Inclusion in This Journal}{13}{subsection.10.3}\protected@file@percent }
\@writefile{lol}{\contentsline {lstlisting}{\numberline {6}Including a generated figure.}{13}{lstlisting.6}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {11}Performance Targets}{13}{section.11}\protected@file@percent }
\newlabel{sec:performance}{{11}{13}{Performance Targets}{section.11}{}}
\newlabel{sec:performance@cref}{{[section][11][]11}{[1][13][]13}{}{}{}}
\@writefile{lot}{\contentsline {table}{\numberline {6}{\ignorespaces Performance targets. Measured via Criterion.}}{13}{table.6}\protected@file@percent }
\@writefile{toc}{\contentsline {section}{\numberline {12}Out of Scope}{13}{section.12}\protected@file@percent }
\newlabel{sec:oos}{{12}{13}{Out of Scope}{section.12}{}}
\newlabel{sec:oos@cref}{{[section][12][]12}{[1][13][]13}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {13}Claude Code Contract}{14}{section.13}\protected@file@percent }
\newlabel{sec:contract}{{13}{14}{Claude Code Contract}{section.13}{}}
\newlabel{sec:contract@cref}{{[section][13][]13}{[1][14][]14}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {14}Open Questions}{15}{section.14}\protected@file@percent }
\newlabel{sec:open}{{14}{15}{Open Questions}{section.14}{}}
\newlabel{sec:open@cref}{{[section][14][]14}{[1][15][]15}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {15}Design Decisions}{15}{section.15}\protected@file@percent }
\newlabel{sec:decisions}{{15}{15}{Design Decisions}{section.15}{}}
\newlabel{sec:decisions@cref}{{[section][15][]15}{[1][15][]15}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {16}Revisions and Deviations}{16}{section.16}\protected@file@percent }
\newlabel{sec:revisions}{{16}{16}{Revisions and Deviations}{section.16}{}}
\newlabel{sec:revisions@cref}{{[section][16][]16}{[1][16][]16}{}{}{}}
\@writefile{toc}{\contentsline {subsection}{\numberline {16.1}2026-04-25 --- Session 1: Milestone 0.1, rectangle end-to-end}{16}{subsection.16.1}\protected@file@percent }
\newlabel{sec:session-1}{{16.1}{16}{2026-04-25 --- Session 1: Milestone 0.1, rectangle end-to-end}{subsection.16.1}{}}
\newlabel{sec:session-1@cref}{{[subsection][1][16]16.1}{[1][16][]16}{}{}{}}
\@writefile{toc}{\contentsline {paragraph}{Goal of the session.}{16}{subsection.16.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{What got built.}{17}{subsection.16.1}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {1}{\ignorespaces The first figure produced by the crate: a $200 \times 100$ rectangular block subdivided at default parameters ($w_f=20$, $d_p=30$, $d_s=1$, $\rho =0$). Roads in black; parcel frontage edges in blue, side edges gray, back edges light-gray dashed. Corner pie-slice parcels are the bisector-clipped triangles at each block corner. The empty central strip is the medial gap: the block's vertical extent ($100$\,m) exceeds $2 d_p$, so the depth cap stops parcels short of the centerline. Generated by \texttt {cargo run --example generate\_figures --features viz}.}}{17}{figure.1}\protected@file@percent }
\newlabel{fig:grid-block}{{1}{17}{The first figure produced by the crate: a $200 \times 100$ rectangular block subdivided at default parameters ($w_f=20$, $d_p=30$, $d_s=1$, $\rho =0$). Roads in black; parcel frontage edges in blue, side edges gray, back edges light-gray dashed. Corner pie-slice parcels are the bisector-clipped triangles at each block corner. The empty central strip is the medial gap: the block's vertical extent ($100$\,m) exceeds $2 d_p$, so the depth cap stops parcels short of the centerline. Generated by \texttt {cargo run --example generate\_figures --features viz}}{figure.1}{}}
\newlabel{fig:grid-block@cref}{{[figure][1][]1}{[1][17][]17}{}{}{}}
\@writefile{toc}{\contentsline {paragraph}{Tooling gates.}{17}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Test status.}{17}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Design decisions locked in this session.}{18}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Spec deviations recorded this session.}{18}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Open questions touched.}{20}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Next session --- milestone 0.2 queue.}{20}{figure.1}\protected@file@percent }
\@writefile{toc}{\contentsline {subsection}{\numberline {16.2}2026-04-25 --- Session 2: Milestone 0.2 (corner parcels, sticky back edges, preserve-on-deform)}{20}{subsection.16.2}\protected@file@percent }
\newlabel{sec:session-2}{{16.2}{20}{2026-04-25 --- Session 2: Milestone 0.2 (corner parcels, sticky back edges, preserve-on-deform)}{subsection.16.2}{}}
\newlabel{sec:session-2@cref}{{[subsection][2][16]16.2}{[1][20][]20}{}{}{}}
\@writefile{toc}{\contentsline {paragraph}{Goal of the session.}{20}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Decisions locked in at session kickoff.}{20}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Status during writing.}{21}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Performance instrumentation.}{21}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Corner parcel rework.}{22}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Preserve-on-deform pipeline.}{22}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{New figures.}{22}{subsection.16.2}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{Deviations from spec.}{22}{figure.5}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {2}{\ignorespaces Default 200×100 rectangle subdivided by milestone 0.2. Compare with milestone 0.1: the four corners are now proper rectangle corner parcels rather than bisector-clipped triangles.}}{23}{figure.2}\protected@file@percent }
\newlabel{fig:grid-block-v02}{{2}{23}{Default 200×100 rectangle subdivided by milestone 0.2. Compare with milestone 0.1: the four corners are now proper rectangle corner parcels rather than bisector-clipped triangles}{figure.2}{}}
\newlabel{fig:grid-block-v02@cref}{{[figure][2][]2}{[1][22][]23}{}{}{}}
\@writefile{toc}{\contentsline {paragraph}{Performance.}{23}{figure.5}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {3}{\ignorespaces Y intersection. Three roads at 120° from the origin meet three outer triangle vertices; corner parcels appear at the inner 120° corners (origin) and are skipped at the 30° outer corners.}}{24}{figure.3}\protected@file@percent }
\newlabel{fig:y-intersection}{{3}{24}{Y intersection. Three roads at 120° from the origin meet three outer triangle vertices; corner parcels appear at the inner 120° corners (origin) and are skipped at the 30° outer corners}{figure.3}{}}
\newlabel{fig:y-intersection@cref}{{[figure][3][]3}{[1][22][]24}{}{}{}}
\@writefile{toc}{\contentsline {paragraph}{Test status.}{24}{figure.5}\protected@file@percent }
\@writefile{toc}{\contentsline {paragraph}{What's next --- milestone 0.3 queue.}{24}{figure.5}\protected@file@percent }
\@writefile{lof}{\contentsline {figure}{\numberline {4}{\ignorespaces Before: \cref {fig:grid-block-v02}'s rectangle.}}{25}{figure.4}\protected@file@percent }
\newlabel{fig:edit-before}{{4}{25}{Before: \cref {fig:grid-block-v02}'s rectangle}{figure.4}{}}
\newlabel{fig:edit-before@cref}{{[figure][4][]4}{[1][22][]25}{}{}{}}
\@writefile{lof}{\contentsline {figure}{\numberline {5}{\ignorespaces After: bottom-right corner moved 8\,m right; deformed parcels are green.}}{25}{figure.5}\protected@file@percent }
\newlabel{fig:edit-after}{{5}{25}{After: bottom-right corner moved 8\,m right; deformed parcels are green}{figure.5}{}}
\newlabel{fig:edit-after@cref}{{[figure][5][]5}{[1][22][]25}{}{}{}}
\@writefile{toc}{\contentsline {section}{\numberline {A}Notation Reference}{25}{appendix.A}\protected@file@percent }
\newlabel{app:notation}{{A}{25}{Notation Reference}{appendix.A}{}}
\newlabel{app:notation@cref}{{[section][1][]A}{[1][24][]25}{}{}{}}
\gdef \@abspage@last{25}

220
journal.fdb_latexmk Normal file
View File

@ -0,0 +1,220 @@
# Fdb version 4
["pdflatex"] 1777140696.64118 "journal.tex" "journal.pdf" "journal" 1777140697.51808 0
"/usr/local/texlive/2025/texmf-dist/fonts/enc/dvips/cm-super/cm-super-ts1.enc" 1136849721 2900 1537cc8184ad1792082cd229ecc269f4 ""
"/usr/local/texlive/2025/texmf-dist/fonts/map/fontname/texfonts.map" 1577235249 3524 cb3e574dea2d1052e39280babc910dc8 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/jknappen/ec/tcbx1095.tfm" 1136768653 1536 0adca69c597a47fe7664bf81057bb53b ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/jknappen/ec/tcrm1095.tfm" 1136768653 1536 02c06700a42be0f5a28664c7273f82e7 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1246382020 1004 54797486969f23fa377b128694d548df ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1246382020 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm" 1246382020 916 f87d7c45f9c908e672703b83b72241a3 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm" 1246382020 928 2dc8d444221b7a635bb58038579b861a ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm" 1246382020 908 2921f8a10601f252058503cc6570e581 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm" 1246382020 940 228d6584342e91276bf566bcf9716b83 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm" 1136768653 1328 c834bbb027764024c09d3d2bf908b5f0 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm" 1136768653 1324 c910af8c371558dc20f2d7822f66fe64 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmex10.tfm" 1136768653 992 662f679a0b3d2d53c1b94050fdaa3f50 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmitt10.tfm" 1136768653 768 2297ad2ac26f37e67f756dad27c77d68 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi10.tfm" 1136768653 1528 abec98dbc43e172678c11b3b9031252a ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm" 1136768653 1524 4414a8315f39513458b80dfc63bff03a ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1136768653 1520 eccf95517727cb11801f4f1aee3a21b4 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr10.tfm" 1136768653 1296 45809c5a464d5f32c8f98ba97c1bb47f ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr12.tfm" 1136768653 1288 655e228510b4c2a1abe905c368440826 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr17.tfm" 1136768653 1292 296a67155bdbfc32aa9c636f21e91433 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1136768653 1292 21c1c5bfeaebccffdb478fd231a0997d ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm" 1136768653 1124 6c73e740cf17375f03eec0ee63599741 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmti10.tfm" 1136768653 1480 aa8e34af0eb6a2941b776984cf1dfdc4 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm" 1136768653 768 1321e9409b4137d6fb428ac9dc956269 ""
"/usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt8.tfm" 1136768653 768 d7b9a2629a0c353102ad947dc9221d49 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb" 1248133631 34811 78b52f49e893bcba91bd7581cdc144c0 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb" 1248133631 32080 340ef9bf63678554ee606688e7b5339d ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmitt10.pfb" 1248133631 26057 fad158094905eaf20f4ae3782af0c45c ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1248133631 36299 5f9df58c2139e7edcf37c8fca4bd384d ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi6.pfb" 1248133631 37166 8ab3487cbe3ab49ebce74c29ea2418db ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb" 1248133631 35469 70d41d2b9ea31d5d813066df7c99281c ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1248133631 35752 024fb6c41858982481f6968b5fc26508 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pfb" 1248133631 32722 d7379af29a190c3f453aba36302ff5a9 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr6.pfb" 1248133631 32734 69e00a6b65cedb993666e42eedb3d48f ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb" 1248133631 32726 0a1aea6fcd6468ee2cf64d891f5c43c8 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy8.pfb" 1248133631 32626 4f5c1b83753b1dd3a97d1b399a005b4b ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb" 1248133631 37944 359e864bd06cde3b1cf57bb20757fb06 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb" 1248133631 31099 c85edf1dd5b9e826d67c9c7293b6786c ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/cm-super/sfbx1095.pfb" 1215737283 154600 ea54091d31de803b613ba9e80ca51709 ""
"/usr/local/texlive/2025/texmf-dist/fonts/type1/public/cm-super/sfrm1095.pfb" 1215737283 145929 f25e56369a345c4ff583b067cd87ce8e ""
"/usr/local/texlive/2025/texmf-dist/tex/context/base/mkii/supp-pdf.mkii" 1461363279 71627 94eb9990bed73c364d7f53f960cc8c5b ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/atbegshi/atbegshi.sty" 1575674566 24708 5584a51a7101caf7e6bbf1fc27d8f7b1 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty" 1576625341 40635 c40361e206be584d448876bba8a64a3b ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/bitset/bitset.sty" 1576016050 33961 6b5c75130e435b2bfdb9f480a09a39f9 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty" 1576625223 8371 9d55b8bd010bc717624922fb3477d92e ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/iftex/iftex.sty" 1734129479 7984 7dbb9280f03c0a315425f1b4f35d43ee ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/iftex/ifvtex.sty" 1572645307 1057 525c2192b5febbd8c1f662c9468335bb ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/infwarerr/infwarerr.sty" 1575499628 8356 7bbb2c2373aa810be568c29e333da8ed ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/intcalc/intcalc.sty" 1576625065 31769 002a487f55041f8e805cfbf6385ffd97 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty" 1576878844 5412 d5a2436094cd7be85769db90f29250a6 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty" 1701727651 17865 1a9bd36b4f98178fa551aca822290953 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pdfescape/pdfescape.sty" 1576015897 19007 15924f7228aca6c6d184b115f4baa231 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty" 1593379760 20089 80423eac55aa175305d35b49e04fe23b ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex" 1673816307 1016 1c2b89187d12a2768764b83b4945667c ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex" 1601326656 43820 1fef971b75380574ab35a0d37fd92608 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex" 1601326656 19324 f4e4c6403dd0f1605fd20ed22fa79dea ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex" 1601326656 6038 ccb406740cc3f03bbfb58ad504fe8c27 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex" 1673816307 6911 f6d4cf5a3fef5cc879d668b810e82868 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex" 1601326656 4883 42daaf41e27c3735286e23e48d2d7af9 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex" 1601326656 2544 8c06d2a7f0f469616ac9e13db6d2f842 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex" 1601326656 44195 5e390c414de027626ca5e2df888fa68d ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex" 1601326656 17311 2ef6b2e29e2fc6a2fc8d6d652176e257 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex" 1601326656 21302 788a79944eb22192a4929e46963a3067 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex" 1673816307 9691 3d42d89522f4650c2f3dc616ca2b925e ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex" 1601326656 33335 dd1fa4814d4e51f18be97d88bf0da60c ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex" 1601326656 2965 4c2b1f4e0826925746439038172e5d6f ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex" 1601326656 5196 2cc249e0ee7e03da5f5f6589257b1e5b ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex" 1673816307 20821 7579108c1e9363e61a0b1584778804aa ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex" 1601326656 35249 abd4adf948f960299a4b3d27c5dddf46 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex" 1673816307 22012 81b34a0aa8fa1a6158cc6220b00e4f10 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex" 1601326656 8893 e851de2175338fdf7c17f3e091d94618 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex" 1601326656 15929 463535aa2c4268fead6674a75c0e8266 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex" 1673816307 5628 dc0ee4ba7f3e40acae5600067ce833de ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.pathmorphing.code.tex" 1601326656 321 cdd11262840e01e25374a2d458f15e99 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex" 1601326656 3937 3f208572dd82c71103831da976d74f1a ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex" 1601326656 339 be0fe46d92a80e3385dd6a83511a46f2 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex" 1608933718 11518 738408f795261b70ce8dd47459171309 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex" 1673816307 186782 af500404a9edec4d362912fe762ded92 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex" 1601326656 8843 5533436db3e30fbad1e0440db6027dac ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.meta.code.tex" 1601326656 58801 1e750fb0692eb99aaac45698bbec96b1 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex" 1601326656 32995 ac577023e12c0e4bd8aa420b2e852d1a ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex" 1673816307 161011 76ab54df0aa1a9d3b27a94864771d38d ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfint.code.tex" 1557692582 3063 8c415c68a0f3394e45cfeca0b65f6ee6 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex" 1673816307 949 cea70942e7b7eddabfb3186befada2e6 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex" 1673816307 13270 2e54f2ce7622437bf37e013d399743e3 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex" 1673816307 104717 9b2393fbf004a0ce7fa688dbce423848 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex" 1601326656 10165 cec5fa73d49da442e56efc2d605ef154 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex" 1601326656 28178 41c17713108e0795aac6fef3d275fbca ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex" 1673816307 9649 85779d3d8d573bfd2cd4137ba8202e60 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex" 1601326656 3865 ac538ab80c5cf82b345016e474786549 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex" 1557692582 3177 27d85c44fbfe09ff3b2cf2879e3ea434 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex" 1621110968 11024 0179538121bc2dba172013a3ef89519f ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex" 1673816307 7890 0a86dbf4edfd88d022e0d889ec78cc03 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex" 1601326656 3379 781797a101f647bab82741a99944a229 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex" 1601326656 92405 f515f31275db273f97b9d8f52e1b0736 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex" 1673816307 37466 97b0a1ba732e306a1a2034f5a73e239f ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex" 1601326656 8471 c2883569d03f69e8e1cabfef4999cfd7 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex" 1673816307 71742 3da44a8be6626eef1c400c68776c7a0f ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex" 1673816307 21211 1e73ec76bd73964d84197cc3d2685b01 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex" 1601326656 16121 346f9013d34804439f7436ff6786cef7 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex" 1673816307 44792 271e2e1934f34c759f4dedb1e14a5015 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/pgf.revision.tex" 1673816307 114 e6d443369d0673933b38834bf99e422d ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg" 1601326656 926 2963ea0dcf6cc6c0a770b69ec46a477b ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def" 1673816307 5542 32f75a31ea6c3a7e1148cd6d5e93dbb7 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def" 1673816307 12612 7774ba67bfd72e593c4436c2de6201e3 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex" 1673816307 61351 bc5f86e0355834391e736e97a61abced ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex" 1601326656 1896 b8e0ca0ac371d74c0ca05583f6313c91 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex" 1601326656 7778 53c8b5623d80238f6a20aa1df1868e63 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex" 1673816307 24033 d8893a1ec4d1bfa101b172754743d340 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex" 1673816307 39784 414c54e866ebab4b801e2ad81d9b21d8 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.tex" 1673816307 37433 940bc6d409f1ffd298adfdcaf125dd86 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex" 1673816307 4385 510565c2f07998c8a0e14f0ec07ff23c ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex" 1673816307 29239 22e8c7516012992a49873eff0d868fed ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def" 1673816307 6950 8524a062d82b7afdc4a88a57cb377784 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/stringenc/stringenc.sty" 1575152242 21514 b7557edcee22835ef6b03ede1802dad4 ""
"/usr/local/texlive/2025/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty" 1576624663 7008 f92eaa0a3872ed622bbf538217cd2ab7 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amscls/amsthm.sty" 1591045760 12594 0d51ac3a545aaaa555021326ff22a6cc ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amsfonts.sty" 1359763108 5949 3f3fd50a8cc94c3d4cbf4fc66cd3df1c ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amssymb.sty" 1359763108 13829 94730e64147574077f8ecfea9bb69af4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsa.fd" 1359763108 961 6518c6525a34feb5e8250ffa91731cff ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsb.fd" 1359763108 961 d02606146ba5601b5645f987c92e6193 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsbsy.sty" 1717359999 2222 2166a1f7827be30ddc30434e5efcee1b ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsgen.sty" 1717359999 4173 d22509bc0c91281d991b2de7c88720dd ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsmath.sty" 1730928152 88370 c780f23aea0ece6add91e09b44dca2cd ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsopn.sty" 1717359999 4474 23ca1d3a79a57b405388059456d0a8df ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amstext.sty" 1717359999 2444 71618ea5f2377e33b04fb97afdd0eac2 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/atveryend/atveryend.sty" 1728505250 1695 be6b4d13b33db697fd3fd30b24716c1a ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/base/article.cls" 1738182759 20144 63d8bacaf52e5abf4db3bc322373e1d4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/base/atbegshi-ltx.sty" 1738182759 2963 d8ec5a1b4e0a106c5c737900202763e4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/base/atveryend-ltx.sty" 1738182759 2378 14b657ee5031da98cf91648f19642694 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/base/size11.clo" 1738182759 8464 e73911cdcc738e82d6adccd28e654bb1 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/booktabs/booktabs.sty" 1579038678 6078 f1cb470c9199e7110a27851508ed7a5c ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/cleveref/cleveref.sty" 1525128982 329481 7fc6b003158402a4c694bc0a1b729308 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/enumitem/enumitem.sty" 1738874546 52272 63d293bc0d496619edb57585740861a2 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/environ/environ.sty" 1399239813 4378 f429f0da968c278653359293040a8f52 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty" 1579991033 13886 d1306dcf79a944f6988e688c1785f9ce ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/etoolbox/etoolbox.sty" 1739306980 46850 d87daedc2abdc653769a6f1067849fe0 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty" 1739048179 31715 19e60610b63819fe670dfa1cd84a4e94 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/geometry/geometry.sty" 1578002852 41601 9cf6c5257b1bc7af01a58859749dd37a ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/color.cfg" 1459978653 1213 620bba36b25224fa9b7e1ccb4ecb76fd ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/graphics.cfg" 1465944070 1224 978390e9c2234eab29404bc21b268d1e ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics-def/pdftex.def" 1713382759 19440 9da9dcbb27470349a580fca7372d454b ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/color.sty" 1730496337 7245 57f7defed4fb41562dc4b6ca13958ca9 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphics.sty" 1730496337 18363 dee506cb8d56825d8a4d020f5d5f8704 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphicx.sty" 1717359999 8010 6f2ad8c2b2ffbd607af6475441c7b5e4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/keyval.sty" 1717359999 2671 70891d50dac933918b827d326687c6e8 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/mathcolor.ltx" 1667332637 2885 9c645d672ae17285bba324998918efd8 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/graphics/trig.sty" 1717359999 4023 2c9f39712cf7b43d3eb93a8bbd5c8f67 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hycolor/hycolor.sty" 1580250785 17914 4c28a13fc3d975e6e81c9bea1d697276 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hpdftex.def" 1730838014 48154 82da9991b9f0390b3a9d3af6c8618af4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hyperref.sty" 1730838014 222112 c22dbd2288f89f7ba942ac22f7d00f11 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/nameref.sty" 1705871765 11026 182c63f139a71afd30a28e5f1ed2cd1c ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/pd1enc.def" 1730838014 14249 ff700eb13ce975a424b2dd99b1a83044 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/puenc.def" 1730838014 117112 7533bff456301d32e6d6356fad15f543 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/kvoptions/kvoptions.sty" 1655478651 22555 6d8e155cfef6d82c3d5c742fea7c992e ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty" 1665067230 13815 760b0c02f691ea230f5359c4e1de23a7 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1716410060 29785 9f93ab201fe5dd053afcc6c1bcf7d266 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg" 1279039959 678 4792914a8f45be57bb98413425e4c7af ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.cfg" 1727126400 1865 301ae3c26fb8c0243307b619a6aa2dd3 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.sty" 1727126400 81640 997090b6c021dc4af9ee00a97b85c5b4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty" 1727126400 206518 4eb59a801ad842a713fa168c34227290 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty" 1727126400 93855 83782cbe438f9229c1d17e92cee1446d ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty" 1727126400 90497 606307f9d31e805cfff0cd62ff91a50e ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty" 1727126400 77051 be68720e5402397a830abb9eed5a2cb4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstpatch.sty" 1710360531 353 9024412f43e92cd5b21fe9ded82d0610 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mathtools.sty" 1728349409 62711 3b0ed1d68b598a75677c10d16646d5e5 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mhsetup.sty" 1616101747 5582 a43dedf8e5ec418356f1e9dfe5d29fc3 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pdfcol/pdfcol.sty" 1663877585 8086 ac143843b6ea88d172677dc3ed532925 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty" 1601326656 1090 bae35ef70b3168089ef166db3e66f5b2 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty" 1673816307 373 00b204b1d7d095b892ad31a7494b0373 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty" 1601326656 21013 f4ff83d25bb56552493b030f27c075ae ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty" 1601326656 989 c49c8ae06d96f8b15869da7428047b1e ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty" 1601326656 339 c2e180022e3afdb99c7d0ea5ce469b7d ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/math/pgfmath.sty" 1601326656 306 c56a323ca5bf9242f54474ced10fca71 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty" 1601326656 443 8c872229db56122037e86bcda49e14f3 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgffor.sty" 1601326656 348 ee405e64380c11319f0e249fed57e6c5 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty" 1601326656 274 5ae372b7df79135d240456a1c6f2cf9a ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty" 1601326656 325 f9f16d12354225b7dd52a3321f085955 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/refcount/refcount.sty" 1576624809 9878 9e94e8fa600d95f9c7731bb21dfb67a4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty" 1657483315 9714 ba3194bd52c8499b3f1e3eb91d409670 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbbreakable.code.tex" 1729629551 34681 5ad5619477798e0585ce97e92ba075bc ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbskins.code.tex" 1729629551 83599 50e7ac6ad41eaf61a7736beda9e3416b ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbskinsjigsaw.code.tex" 1729629551 10040 cd3f87486a5ead1213ec367836f72332 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcolorbox.sty" 1729629551 105823 554671f5b2d97c7efbbaf6dae11c29a8 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill-common.sty" 1691524336 2573 42712c9e0a2df004e43df5b3c95f0c1e ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill.image.sty" 1691524336 1120 ba535da48caa03bf1fd3f03ea87779f8 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzlibraryfill.image.code.tex" 1691524336 10931 717eb52299f416934beb8b2b7cd8cee6 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/titlesec/titlesec.sty" 1736023606 48766 87a17a4ef312a39cd43896e34a679a56 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tools/calc.sty" 1717359999 10214 61188260d324e94bc2f66825d7d3fdf4 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tools/longtable.sty" 1730496337 15900 3cb191e576c7a313634d2813c55d4bf1 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/tools/verbatim.sty" 1717359999 7532 d2e1111e17bfebb1607d8ffa779705ec ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/trimspaces/trimspaces.sty" 1253232110 1380 971a51b00a14503ddf754cab24c3f209 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/url/url.sty" 1388531844 12796 8edb7d69a20b857904dd0ea757c14ec9 ""
"/usr/local/texlive/2025/texmf-dist/tex/latex/xcolor/xcolor.sty" 1727642399 55384 b454dec21c2d9f45ec0b793f0995b992 ""
"/usr/local/texlive/2025/texmf-dist/web2c/texmf.cnf" 1739380943 42148 61becc7c670cd061bb319c643c27fdd4 ""
"/usr/local/texlive/2025/texmf-var/fonts/map/pdftex/updmap/pdftex.map" 1765668892 5467155 19efa205003f9ecad95fbbaa6ff24da1 ""
"/usr/local/texlive/2025/texmf-var/web2c/pdftex/pdflatex.fmt" 1741450574 3345740 46b66fdb0378f7bf5921b5eabf1762b8 ""
"/usr/local/texlive/2025/texmf.cnf" 1741450484 577 418a7058ec8e006d8704f60ecd22c938 ""
"figures/fig_01_grid_block.pdf" 1777140637.75552 2464 4afa36c9ffe6748f9444fe1c6683c57a ""
"figures/fig_04_y_intersection.pdf" 1777140637.72077 4132 48ae290f23898b9c57edaf1e6f41cd8d ""
"figures/fig_06a_road_edit_before.pdf" 1777140637.7328 2464 4afa36c9ffe6748f9444fe1c6683c57a ""
"figures/fig_06b_road_edit_after.pdf" 1777140637.74404 2743 b0ec83cdbf2bfadba761f8a1ac2b9968 ""
"journal.aux" 1777140697.44399 16488 17a40913721efd28c03ecdf32e5c5795 "pdflatex"
"journal.out" 1777140697.44505 7378 77f42c324f401b5e4ee697fb18722086 "pdflatex"
"journal.tex" 1777140692.87318 61768 583822489f15c2459e1db67789800de3 ""
"journal.toc" 1777140697.4452 5375 f068ca2b1e38c68d7c5a2eca61b8d2ee "pdflatex"
(generated)
"journal.aux"
"journal.log"
"journal.out"
"journal.pdf"
"journal.toc"
(rewritten before read)

420
journal.fls Normal file
View File

@ -0,0 +1,420 @@
PWD /Users/danesabo/Projects/Historia_Urbis
INPUT /usr/local/texlive/2025/texmf.cnf
INPUT /usr/local/texlive/2025/texmf-dist/web2c/texmf.cnf
INPUT /usr/local/texlive/2025/texmf-var/web2c/pdftex/pdflatex.fmt
INPUT journal.tex
OUTPUT journal.log
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/article.cls
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/article.cls
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/size11.clo
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/size11.clo
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/size11.clo
INPUT /usr/local/texlive/2025/texmf-dist/fonts/map/fontname/texfonts.map
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/geometry/geometry.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/geometry/geometry.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/keyval.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/keyval.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/iftex/ifvtex.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/iftex/ifvtex.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/iftex/iftex.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/iftex/iftex.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsmath.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsmath.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsopn.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amstext.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amstext.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsgen.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsgen.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsbsy.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsbsy.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsmath/amsopn.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amssymb.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amssymb.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amsfonts.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/amsfonts.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amscls/amsthm.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amscls/amsthm.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mathtools.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mathtools.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/calc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/calc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mhsetup.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/mathtools/mhsetup.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphicx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphicx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphics.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/graphics.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/trig.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/trig.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-def/pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-def/pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-def/pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/pgf.revision.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/pgf.revision.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeyslibraryfiltered.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/xcolor/xcolor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/xcolor/xcolor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/color.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/color.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics-cfg/color.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/mathcolor.ltx
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/mathcolor.ltx
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/mathcolor.ltx
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithmetics.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfint.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/math/pgfmath.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pgf/math/pgfmath.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarytopaths.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarycalc.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.meta.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.meta.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/pgflibraryarrows.meta.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarypositioning.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibraryshapes.geometric.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/shapes/pgflibraryshapes.geometric.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.pathmorphing.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.pathmorphing.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzlibrarydecorations.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/modules/pgfmoduledecorations.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pgf/libraries/decorations/pgflibrarydecorations.pathmorphing.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/booktabs/booktabs.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/booktabs/booktabs.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/longtable.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/longtable.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/enumitem/enumitem.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/enumitem/enumitem.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstpatch.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstpatch.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstpatch.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/listings.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hyperref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hyperref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/kvsetkeys/kvsetkeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/kvdefinekeys/kvdefinekeys.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pdfescape/pdfescape.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pdfescape/pdfescape.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/ltxcmds/ltxcmds.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/pdftexcmds/pdftexcmds.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/infwarerr/infwarerr.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/infwarerr/infwarerr.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hycolor/hycolor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hycolor/hycolor.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/nameref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/nameref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/refcount/refcount.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/refcount/refcount.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/gettitlestring/gettitlestring.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/kvoptions/kvoptions.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/kvoptions/kvoptions.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/etoolbox/etoolbox.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/etoolbox/etoolbox.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/stringenc/stringenc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/stringenc/stringenc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/pd1enc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/pd1enc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/pd1enc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/intcalc/intcalc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/intcalc/intcalc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/puenc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/puenc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/puenc.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/url/url.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/url/url.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/bitset/bitset.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/bitset/bitset.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/bigintcalc/bigintcalc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/atbegshi/atbegshi.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/atbegshi-ltx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/atbegshi-ltx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hpdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hpdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/hyperref/hpdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/atveryend/atveryend.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/atveryend-ltx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/base/atveryend-ltx.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/rerunfilecheck/rerunfilecheck.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/generic/uniquecounter/uniquecounter.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/cleveref/cleveref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/cleveref/cleveref.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/fancyhdr/fancyhdr.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/titlesec/titlesec.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/titlesec/titlesec.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcolorbox.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcolorbox.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/verbatim.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tools/verbatim.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/environ/environ.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/environ/environ.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/trimspaces/trimspaces.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/trimspaces/trimspaces.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbbreakable.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pdfcol/pdfcol.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/pdfcol/pdfcol.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/graphics/color.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbskins.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill.image.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill.image.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill-common.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzfill-common.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzlibraryfill.image.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tikzfill/tikzlibraryfill.image.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/tcolorbox/tcbskinsjigsaw.code.tex
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def
INPUT ./journal.aux
INPUT ./journal.aux
INPUT journal.aux
OUTPUT journal.aux
INPUT /usr/local/texlive/2025/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
INPUT /usr/local/texlive/2025/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
INPUT /usr/local/texlive/2025/texmf-dist/tex/context/base/mkii/supp-pdf.mkii
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/epstopdf-pkg/epstopdf-base.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg
INPUT ./journal.out
INPUT ./journal.out
INPUT journal.out
INPUT journal.out
OUTPUT journal.pdf
INPUT ./journal.out
INPUT ./journal.out
OUTPUT journal.out
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr17.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr12.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr8.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr6.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmex10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsa.fd
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsa.fd
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsa.fd
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsb.fd
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsb.fd
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/amsfonts/umsb.fd
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmr12.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm
INPUT ./journal.toc
INPUT ./journal.toc
INPUT journal.toc
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmmi10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmex10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm
INPUT /usr/local/texlive/2025/texmf-var/fonts/map/pdftex/updmap/pdftex.map
OUTPUT journal.toc
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmti10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/jknappen/ec/tcrm1095.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/enc/dvips/cm-super/cm-super-ts1.enc
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmitt10.tfm
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang1.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang2.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstlang3.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT /usr/local/texlive/2025/texmf-dist/tex/latex/listings/lstmisc.sty
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt8.tfm
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/public/cm/cmtt8.tfm
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_01_grid_block.pdf
INPUT ./figures/fig_04_y_intersection.pdf
INPUT ./figures/fig_04_y_intersection.pdf
INPUT ./figures/fig_04_y_intersection.pdf
INPUT ./figures/fig_04_y_intersection.pdf
INPUT ./figures/fig_04_y_intersection.pdf
INPUT ./figures/fig_06a_road_edit_before.pdf
INPUT ./figures/fig_06a_road_edit_before.pdf
INPUT ./figures/fig_06a_road_edit_before.pdf
INPUT ./figures/fig_06a_road_edit_before.pdf
INPUT ./figures/fig_06a_road_edit_before.pdf
INPUT ./figures/fig_06b_road_edit_after.pdf
INPUT ./figures/fig_06b_road_edit_after.pdf
INPUT ./figures/fig_06b_road_edit_after.pdf
INPUT ./figures/fig_06b_road_edit_after.pdf
INPUT ./figures/fig_06b_road_edit_after.pdf
INPUT /usr/local/texlive/2025/texmf-dist/fonts/tfm/jknappen/ec/tcbx1095.tfm
INPUT journal.aux
INPUT ./journal.out
INPUT ./journal.out
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmitt10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi6.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi8.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr12.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr6.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmr8.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy8.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmti10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/amsfonts/cm/cmtt10.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/cm-super/sfbx1095.pfb
INPUT /usr/local/texlive/2025/texmf-dist/fonts/type1/public/cm-super/sfrm1095.pfb

1297
journal.log Normal file

File diff suppressed because it is too large Load Diff

44
journal.out Normal file
View File

@ -0,0 +1,44 @@
\BOOKMARK [1][-]{section.1}{\376\377\000P\000r\000o\000j\000e\000c\000t\000\040\000C\000o\000n\000t\000e\000x\000t\000\040\000a\000n\000d\000\040\000M\000o\000t\000i\000v\000a\000t\000i\000o\000n}{}% 1
\BOOKMARK [2][-]{subsection.1.1}{\376\377\000W\000h\000y\000\040\000B\000u\000i\000l\000d\000\040\000T\000h\000i\000s}{section.1}% 2
\BOOKMARK [2][-]{subsection.1.2}{\376\377\000S\000c\000o\000p\000e\000\040\000o\000f\000\040\000T\000h\000i\000s\000\040\000D\000o\000c\000u\000m\000e\000n\000t}{section.1}% 3
\BOOKMARK [2][-]{subsection.1.3}{\376\377\000A\000u\000d\000i\000e\000n\000c\000e}{section.1}% 4
\BOOKMARK [1][-]{section.2}{\376\377\000C\000o\000r\000e\000\040\000I\000n\000v\000a\000r\000i\000a\000n\000t\000s}{}% 5
\BOOKMARK [1][-]{section.3}{\376\377\000G\000e\000o\000m\000e\000t\000r\000i\000c\000\040\000F\000o\000u\000n\000d\000a\000t\000i\000o\000n\000s}{}% 6
\BOOKMARK [2][-]{subsection.3.1}{\376\377\000C\000o\000o\000r\000d\000i\000n\000a\000t\000e\000\040\000S\000y\000s\000t\000e\000m\000\040\000a\000n\000d\000\040\000N\000u\000m\000e\000r\000i\000c\000a\000l\000\040\000T\000y\000p\000e}{section.3}% 7
\BOOKMARK [2][-]{subsection.3.2}{\376\377\000R\000o\000a\000d\000\040\000N\000e\000t\000w\000o\000r\000k\000\040\000a\000s\000\040\000a\000\040\000P\000l\000a\000n\000a\000r\000\040\000G\000r\000a\000p\000h}{section.3}% 8
\BOOKMARK [2][-]{subsection.3.3}{\376\377\000B\000l\000o\000c\000k\000\040\000E\000x\000t\000r\000a\000c\000t\000i\000o\000n}{section.3}% 9
\BOOKMARK [2][-]{subsection.3.4}{\376\377\000I\000n\000w\000a\000r\000d\000\040\000O\000f\000f\000s\000e\000t\000t\000i\000n\000g}{section.3}% 10
\BOOKMARK [1][-]{section.4}{\376\377\000S\000u\000b\000d\000i\000v\000i\000s\000i\000o\000n\000\040\000A\000l\000g\000o\000r\000i\000t\000h\000m}{}% 11
\BOOKMARK [2][-]{subsection.4.1}{\376\377\000F\000r\000o\000n\000t\000a\000g\000e\000-\000F\000i\000r\000s\000t\000\040\000S\000u\000b\000d\000i\000v\000i\000s\000i\000o\000n}{section.4}% 12
\BOOKMARK [2][-]{subsection.4.2}{\376\377\000E\000d\000g\000e\000\040\000C\000l\000a\000s\000s\000i\000f\000i\000c\000a\000t\000i\000o\000n}{section.4}% 13
\BOOKMARK [2][-]{subsection.4.3}{\376\377\000R\000e\000g\000u\000l\000a\000r\000i\000z\000a\000t\000i\000o\000n\000\040\000P\000a\000s\000s}{section.4}% 14
\BOOKMARK [1][-]{section.5}{\376\377\000R\000o\000a\000d\000\040\000E\000d\000i\000t\000\040\000H\000a\000n\000d\000l\000i\000n\000g}{}% 15
\BOOKMARK [2][-]{subsection.5.1}{\376\377\000E\000d\000i\000t\000\040\000T\000y\000p\000e\000s}{section.5}% 16
\BOOKMARK [2][-]{subsection.5.2}{\376\377\000D\000e\000f\000o\000r\000m\000a\000t\000i\000o\000n\000\040\000P\000i\000p\000e\000l\000i\000n\000e}{section.5}% 17
\BOOKMARK [2][-]{subsection.5.3}{\376\377\000R\000e\000g\000e\000n\000e\000r\000a\000t\000i\000o\000n\000\040\000T\000h\000r\000e\000s\000h\000o\000l\000d\000s}{section.5}% 18
\BOOKMARK [2][-]{subsection.5.4}{\376\377\000D\000e\000t\000e\000r\000m\000i\000n\000i\000s\000m\000\040\000a\000n\000d\000\040\000I\000d\000e\000m\000p\000o\000t\000e\000n\000c\000e}{section.5}% 19
\BOOKMARK [2][-]{subsection.5.5}{\376\377\000B\000u\000i\000l\000d\000i\000n\000g\000\040\000F\000o\000o\000t\000p\000r\000i\000n\000t\000\040\000P\000r\000e\000s\000e\000r\000v\000a\000t\000i\000o\000n}{section.5}% 20
\BOOKMARK [1][-]{section.6}{\376\377\000D\000e\000g\000e\000n\000e\000r\000a\000t\000e\000\040\000C\000a\000s\000e\000s}{}% 21
\BOOKMARK [1][-]{section.7}{\376\377\000C\000r\000a\000t\000e\000\040\000A\000r\000c\000h\000i\000t\000e\000c\000t\000u\000r\000e}{}% 22
\BOOKMARK [2][-]{subsection.7.1}{\376\377\000M\000o\000d\000u\000l\000e\000\040\000L\000a\000y\000o\000u\000t}{section.7}% 23
\BOOKMARK [2][-]{subsection.7.2}{\376\377\000P\000u\000b\000l\000i\000c\000\040\000A\000P\000I\000\040\000S\000u\000r\000f\000a\000c\000e}{section.7}% 24
\BOOKMARK [2][-]{subsection.7.3}{\376\377\000E\000r\000r\000o\000r\000\040\000T\000y\000p\000e\000s}{section.7}% 25
\BOOKMARK [2][-]{subsection.7.4}{\376\377\000D\000e\000p\000e\000n\000d\000e\000n\000c\000i\000e\000s}{section.7}% 26
\BOOKMARK [1][-]{section.8}{\376\377\000I\000d\000i\000o\000m\000a\000t\000i\000c\000\040\000R\000u\000s\000t\000\040\000R\000e\000q\000u\000i\000r\000e\000m\000e\000n\000t\000s}{}% 27
\BOOKMARK [1][-]{section.9}{\376\377\000T\000e\000s\000t\000i\000n\000g\000\040\000S\000t\000r\000a\000t\000e\000g\000y}{}% 28
\BOOKMARK [2][-]{subsection.9.1}{\376\377\000T\000h\000r\000e\000e\000\040\000L\000a\000y\000e\000r\000s}{section.9}% 29
\BOOKMARK [2][-]{subsection.9.2}{\376\377\000S\000n\000a\000p\000s\000h\000o\000t\000\040\000T\000e\000s\000t\000i\000n\000g}{section.9}% 30
\BOOKMARK [2][-]{subsection.9.3}{\376\377\000C\000o\000v\000e\000r\000a\000g\000e\000\040\000R\000e\000q\000u\000i\000r\000e\000m\000e\000n\000t}{section.9}% 31
\BOOKMARK [1][-]{section.10}{\376\377\000V\000i\000s\000u\000a\000l\000i\000z\000a\000t\000i\000o\000n\000\040\000a\000n\000d\000\040\000F\000i\000g\000u\000r\000e\000s}{}% 32
\BOOKMARK [2][-]{subsection.10.1}{\376\377\000R\000e\000q\000u\000i\000r\000e\000d\000\040\000F\000i\000g\000u\000r\000e\000s}{section.10}% 33
\BOOKMARK [2][-]{subsection.10.2}{\376\377\000C\000o\000l\000o\000r\000\040\000C\000o\000n\000v\000e\000n\000t\000i\000o\000n\000s}{section.10}% 34
\BOOKMARK [2][-]{subsection.10.3}{\376\377\000I\000n\000c\000l\000u\000s\000i\000o\000n\000\040\000i\000n\000\040\000T\000h\000i\000s\000\040\000J\000o\000u\000r\000n\000a\000l}{section.10}% 35
\BOOKMARK [1][-]{section.11}{\376\377\000P\000e\000r\000f\000o\000r\000m\000a\000n\000c\000e\000\040\000T\000a\000r\000g\000e\000t\000s}{}% 36
\BOOKMARK [1][-]{section.12}{\376\377\000O\000u\000t\000\040\000o\000f\000\040\000S\000c\000o\000p\000e}{}% 37
\BOOKMARK [1][-]{section.13}{\376\377\000C\000l\000a\000u\000d\000e\000\040\000C\000o\000d\000e\000\040\000C\000o\000n\000t\000r\000a\000c\000t}{}% 38
\BOOKMARK [1][-]{section.14}{\376\377\000O\000p\000e\000n\000\040\000Q\000u\000e\000s\000t\000i\000o\000n\000s}{}% 39
\BOOKMARK [1][-]{section.15}{\376\377\000D\000e\000s\000i\000g\000n\000\040\000D\000e\000c\000i\000s\000i\000o\000n\000s}{}% 40
\BOOKMARK [1][-]{section.16}{\376\377\000R\000e\000v\000i\000s\000i\000o\000n\000s\000\040\000a\000n\000d\000\040\000D\000e\000v\000i\000a\000t\000i\000o\000n\000s}{}% 41
\BOOKMARK [2][-]{subsection.16.1}{\376\377\0002\0000\0002\0006\000-\0000\0004\000-\0002\0005\000\040\040\024\000\040\000S\000e\000s\000s\000i\000o\000n\000\040\0001\000:\000\040\000M\000i\000l\000e\000s\000t\000o\000n\000e\000\040\0000\000.\0001\000,\000\040\000r\000e\000c\000t\000a\000n\000g\000l\000e\000\040\000e\000n\000d\000-\000t\000o\000-\000e\000n\000d}{section.16}% 42
\BOOKMARK [2][-]{subsection.16.2}{\376\377\0002\0000\0002\0006\000-\0000\0004\000-\0002\0005\000\040\040\024\000\040\000S\000e\000s\000s\000i\000o\000n\000\040\0002\000:\000\040\000M\000i\000l\000e\000s\000t\000o\000n\000e\000\040\0000\000.\0002\000\040\000\050\000c\000o\000r\000n\000e\000r\000\040\000p\000a\000r\000c\000e\000l\000s\000,\000\040\000s\000t\000i\000c\000k\000y\000\040\000b\000a\000c\000k\000\040\000e\000d\000g\000e\000s\000,\000\040\000p\000r\000e\000s\000e\000r\000v\000e\000-\000o\000n\000-\000d\000e\000f\000o\000r\000m\000\051}{section.16}% 43
\BOOKMARK [1][-]{appendix.A}{\376\377\000N\000o\000t\000a\000t\000i\000o\000n\000\040\000R\000e\000f\000e\000r\000e\000n\000c\000e}{}% 44

BIN
journal.pdf Normal file

Binary file not shown.

1316
journal.tex Normal file

File diff suppressed because it is too large Load Diff

68
journal.toc Normal file
View File

@ -0,0 +1,68 @@
\contentsline {section}{\numberline {1}Project Context and Motivation}{3}{section.1}%
\contentsline {subsection}{\numberline {1.1}Why Build This}{3}{subsection.1.1}%
\contentsline {subsection}{\numberline {1.2}Scope of This Document}{3}{subsection.1.2}%
\contentsline {subsection}{\numberline {1.3}Audience}{3}{subsection.1.3}%
\contentsline {section}{\numberline {2}Core Invariants}{3}{section.2}%
\contentsline {section}{\numberline {3}Geometric Foundations}{4}{section.3}%
\contentsline {subsection}{\numberline {3.1}Coordinate System and Numerical Type}{4}{subsection.3.1}%
\contentsline {subsection}{\numberline {3.2}Road Network as a Planar Graph}{4}{subsection.3.2}%
\contentsline {subsection}{\numberline {3.3}Block Extraction}{5}{subsection.3.3}%
\contentsline {subsection}{\numberline {3.4}Inward Offsetting}{5}{subsection.3.4}%
\contentsline {section}{\numberline {4}Subdivision Algorithm}{5}{section.4}%
\contentsline {subsection}{\numberline {4.1}Frontage-First Subdivision}{5}{subsection.4.1}%
\contentsline {paragraph}{Inputs.}{5}{subsection.4.1}%
\contentsline {paragraph}{Procedure.}{5}{subsection.4.1}%
\contentsline {subsection}{\numberline {4.2}Edge Classification}{6}{subsection.4.2}%
\contentsline {subsection}{\numberline {4.3}Regularization Pass}{6}{subsection.4.3}%
\contentsline {section}{\numberline {5}Road Edit Handling}{6}{section.5}%
\contentsline {subsection}{\numberline {5.1}Edit Types}{7}{subsection.5.1}%
\contentsline {subsection}{\numberline {5.2}Deformation Pipeline}{7}{subsection.5.2}%
\contentsline {subsection}{\numberline {5.3}Regeneration Thresholds}{7}{subsection.5.3}%
\contentsline {subsection}{\numberline {5.4}Determinism and Idempotence}{8}{subsection.5.4}%
\contentsline {subsection}{\numberline {5.5}Building Footprint Preservation}{8}{subsection.5.5}%
\contentsline {section}{\numberline {6}Degenerate Cases}{8}{section.6}%
\contentsline {section}{\numberline {7}Crate Architecture}{9}{section.7}%
\contentsline {subsection}{\numberline {7.1}Module Layout}{9}{subsection.7.1}%
\contentsline {subsection}{\numberline {7.2}Public API Surface}{10}{subsection.7.2}%
\contentsline {subsection}{\numberline {7.3}Error Types}{10}{subsection.7.3}%
\contentsline {subsection}{\numberline {7.4}Dependencies}{11}{subsection.7.4}%
\contentsline {section}{\numberline {8}Idiomatic Rust Requirements}{11}{section.8}%
\contentsline {section}{\numberline {9}Testing Strategy}{12}{section.9}%
\contentsline {subsection}{\numberline {9.1}Three Layers}{12}{subsection.9.1}%
\contentsline {paragraph}{Unit tests}{12}{subsection.9.1}%
\contentsline {paragraph}{Integration tests}{12}{subsection.9.1}%
\contentsline {paragraph}{Property tests}{12}{subsection.9.1}%
\contentsline {subsection}{\numberline {9.2}Snapshot Testing}{12}{subsection.9.2}%
\contentsline {subsection}{\numberline {9.3}Coverage Requirement}{12}{subsection.9.3}%
\contentsline {section}{\numberline {10}Visualization and Figures}{12}{section.10}%
\contentsline {subsection}{\numberline {10.1}Required Figures}{12}{subsection.10.1}%
\contentsline {subsection}{\numberline {10.2}Color Conventions}{13}{subsection.10.2}%
\contentsline {subsection}{\numberline {10.3}Inclusion in This Journal}{13}{subsection.10.3}%
\contentsline {section}{\numberline {11}Performance Targets}{13}{section.11}%
\contentsline {section}{\numberline {12}Out of Scope}{13}{section.12}%
\contentsline {section}{\numberline {13}Claude Code Contract}{14}{section.13}%
\contentsline {section}{\numberline {14}Open Questions}{15}{section.14}%
\contentsline {section}{\numberline {15}Design Decisions}{15}{section.15}%
\contentsline {section}{\numberline {16}Revisions and Deviations}{16}{section.16}%
\contentsline {subsection}{\numberline {16.1}2026-04-25 --- Session 1: Milestone 0.1, rectangle end-to-end}{16}{subsection.16.1}%
\contentsline {paragraph}{Goal of the session.}{16}{subsection.16.1}%
\contentsline {paragraph}{What got built.}{17}{subsection.16.1}%
\contentsline {paragraph}{Tooling gates.}{17}{figure.1}%
\contentsline {paragraph}{Test status.}{17}{figure.1}%
\contentsline {paragraph}{Design decisions locked in this session.}{18}{figure.1}%
\contentsline {paragraph}{Spec deviations recorded this session.}{18}{figure.1}%
\contentsline {paragraph}{Open questions touched.}{20}{figure.1}%
\contentsline {paragraph}{Next session --- milestone 0.2 queue.}{20}{figure.1}%
\contentsline {subsection}{\numberline {16.2}2026-04-25 --- Session 2: Milestone 0.2 (corner parcels, sticky back edges, preserve-on-deform)}{20}{subsection.16.2}%
\contentsline {paragraph}{Goal of the session.}{20}{subsection.16.2}%
\contentsline {paragraph}{Decisions locked in at session kickoff.}{20}{subsection.16.2}%
\contentsline {paragraph}{Status during writing.}{21}{subsection.16.2}%
\contentsline {paragraph}{Performance instrumentation.}{21}{subsection.16.2}%
\contentsline {paragraph}{Corner parcel rework.}{22}{subsection.16.2}%
\contentsline {paragraph}{Preserve-on-deform pipeline.}{22}{subsection.16.2}%
\contentsline {paragraph}{New figures.}{22}{subsection.16.2}%
\contentsline {paragraph}{Deviations from spec.}{22}{figure.5}%
\contentsline {paragraph}{Performance.}{23}{figure.5}%
\contentsline {paragraph}{Test status.}{24}{figure.5}%
\contentsline {paragraph}{What's next --- milestone 0.3 queue.}{24}{figure.5}%
\contentsline {section}{\numberline {A}Notation Reference}{25}{appendix.A}%

BIN
road_parceling/.DS_Store vendored Normal file

Binary file not shown.

1007
road_parceling/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

53
road_parceling/Cargo.toml Normal file
View File

@ -0,0 +1,53 @@
[package]
name = "road_parceling"
version = "0.1.0"
edition = "2021"
authors = ["Dane Sabo"]
description = "Road-frontage-based parcel subdivision for a city simulation. Pure logic, no rendering or game loop."
license = "MIT OR Apache-2.0"
repository = "https://github.com/danesabo/Historia_Urbis"
readme = "README.md"
keywords = ["geometry", "city", "simulation", "parcel", "gis"]
categories = ["algorithms", "game-development", "science::geo"]
[features]
default = []
viz = ["dep:svg"]
serde = ["dep:serde", "glam/serde", "slotmap/serde"]
[dependencies]
glam = { version = "0.29", features = ["mint"] }
slotmap = "1"
thiserror = "2"
rand = "0.8"
rand_chacha = "0.3"
# optional
svg = { version = "0.18", optional = true }
serde = { version = "1", features = ["derive"], optional = true }
[dev-dependencies]
proptest = "1"
insta = { version = "1", features = ["yaml"] }
criterion = { version = "0.5", default-features = false, features = ["cargo_bench_support"] }
[[bench]]
name = "subdivision"
harness = false
[[example]]
name = "generate_figures"
required-features = ["viz"]
[lints.rust]
missing_docs = "deny"
unsafe_code = "forbid"
[lints.clippy]
all = { level = "warn", priority = -1 }
# Pragmatic exceptions for a numerical/geometry crate. We deliberately
# do *not* enable `pedantic` here — its naming and idiom checks fight
# the conventions of geometry code (single-letter coordinate names,
# small struct field reassignments) without buying real safety.
if_same_then_else = "allow"
field_reassign_with_default = "allow"

View File

@ -0,0 +1,36 @@
//! Criterion benches against the performance targets in spec §9.
//!
//! Status: stub for milestone 0.1 — we measure `subdivide_all` on a
//! single block and on a 10x10 grid. The 100/10000-block targets
//! land in milestone 0.2.
#![allow(missing_docs)] // criterion_group! generates undocumented items
use criterion::{criterion_group, criterion_main, Criterion};
use glam::DVec2;
use road_parceling::{subdivide_all, RoadGraph, SubdivisionParams};
fn rectangle_graph(w: f64, h: f64) -> RoadGraph {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(w, 0.0));
let c = g.add_node(DVec2::new(w, h));
let d = g.add_node(DVec2::new(0.0, h));
g.add_road(a, b).unwrap();
g.add_road(b, c).unwrap();
g.add_road(c, d).unwrap();
g.add_road(d, a).unwrap();
g.rebuild_topology().unwrap();
g
}
fn bench_one_block(c: &mut Criterion) {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
c.bench_function("subdivide_one_rectangle", |b| {
b.iter(|| subdivide_all(&g, &params).unwrap());
});
}
criterion_group!(benches, bench_one_block);
criterion_main!(benches);

View File

@ -0,0 +1,37 @@
#![allow(missing_docs)]
use glam::DVec2;
use road_parceling::{subdivide_all, RoadGraph, SubdivisionParams};
fn main() {
let r = 100.0;
let third = std::f64::consts::TAU / 3.0;
let mut g = RoadGraph::new();
let center = g.add_node(DVec2::new(0.0, 0.0));
let p1 = g.add_node(DVec2::new(r * 0.0_f64.cos(), r * 0.0_f64.sin()));
let p2 = g.add_node(DVec2::new(r * third.cos(), r * third.sin()));
let p3 = g.add_node(DVec2::new(r * (2.0 * third).cos(), r * (2.0 * third).sin()));
g.add_road(center, p1).unwrap();
g.add_road(center, p2).unwrap();
g.add_road(center, p3).unwrap();
g.add_road(p1, p2).unwrap();
g.add_road(p2, p3).unwrap();
g.add_road(p3, p1).unwrap();
g.rebuild_topology().unwrap();
let params = SubdivisionParams::default();
let parcels = subdivide_all(&g, &params).unwrap();
println!("p1 = {:?}", g.node_position(p1));
println!("p2 = {:?}", g.node_position(p2));
println!("p3 = {:?}", g.node_position(p3));
println!("center = {:?}", g.node_position(center));
println!("\n{} parcels:", parcels.len());
for (i, (id, p)) in parcels.iter().enumerate() {
println!(
" [{}] id={:?} centroid={:?} verts={:?}",
i,
id,
p.polygon().centroid(),
p.vertices()
);
}
}

View File

@ -0,0 +1,26 @@
//! Regenerates every figure referenced in `journal.tex` (spec §8).
//!
//! Run with:
//!
//! ```sh
//! cargo run --example generate_figures --features viz -- <out_dir>
//! ```
#[cfg(not(feature = "viz"))]
fn main() {
eprintln!("rebuild with --features viz");
}
#[cfg(feature = "viz")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::env;
use std::path::PathBuf;
let out: PathBuf = env::args()
.nth(1)
.map_or_else(|| PathBuf::from("figures"), PathBuf::from);
std::fs::create_dir_all(&out)?;
road_parceling::viz::generate_all_figures(&out)?;
println!("wrote figures to {}", out.display());
Ok(())
}

View File

@ -0,0 +1,58 @@
#![allow(missing_docs)]
use glam::DVec2;
use road_parceling::{subdivide_all_with_stats, RoadGraph, SubdivisionParams};
fn rect(g: &mut RoadGraph, x0: f64, y0: f64, w: f64, h: f64) {
let a = g.add_node(DVec2::new(x0, y0));
let b = g.add_node(DVec2::new(x0 + w, y0));
let c = g.add_node(DVec2::new(x0 + w, y0 + h));
let d = g.add_node(DVec2::new(x0, y0 + h));
g.add_road(a, b).unwrap();
g.add_road(b, c).unwrap();
g.add_road(c, d).unwrap();
g.add_road(d, a).unwrap();
}
fn main() {
let mut single = RoadGraph::new();
rect(&mut single, 0.0, 0.0, 200.0, 100.0);
single.rebuild_topology().unwrap();
let params = SubdivisionParams::default();
// warm
for _ in 0..5 {
let _ = subdivide_all_with_stats(&single, &params).unwrap();
}
let (_, s) = subdivide_all_with_stats(&single, &params).unwrap();
println!("single rectangle: {} blocks, {} parcels, total {:.1}us, topo {:.1}us, blocks {:.1}us, sub {:.1}us, {:.0} parcels/s",
s.block_count, s.parcel_count, s.total.as_secs_f64()*1e6, s.topology_rebuild.as_secs_f64()*1e6,
s.block_extraction.as_secs_f64()*1e6, s.block_subdivide_total.as_secs_f64()*1e6, s.parcels_per_second());
println!("{:.2} us/parcel", s.time_per_parcel_us());
// 5x5 grid of small blocks — quickly approaches 25 blocks
let mut grid = RoadGraph::new();
for i in 0..5 {
for j in 0..5 {
rect(
&mut grid,
(i as f64) * 220.0,
(j as f64) * 120.0,
200.0,
100.0,
);
}
}
grid.rebuild_topology().unwrap();
for _ in 0..3 {
let _ = subdivide_all_with_stats(&grid, &params).unwrap();
}
let (_, s) = subdivide_all_with_stats(&grid, &params).unwrap();
println!(
"5x5 grid (25 disjoint blocks): {} blocks, {} parcels, total {:.1}us, {:.0} parcels/s",
s.block_count,
s.parcel_count,
s.total.as_secs_f64() * 1e6,
s.parcels_per_second()
);
println!("{:.2} us/parcel", s.time_per_parcel_us());
}

BIN
road_parceling/src/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,89 @@
//! Tunable parameters for subdivision. (Spec §3.1.)
//!
//! `SubdivisionParams` is the single configuration object passed to
//! every public subdivision and edit operation. All distances are in
//! meters, areas in m², angles in radians.
use crate::error::SubdivisionError;
/// Parameters controlling frontage-first subdivision.
///
/// See spec §3 (algorithm) and §4 (regeneration thresholds).
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SubdivisionParams {
/// Target frontage width `w_f`, meters.
pub frontage_width: f64,
/// Frontage width variance `sigma_f`, meters.
pub frontage_variance: f64,
/// Target parcel depth `d_p`, meters.
pub depth: f64,
/// Depth variance `sigma_d`, meters.
pub depth_variance: f64,
/// Setback from road edge into the block, `d_s` meters.
pub setback: f64,
/// Regularity slider `rho` in `[0, 1]`. 0 = raw, 1 = OBB-snapped.
pub regularity: f64,
/// Minimum frontage `w_min`, meters.
pub min_frontage: f64,
/// Minimum parcel area `A_min`, m².
pub min_area: f64,
/// Maximum side-edge rotation before a deformed parcel is
/// regenerated, `alpha_max` radians. Default ~30°.
pub max_side_rotation: f64,
/// Deterministic RNG seed.
pub seed: u64,
}
impl Default for SubdivisionParams {
fn default() -> Self {
Self {
frontage_width: 20.0,
frontage_variance: 3.0,
depth: 30.0,
depth_variance: 4.0,
setback: 1.0,
regularity: 0.0,
min_frontage: 6.0,
min_area: 60.0,
max_side_rotation: 30.0_f64.to_radians(),
seed: 0xC17_1E5_u64,
}
}
}
impl SubdivisionParams {
/// Validate that every parameter is in a sane range. Called at the
/// top of every public entry point.
pub fn validate(&self) -> Result<(), SubdivisionError> {
let bad = |s: &str| Err(SubdivisionError::InvalidParams(s.to_string()));
if !self.frontage_width.is_finite() || self.frontage_width <= 0.0 {
return bad("frontage_width must be finite and positive");
}
if !self.frontage_variance.is_finite() || self.frontage_variance < 0.0 {
return bad("frontage_variance must be finite and >= 0");
}
if !self.depth.is_finite() || self.depth <= 0.0 {
return bad("depth must be finite and positive");
}
if !self.depth_variance.is_finite() || self.depth_variance < 0.0 {
return bad("depth_variance must be finite and >= 0");
}
if !self.setback.is_finite() || self.setback < 0.0 {
return bad("setback must be finite and >= 0");
}
if !(0.0..=1.0).contains(&self.regularity) {
return bad("regularity must be in [0, 1]");
}
if self.min_frontage <= 0.0 || self.min_frontage > self.frontage_width {
return bad("min_frontage must be in (0, frontage_width]");
}
if self.min_area <= 0.0 {
return bad("min_area must be positive");
}
if self.max_side_rotation <= 0.0 || self.max_side_rotation >= std::f64::consts::PI {
return bad("max_side_rotation must be in (0, π)");
}
Ok(())
}
}

View File

@ -0,0 +1,51 @@
//! Typed errors returned by the public API. (Spec §6.3.)
//!
//! Every fallible operation returns a `Result`. The library does not
//! panic on invalid input; it returns one of the variants below.
use crate::network::NodeId;
/// Errors that can arise during initial subdivision.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum SubdivisionError {
/// The road graph is not planar at the given node (two roads cross
/// without a graph node at the crossing point).
#[error("road graph is not planar at node {0:?}")]
NonPlanarGraph(NodeId),
/// A block boundary failed to close into a cycle.
#[error("block boundary is not closed")]
OpenBlock,
/// `SubdivisionParams` failed validation.
#[error("subdivision parameters invalid: {0}")]
InvalidParams(String),
/// A geometric operation (offset, intersection, ...) failed in a
/// way that suggests degenerate input rather than a bug.
#[error("geometric operation failed: {0}")]
GeometryFailure(String),
/// Reserved for features deferred to a later milestone.
#[error("feature not yet implemented: {0}")]
Unimplemented(&'static str),
}
/// Errors returned by `apply_road_edit`.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ParcelError {
/// The road or node referenced by the edit no longer exists.
#[error("edit references unknown entity")]
UnknownEntity,
/// The edit produced an inconsistent graph; the operation was
/// rolled back.
#[error("edit produced inconsistent graph: {0}")]
InconsistentEdit(String),
/// Re-subdivision of an affected block failed.
#[error("re-subdivision after edit failed: {0}")]
Resubdivide(#[from] SubdivisionError),
}

View File

@ -0,0 +1,63 @@
//! 2-D geometric primitives operating in `glam::DVec2` (f64) coords.
//!
//! All coordinates are in meters in a flat Euclidean plane. See spec
//! §2 (invariants) for the numerical tolerances and §3.1 (D1) for the
//! rationale behind double precision.
pub mod offset;
pub mod polygon;
pub use offset::offset_inward;
pub use polygon::{Polygon, PolygonError};
use glam::DVec2;
/// Signed area of a closed ring. Positive for CCW, negative for CW.
/// Used by both polygon validation and DCEL face classification.
#[must_use]
pub fn signed_area(verts: &[DVec2]) -> f64 {
let n = verts.len();
if n < 3 {
return 0.0;
}
let mut a = 0.0;
for i in 0..n {
let p = verts[i];
let q = verts[(i + 1) % n];
a += p.x * q.y - q.x * p.y;
}
0.5 * a
}
/// Proper segment-segment intersection: true iff the two segments
/// cross strictly in their interiors. Used both by polygon
/// self-intersection checks and planar-graph validation.
#[must_use]
pub fn segments_properly_intersect(p1: DVec2, p2: DVec2, p3: DVec2, p4: DVec2) -> bool {
let d1 = cross_sign(p3, p4, p1);
let d2 = cross_sign(p3, p4, p2);
let d3 = cross_sign(p1, p2, p3);
let d4 = cross_sign(p1, p2, p4);
(d1 * d2 < 0.0) && (d3 * d4 < 0.0)
}
fn cross_sign(a: DVec2, b: DVec2, c: DVec2) -> f64 {
let v = (b - a).perp_dot(c - a);
if v.abs() < EPS_GEOM {
0.0
} else {
v.signum()
}
}
/// Geometric tolerance, meters. Used for "two points are the same" and
/// "edge has zero length" tests.
pub const EPS_GEOM: f64 = 1.0e-6;
/// Area tolerance, m². Used for "polygon has zero area" and "two
/// parcels overlap" tests.
pub const EPS_AREA: f64 = 1.0e-9;
/// Angular tolerance, radians. Used for "three points are collinear"
/// tests.
pub const EPS_ANGLE: f64 = 1.0e-4;

View File

@ -0,0 +1,142 @@
//! Inward offsetting of polygons (spec §2.5).
//!
//! Used to compute the developable polygon `B'` of a block: `B`
//! shrunk by the road setback distance `d_s`. For convex inputs this
//! is exact. For concave inputs the result is the intersection of the
//! inward half-planes of every edge — a conservative approximation
//! that may shave off concave outer-corner detail but never produces
//! an invalid polygon.
use glam::DVec2;
use super::polygon::Polygon;
use super::EPS_GEOM;
/// Compute the inward offset of `poly` by `distance` while preserving
/// a 1-1 correspondence between input edges and output vertices.
///
/// Vertex `i` of the returned ring is the intersection of the inward
/// offset lines of input edges `i-1` and `i`. This is well-defined
/// for convex inputs and modest offsets; for highly non-convex inputs
/// or large offsets the result may self-intersect, in which case
/// the caller should fall back to [`offset_inward`].
#[must_use]
pub fn correlated_offset_inward(poly: &Polygon, distance: f64) -> Option<Vec<DVec2>> {
let n = poly.len();
if n < 3 || distance < 0.0 {
return None;
}
let verts = poly.vertices();
let mut offset_lines: Vec<(DVec2, DVec2)> = Vec::with_capacity(n);
for i in 0..n {
let p = verts[i];
let q = verts[(i + 1) % n];
let edge = q - p;
let len = edge.length();
if len < EPS_GEOM {
return None;
}
let tangent = edge / len;
let inward = DVec2::new(-tangent.y, tangent.x);
let p_off = p + inward * distance;
let q_off = q + inward * distance;
offset_lines.push((p_off, q_off));
}
let mut new_verts = Vec::with_capacity(n);
for i in 0..n {
let prev = offset_lines[(i + n - 1) % n];
let curr = offset_lines[i];
let isect = line_line_intersect(prev.0, prev.1, curr.0, curr.1)?;
new_verts.push(isect);
}
Some(new_verts)
}
fn line_line_intersect(p1: DVec2, p2: DVec2, p3: DVec2, p4: DVec2) -> Option<DVec2> {
let d1 = p2 - p1;
let d2 = p4 - p3;
let denom = d1.x * d2.y - d1.y * d2.x;
if denom.abs() < EPS_GEOM {
return None;
}
let dp = p3 - p1;
let t = (dp.x * d2.y - dp.y * d2.x) / denom;
Some(p1 + d1 * t)
}
/// Compute the inward offset of `poly` by `distance`. Returns `None`
/// when the result is empty (block too narrow to offset) or invalid.
///
/// Implementation: clip `poly` once by the inward-shifted half-plane
/// of each edge and validate the result.
#[must_use]
pub fn offset_inward(poly: &Polygon, distance: f64) -> Option<Polygon> {
if distance.abs() < EPS_GEOM {
return Some(poly.clone());
}
if distance < 0.0 {
return None;
}
let mut working = poly.clone();
for (a, b) in poly.edges().collect::<Vec<_>>() {
let edge = b - a;
let len = edge.length();
if len < EPS_GEOM {
continue;
}
// Inward normal of a CCW polygon: rotate the tangent +90°
// (left-perpendicular).
let tangent = edge / len;
let inward = DVec2::new(-tangent.y, tangent.x);
let shifted = a + inward * distance;
match working.clip_half_plane(shifted, inward) {
Some(p) => working = p,
None => return None,
}
}
Some(working)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rectangle_inward_offset_shrinks() {
let p = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(100.0, 0.0),
DVec2::new(100.0, 50.0),
DVec2::new(0.0, 50.0),
])
.unwrap();
let off = offset_inward(&p, 5.0).unwrap();
// Result should be the rectangle (5,5)-(95,45), area = 90*40.
assert!((off.area() - 90.0 * 40.0).abs() < 1e-6);
}
#[test]
fn too_large_offset_returns_none() {
let p = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(10.0, 0.0),
DVec2::new(10.0, 10.0),
DVec2::new(0.0, 10.0),
])
.unwrap();
assert!(offset_inward(&p, 50.0).is_none());
}
#[test]
fn zero_offset_is_identity() {
let p = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(1.0, 0.0),
DVec2::new(1.0, 1.0),
DVec2::new(0.0, 1.0),
])
.unwrap();
let off = offset_inward(&p, 0.0).unwrap();
assert!((off.area() - p.area()).abs() < 1e-12);
}
}

View File

@ -0,0 +1,372 @@
//! Simple polygon type with validation and basic operations.
//!
//! Per invariant **I1** (spec §2): every parcel polygon must be simple
//! (no self-intersections, no holes), CCW-ordered, with no zero-length
//! edges and no collinear triples within tolerance.
use glam::DVec2;
use super::{segments_properly_intersect, signed_area, EPS_ANGLE, EPS_AREA, EPS_GEOM};
/// Validation errors returned by [`Polygon::new`] and friends.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum PolygonError {
/// Fewer than 3 vertices.
#[error("polygon needs at least 3 vertices, got {0}")]
TooFewVertices(usize),
/// Two consecutive vertices coincide within `EPS_GEOM`.
#[error("polygon has a zero-length edge at vertex {0}")]
ZeroEdge(usize),
/// Three consecutive vertices are collinear within `EPS_ANGLE`.
#[error("polygon has collinear vertices at {0}")]
CollinearTriple(usize),
/// Polygon edges cross.
#[error("polygon is self-intersecting")]
SelfIntersecting,
/// |signed area| < `EPS_AREA`.
#[error("polygon has near-zero area")]
Degenerate,
}
/// A simple polygon stored as a CCW ring of vertices. The ring is not
/// closed in storage (last vertex != first vertex).
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Polygon {
verts: Vec<DVec2>,
}
impl Polygon {
/// Construct a polygon from a vertex ring. Validates I1.
///
/// Cleans up trailing duplicates of the first vertex and reverses
/// the ring if it was passed clockwise.
///
/// # Errors
///
/// Returns a [`PolygonError`] if validation fails.
pub fn new(verts: Vec<DVec2>) -> Result<Self, PolygonError> {
Self::new_inner(verts, true)
}
/// Like [`Polygon::new`] but does *not* reject collinear triples.
/// Used internally for block boundaries, which legitimately
/// contain collinear-corner vertices when adjacent road segments
/// are end-to-end (the `colinear_roads` degenerate case in spec
/// §5).
pub(crate) fn new_relaxed(verts: Vec<DVec2>) -> Result<Self, PolygonError> {
Self::new_inner(verts, false)
}
fn new_inner(mut verts: Vec<DVec2>, strict: bool) -> Result<Self, PolygonError> {
// Drop a closing duplicate if present.
if verts.len() >= 2 {
let first = verts[0];
let last = *verts.last().expect("len >= 2");
if (first - last).length_squared() < EPS_GEOM * EPS_GEOM {
verts.pop();
}
}
if verts.len() < 3 {
return Err(PolygonError::TooFewVertices(verts.len()));
}
// Zero-edge check.
for i in 0..verts.len() {
let j = (i + 1) % verts.len();
if (verts[j] - verts[i]).length_squared() < EPS_GEOM * EPS_GEOM {
return Err(PolygonError::ZeroEdge(i));
}
}
// Self-intersection check first — bowtie shapes can have
// zero algebraic area, which would otherwise be reported as
// `Degenerate` and obscure the real reason.
if has_self_intersection(&verts) {
return Err(PolygonError::SelfIntersecting);
}
// Compute signed area; if negative, reverse to CCW.
let mut area = signed_area(&verts);
if area < 0.0 {
verts.reverse();
area = -area;
}
if area < EPS_AREA {
return Err(PolygonError::Degenerate);
}
if strict {
// Collinear triple check (parcel polygons only — block
// polygons skip this).
for i in 0..verts.len() {
let h = (i + verts.len() - 1) % verts.len();
let j = (i + 1) % verts.len();
let a = (verts[i] - verts[h]).normalize_or_zero();
let b = (verts[j] - verts[i]).normalize_or_zero();
if (a.x * b.y - a.y * b.x).abs() < EPS_ANGLE && a.dot(b) > 0.0 {
return Err(PolygonError::CollinearTriple(i));
}
}
}
Ok(Self { verts })
}
/// Access the vertex ring (CCW, not closed).
#[must_use]
pub fn vertices(&self) -> &[DVec2] {
&self.verts
}
/// Number of vertices in the ring.
#[must_use]
pub fn len(&self) -> usize {
self.verts.len()
}
/// Whether the ring is empty. Always false for a valid polygon
/// (kept for clippy `len_without_is_empty`).
#[must_use]
pub fn is_empty(&self) -> bool {
self.verts.is_empty()
}
/// Signed area; always non-negative for a valid CCW polygon.
#[must_use]
pub fn area(&self) -> f64 {
signed_area(&self.verts).abs()
}
/// Edges as iterator of `(start, end)` pairs in ring order.
pub fn edges(&self) -> impl Iterator<Item = (DVec2, DVec2)> + '_ {
let n = self.verts.len();
(0..n).map(move |i| (self.verts[i], self.verts[(i + 1) % n]))
}
/// Centroid (area-weighted), well-defined for non-degenerate
/// polygons.
#[must_use]
pub fn centroid(&self) -> DVec2 {
let mut a = 0.0;
let mut cx = 0.0;
let mut cy = 0.0;
let n = self.verts.len();
for i in 0..n {
let p = self.verts[i];
let q = self.verts[(i + 1) % n];
let cross = p.x * q.y - q.x * p.y;
a += cross;
cx += (p.x + q.x) * cross;
cy += (p.y + q.y) * cross;
}
a *= 0.5;
if a.abs() < EPS_AREA {
// Fall back to vertex average for near-degenerate input.
let sum: DVec2 = self.verts.iter().copied().sum();
return sum / (n as f64);
}
DVec2::new(cx / (6.0 * a), cy / (6.0 * a))
}
/// Clip this polygon against the closed half-plane defined by a
/// point on the line and an inward normal. Returns `None` if the
/// result is empty (everything was on the outer side) or invalid.
///
/// Implements SutherlandHodgman polygon-vs-half-plane clipping.
/// Robust for convex *and* concave inputs as long as the half
/// plane carves a single piece.
#[must_use]
pub fn clip_half_plane(&self, point: DVec2, inward_normal: DVec2) -> Option<Self> {
let n = inward_normal;
if n.length_squared() < EPS_GEOM * EPS_GEOM {
return Some(self.clone());
}
let n = n.normalize();
let inside = |p: DVec2| (p - point).dot(n) >= -EPS_GEOM;
let intersect = |a: DVec2, b: DVec2| -> Option<DVec2> {
let da = (a - point).dot(n);
let db = (b - point).dot(n);
let denom = da - db;
if denom.abs() < EPS_GEOM {
return None;
}
let t = da / denom;
Some(a + (b - a) * t)
};
let mut out = Vec::with_capacity(self.verts.len() + 2);
let len = self.verts.len();
for i in 0..len {
let a = self.verts[i];
let b = self.verts[(i + 1) % len];
let a_in = inside(a);
let b_in = inside(b);
match (a_in, b_in) {
(true, true) => out.push(b),
(true, false) => {
if let Some(p) = intersect(a, b) {
out.push(p);
}
}
(false, true) => {
if let Some(p) = intersect(a, b) {
out.push(p);
}
out.push(b);
}
(false, false) => {}
}
}
if out.len() < 3 {
return None;
}
// Strip near-duplicate consecutive vertices.
let cleaned = strip_near_duplicates(out);
if cleaned.len() < 3 {
return None;
}
Self::new(cleaned).ok()
}
/// Check whether this polygon contains a point (open interior, by
/// even-odd rule). Valid for simple polygons of any convexity.
#[must_use]
pub fn contains(&self, p: DVec2) -> bool {
let mut inside = false;
let n = self.verts.len();
let mut j = n - 1;
for i in 0..n {
let a = self.verts[i];
let b = self.verts[j];
let cond =
(a.y > p.y) != (b.y > p.y) && p.x < (b.x - a.x) * (p.y - a.y) / (b.y - a.y) + a.x;
if cond {
inside = !inside;
}
j = i;
}
inside
}
}
fn strip_near_duplicates(verts: Vec<DVec2>) -> Vec<DVec2> {
let mut out: Vec<DVec2> = Vec::with_capacity(verts.len());
for v in verts {
if let Some(&last) = out.last() {
if (v - last).length_squared() < EPS_GEOM * EPS_GEOM {
continue;
}
}
out.push(v);
}
if out.len() >= 2 {
let first = out[0];
let last = *out.last().expect("len >= 2");
if (first - last).length_squared() < EPS_GEOM * EPS_GEOM {
out.pop();
}
}
out
}
fn has_self_intersection(verts: &[DVec2]) -> bool {
let n = verts.len();
if n < 4 {
return false;
}
for i in 0..n {
let a1 = verts[i];
let a2 = verts[(i + 1) % n];
// skip self and adjacent edges (share a vertex)
for j in (i + 2)..n {
// adjacency wrap: edge (n-1, 0) is adjacent to edge (0, 1)
if i == 0 && j == n - 1 {
continue;
}
let b1 = verts[j];
let b2 = verts[(j + 1) % n];
if segments_properly_intersect(a1, a2, b1, b2) {
return true;
}
}
}
false
}
#[cfg(test)]
mod tests {
use super::*;
fn unit_square() -> Polygon {
Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(1.0, 0.0),
DVec2::new(1.0, 1.0),
DVec2::new(0.0, 1.0),
])
.unwrap()
}
#[test]
fn unit_square_area_is_one() {
assert!((unit_square().area() - 1.0).abs() < 1e-12);
}
#[test]
fn cw_input_is_normalized_to_ccw() {
let p = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(0.0, 1.0),
DVec2::new(1.0, 1.0),
DVec2::new(1.0, 0.0),
])
.unwrap();
assert!((p.area() - 1.0).abs() < 1e-12);
}
#[test]
fn collinear_triple_rejected() {
let err = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(1.0, 0.0),
DVec2::new(2.0, 0.0),
DVec2::new(2.0, 1.0),
DVec2::new(0.0, 1.0),
])
.unwrap_err();
matches!(err, PolygonError::CollinearTriple(_));
}
#[test]
fn self_intersection_rejected() {
let err = Polygon::new(vec![
DVec2::new(0.0, 0.0),
DVec2::new(1.0, 1.0),
DVec2::new(1.0, 0.0),
DVec2::new(0.0, 1.0),
])
.unwrap_err();
assert!(matches!(err, PolygonError::SelfIntersecting));
}
#[test]
fn contains_point() {
let p = unit_square();
assert!(p.contains(DVec2::new(0.5, 0.5)));
assert!(!p.contains(DVec2::new(1.5, 0.5)));
}
#[test]
fn half_plane_clip_keeps_inside() {
let p = unit_square();
// keep y >= 0.5
let clipped = p
.clip_half_plane(DVec2::new(0.0, 0.5), DVec2::new(0.0, 1.0))
.unwrap();
assert!((clipped.area() - 0.5).abs() < 1e-9);
}
#[test]
fn half_plane_clip_returns_none_when_empty() {
let p = unit_square();
let clipped = p.clip_half_plane(DVec2::new(0.0, 2.0), DVec2::new(0.0, 1.0));
assert!(clipped.is_none());
}
}

50
road_parceling/src/lib.rs Normal file
View File

@ -0,0 +1,50 @@
//! Road-frontage-based parcel subdivision for a city simulation game.
//!
//! This crate is a pure-logic geometric layer: it consumes a planar
//! road graph and produces a set of polygonal parcels with each parcel
//! facing exactly one road. It does not render, simulate buildings, or
//! drive a game loop.
//!
//! See `journal.tex` at the repository root for the full design
//! specification. Section numbers cited here refer to that document.
//!
//! # Quick start
//!
//! ```
//! use road_parceling::{RoadGraph, SubdivisionParams, subdivide_all};
//! use glam::DVec2;
//!
//! let mut g = RoadGraph::new();
//! let a = g.add_node(DVec2::new(0.0, 0.0));
//! let b = g.add_node(DVec2::new(200.0, 0.0));
//! let c = g.add_node(DVec2::new(200.0, 100.0));
//! let d = g.add_node(DVec2::new(0.0, 100.0));
//! g.add_road(a, b).unwrap();
//! g.add_road(b, c).unwrap();
//! g.add_road(c, d).unwrap();
//! g.add_road(d, a).unwrap();
//!
//! let params = SubdivisionParams::default();
//! let parcels = subdivide_all(&g, &params).unwrap();
//! assert!(!parcels.is_empty());
//! ```
#![deny(missing_docs)]
#![forbid(unsafe_code)]
pub mod config;
pub mod error;
pub mod geometry;
pub mod network;
pub mod parcel;
#[cfg(feature = "viz")]
pub mod viz;
pub use config::SubdivisionParams;
pub use error::{ParcelError, SubdivisionError};
pub use network::{NodeId, RoadGraph, RoadId};
pub use parcel::{
apply_road_edit, subdivide_all, subdivide_all_with_stats, BuildingFitCheck, BuildingHandle,
EdgeKind, EditOutcome, Parcel, ParcelId, ParcelSet, RoadEdit, SubdivisionStats,
};

View File

@ -0,0 +1,113 @@
//! Block extraction from the half-edge graph (spec §2.4).
//!
//! A *block* is a bounded face of the planar road graph. Each block
//! carries its boundary polygon (CCW) plus, for every edge in the
//! ring, the [`RoadId`] of the road that edge runs along.
use glam::DVec2;
use crate::geometry::Polygon;
use crate::network::graph::{FaceId, HalfEdgeId, RoadGraph, RoadId};
/// A bounded face — the geometric block to be subdivided.
#[derive(Debug, Clone)]
pub struct Block {
/// Stable face identifier within the graph.
pub(crate) face: FaceId,
/// Boundary polygon, CCW, with vertex \(i\) at the origin of
/// `boundary_edges[i]`.
pub polygon: Polygon,
/// Half-edge cycle bounding this block, parallel to
/// `polygon.vertices()`. Reserved for milestone 0.2's
/// edit-preserves path.
#[allow(dead_code)]
pub(crate) boundary_edges: Vec<HalfEdgeId>,
/// Road associated with each boundary edge, parallel to
/// `polygon.vertices()`.
pub roads: Vec<RoadId>,
}
impl Block {
/// Vertex positions of the block boundary, CCW.
#[must_use]
pub fn boundary(&self) -> &[DVec2] {
self.polygon.vertices()
}
/// Face id this block was extracted from.
#[must_use]
pub(crate) fn face_id(&self) -> FaceId {
self.face
}
/// Half-edge cycle for this block. Reserved for milestone 0.2's
/// edit-preserves path; currently exposed crate-internally so
/// the edit pipeline can wire up to it.
#[must_use]
#[allow(dead_code)]
pub(crate) fn boundary_edges(&self) -> &[HalfEdgeId] {
&self.boundary_edges
}
}
/// Extract every bounded face of `graph` as a [`Block`]. Caller must
/// ensure `rebuild_topology` has been run.
pub(crate) fn extract_blocks(graph: &RoadGraph) -> Vec<Block> {
let mut out = Vec::new();
for (face_id, face) in &graph.faces {
if face.is_exterior {
continue;
}
let mut edges = Vec::new();
let mut verts = Vec::new();
let mut roads = Vec::new();
let start = face.boundary;
let mut h = start;
loop {
edges.push(h);
let he = graph.half_edges[h];
verts.push(graph.nodes[he.origin].pos);
roads.push(he.road);
h = he.next;
if h == start {
break;
}
}
// Block boundaries may legitimately contain collinear-corner
// vertices (where two end-to-end roads share a node), so use
// the relaxed validator here. Parcel polygons remain strict.
let Ok(polygon) = Polygon::new_relaxed(verts) else {
continue;
};
out.push(Block {
face: face_id,
polygon,
boundary_edges: edges,
roads,
});
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn square_yields_one_block() {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(100.0, 0.0));
let c = g.add_node(DVec2::new(100.0, 50.0));
let d = g.add_node(DVec2::new(0.0, 50.0));
g.add_road(a, b).unwrap();
g.add_road(b, c).unwrap();
g.add_road(c, d).unwrap();
g.add_road(d, a).unwrap();
g.rebuild_topology().unwrap();
let blocks = extract_blocks(&g);
assert_eq!(blocks.len(), 1);
assert_eq!(blocks[0].polygon.len(), 4);
assert!((blocks[0].polygon.area() - 100.0 * 50.0).abs() < 1e-6);
}
}

View File

@ -0,0 +1,395 @@
//! Half-edge / DCEL graph (spec §2.3, D2).
//!
//! Construction is two-phase:
//!
//! 1. Caller adds nodes (`add_node`) and roads (`add_road`).
//! 2. The graph runs `rebuild_topology` (called automatically by
//! `subdivide_all`) which builds half-edges, sorts them at each
//! vertex by angle, links `next`/`prev`, and extracts faces.
use glam::DVec2;
use slotmap::{new_key_type, Key, SlotMap};
use crate::error::SubdivisionError;
use crate::geometry::{segments_properly_intersect, signed_area, EPS_GEOM};
new_key_type! {
/// Stable identifier for a graph node (intersection).
pub struct NodeId;
/// Stable identifier for a road segment.
pub struct RoadId;
}
new_key_type! {
pub(crate) struct HalfEdgeId;
pub(crate) struct FaceId;
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct Node {
pub pos: DVec2,
/// Outgoing half-edges sorted CCW by direction angle. Maintained
/// by `rebuild_topology`.
pub outgoing: Vec<HalfEdgeId>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct Road {
pub a: NodeId,
pub b: NodeId,
pub half_a_to_b: HalfEdgeId,
pub half_b_to_a: HalfEdgeId,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct HalfEdge {
pub origin: NodeId,
pub twin: HalfEdgeId,
pub next: HalfEdgeId,
pub prev: HalfEdgeId,
pub face: Option<FaceId>,
pub road: RoadId,
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub(crate) struct Face {
pub boundary: HalfEdgeId,
pub is_exterior: bool,
}
/// Planar road graph. Maintains a half-edge data structure with stable
/// IDs across edits.
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RoadGraph {
pub(crate) nodes: SlotMap<NodeId, Node>,
pub(crate) roads: SlotMap<RoadId, Road>,
pub(crate) half_edges: SlotMap<HalfEdgeId, HalfEdge>,
pub(crate) faces: SlotMap<FaceId, Face>,
/// True after `rebuild_topology`; cleared by any mutation.
pub(crate) topology_valid: bool,
}
impl RoadGraph {
/// Empty graph.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Insert a new node at `pos`. The graph allows two nodes within
/// `EPS_GEOM` of each other but downstream subdivision will reject
/// them via the `near_duplicate_nodes` test in §5.
pub fn add_node(&mut self, pos: DVec2) -> NodeId {
self.topology_valid = false;
self.nodes.insert(Node {
pos,
outgoing: Vec::new(),
})
}
/// Position of a node, or `None` if the ID has been deleted.
#[must_use]
pub fn node_position(&self, id: NodeId) -> Option<DVec2> {
self.nodes.get(id).map(|n| n.pos)
}
/// Insert a road between two existing nodes.
///
/// # Errors
///
/// Returns `InvalidParams` if either node is unknown, the two
/// nodes are the same, or a duplicate road already exists.
pub fn add_road(&mut self, a: NodeId, b: NodeId) -> Result<RoadId, SubdivisionError> {
if a == b {
return Err(SubdivisionError::InvalidParams(
"road endpoints must differ".into(),
));
}
if !self.nodes.contains_key(a) || !self.nodes.contains_key(b) {
return Err(SubdivisionError::InvalidParams("unknown node id".into()));
}
if self.find_road_between(a, b).is_some() {
return Err(SubdivisionError::InvalidParams(
"duplicate road between nodes".into(),
));
}
self.topology_valid = false;
Ok(self.roads.insert(Road {
a,
b,
half_a_to_b: HalfEdgeId::null(),
half_b_to_a: HalfEdgeId::null(),
}))
}
/// Iterate over `(RoadId, NodeId, NodeId)` triples — useful for
/// rendering and for tests.
pub fn road_endpoints(&self) -> impl Iterator<Item = (RoadId, NodeId, NodeId)> + '_ {
self.roads.iter().map(|(rid, r)| (rid, r.a, r.b))
}
/// Number of roads currently in the graph.
#[must_use]
pub fn road_count(&self) -> usize {
self.roads.len()
}
/// Number of nodes currently in the graph.
#[must_use]
pub fn node_count(&self) -> usize {
self.nodes.len()
}
/// Find the road between `a` and `b`, in either direction.
pub(crate) fn find_road_between(&self, a: NodeId, b: NodeId) -> Option<RoadId> {
self.roads
.iter()
.find(|(_, r)| (r.a == a && r.b == b) || (r.a == b && r.b == a))
.map(|(rid, _)| rid)
}
/// Endpoints of a road, in storage order.
pub(crate) fn road_nodes(&self, rid: RoadId) -> Option<(NodeId, NodeId)> {
self.roads.get(rid).map(|r| (r.a, r.b))
}
/// Build twin/next/prev pointers and extract faces. Idempotent.
///
/// # Errors
///
/// `NonPlanarGraph` if two non-incident roads cross. `OpenBlock`
/// if face traversal fails.
pub fn rebuild_topology(&mut self) -> Result<(), SubdivisionError> {
if self.topology_valid {
return Ok(());
}
self.check_planarity()?;
self.build_half_edges();
self.sort_outgoing_by_angle();
self.link_next_and_prev();
self.extract_faces()?;
self.topology_valid = true;
Ok(())
}
fn check_planarity(&self) -> Result<(), SubdivisionError> {
let segs: Vec<(RoadId, DVec2, DVec2)> = self
.roads
.iter()
.map(|(rid, r)| (rid, self.nodes[r.a].pos, self.nodes[r.b].pos))
.collect();
let road_endpts: Vec<(NodeId, NodeId)> = self.roads.values().map(|r| (r.a, r.b)).collect();
for (i, &(_, p1, p2)) in segs.iter().enumerate() {
for j in (i + 1)..segs.len() {
let (_, p3, p4) = segs[j];
let (a1, b1) = road_endpts[i];
let (a2, b2) = road_endpts[j];
// skip pairs that share a node — they meet legally
if a1 == a2 || a1 == b2 || b1 == a2 || b1 == b2 {
continue;
}
if segments_properly_intersect(p1, p2, p3, p4) {
// Pick the closer of (a2, b2) for the error
// message; either is informative.
return Err(SubdivisionError::NonPlanarGraph(a2));
}
}
}
Ok(())
}
fn build_half_edges(&mut self) {
// Clear prior topology.
self.half_edges.clear();
self.faces.clear();
for (_, n) in &mut self.nodes {
n.outgoing.clear();
}
let road_keys: Vec<RoadId> = self.roads.keys().collect();
for rid in road_keys {
let (a, b) = {
let r = &self.roads[rid];
(r.a, r.b)
};
let h_ab = self.half_edges.insert(HalfEdge {
origin: a,
twin: HalfEdgeId::null(),
next: HalfEdgeId::null(),
prev: HalfEdgeId::null(),
face: None,
road: rid,
});
let h_ba = self.half_edges.insert(HalfEdge {
origin: b,
twin: h_ab,
next: HalfEdgeId::null(),
prev: HalfEdgeId::null(),
face: None,
road: rid,
});
self.half_edges[h_ab].twin = h_ba;
self.nodes[a].outgoing.push(h_ab);
self.nodes[b].outgoing.push(h_ba);
let r = &mut self.roads[rid];
r.half_a_to_b = h_ab;
r.half_b_to_a = h_ba;
}
}
fn sort_outgoing_by_angle(&mut self) {
let node_keys: Vec<NodeId> = self.nodes.keys().collect();
for nid in node_keys {
let pos = self.nodes[nid].pos;
let outgoing: Vec<HalfEdgeId> = self.nodes[nid].outgoing.clone();
let mut with_angles: Vec<(f64, HalfEdgeId)> = Vec::with_capacity(outgoing.len());
for h in outgoing {
let twin = self.half_edges[h].twin;
let target = self.half_edges[twin].origin;
let dir = self.nodes[target].pos - pos;
let angle = dir.y.atan2(dir.x);
with_angles.push((angle, h));
}
with_angles.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
self.nodes[nid].outgoing = with_angles.into_iter().map(|(_, h)| h).collect();
}
}
fn link_next_and_prev(&mut self) {
let edge_keys: Vec<HalfEdgeId> = self.half_edges.keys().collect();
for h in edge_keys {
// For a CCW interior face, h's `next` is the half-edge
// making the smallest left turn at h.target. Concretely:
// at v = h.target, scan v's CCW-sorted outgoing list and
// take the immediate predecessor (CW neighbor) of
// h.twin. This is the standard DCEL rule.
let twin = self.half_edges[h].twin;
let v = self.half_edges[twin].origin;
let v_outgoing = &self.nodes[v].outgoing;
let pos_in_v = v_outgoing
.iter()
.position(|&x| x == twin)
.expect("twin must appear in target's outgoing list");
let next = v_outgoing[(pos_in_v + v_outgoing.len() - 1) % v_outgoing.len()];
self.half_edges[h].next = next;
// h.prev = the half-edge whose `.next` is h. That is the
// CCW *successor* of h in h.origin's outgoing list,
// taken twin-of.
let u = self.half_edges[h].origin;
let u_outgoing = &self.nodes[u].outgoing;
let pos_in_u = u_outgoing
.iter()
.position(|&x| x == h)
.expect("h must appear in origin's outgoing list");
let succ = u_outgoing[(pos_in_u + 1) % u_outgoing.len()];
let prev = self.half_edges[succ].twin;
self.half_edges[h].prev = prev;
}
}
fn extract_faces(&mut self) -> Result<(), SubdivisionError> {
let edge_keys: Vec<HalfEdgeId> = self.half_edges.keys().collect();
for h_start in edge_keys {
if self.half_edges[h_start].face.is_some() {
continue;
}
// Walk the cycle, capturing vertex positions for area sign.
let mut cycle = Vec::new();
let mut h = h_start;
let max_iter = self.half_edges.len() + 4;
let mut iters = 0;
loop {
cycle.push(h);
let next = self.half_edges[h].next;
if next.is_null() {
return Err(SubdivisionError::OpenBlock);
}
h = next;
iters += 1;
if iters > max_iter {
return Err(SubdivisionError::OpenBlock);
}
if h == h_start {
break;
}
}
let pts: Vec<DVec2> = cycle
.iter()
.map(|&hid| self.nodes[self.half_edges[hid].origin].pos)
.collect();
let signed = signed_area(&pts);
let is_exterior = signed < 0.0 || signed.abs() < EPS_GEOM;
let face_id = self.faces.insert(Face {
boundary: h_start,
is_exterior,
});
for h in cycle {
self.half_edges[h].face = Some(face_id);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn square_graph() -> RoadGraph {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(1.0, 0.0));
let c = g.add_node(DVec2::new(1.0, 1.0));
let d = g.add_node(DVec2::new(0.0, 1.0));
g.add_road(a, b).unwrap();
g.add_road(b, c).unwrap();
g.add_road(c, d).unwrap();
g.add_road(d, a).unwrap();
g
}
#[test]
fn square_yields_two_faces() {
let mut g = square_graph();
g.rebuild_topology().unwrap();
assert_eq!(g.faces.len(), 2);
let exterior = g.faces.values().filter(|f| f.is_exterior).count();
assert_eq!(exterior, 1);
}
#[test]
fn duplicate_road_rejected() {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(1.0, 0.0));
g.add_road(a, b).unwrap();
assert!(g.add_road(a, b).is_err());
assert!(g.add_road(b, a).is_err());
}
#[test]
fn self_loop_rejected() {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
assert!(g.add_road(a, a).is_err());
}
#[test]
fn non_planar_graph_detected() {
// Two crossing roads with no node at the crossing.
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(2.0, 2.0));
let c = g.add_node(DVec2::new(0.0, 2.0));
let d = g.add_node(DVec2::new(2.0, 0.0));
g.add_road(a, b).unwrap();
g.add_road(c, d).unwrap();
let err = g.rebuild_topology().unwrap_err();
assert!(matches!(err, SubdivisionError::NonPlanarGraph(_)));
}
}

View File

@ -0,0 +1,14 @@
//! Planar road graph in half-edge / DCEL form (spec §2.3).
//!
//! Nodes are intersections; roads are straight edges between two
//! nodes. Each road is represented internally by two opposing
//! half-edges. Blocks are the bounded faces of this graph.
//!
//! Stable IDs are minted by `slotmap` so that the same node, road, or
//! parcel survives unrelated insertions and deletions (D3).
pub mod blocks;
pub mod graph;
pub use blocks::Block;
pub use graph::{NodeId, RoadGraph, RoadId};

View File

@ -0,0 +1,61 @@
//! Edge classification (spec §3.2).
//!
//! After the parcel polygon is built and its frontage edge index
//! recorded, this pass labels every edge as `Frontage`, `Side`, or
//! `Back`. Side edges are the two adjacent to the frontage edge in
//! ring order; back edges are everything else.
use super::EdgeKind;
/// Build an edge-kind vector parallel to a polygon's edge ring.
#[must_use]
pub(crate) fn classify_edges(num_edges: usize, frontage_idx: usize) -> Vec<EdgeKind> {
let mut kinds = vec![EdgeKind::Back; num_edges];
if num_edges == 0 {
return kinds;
}
kinds[frontage_idx] = EdgeKind::Frontage;
let prev = (frontage_idx + num_edges - 1) % num_edges;
let next = (frontage_idx + 1) % num_edges;
if prev != frontage_idx {
kinds[prev] = EdgeKind::Side;
}
if next != frontage_idx && next != prev {
kinds[next] = EdgeKind::Side;
}
kinds
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn quadrilateral_classification() {
let k = classify_edges(4, 0);
assert_eq!(
k,
vec![
EdgeKind::Frontage,
EdgeKind::Side,
EdgeKind::Back,
EdgeKind::Side,
]
);
}
#[test]
fn pentagonal_classification() {
let k = classify_edges(5, 2);
assert_eq!(
k,
vec![
EdgeKind::Back,
EdgeKind::Side,
EdgeKind::Frontage,
EdgeKind::Side,
EdgeKind::Back,
]
);
}
}

View File

@ -0,0 +1,506 @@
//! Road-edit deformation pipeline (spec §4, milestone 0.2).
//!
//! `apply_road_edit` is the public entry point. The implementation
//! tries to *preserve* parcels when possible (re-projecting only the
//! frontage vertices onto the new road geometry; side and back
//! vertices stay fixed in absolute world coordinates per **D13**)
//! and falls back to regenerating the affected blocks when
//! preservation would violate any of:
//!
//! - I1 (resulting polygon is invalid),
//! - the rotation threshold of \cref{sec:edit-handling}
//! (`params.max_side_rotation`),
//! - the area or frontage minimums (`min_area`, `min_frontage`).
//!
//! Buildings attached to surviving parcels are kept iff
//! `BuildingFitCheck::fits_in` returns true post-deformation;
//! otherwise the building is evicted.
use std::collections::BTreeSet;
use glam::DVec2;
use slotmap::Key;
use crate::config::SubdivisionParams;
use crate::error::ParcelError;
use crate::geometry::{Polygon, EPS_GEOM};
use crate::network::graph::FaceId;
use crate::network::{NodeId, RoadGraph, RoadId};
use crate::parcel::subdivide::subdivide_block;
use crate::parcel::ParcelId;
use super::{Parcel, ParcelSet};
/// A road-network mutation that may rearrange surrounding parcels.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum RoadEdit {
/// Move the position of an existing intersection.
MoveNode {
/// Target node.
node: NodeId,
/// New position, meters.
to: DVec2,
},
/// Split a road segment by inserting a new node along it.
SplitSegment {
/// Road being split.
road: RoadId,
/// Position of the new node (must lie on the segment).
at: DVec2,
},
/// Delete a road segment.
DeleteSegment {
/// Road being deleted.
road: RoadId,
},
/// Insert a new road between two existing nodes.
InsertSegment {
/// Endpoint A.
from: NodeId,
/// Endpoint B.
to: NodeId,
},
}
/// Outcome buckets for an edit application (spec §4.2).
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct EditOutcome {
/// Parcels that survived deformation.
pub deformed: Vec<ParcelId>,
/// Parcels whose block was re-subdivided.
pub regenerated: Vec<ParcelId>,
/// Parcels removed by the edit.
pub condemned: Vec<ParcelId>,
/// Parcels newly created during regeneration.
pub created: Vec<ParcelId>,
/// Parcels whose building was evicted because it no longer fits.
pub evicted_buildings: Vec<ParcelId>,
}
/// Apply `edit` to `graph` and update `parcels` to match the new
/// geometry.
///
/// # Errors
///
/// Returns [`ParcelError::UnknownEntity`] if the edit references an
/// id that is no longer present, or [`ParcelError::Resubdivide`] if
/// re-subdivision of an affected block fails.
pub fn apply_road_edit(
parcels: &mut ParcelSet,
graph: &mut RoadGraph,
edit: RoadEdit,
params: &SubdivisionParams,
) -> Result<EditOutcome, ParcelError> {
params.validate()?;
let graph_before = graph.clone();
apply_topology_mutation(graph, edit)?;
graph.rebuild_topology()?;
let mut outcome = EditOutcome::default();
match edit {
RoadEdit::MoveNode { node, .. } => {
move_node_path(parcels, &graph_before, graph, node, params, &mut outcome)?;
}
RoadEdit::DeleteSegment { road } => {
delete_segment_path(parcels, road, graph, params, &mut outcome)?;
}
RoadEdit::SplitSegment { .. } | RoadEdit::InsertSegment { .. } => {
// Split/Insert preservation is milestone-0.3 work — fall
// back to a regenerate of every affected face.
let affected = roads_affected_by_old_id(&graph_before, edit);
regenerate_path(parcels, graph, params, &affected, &mut outcome)?;
}
}
Ok(outcome)
}
// -----------------------------------------------------------------
// Move-node preserve path
// -----------------------------------------------------------------
fn move_node_path(
parcels: &mut ParcelSet,
graph_before: &RoadGraph,
graph_after: &RoadGraph,
node: NodeId,
params: &SubdivisionParams,
outcome: &mut EditOutcome,
) -> Result<(), ParcelError> {
// Collect roads incident to the moved node (those still in the
// graph after the edit).
let incident_roads: Vec<RoadId> = graph_after
.road_endpoints()
.filter_map(|(rid, a, b)| {
if a == node || b == node {
Some(rid)
} else {
None
}
})
.collect();
// For each incident road, walk every parcel on it and try to
// deform.
let mut to_regenerate: BTreeSet<FaceId> = BTreeSet::new();
for rid in &incident_roads {
let parcel_ids: Vec<ParcelId> = parcels.parcels_on_road(*rid).collect::<Vec<_>>();
for pid in parcel_ids {
let result = match parcels.parcels.get_mut(pid) {
Some(parcel) => {
deform_parcel_after_road_move(parcel, *rid, graph_before, graph_after, params)
}
None => continue,
};
match result {
DeformResult::Deformed { evicted_building } => {
outcome.deformed.push(pid);
if evicted_building {
outcome.evicted_buildings.push(pid);
}
}
DeformResult::Condemned => {
let block = parcels.parcels.get(pid).map(|p| p.block);
drop_parcel(parcels, pid);
outcome.condemned.push(pid);
if let Some(face) = block {
to_regenerate.insert(face);
}
}
DeformResult::Regenerate => {
if let Some(p) = parcels.parcels.get(pid) {
to_regenerate.insert(p.block);
}
}
}
}
}
// Regenerate the blocks where any parcel asked for it.
if !to_regenerate.is_empty() {
// Collect all parcels in those blocks; mark them as
// regenerated and drop them, then re-subdivide.
let block_set = to_regenerate.clone();
let pids_in_blocks: Vec<ParcelId> = parcels
.parcels
.iter()
.filter(|(_, p)| block_set.contains(&p.block))
.map(|(id, _)| id)
.collect();
for pid in &pids_in_blocks {
outcome.regenerated.push(*pid);
}
for pid in pids_in_blocks {
drop_parcel(parcels, pid);
}
// Re-subdivide each block.
let blocks = crate::network::blocks::extract_blocks(graph_after);
for (block_idx, block) in blocks.iter().enumerate() {
if !to_regenerate.contains(&block.face_id()) {
continue;
}
let new_parcels = subdivide_block(graph_after, block, params, block_idx)?;
for parcel in new_parcels {
let pid = parcels.insert(parcel);
outcome.created.push(pid);
}
}
}
Ok(())
}
#[derive(Debug, Clone, Copy)]
enum DeformResult {
Deformed { evicted_building: bool },
Condemned,
Regenerate,
}
/// Attempt to deform `parcel` after the road geometry changed.
/// Re-projects the frontage edge endpoints onto the new road
/// segment; side and back vertices stay fixed (D13).
fn deform_parcel_after_road_move(
parcel: &mut Parcel,
road: RoadId,
graph_before: &RoadGraph,
graph_after: &RoadGraph,
params: &SubdivisionParams,
) -> DeformResult {
let Some((a, b)) = graph_after.road_nodes(road) else {
return DeformResult::Condemned;
};
let (Some(pa_before), Some(pb_before), Some(pa_after), Some(pb_after)) = (
graph_before.node_position(a),
graph_before.node_position(b),
graph_after.node_position(a),
graph_after.node_position(b),
) else {
return DeformResult::Condemned;
};
let road_vec_before = pb_before - pa_before;
let len_sq_before = road_vec_before.length_squared();
if len_sq_before < EPS_GEOM * EPS_GEOM {
return DeformResult::Regenerate;
}
let road_vec_after = pb_after - pa_after;
let n = parcel.polygon.len();
let fi = parcel.frontage_edge_index;
let mut new_verts: Vec<DVec2> = parcel.polygon.vertices().to_vec();
let p_a = new_verts[fi];
let p_b = new_verts[(fi + 1) % n];
let t_a = (p_a - pa_before).dot(road_vec_before) / len_sq_before;
let t_b = (p_b - pa_before).dot(road_vec_before) / len_sq_before;
new_verts[fi] = pa_after + road_vec_after * t_a;
new_verts[(fi + 1) % n] = pa_after + road_vec_after * t_b;
// Side rotation check before constructing the polygon (so we can
// bail to Regenerate cheaply).
let prev_idx = (fi + n - 1) % n;
let next_idx = (fi + 2) % n;
let old_side_in =
(parcel.polygon.vertices()[fi] - parcel.polygon.vertices()[prev_idx]).normalize_or_zero();
let new_side_in = (new_verts[fi] - new_verts[prev_idx]).normalize_or_zero();
let old_side_out = (parcel.polygon.vertices()[next_idx]
- parcel.polygon.vertices()[(fi + 1) % n])
.normalize_or_zero();
let new_side_out = (new_verts[next_idx] - new_verts[(fi + 1) % n]).normalize_or_zero();
let rot_in = old_side_in.dot(new_side_in).clamp(-1.0, 1.0).acos();
let rot_out = old_side_out.dot(new_side_out).clamp(-1.0, 1.0).acos();
if rot_in > params.max_side_rotation || rot_out > params.max_side_rotation {
return DeformResult::Regenerate;
}
let Ok(new_poly) = Polygon::new(new_verts) else {
return DeformResult::Regenerate;
};
if new_poly.area() < params.min_area {
return DeformResult::Condemned;
}
let v = new_poly.vertices();
// Find the new frontage edge index — Polygon::new may have
// reordered vertices if it had to flip orientation. We locate the
// edge whose midpoint lies on the new road segment.
let new_fi = match find_frontage_index(&new_poly, pa_after, pb_after) {
Some(idx) => idx,
None => return DeformResult::Regenerate,
};
let new_frontage = (v[(new_fi + 1) % v.len()] - v[new_fi]).length();
if new_frontage < params.min_frontage {
return DeformResult::Condemned;
}
// Commit.
parcel.polygon = new_poly;
parcel.frontage_edge_index = new_fi;
// BuildingFitCheck eviction.
let mut evicted = false;
if parcel.building.is_some() {
let fits = parcel.building.as_ref().is_some_and(|b| b.fits_in(parcel));
if !fits {
parcel.building = None;
evicted = true;
}
}
DeformResult::Deformed {
evicted_building: evicted,
}
}
fn find_frontage_index(polygon: &Polygon, road_a: DVec2, road_b: DVec2) -> Option<usize> {
let road_dir = (road_b - road_a).normalize_or_zero();
if road_dir.length_squared() < EPS_GEOM * EPS_GEOM {
return None;
}
let road_normal = DVec2::new(-road_dir.y, road_dir.x);
let v = polygon.vertices();
let n = v.len();
let tol = 100.0 * EPS_GEOM;
let mut best: Option<(usize, f64)> = None;
for i in 0..n {
let p = v[i];
let q = v[(i + 1) % n];
let dp = (p - road_a).dot(road_normal).abs();
let dq = (q - road_a).dot(road_normal).abs();
if dp < tol && dq < tol {
let edge_dir = (q - p).normalize_or_zero();
if edge_dir.dot(road_dir).abs() > 0.5 {
let len = (q - p).length();
if best.is_none_or(|(_, blen)| len > blen) {
best = Some((i, len));
}
}
}
}
best.map(|(i, _)| i)
}
// -----------------------------------------------------------------
// Delete-segment path: condemn + regenerate-block
// -----------------------------------------------------------------
fn delete_segment_path(
parcels: &mut ParcelSet,
road: RoadId,
graph_after: &RoadGraph,
params: &SubdivisionParams,
outcome: &mut EditOutcome,
) -> Result<(), ParcelError> {
// Collect parcels with frontage on the deleted road; condemn
// them. The block topology likely changed; regenerate every
// block touched by any condemned parcel.
let pids: Vec<ParcelId> = parcels.parcels_on_road(road).collect::<Vec<_>>();
let mut affected_blocks: BTreeSet<FaceId> = BTreeSet::new();
for pid in pids {
if let Some(p) = parcels.parcels.get(pid) {
affected_blocks.insert(p.block);
}
outcome.condemned.push(pid);
drop_parcel(parcels, pid);
}
// Regenerate.
let blocks = crate::network::blocks::extract_blocks(graph_after);
for (block_idx, block) in blocks.iter().enumerate() {
// After deletion, the face IDs may be different — regenerate
// anything that was previously affected. The simplest
// correct rule: regenerate any face that currently has no
// parcels.
let face = block.face_id();
if parcels.by_block.get(&face).is_some_and(|v| !v.is_empty()) {
continue;
}
let new_parcels = subdivide_block(graph_after, block, params, block_idx)?;
for parcel in new_parcels {
let pid = parcels.insert(parcel);
outcome.created.push(pid);
}
let _ = affected_blocks;
}
Ok(())
}
// -----------------------------------------------------------------
// Regenerate-only fallback (Split, Insert)
// -----------------------------------------------------------------
fn regenerate_path(
parcels: &mut ParcelSet,
graph_after: &RoadGraph,
params: &SubdivisionParams,
affected_roads: &[RoadId],
outcome: &mut EditOutcome,
) -> Result<(), ParcelError> {
let mut to_drop: Vec<ParcelId> = Vec::new();
for rid in affected_roads {
if let Some(ids) = parcels.by_road.get(rid) {
to_drop.extend(ids.iter().copied());
}
}
to_drop.sort();
to_drop.dedup();
for pid in &to_drop {
outcome.condemned.push(*pid);
}
for pid in to_drop {
drop_parcel(parcels, pid);
}
let blocks = crate::network::blocks::extract_blocks(graph_after);
for (block_idx, block) in blocks.iter().enumerate() {
let face = block.face_id();
if parcels.by_block.get(&face).is_some_and(|v| !v.is_empty()) {
continue;
}
let new_parcels = subdivide_block(graph_after, block, params, block_idx)?;
for parcel in new_parcels {
let pid = parcels.insert(parcel);
outcome.created.push(pid);
}
}
Ok(())
}
fn roads_affected_by_old_id(graph: &RoadGraph, edit: RoadEdit) -> Vec<RoadId> {
let mut roads = Vec::new();
match edit {
RoadEdit::MoveNode { node, .. } => {
for (rid, a, b) in graph.road_endpoints() {
if a == node || b == node {
roads.push(rid);
}
}
}
RoadEdit::SplitSegment { road, .. } | RoadEdit::DeleteSegment { road } => {
roads.push(road);
}
RoadEdit::InsertSegment { from, to } => {
for (rid, a, b) in graph.road_endpoints() {
if a == from || b == from || a == to || b == to {
roads.push(rid);
}
}
}
}
roads
}
// -----------------------------------------------------------------
// Mutation helpers + parcel-set bookkeeping
// -----------------------------------------------------------------
fn apply_topology_mutation(graph: &mut RoadGraph, edit: RoadEdit) -> Result<(), ParcelError> {
match edit {
RoadEdit::MoveNode { node, to } => {
let n = graph
.nodes
.get_mut(node)
.ok_or(ParcelError::UnknownEntity)?;
n.pos = to;
graph.topology_valid = false;
Ok(())
}
RoadEdit::DeleteSegment { road } => {
graph.roads.remove(road).ok_or(ParcelError::UnknownEntity)?;
graph.topology_valid = false;
Ok(())
}
RoadEdit::SplitSegment { road, at } => {
let (a, b) = graph.road_nodes(road).ok_or(ParcelError::UnknownEntity)?;
graph.roads.remove(road);
let m = graph.add_node(at);
graph
.add_road(a, m)
.map_err(|_| ParcelError::InconsistentEdit("split a->m".into()))?;
graph
.add_road(m, b)
.map_err(|_| ParcelError::InconsistentEdit("split m->b".into()))?;
graph.topology_valid = false;
Ok(())
}
RoadEdit::InsertSegment { from, to } => {
graph
.add_road(from, to)
.map_err(|_| ParcelError::InconsistentEdit("insert".into()))?;
graph.topology_valid = false;
Ok(())
}
}
}
fn drop_parcel(parcels: &mut ParcelSet, pid: ParcelId) {
if let Some(p) = parcels.parcels.remove(pid) {
if let Some(v) = parcels.by_block.get_mut(&p.block) {
v.retain(|&x| x != pid);
}
if let Some(v) = parcels.by_road.get_mut(&p.frontage_road) {
v.retain(|&x| x != pid);
}
}
}
#[allow(dead_code)]
fn _unused_face_id() -> FaceId {
FaceId::null()
}

View File

@ -0,0 +1,213 @@
//! Parcel data model and subdivision/edit entry points (spec §3, §4).
//!
//! A [`Parcel`] is a simple polygon with exactly one frontage edge
//! coincident with a road segment (invariant **I2**). Parcels are
//! grouped by their containing block (face), and the whole crate's
//! public output is a [`ParcelSet`].
//!
//! Subdivision and edit operations are exposed via the free
//! functions [`subdivide_all`] and [`apply_road_edit`].
pub mod classify;
pub mod deform;
pub mod regularize;
pub mod subdivide;
use std::collections::HashMap;
use std::fmt;
use glam::DVec2;
use slotmap::{new_key_type, SlotMap};
use crate::geometry::Polygon;
use crate::network::graph::FaceId;
use crate::network::RoadId;
pub use deform::{apply_road_edit, EditOutcome, RoadEdit};
pub use subdivide::{subdivide_all, subdivide_all_with_stats, SubdivisionStats};
new_key_type! {
/// Stable identifier for a [`Parcel`].
pub struct ParcelId;
}
/// Kind of a parcel boundary edge (spec §3.2).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EdgeKind {
/// Edge lies coincident with a road segment.
Frontage,
/// Edge is adjacent to the frontage edge in the parcel ring.
Side,
/// All other edges.
Back,
}
/// Trait that downstream code implements for buildings to participate
/// in deform-time fit checks (spec §4.6).
pub trait BuildingFitCheck: 'static {
/// True iff the building still fits inside `parcel`.
fn fits_in(&self, parcel: &Parcel) -> bool;
}
/// Owning wrapper around a `BuildingFitCheck`. Stored on parcels via
/// `Option<BuildingHandle>`.
///
/// Note: the spec describes `BuildingHandle` as "opaque". This crate
/// implements it as a heap-allocated trait object so that the
/// deformation pipeline can call `fits_in` without taking a callback
/// from the user. Recorded as revision §11 (R1).
pub struct BuildingHandle {
inner: Box<dyn BuildingFitCheck>,
}
impl BuildingHandle {
/// Wrap a building implementation.
#[must_use]
pub fn new<B: BuildingFitCheck>(b: B) -> Self {
Self { inner: Box::new(b) }
}
/// Forward to the inner [`BuildingFitCheck::fits_in`].
#[must_use]
pub fn fits_in(&self, parcel: &Parcel) -> bool {
self.inner.fits_in(parcel)
}
}
impl fmt::Debug for BuildingHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BuildingHandle").finish_non_exhaustive()
}
}
/// A single parcel.
#[derive(Debug)]
pub struct Parcel {
pub(crate) polygon: Polygon,
pub(crate) edge_kinds: Vec<EdgeKind>,
pub(crate) frontage_road: RoadId,
pub(crate) frontage_edge_index: usize,
pub(crate) block: FaceId,
pub(crate) building: Option<BuildingHandle>,
}
impl Parcel {
/// Vertex ring of the parcel polygon (CCW, not closed).
#[must_use]
pub fn vertices(&self) -> &[DVec2] {
self.polygon.vertices()
}
/// Polygon view.
#[must_use]
pub fn polygon(&self) -> &Polygon {
&self.polygon
}
/// Edge classification, parallel to `vertices()`.
#[must_use]
pub fn edge_kinds(&self) -> &[EdgeKind] {
&self.edge_kinds
}
/// The single road this parcel faces (invariant **I2**).
#[must_use]
pub fn frontage_road(&self) -> RoadId {
self.frontage_road
}
/// Endpoints of the frontage edge.
#[must_use]
pub fn frontage_edge(&self) -> (DVec2, DVec2) {
let v = self.polygon.vertices();
let i = self.frontage_edge_index;
(v[i], v[(i + 1) % v.len()])
}
/// Frontage length, meters.
#[must_use]
pub fn frontage_length(&self) -> f64 {
let (a, b) = self.frontage_edge();
(b - a).length()
}
/// Parcel area, m².
#[must_use]
pub fn area(&self) -> f64 {
self.polygon.area()
}
/// Attach a building. Returns the previously-attached building, if
/// any.
pub fn attach_building(&mut self, b: BuildingHandle) -> Option<BuildingHandle> {
self.building.replace(b)
}
/// Detach (and return) the currently attached building, if any.
pub fn detach_building(&mut self) -> Option<BuildingHandle> {
self.building.take()
}
/// Whether a building is currently attached.
#[must_use]
pub fn has_building(&self) -> bool {
self.building.is_some()
}
}
/// The output of `subdivide_all`: a stable-ID indexed collection of
/// parcels, organized by their containing block.
#[derive(Debug, Default)]
pub struct ParcelSet {
pub(crate) parcels: SlotMap<ParcelId, Parcel>,
pub(crate) by_block: HashMap<FaceId, Vec<ParcelId>>,
pub(crate) by_road: HashMap<RoadId, Vec<ParcelId>>,
}
impl ParcelSet {
/// Number of parcels.
#[must_use]
pub fn len(&self) -> usize {
self.parcels.len()
}
/// True if there are no parcels.
#[must_use]
pub fn is_empty(&self) -> bool {
self.parcels.is_empty()
}
/// Iterate over `(ParcelId, &Parcel)` pairs.
pub fn iter(&self) -> impl Iterator<Item = (ParcelId, &Parcel)> {
self.parcels.iter()
}
/// Look up a parcel by id.
#[must_use]
pub fn get(&self, id: ParcelId) -> Option<&Parcel> {
self.parcels.get(id)
}
/// Look up a parcel mutably by id.
pub fn get_mut(&mut self, id: ParcelId) -> Option<&mut Parcel> {
self.parcels.get_mut(id)
}
/// Iterate over the parcel ids within a single road's frontage.
pub fn parcels_on_road(&self, road: RoadId) -> impl Iterator<Item = ParcelId> + '_ {
self.by_road
.get(&road)
.into_iter()
.flat_map(|v| v.iter().copied())
}
pub(crate) fn insert(&mut self, parcel: Parcel) -> ParcelId {
let block = parcel.block;
let road = parcel.frontage_road;
let id = self.parcels.insert(parcel);
self.by_block.entry(block).or_default().push(id);
self.by_road.entry(road).or_default().push(id);
id
}
}

View File

@ -0,0 +1,20 @@
//! OBB-snapping regularization pass (spec §3.3).
//!
//! When `params.regularity > 0`, parcel side-edge vertices are
//! linearly interpolated toward their oriented-bounding-box-snapped
//! positions with weight `ρ`. At ρ=1 parcels become perfect
//! road-aligned rectangles; at ρ=0 the raw subdivision is preserved.
//!
//! Status: **stub** for milestone 0.1. The default
//! `SubdivisionParams::regularity = 0.0` makes this a no-op for now.
//! Recorded as revision §11 (R3).
use crate::config::SubdivisionParams;
use crate::parcel::Parcel;
/// Run the regularization pass over a single parcel in place. No-op
/// while `params.regularity == 0.0` (the default).
pub(crate) fn regularize_parcel(_parcel: &mut Parcel, _params: &SubdivisionParams) {
// TODO(milestone-0.2): OBB-snap side vertices with weight ρ;
// validate and revert on failure.
}

View File

@ -0,0 +1,744 @@
//! Frontage-first block subdivision (spec §3.1, milestone 0.2).
//!
//! For each block face we:
//!
//! 1. Identify "real" corners — block-boundary vertices whose
//! underlying graph node has degree ≥3, or degree 2 with a bend
//! angle below 150°. Smooth continuations (collinear-roads case)
//! are not real corners.
//! 2. At each real corner, build a corner parcel: a parallelogram
//! extending `frontage_width` along each adjacent block edge and
//! closing diagonally inward. The longer adjacent edge becomes the
//! parcel's frontage; the other road-adjacent edge is reclassified
//! as `Side`. (See §11 D9D11.)
//! 3. For each block boundary edge, walk the *remaining* frontage
//! (between the corner footprints, if any) at arc-length intervals
//! of approximately `params.frontage_width`, extruding perpendicular
//! to the block interior. No more bisector clipping — the corners
//! take care of corner geometry up front.
//!
//! Determinism (invariant **I6**) is preserved: random jitter is
//! drawn from a `ChaCha8Rng` whose seed is mixed from `params.seed`
//! and the road-segment id; corner parcels are pure functions of
//! geometry.
use std::time::{Duration, Instant};
use glam::DVec2;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;
use slotmap::Key;
use crate::config::SubdivisionParams;
use crate::error::SubdivisionError;
use crate::geometry::{Polygon, EPS_GEOM};
use crate::network::blocks::{extract_blocks, Block};
use crate::network::graph::FaceId;
use crate::network::{NodeId, RoadGraph, RoadId};
use super::classify::classify_edges;
use super::regularize::regularize_parcel;
use super::{Parcel, ParcelSet};
/// Per-phase timing collected by [`subdivide_all_with_stats`].
///
/// Phases are non-overlapping wall-clock spans. `total` is measured
/// independently and may be slightly larger than the sum of the
/// phases due to overhead between phases.
#[derive(Debug, Clone, Default)]
pub struct SubdivisionStats {
/// Number of bounded faces (blocks) extracted from the graph.
pub block_count: usize,
/// Total parcels produced across all blocks.
pub parcel_count: usize,
/// Time to (re)build the half-edge / DCEL topology if needed.
pub topology_rebuild: Duration,
/// Time to extract bounded faces into [`Block`] objects.
pub block_extraction: Duration,
/// Cumulative time spent in [`subdivide_block`] across all blocks.
pub block_subdivide_total: Duration,
/// Wall-clock total of [`subdivide_all_with_stats`].
pub total: Duration,
}
impl SubdivisionStats {
/// Average parcels generated per second over the whole call.
#[must_use]
pub fn parcels_per_second(&self) -> f64 {
let secs = self.total.as_secs_f64();
if secs > 0.0 {
self.parcel_count as f64 / secs
} else {
0.0
}
}
/// Average wall-clock time per parcel, in microseconds.
#[must_use]
pub fn time_per_parcel_us(&self) -> f64 {
if self.parcel_count > 0 {
(self.total.as_nanos() as f64 / self.parcel_count as f64) / 1_000.0
} else {
0.0
}
}
}
/// Subdivide every bounded face of `graph` into parcels.
///
/// # Errors
///
/// Returns a [`SubdivisionError`] if `params` is invalid, the graph
/// is non-planar, or an internal geometric failure occurs.
pub fn subdivide_all(
graph: &RoadGraph,
params: &SubdivisionParams,
) -> Result<ParcelSet, SubdivisionError> {
subdivide_all_with_stats(graph, params).map(|(p, _)| p)
}
/// Subdivide every bounded face and return per-phase performance
/// statistics alongside the parcel set.
///
/// # Errors
///
/// Same as [`subdivide_all`].
pub fn subdivide_all_with_stats(
graph: &RoadGraph,
params: &SubdivisionParams,
) -> Result<(ParcelSet, SubdivisionStats), SubdivisionError> {
params.validate()?;
let mut stats = SubdivisionStats::default();
let total_start = Instant::now();
let topo_start = Instant::now();
let mut working: RoadGraph;
let g_ref = if graph.topology_valid {
graph
} else {
working = graph.clone();
working.rebuild_topology()?;
&working
};
stats.topology_rebuild = topo_start.elapsed();
let blocks_start = Instant::now();
let blocks = extract_blocks(g_ref);
stats.block_extraction = blocks_start.elapsed();
stats.block_count = blocks.len();
let mut parcels = ParcelSet::default();
for (block_idx, block) in blocks.iter().enumerate() {
let block_start = Instant::now();
let new = subdivide_block(g_ref, block, params, block_idx)?;
stats.block_subdivide_total += block_start.elapsed();
for parcel in new {
parcels.insert(parcel);
}
}
stats.parcel_count = parcels.len();
stats.total = total_start.elapsed();
Ok((parcels, stats))
}
/// Subdivide a single block. Internal entry used both by
/// `subdivide_all` and by `apply_road_edit`'s regenerate path.
pub(crate) fn subdivide_block(
graph: &RoadGraph,
block: &Block,
params: &SubdivisionParams,
_block_idx: usize,
) -> Result<Vec<Parcel>, SubdivisionError> {
let n = block.polygon.len();
if n < 3 {
return Ok(Vec::new());
}
let verts: Vec<DVec2> = block.polygon.vertices().to_vec();
let face = block.face_id();
// Edge lengths (parallel to verts).
let edge_lens: Vec<f64> = (0..n)
.map(|i| (verts[(i + 1) % n] - verts[i]).length())
.collect();
// Real-corner classification per vertex (D11). Acute corners
// (interior angle < 60°) are skipped here — sliver-merge is
// milestone 0.3 — so parcels there fall back to whatever the
// regular walk produces (usually a malformed shape that triggers
// I1 rejection during validation, but never a panic).
let real_corner: Vec<bool> = (0..n)
.map(|i| {
if !is_real_corner(graph, block, i, params) {
return false;
}
let v_prev = block.polygon.vertices()[(i + n - 1) % n];
let v_curr = block.polygon.vertices()[i];
let v_next = block.polygon.vertices()[(i + 1) % n];
let t_in = (v_prev - v_curr).normalize_or_zero();
let t_out = (v_next - v_curr).normalize_or_zero();
let t_arrive = -t_in;
let cross = t_arrive.x * t_out.y - t_arrive.y * t_out.x;
let dot = t_arrive.dot(t_out);
let turn = cross.atan2(dot);
let interior = std::f64::consts::PI - turn;
interior > 60.0_f64.to_radians()
})
.collect();
// Per-edge depth cap (block half-depth bound).
let depth_caps = edge_depth_caps(&block.polygon);
let r_target = params.frontage_width;
// First pass: decide which adjacent road wins frontage at each
// real corner. This drives both the corner geometry below and the
// per-edge walk bounds.
let mut frontage_on_next: Vec<bool> = vec![false; n];
for i in 0..n {
if !real_corner[i] {
continue;
}
let prev_len = edge_lens[(i + n - 1) % n];
let next_len = edge_lens[i];
let prev_road = block.roads[(i + n - 1) % n];
let next_road = block.roads[i];
frontage_on_next[i] = if (next_len - prev_len).abs() < EPS_GEOM {
// tie → deterministic by RoadId
next_road.data().as_ffi() <= prev_road.data().as_ffi()
} else {
next_len > prev_len
};
}
let mut out: Vec<Parcel> = Vec::new();
// Build corner parcels.
for i in 0..n {
if !real_corner[i] {
continue;
}
let v_curr = verts[i];
let v_prev = verts[(i + n - 1) % n];
let v_next = verts[(i + 1) % n];
let prev_road = block.roads[(i + n - 1) % n];
let next_road = block.roads[i];
let t_in = (v_prev - v_curr).normalize_or_zero();
let t_out = (v_next - v_curr).normalize_or_zero();
if t_in.length_squared() < EPS_GEOM * EPS_GEOM
|| t_out.length_squared() < EPS_GEOM * EPS_GEOM
{
continue;
}
let prev_len = edge_lens[(i + n - 1) % n];
let next_len = edge_lens[i];
let r = r_target
.min(prev_len * 0.5)
.min(next_len * 0.5)
.max(params.min_frontage * 0.5);
if r < params.min_frontage * 0.5 {
continue;
}
// Inward normals.
let n_out = DVec2::new(-t_out.y, t_out.x);
let n_in = DVec2::new(t_in.y, -t_in.x);
// Two flavors of corner parcel, both with frontage = R on the
// *winning* road and side2 = depth along the other:
//
// A. frontage on NEXT road: v0 → v0+t_out·R is frontage,
// v0+t_in·depth → v0 is the side along the prev road.
// B. frontage on PREV road: v0+t_in·R → v0 is frontage,
// v0 → v0+t_out·depth is the side along the next road.
let (frontage_road, polygon, frontage_idx, consume_next, consume_prev) =
if frontage_on_next[i] {
let perp_depth = params.depth.min(prev_len * 0.5);
let v0 = v_curr;
let v1 = v_curr + t_out * r;
let v2 = v_curr + t_out * r + n_out * perp_depth;
let v3 = v_curr + t_in * perp_depth;
let Ok(p) = Polygon::new(vec![v0, v1, v2, v3]) else {
continue;
};
(next_road, p, 0_usize, r, perp_depth)
} else {
let perp_depth = params.depth.min(next_len * 0.5);
let v0 = v_curr;
let v1 = v_curr + t_out * perp_depth;
let v2 = v_curr + t_out * perp_depth + n_out * r;
let v3 = v_curr + t_in * r;
let Ok(p) = Polygon::new(vec![v0, v1, v2, v3]) else {
continue;
};
// Frontage edge in CCW order is v3 → v0, i.e. edge 3.
(prev_road, p, 3_usize, perp_depth, r)
};
// The variables `consume_next` and `consume_prev` propagate
// out via the per-vertex helpers below.
let _ = (consume_next, consume_prev, n_in);
// Clip to the block boundary so the parcel can never extend
// past it. Required for non-rectangular blocks where the
// R×depth rectangle would otherwise cross another block edge.
let Some(polygon) = clip_polygon_to_block(&polygon, &block.polygon) else {
continue;
};
if polygon.area() < params.min_area {
continue;
}
let edge_kinds = classify_edges(polygon.len(), frontage_idx);
let mut parcel = Parcel {
polygon,
edge_kinds,
frontage_road,
frontage_edge_index: frontage_idx,
block: face,
building: None,
};
regularize_parcel(&mut parcel, params);
out.push(parcel);
}
// For each edge, walk between the corner footprints (if any) and
// emit regular frontage parcels.
for i in 0..n {
let p = verts[i];
let q = verts[(i + 1) % n];
let road = block.roads[i];
let edge_vec = q - p;
let edge_len = edge_vec.length();
if edge_len < params.min_frontage {
continue;
}
let edge_dir = edge_vec / edge_len;
let inward = DVec2::new(-edge_dir.y, edge_dir.x);
// Walk-bounds. The corner at vertex i (start of this edge)
// consumes the corner's `r` along this edge (it's the corner's
// *next* side). The corner at vertex i+1 (end of this edge)
// consumes the corner's `perp_depth` along this edge (it's
// *that* corner's *prev* side, i.e., its Side2 runs along
// here for a depth-equivalent distance).
let consume_at_start = if real_corner[i] {
// Edge i is the *next* edge of corner at vertex i.
// - frontage_on_next[i] true → consume R (frontage).
// - frontage_on_next[i] false → consume depth (side2).
if frontage_on_next[i] {
corner_consume_short(&edge_lens, i, r_target, params)
} else {
corner_consume_long(&edge_lens, i, params)
}
} else {
0.0
};
let next_v = (i + 1) % n;
let consume_at_end = if real_corner[next_v] {
// Edge i is the *prev* edge of corner at vertex (i+1).
// - frontage_on_next[next_v] true → consume depth (side2 along prev).
// - frontage_on_next[next_v] false → consume R (frontage on prev).
if frontage_on_next[next_v] {
corner_consume_long(&edge_lens, next_v, params)
} else {
corner_consume_short(&edge_lens, next_v, r_target, params)
}
} else {
0.0
};
let t_start = consume_at_start;
let t_end = edge_len - consume_at_end;
if t_end - t_start < params.min_frontage {
continue;
}
let mut rng = rng_for_road(params.seed, road);
let max_depth = depth_caps[i].min(params.depth + params.depth_variance.abs() + EPS_GEOM);
let setback = params.setback.max(0.0);
// Place split positions between t_start and t_end.
let splits = split_positions_between(t_start, t_end, params, &mut rng);
for k in 0..splits.len() - 1 {
let t_a = splits[k];
let t_b = splits[k + 1];
if t_b - t_a < params.min_frontage - EPS_GEOM {
continue;
}
let p_a = p + edge_dir * t_a;
let p_b = p + edge_dir * t_b;
let depth_jitter = params.depth_variance * (rng.gen::<f64>() * 2.0 - 1.0);
let raw_depth = (params.depth + depth_jitter).max(params.min_frontage);
let total_depth = (setback + raw_depth)
.min(max_depth)
.max(params.min_frontage * 0.5);
if total_depth < params.min_frontage * 0.25 {
continue;
}
let q_b = p_b + inward * total_depth;
let q_a = p_a + inward * total_depth;
let Ok(raw_poly) = Polygon::new(vec![p_a, p_b, q_b, q_a]) else {
continue;
};
// Clip to block boundary so parcels can never escape the
// block (matters at acute corners and on non-rectangular
// blocks).
let Some(polygon) = clip_polygon_to_block(&raw_poly, &block.polygon) else {
continue;
};
if polygon.area() < params.min_area {
continue;
}
// After clipping, find the frontage edge — it's the one
// that lies on the road segment within tolerance.
let frontage_idx = match find_frontage_edge_after_clip(&polygon, p, q) {
Some(idx) => idx,
None => continue,
};
let frontage_len = {
let v = polygon.vertices();
(v[(frontage_idx + 1) % v.len()] - v[frontage_idx]).length()
};
if frontage_len < params.min_frontage {
continue;
}
let edge_kinds = classify_edges(polygon.len(), frontage_idx);
let mut parcel = Parcel {
polygon,
edge_kinds,
frontage_road: road,
frontage_edge_index: frontage_idx,
block: face,
building: None,
};
regularize_parcel(&mut parcel, params);
out.push(parcel);
}
}
Ok(out)
}
/// True iff vertex `i` of the block is a "real corner" — graph degree
/// ≥3, or degree 2 with a bend sharper than 150°. (D11.)
fn is_real_corner(graph: &RoadGraph, block: &Block, i: usize, _params: &SubdivisionParams) -> bool {
let n = block.polygon.len();
let half_edge_id = block.boundary_edges[i];
let Some(he) = graph.half_edges.get(half_edge_id) else {
return false;
};
let node_id = he.origin;
let Some(node) = graph.nodes.get(node_id) else {
return false;
};
if node.outgoing.len() >= 3 {
return true;
}
// Degree-2: bend test.
let v_prev = block.polygon.vertices()[(i + n - 1) % n];
let v_curr = block.polygon.vertices()[i];
let v_next = block.polygon.vertices()[(i + 1) % n];
let in_dir = (v_curr - v_prev).normalize_or_zero();
let out_dir = (v_next - v_curr).normalize_or_zero();
if in_dir.length_squared() < EPS_GEOM * EPS_GEOM
|| out_dir.length_squared() < EPS_GEOM * EPS_GEOM
{
return false;
}
let cos_turn = in_dir.dot(out_dir).clamp(-1.0, 1.0);
let turn_angle = cos_turn.acos();
// turn > 30° means interior < 150° = "real corner".
turn_angle > 30.0_f64.to_radians()
}
/// Iteratively clip `parcel` by each inward half-plane of `block`.
/// Returns `None` if the result is empty or invalid. Correct for
/// convex blocks; may over-clip mild concavities (acceptable for
/// milestone 0.2).
fn clip_polygon_to_block(parcel: &Polygon, block: &Polygon) -> Option<Polygon> {
let mut current = parcel.clone();
let block_verts = block.vertices();
let n = block_verts.len();
for i in 0..n {
let a = block_verts[i];
let b = block_verts[(i + 1) % n];
let edge = b - a;
let len = edge.length();
if len < EPS_GEOM {
continue;
}
let dir = edge / len;
let inward = DVec2::new(-dir.y, dir.x);
current = current.clip_half_plane(a, inward)?;
}
Some(current)
}
/// Locate the polygon edge that lies on the road segment
/// `(road_a, road_b)` within tolerance.
fn find_frontage_edge_after_clip(polygon: &Polygon, road_a: DVec2, road_b: DVec2) -> Option<usize> {
let road_dir = (road_b - road_a).normalize_or_zero();
if road_dir.length_squared() < EPS_GEOM * EPS_GEOM {
return None;
}
let road_normal = DVec2::new(-road_dir.y, road_dir.x);
let v = polygon.vertices();
let n = v.len();
let tol = 100.0 * EPS_GEOM;
let mut best: Option<(usize, f64)> = None;
for i in 0..n {
let p = v[i];
let q = v[(i + 1) % n];
let dp = (p - road_a).dot(road_normal).abs();
let dq = (q - road_a).dot(road_normal).abs();
if dp < tol && dq < tol {
let edge_dir = (q - p).normalize_or_zero();
if edge_dir.dot(road_dir).abs() > 0.5 {
let len = (q - p).length();
if best.is_none_or(|(_, blen)| len > blen) {
best = Some((i, len));
}
}
}
}
best.map(|(i, _)| i)
}
/// "Short" corner consumption — equal to the corner radius `R`,
/// capped at half each adjacent edge.
fn corner_consume_short(
edge_lens: &[f64],
i: usize,
r_target: f64,
params: &SubdivisionParams,
) -> f64 {
let n = edge_lens.len();
let prev_len = edge_lens[(i + n - 1) % n];
let next_len = edge_lens[i];
r_target
.min(prev_len * 0.5)
.min(next_len * 0.5)
.max(params.min_frontage * 0.5)
}
/// "Long" corner consumption — equal to the parcel depth, capped at
/// half each adjacent edge.
fn corner_consume_long(edge_lens: &[f64], i: usize, params: &SubdivisionParams) -> f64 {
let n = edge_lens.len();
let prev_len = edge_lens[(i + n - 1) % n];
let next_len = edge_lens[i];
params
.depth
.min(prev_len * 0.5)
.min(next_len * 0.5)
.max(params.min_frontage * 0.5)
}
/// Per-edge depth cap (half the perpendicular distance from edge
/// midpoint to the next polygon edge along the inward normal).
fn edge_depth_caps(poly: &Polygon) -> Vec<f64> {
let verts = poly.vertices();
let n = verts.len();
let mut caps = Vec::with_capacity(n);
for i in 0..n {
let p = verts[i];
let q = verts[(i + 1) % n];
let edge = q - p;
let len = edge.length();
if len < EPS_GEOM {
caps.push(0.0);
continue;
}
let dir = edge / len;
let inward = DVec2::new(-dir.y, dir.x);
let mid = (p + q) * 0.5;
let mut min_t = f64::INFINITY;
for j in 0..n {
if j == i {
continue;
}
let a = verts[j];
let b = verts[(j + 1) % n];
if let Some(t) = ray_segment_distance(mid, inward, a, b) {
if t > EPS_GEOM && t < min_t {
min_t = t;
}
}
}
caps.push(min_t * 0.5);
}
caps
}
fn ray_segment_distance(origin: DVec2, dir: DVec2, a: DVec2, b: DVec2) -> Option<f64> {
let v1 = origin - a;
let v2 = b - a;
let v3 = DVec2::new(-dir.y, dir.x);
let denom = v2.dot(v3);
if denom.abs() < EPS_GEOM {
return None;
}
let t = v2.perp_dot(v1) / denom;
let s = v1.dot(v3) / denom;
if t >= 0.0 && (-EPS_GEOM..=1.0 + EPS_GEOM).contains(&s) {
Some(t)
} else {
None
}
}
/// Split positions between [`start`, `end`] along an edge. Begins
/// with `start` and ends with `end`.
fn split_positions_between(
start: f64,
end: f64,
params: &SubdivisionParams,
rng: &mut ChaCha8Rng,
) -> Vec<f64> {
let mut splits: Vec<f64> = vec![start];
let mut t = start;
loop {
let jitter = params.frontage_variance * (rng.gen::<f64>() * 2.0 - 1.0);
let dt = (params.frontage_width + jitter).max(params.min_frontage);
t += dt;
if t >= end - params.min_frontage * 0.5 {
break;
}
splits.push(t);
}
splits.push(end);
splits
}
fn rng_for_road(seed: u64, road: RoadId) -> ChaCha8Rng {
let road_bits = road.data().as_ffi();
let mixed = seed
.wrapping_mul(0x9E37_79B9_7F4A_7C15)
.wrapping_add(road_bits)
.wrapping_mul(0xBF58_476D_1CE4_E5B9);
ChaCha8Rng::seed_from_u64(mixed)
}
#[allow(dead_code)]
fn _unused_node_id() -> NodeId {
NodeId::null()
}
#[allow(dead_code)]
fn _unused_face_id() -> FaceId {
FaceId::null()
}
#[cfg(test)]
mod tests {
use super::*;
fn rectangle_graph(w: f64, h: f64) -> RoadGraph {
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(w, 0.0));
let c = g.add_node(DVec2::new(w, h));
let d = g.add_node(DVec2::new(0.0, h));
g.add_road(a, b).unwrap();
g.add_road(b, c).unwrap();
g.add_road(c, d).unwrap();
g.add_road(d, a).unwrap();
g.rebuild_topology().unwrap();
g
}
#[test]
fn rectangle_subdivides_into_many_parcels() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let set = subdivide_all(&g, &params).unwrap();
assert!(set.len() > 8, "got {} parcels", set.len());
}
#[test]
fn rectangle_parcels_have_one_frontage_each() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let set = subdivide_all(&g, &params).unwrap();
for (_, p) in set.iter() {
let frontage_count = p
.edge_kinds()
.iter()
.filter(|k| matches!(k, super::super::EdgeKind::Frontage))
.count();
assert_eq!(frontage_count, 1, "I2 violation");
}
}
#[test]
fn rectangle_parcels_do_not_overlap() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let set = subdivide_all(&g, &params).unwrap();
let total_area: f64 = set.iter().map(|(_, p)| p.area()).sum();
let block_area = 200.0 * 100.0;
assert!(
total_area <= block_area + 1e-3,
"parcel areas {total_area} > block area {block_area}",
);
}
#[test]
fn deterministic_with_fixed_seed() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let a = subdivide_all(&g, &params).unwrap();
let b = subdivide_all(&g, &params).unwrap();
assert_eq!(a.len(), b.len());
for ((_, p1), (_, p2)) in a.iter().zip(b.iter()) {
assert_eq!(p1.vertices().len(), p2.vertices().len());
for (v1, v2) in p1.vertices().iter().zip(p2.vertices().iter()) {
assert!((v1.x - v2.x).abs() < 1e-12);
assert!((v1.y - v2.y).abs() < 1e-12);
}
}
}
#[test]
fn invalid_params_rejected() {
let g = rectangle_graph(50.0, 50.0);
let params = SubdivisionParams {
frontage_width: -1.0,
..SubdivisionParams::default()
};
let err = subdivide_all(&g, &params).unwrap_err();
assert!(matches!(err, SubdivisionError::InvalidParams(_)));
}
#[test]
fn rectangle_has_corner_parcels() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let set = subdivide_all(&g, &params).unwrap();
// Parcel at the (0, 0) corner: its polygon should contain the
// origin (or a vertex right at it) and have 4 vertices.
let near_origin = set
.iter()
.find(|(_, p)| p.vertices().iter().any(|v| v.length_squared() < 1.0))
.map(|(_, p)| p.vertices().len());
assert_eq!(
near_origin,
Some(4),
"expected a 4-vertex corner parcel near the origin"
);
}
#[test]
fn stats_report_nonzero_phases() {
let g = rectangle_graph(200.0, 100.0);
let params = SubdivisionParams::default();
let (set, stats) = subdivide_all_with_stats(&g, &params).unwrap();
assert_eq!(stats.parcel_count, set.len());
assert!(stats.block_count >= 1);
assert!(stats.total > Duration::ZERO);
assert!(stats.parcels_per_second() > 0.0);
}
}

View File

@ -0,0 +1,192 @@
//! SVG visualization (spec §8). Feature-gated behind `viz`.
//!
//! `render_scene` writes a single graph + parcel set to an SVG using
//! the color conventions of §8.2. `generate_all_figures` regenerates
//! every figure listed in the journal.
pub mod svg_render;
use std::path::Path;
pub use svg_render::{render_scene, RenderOptions};
/// Regenerate every figure listed in spec §8.1, writing into
/// `out_dir`.
///
/// # Errors
///
/// Propagates IO errors (`io::Error`) and subdivision errors. The
/// IO error path is wrapped into a `Box<dyn Error>` to keep the
/// signature simple.
pub fn generate_all_figures(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
use crate::{subdivide_all, RoadGraph, SubdivisionParams};
use glam::DVec2;
std::fs::create_dir_all(out_dir)?;
// fig_01_grid_block: rectangular block subdivided.
{
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(200.0, 0.0));
let c = g.add_node(DVec2::new(200.0, 100.0));
let d = g.add_node(DVec2::new(0.0, 100.0));
g.add_road(a, b)?;
g.add_road(b, c)?;
g.add_road(c, d)?;
g.add_road(d, a)?;
g.rebuild_topology()?;
let params = SubdivisionParams::default();
let parcels = subdivide_all(&g, &params)?;
let svg = render_scene(&g, &parcels, &RenderOptions::default());
std::fs::write(out_dir.join("fig_01_grid_block.svg"), svg)?;
}
// fig_02_curved_road: parcels on a polyline-curved frontage. We
// approximate "curved" by a 12-segment arc.
{
let mut g = RoadGraph::new();
let arc_radius = 200.0_f64;
let arc_segments = 8;
let arc_half_angle = std::f64::consts::FRAC_PI_4;
// Arc on the bottom: from (-r sin θ, -r cos θ) to (r sin θ, -r cos θ),
// centered at (0, 0). Then a back rectangle to close the block.
let mut arc_nodes = Vec::new();
for i in 0..=arc_segments {
let t = -arc_half_angle + (i as f64) * (2.0 * arc_half_angle) / (arc_segments as f64);
let p = DVec2::new(arc_radius * t.sin(), -arc_radius * t.cos() + arc_radius);
arc_nodes.push(g.add_node(p));
}
let depth = 50.0;
let last = *arc_nodes.last().expect("non-empty");
let first = arc_nodes[0];
let pos_last = g.node_position(last).expect("present");
let pos_first = g.node_position(first).expect("present");
let back_right = g.add_node(pos_last + DVec2::new(0.0, depth));
let back_left = g.add_node(pos_first + DVec2::new(0.0, depth));
for i in 0..arc_segments {
g.add_road(arc_nodes[i], arc_nodes[i + 1])?;
}
g.add_road(last, back_right)?;
g.add_road(back_right, back_left)?;
g.add_road(back_left, first)?;
if g.rebuild_topology().is_ok() {
let params = SubdivisionParams::default();
if let Ok(parcels) = subdivide_all(&g, &params) {
let svg = render_scene(&g, &parcels, &RenderOptions::default());
std::fs::write(out_dir.join("fig_02_curved_road.svg"), svg)?;
}
}
}
// fig_04_y_intersection: three roads at 120°, surrounded by a
// triangular outer perimeter so the three blocks are bounded.
{
use std::f64::consts::TAU;
let mut g = RoadGraph::new();
let center = g.add_node(DVec2::new(0.0, 0.0));
let r = 90.0_f64;
let third = TAU / 3.0;
let p1 = g.add_node(DVec2::new(r * 0.0_f64.cos(), r * 0.0_f64.sin()));
let p2 = g.add_node(DVec2::new(r * third.cos(), r * third.sin()));
let p3 = g.add_node(DVec2::new(r * (2.0 * third).cos(), r * (2.0 * third).sin()));
g.add_road(center, p1)?;
g.add_road(center, p2)?;
g.add_road(center, p3)?;
g.add_road(p1, p2)?;
g.add_road(p2, p3)?;
g.add_road(p3, p1)?;
g.rebuild_topology()?;
let params = SubdivisionParams::default();
let parcels = subdivide_all(&g, &params)?;
let svg = render_scene(&g, &parcels, &RenderOptions::default());
std::fs::write(out_dir.join("fig_04_y_intersection.svg"), svg)?;
}
// fig_06a + fig_06b: a 200×100 rectangle, before and after a
// node move that produces a mix of edit outcomes. Post-edit
// parcels are colored by their `EditOutcome` category.
{
use crate::{apply_road_edit, RoadEdit};
use std::collections::HashMap;
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(200.0, 0.0));
let c = g.add_node(DVec2::new(200.0, 100.0));
let d = g.add_node(DVec2::new(0.0, 100.0));
g.add_road(a, b)?;
g.add_road(b, c)?;
g.add_road(c, d)?;
g.add_road(d, a)?;
g.rebuild_topology()?;
let params = SubdivisionParams::default();
let mut parcels = subdivide_all(&g, &params)?;
let svg_before = render_scene(&g, &parcels, &RenderOptions::default());
std::fs::write(out_dir.join("fig_06a_road_edit_before.svg"), svg_before)?;
// Move node B (the bottom-right corner) right by 8 m. That's
// big enough to drag the bottom-edge parcels' frontages but
// small enough that none of them get condemned.
let pos_b = g.node_position(b).unwrap_or(DVec2::ZERO);
let outcome = apply_road_edit(
&mut parcels,
&mut g,
RoadEdit::MoveNode {
node: b,
to: pos_b + DVec2::new(8.0, 0.0),
},
&params,
)?;
let mut fills: HashMap<crate::ParcelId, String> = HashMap::new();
for &pid in &outcome.deformed {
fills.insert(pid, "#9fd99a".to_string()); // green
}
for &pid in &outcome.regenerated {
fills.insert(pid, "#ffc070".to_string()); // orange
}
for &pid in &outcome.created {
fills.insert(pid, "#7fc8ff".to_string()); // blue (created)
}
// Condemned parcels are gone; we paint the empty space red
// by overlaying the rendering — but with the lite parcel
// model we just note that condemned ids no longer exist in
// `parcels`, so the rendering naturally shows nothing in
// their place.
let mut opts = RenderOptions::default();
opts.parcel_fills = fills;
let svg_after = render_scene(&g, &parcels, &opts);
std::fs::write(out_dir.join("fig_06b_road_edit_after.svg"), svg_after)?;
}
// fig_07_regularity_slider: same scene at ρ ∈ {0, 0.5, 1.0}.
// For milestone 0.1 the regularization pass is a stub, so the
// three panels are identical — we still emit the figure so the
// journal compiles, with a TODO recorded in §11 (R3).
{
let mut g = RoadGraph::new();
let a = g.add_node(DVec2::new(0.0, 0.0));
let b = g.add_node(DVec2::new(180.0, 0.0));
let c = g.add_node(DVec2::new(180.0, 110.0));
let d = g.add_node(DVec2::new(0.0, 110.0));
g.add_road(a, b)?;
g.add_road(b, c)?;
g.add_road(c, d)?;
g.add_road(d, a)?;
g.rebuild_topology()?;
for (rho, suffix) in [(0.0, "rho_0_0"), (0.5, "rho_0_5"), (1.0, "rho_1_0")] {
let params = SubdivisionParams {
regularity: rho,
..SubdivisionParams::default()
};
let parcels = subdivide_all(&g, &params)?;
let svg = render_scene(&g, &parcels, &RenderOptions::default());
std::fs::write(
out_dir.join(format!("fig_07_regularity_slider_{suffix}.svg")),
svg,
)?;
}
}
Ok(())
}

View File

@ -0,0 +1,228 @@
//! SVG rendering of a graph + parcel set.
use std::collections::HashMap;
use glam::DVec2;
use svg::node::element::path::Data;
use svg::node::element::{Group, Line, Path as SvgPath, Rectangle};
use svg::Document;
use crate::network::RoadGraph;
use crate::parcel::{EdgeKind, ParcelId, ParcelSet};
/// Knobs for the SVG renderer.
#[derive(Debug, Clone)]
pub struct RenderOptions {
/// Pixels per meter.
pub pixels_per_meter: f64,
/// Padding around the scene, meters.
pub margin_m: f64,
/// Whether to draw vertex dots on roads.
pub draw_nodes: bool,
/// Per-parcel fill overrides — keyed by [`ParcelId`], values are
/// SVG-compatible color strings. Used by the post-edit figures
/// to paint each parcel by its [`EditOutcome`] category.
pub parcel_fills: HashMap<ParcelId, String>,
}
impl Default for RenderOptions {
fn default() -> Self {
Self {
pixels_per_meter: 4.0,
margin_m: 5.0,
draw_nodes: true,
parcel_fills: HashMap::new(),
}
}
}
/// Render the whole scene to an SVG `String`.
#[must_use]
pub fn render_scene(graph: &RoadGraph, parcels: &ParcelSet, opts: &RenderOptions) -> String {
let bbox = compute_bbox(graph, parcels);
let (min, max) = bbox;
let m = opts.margin_m;
let world_w = (max.x - min.x) + 2.0 * m;
let world_h = (max.y - min.y) + 2.0 * m;
let px_w = world_w * opts.pixels_per_meter;
let px_h = world_h * opts.pixels_per_meter;
// World y is up, SVG y is down. Flip and translate via viewBox so
// we can write coordinates in world units throughout.
let view_min_x = min.x - m;
let view_min_y = min.y - m;
let view_w = world_w;
let view_h = world_h;
// We use an inverted y by setting viewBox to a flipped rectangle,
// then a transform on the root group that applies (1, -1) and a
// y-translation.
let view_box = format!("{view_min_x} {view_min_y} {view_w} {view_h}");
let bg = Rectangle::new()
.set("x", view_min_x)
.set("y", view_min_y)
.set("width", view_w)
.set("height", view_h)
.set("fill", "#ffffff");
// Group with y-flip to put world +y up.
let flip = Group::new().set(
"transform",
format!("translate(0, {}) scale(1, -1)", 2.0 * view_min_y + view_h),
);
// Draw roads first (so frontage edges of parcels paint on top of
// the road stroke and stay visible), then parcels, then nodes.
let roads_layer = roads_group(graph);
let parcels_layer = parcels_group(parcels, &opts.parcel_fills);
let nodes_layer = if opts.draw_nodes {
nodes_group(graph)
} else {
Group::new()
};
let flip = flip.add(roads_layer).add(parcels_layer).add(nodes_layer);
Document::new()
.set("viewBox", view_box)
.set("width", px_w)
.set("height", px_h)
.set("xmlns", "http://www.w3.org/2000/svg")
.add(bg)
.add(flip)
.to_string()
}
fn compute_bbox(graph: &RoadGraph, parcels: &ParcelSet) -> (DVec2, DVec2) {
let mut min = DVec2::splat(f64::INFINITY);
let mut max = DVec2::splat(f64::NEG_INFINITY);
let mut update = |p: DVec2| {
if p.x < min.x {
min.x = p.x;
}
if p.y < min.y {
min.y = p.y;
}
if p.x > max.x {
max.x = p.x;
}
if p.y > max.y {
max.y = p.y;
}
};
for (_, _, _) in graph.road_endpoints() {
// road_endpoints gives us ids; iterate nodes instead.
}
for nid in graph_node_ids(graph) {
if let Some(p) = graph.node_position(nid) {
update(p);
}
}
for (_, parcel) in parcels.iter() {
for &v in parcel.vertices() {
update(v);
}
}
if !min.x.is_finite() {
// No content — fall back to a unit square.
return (DVec2::ZERO, DVec2::splat(1.0));
}
(min, max)
}
fn graph_node_ids(graph: &RoadGraph) -> Vec<crate::network::NodeId> {
let mut seen = Vec::new();
for (_, a, b) in graph.road_endpoints() {
seen.push(a);
seen.push(b);
}
seen.sort_by_key(|n| format!("{n:?}"));
seen.dedup();
seen
}
fn parcels_group(parcels: &ParcelSet, overrides: &HashMap<ParcelId, String>) -> Group {
let mut group = Group::new().set("id", "parcels");
for (id, parcel) in parcels.iter() {
let verts = parcel.vertices();
if verts.is_empty() {
continue;
}
let mut data = Data::new().move_to((verts[0].x, verts[0].y));
for v in &verts[1..] {
data = data.line_to((v.x, v.y));
}
data = data.close();
let (fill_color, fill_opacity) = match overrides.get(&id) {
Some(c) => (c.clone(), 0.55_f64),
None => ("#fff7c2".to_string(), 0.3_f64),
};
let fill = SvgPath::new()
.set("d", data)
.set("fill", fill_color)
.set("fill-opacity", fill_opacity)
.set("stroke", "none");
group = group.add(fill);
let _ = id;
// Stroke each edge by its kind.
for (i, kind) in parcel.edge_kinds().iter().enumerate() {
let a = verts[i];
let b = verts[(i + 1) % verts.len()];
let (color, dashed, width) = match kind {
EdgeKind::Frontage => ("#1f5fb4", false, 0.9_f64),
EdgeKind::Side => ("#6b6b6b", false, 0.4_f64),
EdgeKind::Back => ("#bdbdbd", true, 0.4_f64),
};
let mut line = Line::new()
.set("x1", a.x)
.set("y1", a.y)
.set("x2", b.x)
.set("y2", b.y)
.set("stroke", color)
.set("stroke-width", width);
if dashed {
line = line.set("stroke-dasharray", "1.6,1.2");
}
group = group.add(line);
}
}
group
}
fn roads_group(graph: &RoadGraph) -> Group {
let mut group = Group::new().set("id", "roads");
for (_, a, b) in graph.road_endpoints() {
let pa = graph.node_position(a);
let pb = graph.node_position(b);
if let (Some(pa), Some(pb)) = (pa, pb) {
let line = Line::new()
.set("x1", pa.x)
.set("y1", pa.y)
.set("x2", pb.x)
.set("y2", pb.y)
.set("stroke", "#000000")
.set("stroke-width", 0.6_f64)
.set("stroke-linecap", "round");
group = group.add(line);
}
}
group
}
fn nodes_group(graph: &RoadGraph) -> Group {
use svg::node::element::Circle;
let mut group = Group::new().set("id", "nodes");
for nid in graph_node_ids(graph) {
if let Some(p) = graph.node_position(nid) {
let c = Circle::new()
.set("cx", p.x)
.set("cy", p.y)
.set("r", 0.7_f64)
.set("fill", "#000000");
group = group.add(c);
}
}
group
}

View File

@ -0,0 +1 @@
{"rustc_fingerprint":4812792677330984170,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.92.0 (ded5c06cf 2025-12-08) (Homebrew)\nbinary: rustc\ncommit-hash: ded5c06cf21d2b93bffd5d884aa6e96934ee4234\ncommit-date: 2025-12-08\nhost: aarch64-apple-darwin\nrelease: 1.92.0\nLLVM version: 21.1.7\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/opt/homebrew/Cellar/rust/1.92.0\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""}},"successes":{}}

View File

@ -0,0 +1 @@
{"rustc_vv":"rustc 1.92.0 (ded5c06cf 2025-12-08) (Homebrew)\nbinary: rustc\ncommit-hash: ded5c06cf21d2b93bffd5d884aa6e96934ee4234\ncommit-date: 2025-12-08\nhost: aarch64-apple-darwin\nrelease: 1.92.0\nLLVM version: 21.1.7\n"}

View File

@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/

View File

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
047e95b7d0e15acc

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\"]","declared_features":"[\"bitflags\", \"default\", \"parser\"]","target":15514848761019652899,"profile":8276155916380437441,"path":1381630239697283883,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/anes-9ee70e632c90772d/dep-lib-anes","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
53e3de259b22e5ee

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\"]","declared_features":"[\"bitflags\", \"default\", \"parser\"]","target":15514848761019652899,"profile":5347358027863023418,"path":1381630239697283883,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/anes-f7727b7c02c2c54f/dep-lib-anes","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
e6d4e801df4a56e6

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"std\"]","target":6165884447290141869,"profile":790325420539221616,"path":5326910857737216307,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/anstyle-71183a543eeda23e/dep-lib-anstyle","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
abd93f4afaf69924

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"std\"]","target":6165884447290141869,"profile":8255941854203129366,"path":5326910857737216307,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/anstyle-dee35890a12dd6ce/dep-lib-anstyle","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
3e0b4b221a4c2750

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[]","declared_features":"[]","target":6962977057026645649,"profile":3033921117576893,"path":4405214941553509729,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/autocfg-c7e245154ebc45d3/dep-lib-autocfg","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
3e6425f0d8f0397f

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"serde\", \"std\"]","target":1565461888733056401,"profile":5347358027863023418,"path":4377444822781387660,"deps":[[5692597712387868707,"bit_vec",false,2150889985839040455]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bit-set-4b2fefdf0afbe4cd/dep-lib-bit_set","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
8a2231a955bb0ff5

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"serde\", \"std\"]","target":1565461888733056401,"profile":8276155916380437441,"path":4377444822781387660,"deps":[[5692597712387868707,"bit_vec",false,1129808780249088941]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bit-set-a3f155482d8725e2/dep-lib-bit_set","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
ad43dd8219e3ad0f

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"borsh\", \"borsh_std\", \"default\", \"miniserde\", \"nanoserde\", \"serde\", \"serde_no_std\", \"serde_std\", \"std\"]","target":1886748672988989682,"profile":8276155916380437441,"path":17936763483328278557,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bit-vec-8893692845f45650/dep-lib-bit_vec","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
c787e1d40b7fd91d

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"default\", \"std\"]","declared_features":"[\"borsh\", \"borsh_std\", \"default\", \"miniserde\", \"nanoserde\", \"serde\", \"serde_no_std\", \"serde_std\", \"std\"]","target":1886748672988989682,"profile":5347358027863023418,"path":17936763483328278557,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bit-vec-f19c8d0b3ed7fe20/dep-lib-bit_vec","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
a01b101bd3fcb932

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"std\"]","declared_features":"[\"arbitrary\", \"bytemuck\", \"example_generated\", \"serde\", \"serde_core\", \"std\"]","target":7691312148208718491,"profile":8276155916380437441,"path":5561816516557774638,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bitflags-63dd709ab55cd2cc/dep-lib-bitflags","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
2b7eaa6178f77599

View File

@ -0,0 +1 @@
{"rustc":12211934750107851607,"features":"[\"std\"]","declared_features":"[\"arbitrary\", \"bytemuck\", \"example_generated\", \"serde\", \"serde_core\", \"std\"]","target":7691312148208718491,"profile":5347358027863023418,"path":5561816516557774638,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/bitflags-7fd589d26c2da380/dep-lib-bitflags","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}

View File

@ -0,0 +1 @@
This file has an mtime of when this was started.

View File

@ -0,0 +1 @@
23047bf6a02ccfd9

Some files were not shown because too many files have changed in this diff Show More