feat(three/tree-layer): crop/fruit visualisation, organic canopy & per-tree variety#533
Merged
charlieforward9 merged 2 commits intomasterfrom Feb 28, 2026
Merged
Conversation
cae6f89 to
b074a48
Compare
…-tree variety
## TreeLayer additions
### Crop / fruit / flower (getCrop)
- New `CropConfig` type exported from index: `{ color, count, droppedCount?, radius }`
- New `getCrop` accessor (returns `CropConfig | null`) renders two new sub-layers:
- `live-crops` — spheres placed in the outer 90–102 % shell of the canopy
ellipsoid (equatorial band only, never on crown or base)
- `dropped-crops` — uniform-disk ground scatter within canopy footprint,
rendered at 45 % opacity for a fallen/weathered look
- Crop positions are seeded deterministically from each tree's lng/lat via
splitmix32 so they are stable across re-renders and sizeScale changes
- `sizeScale` applied to crop sphere radius in lock-step with all other dims
### Organic canopy geometry (tree-geometry.ts)
- `jitterSmooth()`: per-species smooth low-frequency sinusoidal vertex
displacement (4 waves, 2–5 cycles per axis) — no mesh gaps, zero runtime cost
- oak ±18 %, cherry ±20 %, birch ±14 %, palm ±10 %
- Pine tiers: per-tier radius ±20 %, height ±15 %, lateral drift that
increases from zero at the bottom tier (clean trunk join) to ±0.10 units
at the top (windswept silhouette)
### Per-tree variety (tree-layer.ts)
- `getOrientation [0, angle, 0]`: position-derived yaw (vertical-axis bearing)
so pine tiers face different compass directions per tree
- Asymmetric XY canopy scale ±15 % from position hash: every canopy is a
unique oval, never a perfect circle in plan view
- Both are zero extra draw calls — pure GPU instancing attributes
### Bug fixes
- Pine level→mesh bug: previously all pine instances used the first level's
mesh; now one sub-layer is created per branch-level count with filtered data
- `CANOPY_TRUNK_OVERLAP` increased 0.10→0.22: canopy base embeds 22 % of
canopy height into trunk, reliably hiding the trunk-top disk
- `updateTriggers` added to trunk and canopy sub-layers so sizeScale slider
correctly invalidates `getScale` attribute buffers
- Crop phi clamped to equatorial band (cosPhi in [-0.80, 0.80]) so crops
never appear on the canopy crown
### Wild-forest example
- 9 forest zones including citrus orchard and almond grove with crop configs
- `showCrops` checkbox toggle + `sizeScale` slider
- `vite.config.ts` aliases `@deck.gl-community/three` to TypeScript source
for instant HMR without dist rebuild
### Docs
- README fully updated with `CropConfig` type reference, crop placement
details, updated feature list, and wild-forest example instructions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b074a48 to
18779ef
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
getCrop/CropConfigAPI — new accessor renders live fruit/flower spheres nestled into the outer canopy shell and semi-transparent dropped fruit on the ground; positions are deterministically seeded from each tree's lng/lat so they are stable across re-rendersjitterSmooth) baked into each species mesh at init time; species-tuned magnitudes (oak ±18 %, cherry ±20 %, birch ±14 %, palm ±10 %); no mesh gaps, zero runtime costCANOPY_TRUNK_OVERLAPraised to 0.22,updateTriggersfor sizeScale on trunk + canopy sub-layers, crop phi clamped to equatorial band (no crops on canopy crown)vite.config.tsfor source-aliased HMR without dist rebuildCropConfigtype reference, crop placement mechanics, updated feature list, example run instructionsTest plan
yarn startfromexamples/three/wild-forest/and verify all 9 zones rendersizeScaleslider — both trunk and canopy scale correctly at all values🤖 Generated with Claude Code