@jgengine/core 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (727) hide show
  1. package/CHANGELOG.md +39 -2
  2. package/LICENSE +203 -661
  3. package/README.md +1 -1
  4. package/dist/ai/crowd.d.ts +55 -0
  5. package/dist/ai/crowd.js +114 -0
  6. package/dist/ai/flock.d.ts +5 -2
  7. package/dist/ai/flock.js +5 -2
  8. package/dist/ai/groupAssist.d.ts +1 -0
  9. package/dist/ai/groupAssist.js +1 -0
  10. package/dist/ai/heatSystem.d.ts +76 -0
  11. package/dist/ai/heatSystem.js +97 -0
  12. package/dist/ai/interestScheduler.d.ts +107 -0
  13. package/dist/ai/interestScheduler.js +118 -0
  14. package/dist/ai/jobBoard.d.ts +1 -0
  15. package/dist/ai/jobBoard.js +1 -0
  16. package/dist/ai/laneSelect.d.ts +5 -2
  17. package/dist/ai/laneSelect.js +5 -2
  18. package/dist/ai/mobBrain.d.ts +1 -0
  19. package/dist/ai/mobBrain.js +1 -0
  20. package/dist/ai/populationDirector.d.ts +103 -0
  21. package/dist/ai/populationDirector.js +190 -0
  22. package/dist/ai/pursuit.d.ts +56 -0
  23. package/dist/ai/pursuit.js +53 -0
  24. package/dist/ai/spawnDirector.d.ts +7 -2
  25. package/dist/ai/spawnDirector.js +18 -15
  26. package/dist/ai/spawnPoint.d.ts +3 -1
  27. package/dist/ai/spawnPoint.js +3 -1
  28. package/dist/ai/targetAcquisition.d.ts +84 -0
  29. package/dist/ai/targetAcquisition.js +92 -0
  30. package/dist/anim/easing.d.ts +19 -0
  31. package/dist/anim/easing.js +19 -0
  32. package/dist/anim/oscillator.d.ts +12 -0
  33. package/dist/anim/oscillator.js +25 -0
  34. package/dist/area/areaEffectField.d.ts +127 -0
  35. package/dist/area/areaEffectField.js +175 -0
  36. package/dist/area/stackPolicy.d.ts +47 -0
  37. package/dist/area/stackPolicy.js +99 -0
  38. package/dist/authoring.d.ts +2 -1
  39. package/dist/authoring.js +2 -1
  40. package/dist/board/laneBoard.d.ts +13 -0
  41. package/dist/board/laneBoard.js +13 -0
  42. package/dist/board/timelineBoard.d.ts +10 -0
  43. package/dist/board/timelineBoard.js +10 -0
  44. package/dist/combat/abilityKit.d.ts +18 -0
  45. package/dist/combat/abilityKit.js +24 -9
  46. package/dist/combat/animationState.d.ts +5 -0
  47. package/dist/combat/animationState.js +5 -0
  48. package/dist/combat/breakMeters.d.ts +10 -0
  49. package/dist/combat/breakMeters.js +10 -0
  50. package/dist/combat/castRunner.d.ts +5 -0
  51. package/dist/combat/castRunner.js +5 -0
  52. package/dist/combat/comboPoints.d.ts +5 -0
  53. package/dist/combat/comboPoints.js +5 -0
  54. package/dist/combat/comboString.d.ts +5 -0
  55. package/dist/combat/comboString.js +5 -0
  56. package/dist/combat/damageInterceptors.d.ts +192 -0
  57. package/dist/combat/damageInterceptors.js +251 -0
  58. package/dist/combat/damageMatchup.d.ts +68 -0
  59. package/dist/combat/damageMatchup.js +42 -0
  60. package/dist/combat/damageResolution.d.ts +63 -0
  61. package/dist/combat/damageResolution.js +50 -0
  62. package/dist/combat/death.d.ts +7 -1
  63. package/dist/combat/death.js +5 -0
  64. package/dist/combat/deathReason.d.ts +14 -0
  65. package/dist/combat/deathReason.js +1 -0
  66. package/dist/combat/defensiveWindow.d.ts +5 -0
  67. package/dist/combat/defensiveWindow.js +5 -0
  68. package/dist/combat/dotField.d.ts +5 -1
  69. package/dist/combat/dotField.js +5 -1
  70. package/dist/combat/downed.d.ts +5 -0
  71. package/dist/combat/downed.js +5 -0
  72. package/dist/combat/effects.d.ts +5 -0
  73. package/dist/combat/effects.js +5 -0
  74. package/dist/combat/encounterSequence.d.ts +258 -0
  75. package/dist/combat/encounterSequence.js +426 -0
  76. package/dist/combat/hitReaction.d.ts +57 -1
  77. package/dist/combat/hitReaction.js +45 -5
  78. package/dist/combat/magazine.d.ts +54 -0
  79. package/dist/combat/magazine.js +96 -0
  80. package/dist/combat/projectiles.d.ts +5 -0
  81. package/dist/combat/projectiles.js +32 -14
  82. package/dist/combat/receivedDamage.d.ts +117 -0
  83. package/dist/combat/receivedDamage.js +92 -0
  84. package/dist/combat/regenShield.d.ts +5 -1
  85. package/dist/combat/regenShield.js +5 -1
  86. package/dist/combat/renderCues.d.ts +51 -0
  87. package/dist/combat/renderCues.js +44 -0
  88. package/dist/combat/resourcePool.d.ts +5 -0
  89. package/dist/combat/resourcePool.js +5 -0
  90. package/dist/combat/rewardAllocation.d.ts +121 -0
  91. package/dist/combat/rewardAllocation.js +251 -0
  92. package/dist/combat/shotOrigin.d.ts +18 -1
  93. package/dist/combat/shotOrigin.js +39 -0
  94. package/dist/combat/statusApplication.d.ts +96 -0
  95. package/dist/combat/statusApplication.js +81 -0
  96. package/dist/combat/telegraph.d.ts +11 -2
  97. package/dist/combat/telegraph.js +11 -2
  98. package/dist/combat/thresholdCrossings.d.ts +85 -0
  99. package/dist/combat/thresholdCrossings.js +116 -0
  100. package/dist/combat.d.ts +35 -0
  101. package/dist/combat.js +35 -0
  102. package/dist/commands/commandRegistry.d.ts +11 -0
  103. package/dist/commands/commandRegistry.js +9 -2
  104. package/dist/crafting/crop.d.ts +18 -0
  105. package/dist/crafting/crop.js +18 -0
  106. package/dist/crafting/production.d.ts +5 -0
  107. package/dist/crafting/production.js +5 -0
  108. package/dist/devtools/controls.d.ts +38 -0
  109. package/dist/devtools/controls.js +181 -0
  110. package/dist/devtools/devtools.d.ts +5 -197
  111. package/dist/devtools/devtools.js +38 -774
  112. package/dist/devtools/discover.d.ts +32 -0
  113. package/dist/devtools/discover.js +240 -0
  114. package/dist/devtools/fallbackSeams.d.ts +50 -0
  115. package/dist/devtools/fallbackSeams.js +111 -0
  116. package/dist/devtools/frame.d.ts +15 -0
  117. package/dist/devtools/frame.js +247 -0
  118. package/dist/devtools/logs.d.ts +15 -0
  119. package/dist/devtools/logs.js +86 -0
  120. package/dist/devtools/overrides.d.ts +16 -0
  121. package/dist/devtools/overrides.js +66 -0
  122. package/dist/devtools/rewriteTunables.d.ts +17 -0
  123. package/dist/devtools/rewriteTunables.js +276 -0
  124. package/dist/devtools/saveEndpoint.d.ts +46 -0
  125. package/dist/devtools/saveEndpoint.js +48 -0
  126. package/dist/devtools/transformTunables.d.ts +3 -0
  127. package/dist/devtools/transformTunables.js +3 -0
  128. package/dist/devtools/tunableSchema.d.ts +31 -0
  129. package/dist/devtools/tunableSchema.js +31 -0
  130. package/dist/devtools/types.d.ts +212 -0
  131. package/dist/devtools/types.js +1 -0
  132. package/dist/economy/auctionBook.d.ts +122 -0
  133. package/dist/economy/auctionBook.js +213 -0
  134. package/dist/economy/currency.d.ts +12 -1
  135. package/dist/economy/currency.js +12 -1
  136. package/dist/economy/listingBook.d.ts +91 -0
  137. package/dist/economy/listingBook.js +144 -0
  138. package/dist/economy/priceHistory.d.ts +44 -0
  139. package/dist/economy/priceHistory.js +89 -0
  140. package/dist/economy/resourceLedger.d.ts +222 -0
  141. package/dist/economy/resourceLedger.js +353 -0
  142. package/dist/economy/sharedWallet.d.ts +14 -0
  143. package/dist/economy/sharedWallet.js +14 -0
  144. package/dist/economy/techTree.d.ts +12 -0
  145. package/dist/economy/techTree.js +12 -0
  146. package/dist/economy/unlockPoints.d.ts +92 -0
  147. package/dist/economy/unlockPoints.js +103 -0
  148. package/dist/economy/wallet.d.ts +26 -2
  149. package/dist/economy/wallet.js +35 -6
  150. package/dist/editor/commandApply.d.ts +5 -0
  151. package/dist/editor/commandApply.js +755 -0
  152. package/dist/editor/commands.d.ts +235 -7
  153. package/dist/editor/commands.js +82 -124
  154. package/dist/editor/directives.d.ts +94 -0
  155. package/dist/editor/directives.js +206 -0
  156. package/dist/editor/document.d.ts +194 -13
  157. package/dist/editor/document.js +1166 -19
  158. package/dist/editor/entityCatalog.d.ts +42 -0
  159. package/dist/editor/entityCatalog.js +101 -0
  160. package/dist/editor/environment.d.ts +74 -0
  161. package/dist/editor/environment.js +169 -0
  162. package/dist/editor/grid.d.ts +229 -0
  163. package/dist/editor/grid.js +366 -0
  164. package/dist/editor/gridAdapters.d.ts +80 -0
  165. package/dist/editor/gridAdapters.js +134 -0
  166. package/dist/editor/index.d.ts +98 -6
  167. package/dist/editor/index.js +13 -2
  168. package/dist/editor/liveSync.d.ts +127 -0
  169. package/dist/editor/liveSync.js +249 -0
  170. package/dist/editor/minimap.d.ts +31 -0
  171. package/dist/editor/minimap.js +73 -0
  172. package/dist/editor/runtimeInspector.d.ts +88 -0
  173. package/dist/editor/runtimeInspector.js +239 -0
  174. package/dist/editor/streamer.d.ts +66 -0
  175. package/dist/editor/streamer.js +68 -0
  176. package/dist/editor/types.d.ts +250 -1
  177. package/dist/editor/world.d.ts +136 -0
  178. package/dist/editor/world.js +303 -0
  179. package/dist/faction/reputation.d.ts +5 -0
  180. package/dist/faction/reputation.js +5 -0
  181. package/dist/format/distance.d.ts +17 -0
  182. package/dist/format/distance.js +18 -0
  183. package/dist/format/duration.d.ts +27 -0
  184. package/dist/format/duration.js +45 -0
  185. package/dist/format/speed.d.ts +17 -0
  186. package/dist/format/speed.js +25 -0
  187. package/dist/game/breeding.d.ts +161 -0
  188. package/dist/game/breeding.js +181 -0
  189. package/dist/game/cameraConfig.d.ts +451 -0
  190. package/dist/game/cameraConfig.js +1 -0
  191. package/dist/game/connectedPlayers.d.ts +6 -4
  192. package/dist/game/connectedPlayers.js +2 -2
  193. package/dist/game/cosmetics.d.ts +9 -0
  194. package/dist/game/cosmetics.js +16 -0
  195. package/dist/game/defineGame.d.ts +110 -5
  196. package/dist/game/defineGame.js +49 -4
  197. package/dist/game/defineSystem.d.ts +90 -0
  198. package/dist/game/defineSystem.js +47 -0
  199. package/dist/game/dialogue.d.ts +33 -0
  200. package/dist/game/dialogue.js +21 -0
  201. package/dist/game/events.d.ts +63 -13
  202. package/dist/game/events.js +5 -0
  203. package/dist/game/feed.d.ts +33 -0
  204. package/dist/game/feed.js +35 -2
  205. package/dist/game/gamePhase.d.ts +11 -0
  206. package/dist/game/gamePhase.js +16 -0
  207. package/dist/game/leaderboard.d.ts +5 -0
  208. package/dist/game/leaderboard.js +5 -0
  209. package/dist/game/loadout.d.ts +5 -0
  210. package/dist/game/loadout.js +5 -0
  211. package/dist/game/lootFilter.d.ts +2 -0
  212. package/dist/game/lootFilter.js +2 -0
  213. package/dist/game/lootPipeline.d.ts +164 -0
  214. package/dist/game/lootPipeline.js +216 -0
  215. package/dist/game/lootTable.d.ts +19 -2
  216. package/dist/game/lootTable.js +15 -5
  217. package/dist/game/objectives.d.ts +43 -0
  218. package/dist/game/objectives.js +36 -0
  219. package/dist/game/paramLayers.d.ts +148 -0
  220. package/dist/game/paramLayers.js +185 -0
  221. package/dist/game/ping.d.ts +5 -0
  222. package/dist/game/ping.js +5 -0
  223. package/dist/game/playableGame.d.ts +116 -432
  224. package/dist/game/playableGame.js +1 -1
  225. package/dist/game/progression.d.ts +35 -0
  226. package/dist/game/progression.js +19 -0
  227. package/dist/game/quest.d.ts +8 -0
  228. package/dist/game/quest.js +30 -3
  229. package/dist/game/race.d.ts +141 -0
  230. package/dist/game/race.js +169 -0
  231. package/dist/game/recordBook.d.ts +3 -1
  232. package/dist/game/recordBook.js +4 -1
  233. package/dist/game/ruleSelection.d.ts +75 -0
  234. package/dist/game/ruleSelection.js +127 -0
  235. package/dist/game/runDraft.d.ts +5 -0
  236. package/dist/game/runDraft.js +5 -0
  237. package/dist/game/saveStore.d.ts +111 -0
  238. package/dist/game/saveStore.js +312 -0
  239. package/dist/game/snapshotHistory.d.ts +67 -0
  240. package/dist/game/snapshotHistory.js +95 -0
  241. package/dist/game/social.d.ts +38 -1
  242. package/dist/game/social.js +71 -0
  243. package/dist/game/spawnPoints.d.ts +5 -0
  244. package/dist/game/spawnPoints.js +5 -0
  245. package/dist/game/systemRuntime.d.ts +50 -0
  246. package/dist/game/systemRuntime.js +157 -0
  247. package/dist/game/systemSchedule.d.ts +72 -0
  248. package/dist/game/systemSchedule.js +242 -0
  249. package/dist/game/toasts.d.ts +43 -0
  250. package/dist/game/toasts.js +44 -0
  251. package/dist/game/trade.d.ts +10 -0
  252. package/dist/game/trade.js +10 -0
  253. package/dist/game/tribe.d.ts +184 -0
  254. package/dist/game/tribe.js +432 -0
  255. package/dist/game/unlocks.d.ts +8 -0
  256. package/dist/game/unlocks.js +16 -0
  257. package/dist/game/vfxInstance.d.ts +138 -0
  258. package/dist/game/vfxInstance.js +96 -0
  259. package/dist/game/worldItem.d.ts +17 -4
  260. package/dist/game/worldItem.js +17 -4
  261. package/dist/gameplay.d.ts +93 -0
  262. package/dist/gameplay.js +93 -0
  263. package/dist/index.d.ts +5 -14
  264. package/dist/index.js +5 -14
  265. package/dist/input/actionBindings.d.ts +23 -6
  266. package/dist/input/actionBindings.js +23 -6
  267. package/dist/input/axisInput.d.ts +7 -2
  268. package/dist/input/axisInput.js +7 -2
  269. package/dist/input/controlGroups.d.ts +67 -0
  270. package/dist/input/controlGroups.js +41 -0
  271. package/dist/input/touchScheme.d.ts +82 -6
  272. package/dist/input/touchScheme.js +123 -10
  273. package/dist/interaction/lockpick.d.ts +61 -0
  274. package/dist/interaction/lockpick.js +168 -0
  275. package/dist/interaction/proximityPrompt.d.ts +2 -0
  276. package/dist/interaction/proximityPrompt.js +2 -0
  277. package/dist/interaction/qte.d.ts +5 -0
  278. package/dist/interaction/qte.js +5 -0
  279. package/dist/interaction/skillCheck.d.ts +5 -0
  280. package/dist/interaction/skillCheck.js +5 -0
  281. package/dist/inventory/encumbrance.d.ts +69 -0
  282. package/dist/inventory/encumbrance.js +68 -0
  283. package/dist/inventory/inventoryModel.d.ts +12 -0
  284. package/dist/inventory/inventoryModel.js +12 -0
  285. package/dist/inventory/shapedGrid.d.ts +16 -0
  286. package/dist/inventory/shapedGrid.js +16 -0
  287. package/dist/inventory/slotModel.d.ts +10 -0
  288. package/dist/inventory/slotModel.js +10 -0
  289. package/dist/item/durability.d.ts +5 -0
  290. package/dist/item/durability.js +6 -3
  291. package/dist/item/itemIdentity.d.ts +171 -0
  292. package/dist/item/itemIdentity.js +138 -0
  293. package/dist/item/itemInstanceRegistry.d.ts +32 -0
  294. package/dist/item/itemInstanceRegistry.js +34 -0
  295. package/dist/item/itemgen.d.ts +163 -0
  296. package/dist/item/itemgen.js +151 -0
  297. package/dist/item/modularItem.d.ts +5 -0
  298. package/dist/item/modularItem.js +5 -0
  299. package/dist/item/use.d.ts +5 -0
  300. package/dist/item/use.js +5 -0
  301. package/dist/item/useBehavior.d.ts +142 -0
  302. package/dist/item/useBehavior.js +105 -0
  303. package/dist/item/weapon.d.ts +5 -0
  304. package/dist/item/weapon.js +5 -0
  305. package/dist/math/scalar.d.ts +25 -0
  306. package/dist/math/scalar.js +47 -0
  307. package/dist/meta/changelog.d.ts +4 -1
  308. package/dist/meta/changelog.js +39 -2
  309. package/dist/movement/avoidance.d.ts +45 -0
  310. package/dist/movement/avoidance.js +123 -0
  311. package/dist/movement/cameraRig.d.ts +8 -0
  312. package/dist/movement/cameraRig.js +8 -0
  313. package/dist/movement/dash.d.ts +11 -0
  314. package/dist/movement/dash.js +11 -0
  315. package/dist/movement/formation.d.ts +135 -0
  316. package/dist/movement/formation.js +194 -0
  317. package/dist/movement/glideModel.d.ts +5 -0
  318. package/dist/movement/glideModel.js +5 -0
  319. package/dist/movement/grappleSwing.d.ts +5 -0
  320. package/dist/movement/grappleSwing.js +5 -0
  321. package/dist/movement/leaderTrail.d.ts +5 -0
  322. package/dist/movement/leaderTrail.js +5 -0
  323. package/dist/movement/movementModel.d.ts +50 -9
  324. package/dist/movement/movementModel.js +75 -29
  325. package/dist/movement/playerMovement.js +167 -12
  326. package/dist/movement/poseState.d.ts +13 -0
  327. package/dist/movement/poseState.js +16 -0
  328. package/dist/movement/steering.d.ts +8 -0
  329. package/dist/movement/steering.js +18 -0
  330. package/dist/multiplayer/combatSnapshot.d.ts +3 -0
  331. package/dist/multiplayer/combatSnapshot.js +16 -8
  332. package/dist/multiplayer/lagCompensation.d.ts +5 -0
  333. package/dist/multiplayer/lagCompensation.js +5 -0
  334. package/dist/multiplayer/presenceModel.d.ts +11 -1
  335. package/dist/multiplayer/presenceModel.js +17 -1
  336. package/dist/multiplayer/simultaneousCommit.d.ts +3 -0
  337. package/dist/multiplayer/simultaneousCommit.js +3 -0
  338. package/dist/multiplayer.d.ts +11 -0
  339. package/dist/multiplayer.js +11 -0
  340. package/dist/nav/pathFollow.d.ts +57 -0
  341. package/dist/nav/pathFollow.js +109 -0
  342. package/dist/orders/orderKinds.d.ts +156 -0
  343. package/dist/orders/orderKinds.js +197 -0
  344. package/dist/orders/orderQueue.d.ts +208 -0
  345. package/dist/orders/orderQueue.js +211 -0
  346. package/dist/physics/drivableVehicle.d.ts +31 -0
  347. package/dist/physics/drivableVehicle.js +16 -0
  348. package/dist/physics/flightDynamics.d.ts +82 -0
  349. package/dist/physics/flightDynamics.js +165 -0
  350. package/dist/physics/gravityField.d.ts +25 -0
  351. package/dist/physics/gravityField.js +41 -0
  352. package/dist/physics/kinematicVehicle.d.ts +108 -1
  353. package/dist/physics/kinematicVehicle.js +212 -15
  354. package/dist/physics/physicsWorld.d.ts +50 -12
  355. package/dist/physics/physicsWorld.js +120 -122
  356. package/dist/physics/radialImpulse.d.ts +25 -0
  357. package/dist/physics/radialImpulse.js +27 -0
  358. package/dist/physics/vehicleObstacles.d.ts +44 -0
  359. package/dist/physics/vehicleObstacles.js +115 -0
  360. package/dist/procedural.d.ts +3 -0
  361. package/dist/procedural.js +3 -0
  362. package/dist/progression/statGraph.d.ts +116 -0
  363. package/dist/progression/statGraph.js +318 -0
  364. package/dist/puzzle/cellGrid.d.ts +9 -0
  365. package/dist/puzzle/cellGrid.js +9 -0
  366. package/dist/puzzle/fallingPiece.d.ts +9 -0
  367. package/dist/puzzle/fallingPiece.js +9 -0
  368. package/dist/puzzle/nonogram.d.ts +39 -0
  369. package/dist/puzzle/nonogram.js +136 -0
  370. package/dist/random/nameGen.d.ts +5 -0
  371. package/dist/random/nameGen.js +5 -0
  372. package/dist/random/pick.d.ts +8 -0
  373. package/dist/random/pick.js +31 -0
  374. package/dist/random/rng.d.ts +26 -1
  375. package/dist/random/rng.js +32 -9
  376. package/dist/random/seedLink.d.ts +4 -0
  377. package/dist/random/seedLink.js +4 -0
  378. package/dist/relation/keyedValues.d.ts +54 -0
  379. package/dist/relation/keyedValues.js +115 -0
  380. package/dist/relation/thresholds.d.ts +62 -0
  381. package/dist/relation/thresholds.js +62 -0
  382. package/dist/render/color.d.ts +12 -0
  383. package/dist/render/color.js +26 -0
  384. package/dist/render/lookPreset.d.ts +41 -0
  385. package/dist/render/lookPreset.js +29 -0
  386. package/dist/render/postProcessing.d.ts +20 -0
  387. package/dist/render/postProcessing.js +21 -1
  388. package/dist/rules/predicate.d.ts +53 -0
  389. package/dist/rules/predicate.js +64 -0
  390. package/dist/rules/ruleEffects.d.ts +38 -0
  391. package/dist/rules/ruleEffects.js +37 -0
  392. package/dist/rules/triggeredRules.d.ts +160 -0
  393. package/dist/rules/triggeredRules.js +241 -0
  394. package/dist/runtime/adapter.d.ts +55 -3
  395. package/dist/runtime/adapter.js +57 -1
  396. package/dist/runtime/cameraDirector.d.ts +1 -0
  397. package/dist/runtime/cameraDirector.js +1 -0
  398. package/dist/runtime/commandRunner.d.ts +1 -0
  399. package/dist/runtime/commandRunner.js +1 -0
  400. package/dist/runtime/context/combat.d.ts +51 -0
  401. package/dist/runtime/context/combat.js +130 -0
  402. package/dist/runtime/context/combatFx.d.ts +23 -0
  403. package/dist/runtime/context/combatFx.js +161 -0
  404. package/dist/runtime/context/deathLoot.d.ts +30 -0
  405. package/dist/runtime/context/deathLoot.js +39 -0
  406. package/dist/runtime/context/player.d.ts +48 -0
  407. package/dist/runtime/context/player.js +125 -0
  408. package/dist/runtime/context/registries.d.ts +13 -0
  409. package/dist/runtime/context/registries.js +84 -0
  410. package/dist/runtime/context/scene.d.ts +59 -0
  411. package/dist/runtime/context/scene.js +291 -0
  412. package/dist/runtime/context/snapshotCodecs.d.ts +12 -0
  413. package/dist/runtime/context/snapshotCodecs.js +26 -0
  414. package/dist/runtime/context/worldItems.d.ts +21 -0
  415. package/dist/runtime/context/worldItems.js +37 -0
  416. package/dist/runtime/descriptors/baseline.d.ts +48 -0
  417. package/dist/runtime/descriptors/baseline.js +157 -0
  418. package/dist/runtime/descriptors/features.d.ts +79 -0
  419. package/dist/runtime/descriptors/features.js +299 -0
  420. package/dist/runtime/gameContext.d.ts +2 -327
  421. package/dist/runtime/gameContext.js +273 -778
  422. package/dist/runtime/gameContextTypes.d.ts +467 -0
  423. package/dist/runtime/gameContextTypes.js +1 -0
  424. package/dist/runtime/gameRuntime.d.ts +1 -0
  425. package/dist/runtime/gameRuntime.js +1 -0
  426. package/dist/runtime/headlessRunner.d.ts +62 -0
  427. package/dist/runtime/headlessRunner.js +45 -0
  428. package/dist/runtime/hostPersistence.d.ts +12 -0
  429. package/dist/runtime/hostPersistence.js +12 -0
  430. package/dist/runtime/hostPolicy.d.ts +51 -0
  431. package/dist/runtime/hostPolicy.js +68 -0
  432. package/dist/runtime/hostedGameRunner.d.ts +11 -4
  433. package/dist/runtime/hostedGameRunner.js +27 -3
  434. package/dist/runtime/hostedWorldSession.d.ts +15 -5
  435. package/dist/runtime/hostedWorldSession.js +12 -4
  436. package/dist/runtime/inputSnapshot.d.ts +8 -3
  437. package/dist/runtime/inputSnapshot.js +6 -2
  438. package/dist/runtime/motionIntents.d.ts +11 -2
  439. package/dist/runtime/motionIntents.js +16 -2
  440. package/dist/runtime/perContext.d.ts +15 -0
  441. package/dist/runtime/perContext.js +21 -0
  442. package/dist/runtime/runtimeSave.d.ts +79 -0
  443. package/dist/runtime/runtimeSave.js +105 -0
  444. package/dist/runtime/save.d.ts +4 -0
  445. package/dist/runtime/save.js +4 -0
  446. package/dist/runtime/snapshot.d.ts +7 -0
  447. package/dist/runtime/snapshot.js +7 -0
  448. package/dist/runtime/visibility.d.ts +2 -1
  449. package/dist/runtime/visibility.js +2 -1
  450. package/dist/runtime/worldChannel.d.ts +7 -3
  451. package/dist/runtime/worldChannel.js +51 -6
  452. package/dist/runtime/worldMirror.d.ts +14 -2
  453. package/dist/runtime/worldMirror.js +15 -2
  454. package/dist/runtime/worldProjection.d.ts +47 -0
  455. package/dist/runtime/worldProjection.js +52 -0
  456. package/dist/runtime/worldReplication.d.ts +30 -7
  457. package/dist/runtime/worldReplication.js +41 -9
  458. package/dist/runtime/worldSnapshot.d.ts +42 -3
  459. package/dist/runtime/worldSnapshot.js +34 -7
  460. package/dist/scene/assetCatalog.d.ts +26 -0
  461. package/dist/scene/assetCatalog.js +4 -0
  462. package/dist/scene/assetDiagnostics.d.ts +69 -0
  463. package/dist/scene/assetDiagnostics.js +110 -0
  464. package/dist/scene/assetGenerator.d.ts +65 -0
  465. package/dist/scene/assetGenerator.js +64 -0
  466. package/dist/scene/assetSpace.d.ts +137 -0
  467. package/dist/scene/assetSpace.js +257 -0
  468. package/dist/scene/authoredTriggers.d.ts +219 -0
  469. package/dist/scene/authoredTriggers.js +454 -0
  470. package/dist/scene/behaviorRuntime.d.ts +75 -0
  471. package/dist/scene/behaviorRuntime.js +217 -0
  472. package/dist/scene/behaviors.d.ts +14 -2
  473. package/dist/scene/behaviors.js +7 -2
  474. package/dist/scene/bodyBind.d.ts +60 -0
  475. package/dist/scene/bodyBind.js +51 -0
  476. package/dist/scene/builtinSceneKinds.d.ts +5 -0
  477. package/dist/scene/builtinSceneKinds.js +67 -0
  478. package/dist/scene/colliders.d.ts +71 -1
  479. package/dist/scene/colliders.js +145 -1
  480. package/dist/scene/collisionMesh.d.ts +94 -0
  481. package/dist/scene/collisionMesh.js +699 -0
  482. package/dist/scene/companion.d.ts +147 -0
  483. package/dist/scene/companion.js +193 -0
  484. package/dist/scene/entityStats.d.ts +18 -11
  485. package/dist/scene/entityStats.js +21 -16
  486. package/dist/scene/entityStore.d.ts +12 -1
  487. package/dist/scene/entityStore.js +15 -0
  488. package/dist/scene/facing.d.ts +18 -0
  489. package/dist/scene/facing.js +26 -0
  490. package/dist/scene/modelSockets.d.ts +33 -0
  491. package/dist/scene/modelSockets.js +29 -0
  492. package/dist/scene/objectQuery.d.ts +2 -0
  493. package/dist/scene/objectQuery.js +2 -0
  494. package/dist/scene/objectStore.d.ts +4 -0
  495. package/dist/scene/objectStore.js +6 -0
  496. package/dist/scene/possession.d.ts +8 -0
  497. package/dist/scene/possession.js +14 -0
  498. package/dist/scene/roster.d.ts +3 -0
  499. package/dist/scene/roster.js +12 -0
  500. package/dist/scene/sceneKinds.d.ts +256 -0
  501. package/dist/scene/sceneKinds.js +218 -0
  502. package/dist/scene/sceneOwnership.d.ts +169 -0
  503. package/dist/scene/sceneOwnership.js +179 -0
  504. package/dist/scene/sceneRaycast.js +15 -0
  505. package/dist/scene/selectionBookmarks.d.ts +93 -0
  506. package/dist/scene/selectionBookmarks.js +122 -0
  507. package/dist/scene/vehicleSeat.d.ts +68 -0
  508. package/dist/scene/vehicleSeat.js +59 -0
  509. package/dist/sensor/concealment.d.ts +3 -0
  510. package/dist/sensor/concealment.js +3 -0
  511. package/dist/sensor/freezeMonitor.d.ts +1 -0
  512. package/dist/sensor/freezeMonitor.js +1 -0
  513. package/dist/sensor/frustumSensor.d.ts +7 -3
  514. package/dist/sensor/frustumSensor.js +7 -3
  515. package/dist/sensor/hiddenStateProbe.d.ts +5 -2
  516. package/dist/sensor/hiddenStateProbe.js +5 -2
  517. package/dist/sensor/recordingBuffer.d.ts +2 -1
  518. package/dist/sensor/recordingBuffer.js +2 -1
  519. package/dist/sensor/replayLoop.d.ts +6 -2
  520. package/dist/sensor/replayLoop.js +6 -2
  521. package/dist/sensor/revealQuery.d.ts +2 -1
  522. package/dist/sensor/revealQuery.js +2 -1
  523. package/dist/sensor/visionCone.d.ts +10 -3
  524. package/dist/sensor/visionCone.js +10 -3
  525. package/dist/session/contestedChannel.d.ts +1 -0
  526. package/dist/session/contestedChannel.js +1 -0
  527. package/dist/session/extraction.d.ts +1 -0
  528. package/dist/session/extraction.js +1 -0
  529. package/dist/session/roles.d.ts +6 -0
  530. package/dist/session/roles.js +6 -0
  531. package/dist/session/roundState.d.ts +8 -0
  532. package/dist/session/roundState.js +2 -0
  533. package/dist/settings/settingsModel.d.ts +1 -0
  534. package/dist/settings/settingsModel.js +1 -0
  535. package/dist/stats/accumulatorMeter.d.ts +6 -0
  536. package/dist/stats/accumulatorMeter.js +6 -0
  537. package/dist/stats/eventMeter.d.ts +5 -0
  538. package/dist/stats/eventMeter.js +5 -0
  539. package/dist/stats/rollCheck.d.ts +5 -0
  540. package/dist/stats/rollCheck.js +5 -0
  541. package/dist/stats/spawnLevelStats.d.ts +110 -0
  542. package/dist/stats/spawnLevelStats.js +140 -0
  543. package/dist/stats/statModifiers.d.ts +5 -0
  544. package/dist/stats/statModifiers.js +5 -0
  545. package/dist/stats/statPool.d.ts +70 -0
  546. package/dist/stats/statPool.js +59 -0
  547. package/dist/store/changeSignal.d.ts +3 -0
  548. package/dist/store/changeSignal.js +2 -0
  549. package/dist/store/defineKeyedStore.d.ts +38 -0
  550. package/dist/store/defineKeyedStore.js +42 -0
  551. package/dist/store/defineStore.d.ts +33 -0
  552. package/dist/store/defineStore.js +35 -0
  553. package/dist/store/observableKeyedStore.d.ts +7 -0
  554. package/dist/store/observableKeyedStore.js +35 -2
  555. package/dist/survival/decayMeter.d.ts +55 -0
  556. package/dist/survival/decayMeter.js +130 -57
  557. package/dist/survival/moodle.d.ts +2 -0
  558. package/dist/survival/regionHealth.d.ts +6 -0
  559. package/dist/survival/regionHealth.js +6 -3
  560. package/dist/tactics/fallingGrid.d.ts +4 -2
  561. package/dist/tactics/fallingGrid.js +4 -2
  562. package/dist/tactics/predictiveQuery.d.ts +3 -0
  563. package/dist/tactics/predictiveQuery.js +3 -0
  564. package/dist/tactics/snapshot.d.ts +2 -0
  565. package/dist/tactics/snapshot.js +2 -0
  566. package/dist/tactics/surface.d.ts +1 -0
  567. package/dist/tactics/surface.js +1 -0
  568. package/dist/tactics/tacticalGrid.d.ts +1 -0
  569. package/dist/tactics/tacticalGrid.js +1 -0
  570. package/dist/time/beatClock.d.ts +14 -4
  571. package/dist/time/beatClock.js +14 -4
  572. package/dist/time/calendarClock.d.ts +3 -0
  573. package/dist/time/calendarClock.js +3 -0
  574. package/dist/time/idleProgress.d.ts +4 -0
  575. package/dist/time/idleProgress.js +4 -0
  576. package/dist/time/simClock.d.ts +2 -0
  577. package/dist/time/simClock.js +20 -5
  578. package/dist/ui/actionModel.d.ts +110 -0
  579. package/dist/ui/actionModel.js +159 -0
  580. package/dist/ui/gameLayout.d.ts +30 -11
  581. package/dist/ui/gameLayout.js +30 -11
  582. package/dist/ui/hudDocument.d.ts +114 -0
  583. package/dist/ui/hudDocument.js +220 -0
  584. package/dist/ui/hudLayout.d.ts +38 -1
  585. package/dist/ui/hudLayout.js +220 -17
  586. package/dist/ui/selectionModel.d.ts +92 -0
  587. package/dist/ui/selectionModel.js +88 -0
  588. package/dist/ui/tooltipModel.d.ts +63 -0
  589. package/dist/ui/tooltipModel.js +82 -0
  590. package/dist/ui.d.ts +15 -0
  591. package/dist/ui.js +15 -0
  592. package/dist/visibility/bounds.d.ts +8 -1
  593. package/dist/visibility/bounds.js +8 -1
  594. package/dist/visibility/frustum.d.ts +12 -3
  595. package/dist/visibility/frustum.js +12 -3
  596. package/dist/work/jobQueue.d.ts +201 -0
  597. package/dist/work/jobQueue.js +253 -0
  598. package/dist/work/unitTraining.d.ts +58 -0
  599. package/dist/work/unitTraining.js +52 -0
  600. package/dist/world/authoredEntities.d.ts +29 -0
  601. package/dist/world/authoredEntities.js +29 -0
  602. package/dist/world/authoredObjects.d.ts +72 -0
  603. package/dist/world/authoredObjects.js +81 -0
  604. package/dist/world/authoredSpawn.d.ts +39 -0
  605. package/dist/world/authoredSpawn.js +37 -0
  606. package/dist/world/buildSockets.d.ts +152 -0
  607. package/dist/world/buildSockets.js +188 -0
  608. package/dist/world/buildingGenerator.d.ts +18 -0
  609. package/dist/world/buildingGenerator.js +62 -0
  610. package/dist/world/buildings.d.ts +5 -0
  611. package/dist/world/buildings.js +5 -0
  612. package/dist/world/catenary.d.ts +22 -0
  613. package/dist/world/catenary.js +69 -0
  614. package/dist/world/cityBlocks.d.ts +152 -0
  615. package/dist/world/cityBlocks.js +670 -0
  616. package/dist/world/cityContent.d.ts +77 -0
  617. package/dist/world/cityContent.js +261 -0
  618. package/dist/world/cityGeometry.d.ts +114 -0
  619. package/dist/world/cityGeometry.js +573 -0
  620. package/dist/world/cityKind.d.ts +314 -0
  621. package/dist/world/cityKind.js +1578 -0
  622. package/dist/world/features.d.ts +133 -10
  623. package/dist/world/features.js +66 -6
  624. package/dist/world/footprintGrid.d.ts +59 -0
  625. package/dist/world/footprintGrid.js +141 -0
  626. package/dist/world/geometry.d.ts +64 -0
  627. package/dist/world/geometry.js +94 -0
  628. package/dist/world/grassKind.d.ts +51 -0
  629. package/dist/world/grassKind.js +101 -0
  630. package/dist/world/markers.d.ts +50 -4
  631. package/dist/world/markers.js +49 -0
  632. package/dist/world/massing.js +1 -1
  633. package/dist/world/minimapBake.d.ts +78 -0
  634. package/dist/world/minimapBake.js +208 -0
  635. package/dist/world/minimapTrack.d.ts +17 -0
  636. package/dist/world/minimapTrack.js +22 -0
  637. package/dist/world/pathInstances.d.ts +27 -0
  638. package/dist/world/pathInstances.js +27 -0
  639. package/dist/world/pathNetwork.d.ts +159 -0
  640. package/dist/world/pathNetwork.js +684 -0
  641. package/dist/world/pathTerrain.d.ts +80 -0
  642. package/dist/world/pathTerrain.js +142 -0
  643. package/dist/world/place.d.ts +166 -0
  644. package/dist/world/place.js +96 -0
  645. package/dist/world/placeAsset.d.ts +81 -0
  646. package/dist/world/placeAsset.js +79 -0
  647. package/dist/world/placement.d.ts +4 -0
  648. package/dist/world/placement.js +4 -0
  649. package/dist/world/placementController.d.ts +6 -0
  650. package/dist/world/placementController.js +6 -0
  651. package/dist/world/poleLineKind.d.ts +40 -0
  652. package/dist/world/poleLineKind.js +97 -0
  653. package/dist/world/polyline.d.ts +34 -0
  654. package/dist/world/polyline.js +88 -0
  655. package/dist/world/resourceNode.d.ts +109 -0
  656. package/dist/world/resourceNode.js +140 -0
  657. package/dist/world/roads.d.ts +8 -0
  658. package/dist/world/roads.js +38 -0
  659. package/dist/world/scatter.js +2 -20
  660. package/dist/world/scatterRegion.d.ts +164 -0
  661. package/dist/world/scatterRegion.js +409 -0
  662. package/dist/world/sceneShapes.d.ts +43 -0
  663. package/dist/world/sceneShapes.js +1 -0
  664. package/dist/world/segment.d.ts +27 -0
  665. package/dist/world/segment.js +43 -0
  666. package/dist/world/soilKind.d.ts +48 -0
  667. package/dist/world/soilKind.js +92 -0
  668. package/dist/world/spatialSample.d.ts +241 -0
  669. package/dist/world/spatialSample.js +465 -0
  670. package/dist/world/streets.d.ts +29 -0
  671. package/dist/world/streets.js +55 -0
  672. package/dist/world/terraform.d.ts +213 -1
  673. package/dist/world/terraform.js +627 -22
  674. package/dist/world/terrain.d.ts +164 -20
  675. package/dist/world/terrain.js +325 -41
  676. package/dist/world/terrainGuides.d.ts +182 -0
  677. package/dist/world/terrainGuides.js +343 -0
  678. package/dist/world/vec2.d.ts +81 -0
  679. package/dist/world/vec2.js +128 -0
  680. package/dist/world/vec3.d.ts +49 -0
  681. package/dist/world/vec3.js +76 -0
  682. package/dist/world/vegetation.d.ts +71 -0
  683. package/dist/world/vegetation.js +151 -0
  684. package/dist/world/visibilityField.d.ts +154 -0
  685. package/dist/world/visibilityField.js +271 -0
  686. package/dist/world/volumetricClouds.d.ts +52 -0
  687. package/dist/world/volumetricClouds.js +43 -0
  688. package/dist/world/walls.d.ts +9 -0
  689. package/dist/world/walls.js +9 -0
  690. package/dist/world/waterKind.d.ts +55 -0
  691. package/dist/world/waterKind.js +99 -0
  692. package/dist/world.d.ts +141 -0
  693. package/dist/world.js +141 -0
  694. package/package.json +32 -8
  695. package/skills/game-design/SKILL.md +28 -0
  696. package/skills/game-design/agents/openai.yaml +4 -0
  697. package/skills/game-design/references/application-playbook.md +170 -0
  698. package/skills/game-design/references/field-guide.md +288 -0
  699. package/skills/jgengine/SKILL.md +54 -0
  700. package/skills/jgengine/api.md +527 -0
  701. package/skills/jgengine/capabilities.md +131 -0
  702. package/skills/jgengine/recipes/minimal-game.md +88 -0
  703. package/skills/jgengine-combat/SKILL.md +38 -0
  704. package/skills/jgengine-combat/api.md +585 -0
  705. package/skills/jgengine-combat/capabilities.md +213 -0
  706. package/skills/jgengine-combat/recipes/portable-stat-pools.md +122 -0
  707. package/skills/jgengine-combat/reference.md +108 -0
  708. package/skills/jgengine-gameplay/SKILL.md +39 -0
  709. package/skills/jgengine-gameplay/api.md +1920 -0
  710. package/skills/jgengine-gameplay/capabilities.md +454 -0
  711. package/skills/jgengine-gameplay/reference-systems.md +56 -0
  712. package/skills/jgengine-world/SKILL.md +51 -0
  713. package/skills/jgengine-world/api.md +3056 -0
  714. package/skills/jgengine-world/capabilities.md +476 -0
  715. package/skills/jgengine-world/recipes/commanding-entities.md +128 -0
  716. package/skills/jgengine-world/reference.md +261 -0
  717. package/dist/cartridge/runtime.d.ts +0 -15
  718. package/dist/cartridge/runtime.js +0 -556
  719. package/dist/cartridge/spec.d.ts +0 -188
  720. package/dist/cartridge/spec.js +0 -19
  721. package/dist/cartridge/validate.d.ts +0 -2
  722. package/dist/cartridge/validate.js +0 -78
  723. package/dist/store/engineStore.d.ts +0 -22
  724. package/dist/store/engineStore.js +0 -50
  725. package/dist/store/features.d.ts +0 -10
  726. package/dist/store/features.js +0 -23
  727. package/llms.txt +0 -5389
@@ -0,0 +1,670 @@
1
+ /**
2
+ * Block-first city fabric for the `city` studio: turns the synthesized street network into a
3
+ * planar graph, extracts its faces as CLOSED BLOCK POLYGONS (a curved street yields a curved
4
+ * block edge, a diagonal one a diagonal edge), insets each block by its bordering roads'
5
+ * half-widths into curb and land rings (the sidewalk band lives between them), classifies every
6
+ * block (buildable, park, plaza, field, buffer sliver), subdivides buildable block frontage into
7
+ * POLYGONAL PARCELS with street references and corner status, and computes per-parcel BUILDABLE
8
+ * polygons after setbacks. Buildings then derive from parcel geometry instead of nearest-road
9
+ * guesses. Pure seeded math over `cityGeometry` primitives — no rendering, bounded work.
10
+ *
11
+ * @capability city-district road-derived block polygons, parcel subdivision, buildable footprints
12
+ */
13
+ import { clipHalfPlane, dedupeRing, distanceToRing, ensureCcw, extractSimpleLoop, insetRing, pointInPolygon, polygonArea, polygonMeanWidth, polygonSignedArea, } from "./cityGeometry.js";
14
+ const MAX_BLOCKS = 420;
15
+ const WELD_RADIUS = 1.4;
16
+ const RIM_SNAP = 6;
17
+ /** Sidewalk band width by street hierarchy (base = the district's street-level width). */
18
+ export function sidewalkWidthFor(level, base) {
19
+ switch (level) {
20
+ case "boulevard":
21
+ return base * 1.6;
22
+ case "avenue":
23
+ return base * 1.25;
24
+ case "street":
25
+ return base;
26
+ case "lane":
27
+ return base * 0.75;
28
+ }
29
+ }
30
+ function segIntersect(a, b, c, d) {
31
+ const rx = b[0] - a[0];
32
+ const rz = b[1] - a[1];
33
+ const sx = d[0] - c[0];
34
+ const sz = d[1] - c[1];
35
+ const denom = rx * sz - rz * sx;
36
+ if (Math.abs(denom) < 1e-12)
37
+ return null;
38
+ const t = ((c[0] - a[0]) * sz - (c[1] - a[1]) * sx) / denom;
39
+ const u = ((c[0] - a[0]) * rz - (c[1] - a[1]) * rx) / denom;
40
+ if (t < -1e-9 || t > 1 + 1e-9 || u < -1e-9 || u > 1 + 1e-9)
41
+ return null;
42
+ return { t: Math.max(0, Math.min(1, t)), u: Math.max(0, Math.min(1, u)), x: a[0] + rx * t, z: a[1] + rz * t };
43
+ }
44
+ /**
45
+ * Build the planar road graph: split every street polyline at its crossings with other streets,
46
+ * weld endpoints into shared nodes, extend near-rim ends onto the district boundary, close the
47
+ * boundary as edges, and prune dangling chains (dead ends can't border a closed face). Returns
48
+ * the half-edge structure plus the pruned dead-end chains (cul-de-sacs) for corridor carving.
49
+ */
50
+ function buildGraph(streets, hx, hz) {
51
+ // --- 1. collect crossings per street segment (spatial hash to bound pair tests) ---
52
+ const cell = 14;
53
+ const buckets = new Map();
54
+ const segsOf = [];
55
+ for (let s = 0; s < streets.length; s += 1) {
56
+ const pts = streets[s].points;
57
+ const segs = [];
58
+ for (let i = 0; i + 1 < pts.length; i += 1) {
59
+ const a = [pts[i][0], pts[i][1]];
60
+ const b = [pts[i + 1][0], pts[i + 1][1]];
61
+ segs.push({ a, b });
62
+ const minX = Math.min(a[0], b[0]);
63
+ const maxX = Math.max(a[0], b[0]);
64
+ const minZ = Math.min(a[1], b[1]);
65
+ const maxZ = Math.max(a[1], b[1]);
66
+ for (let cx = Math.floor(minX / cell); cx <= Math.floor(maxX / cell); cx += 1) {
67
+ for (let cz = Math.floor(minZ / cell); cz <= Math.floor(maxZ / cell); cz += 1) {
68
+ const key = `${cx}:${cz}`;
69
+ const bucket = buckets.get(key);
70
+ const entry = { street: s, seg: i, a, b };
71
+ if (bucket === undefined)
72
+ buckets.set(key, [entry]);
73
+ else
74
+ bucket.push(entry);
75
+ }
76
+ }
77
+ }
78
+ segsOf.push(segs);
79
+ }
80
+ const splits = streets.map(() => []);
81
+ const seen = new Set();
82
+ for (const bucket of buckets.values()) {
83
+ for (let i = 0; i < bucket.length; i += 1) {
84
+ for (let j = i + 1; j < bucket.length; j += 1) {
85
+ const p = bucket[i];
86
+ const q = bucket[j];
87
+ if (p.street === q.street)
88
+ continue;
89
+ const pairKey = `${p.street}:${p.seg}:${q.street}:${q.seg}`;
90
+ if (seen.has(pairKey))
91
+ continue;
92
+ seen.add(pairKey);
93
+ const hit = segIntersect(p.a, p.b, q.a, q.b);
94
+ if (hit === null)
95
+ continue;
96
+ splits[p.street].push({ seg: p.seg, t: hit.t, x: hit.x, z: hit.z });
97
+ splits[q.street].push({ seg: q.seg, t: hit.u, x: hit.x, z: hit.z });
98
+ }
99
+ }
100
+ }
101
+ // --- 2. weld nodes ---
102
+ const nodes = [];
103
+ const nodeGrid = new Map();
104
+ const nodeAt = (x, z) => {
105
+ const gx = Math.floor(x / 4);
106
+ const gz = Math.floor(z / 4);
107
+ for (let i = gx - 1; i <= gx + 1; i += 1) {
108
+ for (let j = gz - 1; j <= gz + 1; j += 1) {
109
+ const bucket = nodeGrid.get(`${i}:${j}`);
110
+ if (bucket === undefined)
111
+ continue;
112
+ for (const id of bucket) {
113
+ const node = nodes[id];
114
+ if (Math.hypot(node.x - x, node.z - z) <= WELD_RADIUS)
115
+ return id;
116
+ }
117
+ }
118
+ }
119
+ const id = nodes.length;
120
+ nodes.push({ x, z, out: [] });
121
+ const key = `${gx}:${gz}`;
122
+ const bucket = nodeGrid.get(key);
123
+ if (bucket === undefined)
124
+ nodeGrid.set(key, [id]);
125
+ else
126
+ bucket.push(id);
127
+ return id;
128
+ };
129
+ const chains = [];
130
+ for (let s = 0; s < streets.length; s += 1) {
131
+ const pts = streets[s].points;
132
+ if (pts.length < 2)
133
+ continue;
134
+ const cuts = splits[s];
135
+ cuts.sort((a, b) => (a.seg === b.seg ? a.t - b.t : a.seg - b.seg));
136
+ let current = [[pts[0][0], pts[0][1]]];
137
+ let cutIdx = 0;
138
+ for (let i = 0; i + 1 < pts.length; i += 1) {
139
+ while (cutIdx < cuts.length && cuts[cutIdx].seg === i) {
140
+ const cut = cuts[cutIdx];
141
+ cutIdx += 1;
142
+ const point = [cut.x, cut.z];
143
+ const last = current[current.length - 1];
144
+ if (Math.hypot(last[0] - point[0], last[1] - point[1]) > 0.05)
145
+ current.push(point);
146
+ if (current.length >= 2)
147
+ chains.push({ pts: current, street: s });
148
+ current = [point];
149
+ }
150
+ const next = [pts[i + 1][0], pts[i + 1][1]];
151
+ const last = current[current.length - 1];
152
+ if (Math.hypot(last[0] - next[0], last[1] - next[1]) > 0.05)
153
+ current.push(next);
154
+ }
155
+ if (current.length >= 2)
156
+ chains.push({ pts: current, street: s });
157
+ }
158
+ // --- 4. snap near-rim chain ends onto the boundary rectangle ---
159
+ const snapRim = (p) => {
160
+ const dxs = [Math.abs(p[0] + hx), Math.abs(p[0] - hx), Math.abs(p[1] + hz), Math.abs(p[1] - hz)];
161
+ const nearest = Math.min(...dxs);
162
+ if (nearest > RIM_SNAP)
163
+ return null;
164
+ if (nearest === dxs[0])
165
+ return [-hx, Math.max(-hz, Math.min(hz, p[1]))];
166
+ if (nearest === dxs[1])
167
+ return [hx, Math.max(-hz, Math.min(hz, p[1]))];
168
+ if (nearest === dxs[2])
169
+ return [Math.max(-hx, Math.min(hx, p[0])), -hz];
170
+ return [Math.max(-hx, Math.min(hx, p[0])), hz];
171
+ };
172
+ const edges = [];
173
+ const rimNodeIds = new Set();
174
+ for (const chain of chains) {
175
+ const first = chain.pts[0];
176
+ const last = chain.pts[chain.pts.length - 1];
177
+ let start = first;
178
+ let end = last;
179
+ const rimStart = snapRim(first);
180
+ const rimEnd = snapRim(last);
181
+ const pts = chain.pts.map((p) => p);
182
+ if (rimStart !== null) {
183
+ start = rimStart;
184
+ pts[0] = rimStart;
185
+ }
186
+ if (rimEnd !== null) {
187
+ end = rimEnd;
188
+ pts[pts.length - 1] = rimEnd;
189
+ }
190
+ const from = nodeAt(start[0], start[1]);
191
+ const to = nodeAt(end[0], end[1]);
192
+ if (from === to && chain.pts.length < 4)
193
+ continue;
194
+ pts[0] = [nodes[from].x, nodes[from].z];
195
+ pts[pts.length - 1] = [nodes[to].x, nodes[to].z];
196
+ if (rimStart !== null)
197
+ rimNodeIds.add(from);
198
+ if (rimEnd !== null)
199
+ rimNodeIds.add(to);
200
+ edges.push({ from, to, pts: dedupePolyline(pts), street: chain.street, width: streets[chain.street].width });
201
+ }
202
+ // --- 5. boundary edges: corners + rim nodes, walked around the perimeter ---
203
+ const corners = [nodeAt(-hx, -hz), nodeAt(hx, -hz), nodeAt(hx, hz), nodeAt(-hx, hz)];
204
+ rimNodeIds.add(corners[0]);
205
+ rimNodeIds.add(corners[1]);
206
+ rimNodeIds.add(corners[2]);
207
+ rimNodeIds.add(corners[3]);
208
+ const perimParam = (id) => {
209
+ const node = nodes[id];
210
+ const x = node.x;
211
+ const z = node.z;
212
+ // Walk: bottom (z=-hz, x -hx→hx), right (x=hx, z -hz→hz), top (z=hz, x hx→-hx), left.
213
+ if (Math.abs(z + hz) <= RIM_SNAP + 0.01 && Math.abs(x) <= hx + 0.01)
214
+ return x + hx;
215
+ if (Math.abs(x - hx) <= RIM_SNAP + 0.01)
216
+ return 2 * hx + (z + hz);
217
+ if (Math.abs(z - hz) <= RIM_SNAP + 0.01)
218
+ return 2 * hx + 2 * hz + (hx - x);
219
+ return 4 * hx + 2 * hz + (hz - z);
220
+ };
221
+ const rimSorted = [...rimNodeIds].sort((a, b) => perimParam(a) - perimParam(b));
222
+ for (let i = 0; i < rimSorted.length; i += 1) {
223
+ const from = rimSorted[i];
224
+ const to = rimSorted[(i + 1) % rimSorted.length];
225
+ if (from === to)
226
+ continue;
227
+ const a = [nodes[from].x, nodes[from].z];
228
+ const b = [nodes[to].x, nodes[to].z];
229
+ // Insert the corner(s) the arc passes so boundary edges stay on the rectangle.
230
+ const pts = [a];
231
+ const pa = perimParam(from);
232
+ let pb = perimParam(to);
233
+ if (pb <= pa)
234
+ pb += 4 * hx + 4 * hz;
235
+ const cornerParams = [
236
+ { p: 2 * hx, at: [hx, -hz] },
237
+ { p: 2 * hx + 2 * hz, at: [hx, hz] },
238
+ { p: 4 * hx + 2 * hz, at: [-hx, hz] },
239
+ { p: 4 * hx + 4 * hz, at: [-hx, -hz] },
240
+ { p: 6 * hx + 4 * hz, at: [hx, -hz] },
241
+ ];
242
+ for (const corner of cornerParams) {
243
+ if (corner.p > pa + 0.01 && corner.p < pb - 0.01)
244
+ pts.push(corner.at);
245
+ }
246
+ pts.push(b);
247
+ edges.push({ from, to, pts: dedupePolyline(pts), street: -1, width: 0 });
248
+ }
249
+ // --- 6. prune dangling chains (dead ends) so every remaining edge borders two faces ---
250
+ const degree = new Map();
251
+ for (const edge of edges) {
252
+ degree.set(edge.from, (degree.get(edge.from) ?? 0) + 1);
253
+ degree.set(edge.to, (degree.get(edge.to) ?? 0) + 1);
254
+ }
255
+ const alive = edges.map(() => true);
256
+ let pruned = true;
257
+ const deadEnds = [];
258
+ while (pruned) {
259
+ pruned = false;
260
+ for (let i = 0; i < edges.length; i += 1) {
261
+ if (!alive[i])
262
+ continue;
263
+ const edge = edges[i];
264
+ if ((degree.get(edge.from) ?? 0) === 1 || (degree.get(edge.to) ?? 0) === 1) {
265
+ alive[i] = false;
266
+ if (edge.street >= 0)
267
+ deadEnds.push({ pts: edge.pts, width: edge.width });
268
+ degree.set(edge.from, (degree.get(edge.from) ?? 1) - 1);
269
+ degree.set(edge.to, (degree.get(edge.to) ?? 1) - 1);
270
+ pruned = true;
271
+ }
272
+ }
273
+ }
274
+ // --- 7. half-edge structure with angular ordering ---
275
+ const halves = [];
276
+ for (let i = 0; i < edges.length; i += 1) {
277
+ if (!alive[i])
278
+ continue;
279
+ const edge = edges[i];
280
+ if (edge.pts.length < 2)
281
+ continue;
282
+ const fwdAngle = Math.atan2(edge.pts[1][1] - edge.pts[0][1], edge.pts[1][0] - edge.pts[0][0]);
283
+ const n = edge.pts.length;
284
+ const revAngle = Math.atan2(edge.pts[n - 2][1] - edge.pts[n - 1][1], edge.pts[n - 2][0] - edge.pts[n - 1][0]);
285
+ const a = halves.length;
286
+ const b = a + 1;
287
+ halves.push({ from: edge.from, to: edge.to, pts: edge.pts, street: edge.street, twin: b, angle: fwdAngle, visited: false });
288
+ halves.push({ from: edge.to, to: edge.from, pts: [...edge.pts].reverse(), street: edge.street, twin: a, angle: revAngle, visited: false });
289
+ nodes[edge.from].out.push(a);
290
+ nodes[edge.to].out.push(b);
291
+ }
292
+ for (const node of nodes)
293
+ node.out.sort((a, b) => halves[a].angle - halves[b].angle);
294
+ return { nodes, halves, deadEnds };
295
+ }
296
+ function dedupePolyline(pts) {
297
+ const out = [];
298
+ for (const p of pts) {
299
+ const prev = out[out.length - 1];
300
+ if (prev !== undefined && Math.hypot(prev[0] - p[0], prev[1] - p[1]) < 0.05)
301
+ continue;
302
+ out.push(p);
303
+ }
304
+ return out;
305
+ }
306
+ /** Trace interior (CCW) faces of the half-edge graph. */
307
+ function extractFaces(nodes, halves) {
308
+ const faces = [];
309
+ for (let start = 0; start < halves.length; start += 1) {
310
+ if (halves[start].visited)
311
+ continue;
312
+ const polygon = [];
313
+ const streets = [];
314
+ let current = start;
315
+ let guard = 0;
316
+ while (guard < halves.length + 4) {
317
+ guard += 1;
318
+ const half = halves[current];
319
+ if (half.visited)
320
+ break;
321
+ half.visited = true;
322
+ for (let i = 0; i + 1 < half.pts.length; i += 1) {
323
+ polygon.push(half.pts[i]);
324
+ streets.push(half.street);
325
+ }
326
+ // Next: at the destination node, rotate clockwise from the twin.
327
+ const node = nodes[half.to];
328
+ const idx = node.out.indexOf(half.twin);
329
+ if (idx < 0)
330
+ break;
331
+ current = node.out[(idx - 1 + node.out.length) % node.out.length];
332
+ if (current === start) {
333
+ if (polygon.length >= 3 && polygonSignedArea(polygon) > 1)
334
+ faces.push({ polygon, streets });
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ return faces;
340
+ }
341
+ /**
342
+ * Extract closed blocks from the street network: planar faces of the graph, each inset to its
343
+ * curb ring (pavement edge) and land ring (behind the sidewalk band). Faces that collapse under
344
+ * the inset come back with an empty `land` and are the caller's slivers/buffers.
345
+ */
346
+ export function extractBlocks(streets, hx, hz, params) {
347
+ const { nodes, halves, deadEnds } = buildGraph(streets, hx, hz);
348
+ const faces = extractFaces(nodes, halves);
349
+ faces.sort((a, b) => polygonArea(b.polygon) - polygonArea(a.polygon));
350
+ const blocks = [];
351
+ for (const face of faces) {
352
+ if (blocks.length >= MAX_BLOCKS)
353
+ break;
354
+ const ring = dedupeRing(face.polygon, 0.02);
355
+ if (ring.length < 3)
356
+ continue;
357
+ // dedupeRing may drop vertices; rebuild the street map by nearest original vertex.
358
+ const streetsPerEdge = [];
359
+ for (let i = 0; i < ring.length; i += 1) {
360
+ const v = ring[i];
361
+ let best = 0;
362
+ let bestDist = Infinity;
363
+ for (let j = 0; j < face.polygon.length; j += 1) {
364
+ const o = face.polygon[j];
365
+ const d = Math.hypot(o[0] - v[0], o[1] - v[1]);
366
+ if (d < bestDist) {
367
+ bestDist = d;
368
+ best = j;
369
+ }
370
+ }
371
+ streetsPerEdge.push(face.streets[best] ?? -1);
372
+ }
373
+ const ccw = ensureCcw(ring);
374
+ const flipped = ccw[0] !== ring[0] || ccw[1] !== ring[1];
375
+ const edgeStreets = flipped ? remapReversedEdgeStreets(streetsPerEdge) : streetsPerEdge;
376
+ const curbDist = ccw.map((_, i) => {
377
+ const s = edgeStreets[i] ?? -1;
378
+ return s >= 0 ? streets[s].width / 2 + params.curbMargin : 0.8;
379
+ });
380
+ const landDist = ccw.map((_, i) => {
381
+ const s = edgeStreets[i] ?? -1;
382
+ if (s < 0)
383
+ return 0.8;
384
+ const street = streets[s];
385
+ const walk = street.sidewalk ? sidewalkWidthFor(street.level, params.sidewalkBase) : 0;
386
+ return street.width / 2 + params.curbMargin + walk;
387
+ });
388
+ const curb = insetRing(ccw, curbDist);
389
+ const land = curb.length >= 3 ? insetRing(ccw, landDist) : [];
390
+ blocks.push({ face: ccw, faceStreets: edgeStreets, curb, land });
391
+ }
392
+ return { blocks, deadEnds };
393
+ }
394
+ /** Reverse per-edge street labels when a ring got rewound to CCW. */
395
+ function remapReversedEdgeStreets(streetsPerEdge) {
396
+ const n = streetsPerEdge.length;
397
+ const out = new Array(n).fill(-1);
398
+ // Edge i of the reversed ring (v'_i = v_{n-1-i}) runs v_{n-1-i} → v_{n-2-i}: original edge n-2-i.
399
+ for (let i = 0; i < n; i += 1)
400
+ out[i] = streetsPerEdge[(2 * n - 2 - i) % n];
401
+ return out;
402
+ }
403
+ /** Arc-length station lookup along a closed ring. */
404
+ export class RingWalker {
405
+ ring;
406
+ cum;
407
+ total;
408
+ constructor(ring) {
409
+ this.ring = ring;
410
+ this.cum = [0];
411
+ let sum = 0;
412
+ for (let i = 0; i < ring.length; i += 1) {
413
+ const a = ring[i];
414
+ const b = ring[(i + 1) % ring.length];
415
+ sum += Math.hypot(b[0] - a[0], b[1] - a[1]);
416
+ this.cum.push(sum);
417
+ }
418
+ this.total = sum;
419
+ }
420
+ /** Point, unit tangent, and inward normal (CCW ring: interior on the left) at arc length `s`. */
421
+ at(s) {
422
+ const wrapped = ((s % this.total) + this.total) % this.total;
423
+ let lo = 0;
424
+ let hi = this.ring.length;
425
+ while (lo + 1 < hi) {
426
+ const mid = (lo + hi) >> 1;
427
+ if (this.cum[mid] <= wrapped)
428
+ lo = mid;
429
+ else
430
+ hi = mid;
431
+ }
432
+ const a = this.ring[lo % this.ring.length];
433
+ const b = this.ring[(lo + 1) % this.ring.length];
434
+ const len = this.cum[lo + 1] - this.cum[lo] || 1;
435
+ const t = (wrapped - this.cum[lo]) / len;
436
+ const tx = (b[0] - a[0]) / len;
437
+ const tz = (b[1] - a[1]) / len;
438
+ return {
439
+ p: [a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t],
440
+ tangent: [tx, tz],
441
+ normal: [-tz, tx],
442
+ edge: lo % this.ring.length,
443
+ };
444
+ }
445
+ /** Ring vertex indices whose arc position lies strictly inside (s0, s1) (wrapping). */
446
+ verticesBetween(s0, s1) {
447
+ const out = [];
448
+ const span = s1 - s0;
449
+ for (let i = 0; i < this.ring.length; i += 1) {
450
+ let d = this.cum[i] - s0;
451
+ d = ((d % this.total) + this.total) % this.total;
452
+ if (d > 0.01 && d < span - 0.01)
453
+ out.push(i);
454
+ }
455
+ out.sort((a, b) => {
456
+ const da = (((this.cum[a] - s0) % this.total) + this.total) % this.total;
457
+ const db = (((this.cum[b] - s0) % this.total) + this.total) % this.total;
458
+ return da - db;
459
+ });
460
+ return out;
461
+ }
462
+ /** Interior angle (radians) at ring vertex i — π on straight runs, < π at convex corners. */
463
+ interiorAngle(i) {
464
+ const prev = this.ring[(i + this.ring.length - 1) % this.ring.length];
465
+ const cur = this.ring[i];
466
+ const next = this.ring[(i + 1) % this.ring.length];
467
+ const a = Math.atan2(prev[1] - cur[1], prev[0] - cur[0]);
468
+ const b = Math.atan2(next[1] - cur[1], next[0] - cur[0]);
469
+ let diff = a - b;
470
+ while (diff < 0)
471
+ diff += Math.PI * 2;
472
+ while (diff >= Math.PI * 2)
473
+ diff -= Math.PI * 2;
474
+ return diff;
475
+ }
476
+ }
477
+ /**
478
+ * Cut one polygonal parcel out of a block land ring: the frontage arc between two stations,
479
+ * extruded inward along the station normals, clipped back to the block. Returns null when the
480
+ * strip collapses (over-tight corner, sliver arc).
481
+ */
482
+ export function cutParcel(walker, blockRing, cut) {
483
+ const a = walker.at(cut.s0);
484
+ const b = walker.at(cut.s1);
485
+ const outer = [a.p];
486
+ const inner = [[a.p[0] + a.normal[0] * cut.depth, a.p[1] + a.normal[1] * cut.depth]];
487
+ for (const vi of walker.verticesBetween(cut.s0, cut.s1)) {
488
+ const v = walker.ring[vi];
489
+ outer.push(v);
490
+ // Vertex normal: bisector of adjacent edge normals.
491
+ const prev = walker.ring[(vi + walker.ring.length - 1) % walker.ring.length];
492
+ const next = walker.ring[(vi + 1) % walker.ring.length];
493
+ const l0 = Math.hypot(v[0] - prev[0], v[1] - prev[1]) || 1;
494
+ const l1 = Math.hypot(next[0] - v[0], next[1] - v[1]) || 1;
495
+ const n0 = [-(v[1] - prev[1]) / l0, (v[0] - prev[0]) / l0];
496
+ const n1 = [-(next[1] - v[1]) / l1, (next[0] - v[0]) / l1];
497
+ let nx = n0[0] + n1[0];
498
+ let nz = n0[1] + n1[1];
499
+ const nl = Math.hypot(nx, nz) || 1;
500
+ nx /= nl;
501
+ nz /= nl;
502
+ inner.push([v[0] + nx * cut.depth, v[1] + nz * cut.depth]);
503
+ }
504
+ outer.push(b.p);
505
+ inner.push([b.p[0] + b.normal[0] * cut.depth, b.p[1] + b.normal[1] * cut.depth]);
506
+ inner.reverse();
507
+ const raw = extractSimpleLoop([...outer, ...inner]);
508
+ if (raw.length < 3)
509
+ return null;
510
+ // Conform to the block: deep cuts on thin or concave blocks can push vertices — or whole edge
511
+ // spans between two near-boundary vertices — across a notch to the outside.
512
+ const clipped = conformPolygonToRing(raw, blockRing);
513
+ if (clipped.length < 3 || polygonArea(clipped) < 4)
514
+ return null;
515
+ return clipped;
516
+ }
517
+ /**
518
+ * Conform a polygon to a containing ring: sample every edge (a straight edge can bulge across a
519
+ * concave notch even when its endpoints hug the boundary), then pull each escaping point to its
520
+ * nearest boundary point. Callers re-run this after any operation that adds mid-edge vertices.
521
+ */
522
+ export function conformPolygonToRing(poly, ring) {
523
+ const refined = [];
524
+ for (let i = 0; i < poly.length; i += 1) {
525
+ const a = poly[i];
526
+ const b = poly[(i + 1) % poly.length];
527
+ refined.push(a);
528
+ const len = Math.hypot(b[0] - a[0], b[1] - a[1]);
529
+ const steps = Math.min(24, Math.floor(len / 2.5));
530
+ for (let s = 1; s <= steps; s += 1) {
531
+ const t = s / (steps + 1);
532
+ const x = a[0] + (b[0] - a[0]) * t;
533
+ const z = a[1] + (b[1] - a[1]) * t;
534
+ if (!pointInPolygon(ring, x, z) && distanceToRing(ring, x, z) > 0.3)
535
+ refined.push([x, z]);
536
+ }
537
+ }
538
+ const out = [];
539
+ for (const [x, z] of refined) {
540
+ if (pointInPolygon(ring, x, z) || distanceToRing(ring, x, z) <= 0.4) {
541
+ out.push([x, z]);
542
+ continue;
543
+ }
544
+ // Pull the vertex to the nearest point on the ring boundary.
545
+ let bestX = x;
546
+ let bestZ = z;
547
+ let best = Infinity;
548
+ for (let i = 0; i < ring.length; i += 1) {
549
+ const a = ring[i];
550
+ const b = ring[(i + 1) % ring.length];
551
+ const vx = b[0] - a[0];
552
+ const vz = b[1] - a[1];
553
+ const len2 = vx * vx + vz * vz;
554
+ const t = len2 === 0 ? 0 : Math.max(0, Math.min(1, ((x - a[0]) * vx + (z - a[1]) * vz) / len2));
555
+ const px = a[0] + vx * t;
556
+ const pz = a[1] + vz * t;
557
+ const d = Math.hypot(x - px, z - pz);
558
+ if (d < best) {
559
+ best = d;
560
+ bestX = px;
561
+ bestZ = pz;
562
+ }
563
+ }
564
+ out.push([bestX, bestZ]);
565
+ }
566
+ return extractSimpleLoop(out);
567
+ }
568
+ /**
569
+ * Buildable polygon for a parcel: the parcel clipped by the front setback (a half-plane pushed in
570
+ * from the frontage chord), side gaps along both cut lines, and a rear margin. Works on any
571
+ * parcel polygon — no vertex correspondence needed.
572
+ */
573
+ export function buildablePolygon(parcel, frontA, frontB, frontSetback, sideGap, rearMargin, depth) {
574
+ const tx = frontB[0] - frontA[0];
575
+ const tz = frontB[1] - frontA[1];
576
+ const len = Math.hypot(tx, tz) || 1;
577
+ const t = [tx / len, tz / len];
578
+ const n = [-t[1], t[0]]; // inward (CCW ring, interior left of frontage direction)
579
+ let poly = [...parcel];
580
+ // Front: keep the side of the chord's inward offset. dot(p, n) >= dot(A, n) + setback.
581
+ const dFront = frontA[0] * n[0] + frontA[1] * n[1];
582
+ poly = clipHalfPlane(poly, [-n[0], -n[1]], -(dFront + frontSetback));
583
+ if (poly.length < 3)
584
+ return [];
585
+ // Rear: dot(p, n) <= dFront + depth - rearMargin.
586
+ poly = clipHalfPlane(poly, n, dFront + Math.max(frontSetback + 1, depth - rearMargin));
587
+ if (poly.length < 3)
588
+ return [];
589
+ // Sides: keep between the two station cut lines pushed in by sideGap.
590
+ const dA = frontA[0] * t[0] + frontA[1] * t[1];
591
+ const dB = frontB[0] * t[0] + frontB[1] * t[1];
592
+ poly = clipHalfPlane(poly, [-t[0], -t[1]], -(dA + sideGap));
593
+ if (poly.length < 3)
594
+ return [];
595
+ poly = clipHalfPlane(poly, t, dB - sideGap);
596
+ return poly.length >= 3 ? poly : [];
597
+ }
598
+ /**
599
+ * Carve dead-end road corridors (cul-de-sac lanes pruned from the face graph) out of a polygon:
600
+ * for every corridor segment crossing it, keep the polygon piece on the side where `keep` lies.
601
+ */
602
+ export function carveCorridors(poly, keep, corridors, margin) {
603
+ let current = poly;
604
+ for (const corridor of corridors) {
605
+ const half = corridor.width / 2 + margin;
606
+ for (let i = 0; i + 1 < corridor.pts.length && current.length >= 3; i += 1) {
607
+ const a = corridor.pts[i];
608
+ const b = corridor.pts[i + 1];
609
+ const tx = b[0] - a[0];
610
+ const tz = b[1] - a[1];
611
+ const len = Math.hypot(tx, tz);
612
+ if (len < 0.5)
613
+ continue;
614
+ const n = [-tz / len, tx / len];
615
+ const dKeep = keep[0] * n[0] + keep[1] * n[1];
616
+ const dLine = a[0] * n[0] + a[1] * n[1];
617
+ // Quick reject: does the segment's corridor even touch the polygon bbox region?
618
+ let near = false;
619
+ for (const [x, z] of current) {
620
+ const along = ((x - a[0]) * tx + (z - a[1]) * tz) / len;
621
+ if (along < -half || along > len + half)
622
+ continue;
623
+ if (Math.abs(x * n[0] + z * n[1] - dLine) <= half + 0.5) {
624
+ near = true;
625
+ break;
626
+ }
627
+ }
628
+ if (!near)
629
+ continue;
630
+ if (dKeep >= dLine)
631
+ current = clipHalfPlane(current, [-n[0], -n[1]], -(dLine + half));
632
+ else
633
+ current = clipHalfPlane(current, n, dLine - half);
634
+ }
635
+ }
636
+ return current.length >= 3 ? current : [];
637
+ }
638
+ /** Deterministic sliver verdict shared by classification and tests. */
639
+ export function isSliverBlock(land, minArea, minWidth) {
640
+ if (land.length < 3)
641
+ return true;
642
+ return polygonArea(land) < minArea || polygonMeanWidth(land) < minWidth;
643
+ }
644
+ /** Sample deterministic points inside a polygon by seeded rejection over its bounds. */
645
+ export function pointsInPolygon(ring, count, rng, margin = 1.5) {
646
+ if (ring.length < 3 || count <= 0)
647
+ return [];
648
+ let minX = Infinity;
649
+ let minZ = Infinity;
650
+ let maxX = -Infinity;
651
+ let maxZ = -Infinity;
652
+ for (const [x, z] of ring) {
653
+ minX = Math.min(minX, x);
654
+ minZ = Math.min(minZ, z);
655
+ maxX = Math.max(maxX, x);
656
+ maxZ = Math.max(maxZ, z);
657
+ }
658
+ const out = [];
659
+ const maxTries = count * 12;
660
+ for (let i = 0; i < maxTries && out.length < count; i += 1) {
661
+ const x = minX + rng() * (maxX - minX);
662
+ const z = minZ + rng() * (maxZ - minZ);
663
+ if (!pointInPolygon(ring, x, z))
664
+ continue;
665
+ if (margin > 0 && distanceToRing(ring, x, z) < margin)
666
+ continue;
667
+ out.push([x, z]);
668
+ }
669
+ return out;
670
+ }