@deck.gl-community/three 9.2.5 → 9.2.8
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.
- package/README.md +127 -13
- package/dist/index.cjs +249 -33
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/tree-layer/tree-geometry.d.ts +19 -2
- package/dist/tree-layer/tree-geometry.d.ts.map +1 -1
- package/dist/tree-layer/tree-geometry.js +109 -18
- package/dist/tree-layer/tree-geometry.js.map +1 -1
- package/dist/tree-layer/tree-layer.d.ts +58 -4
- package/dist/tree-layer/tree-layer.d.ts.map +1 -1
- package/dist/tree-layer/tree-layer.js +231 -28
- package/dist/tree-layer/tree-layer.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +1 -1
- package/src/tree-layer/tree-geometry.ts +119 -18
- package/src/tree-layer/tree-layer.ts +351 -28
package/dist/index.cjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/tree-layer/tree-layer.ts", "../src/tree-layer/tree-geometry.ts"],
|
|
4
|
-
"sourcesContent": ["// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport type {TreeLayerProps, TreeType, Season} from './tree-layer/tree-layer';\nexport {TreeLayer} from './tree-layer/tree-layer';\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {CompositeLayer} from '@deck.gl/core';\nimport type {Color, DefaultProps, LayerProps, Position} from '@deck.gl/core';\nimport {SimpleMeshLayer} from '@deck.gl/mesh-layers';\nimport {\n createTrunkMesh,\n createPineCanopyMesh,\n createOakCanopyMesh,\n createPalmCanopyMesh,\n createBirchCanopyMesh,\n createCherryCanopyMesh\n} from './tree-geometry';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Tree species / silhouette variant. */\nexport type TreeType = 'pine' | 'oak' | 'palm' | 'birch' | 'cherry';\n\n/** Season that drives default canopy colour when no explicit colour is supplied. */\nexport type Season = 'spring' | 'summer' | 'autumn' | 'winter';\n\n// ---------------------------------------------------------------------------\n// Default colours\n// ---------------------------------------------------------------------------\n\n/** Default trunk colours per tree type [r, g, b, a]. */\nconst DEFAULT_TRUNK_COLORS: Record<TreeType, Color> = {\n pine: [80, 50, 20, 255],\n oak: [91, 57, 23, 255],\n palm: [140, 100, 55, 255],\n birch: [220, 215, 205, 255], // white-grey birch bark\n cherry: [100, 60, 40, 255]\n};\n\n/** Default canopy colours per (tree type, season) [r, g, b, a]. */\nconst DEFAULT_CANOPY_COLORS: Record<TreeType, Record<Season, Color>> = {\n pine: {\n spring: [34, 100, 34, 255],\n summer: [0, 64, 0, 255],\n autumn: [0, 64, 0, 255], // evergreen \u2014 no colour change\n winter: [0, 55, 0, 255]\n },\n oak: {\n spring: [100, 180, 80, 255],\n summer: [34, 120, 15, 255],\n autumn: [180, 85, 20, 255],\n winter: [100, 80, 60, 160] // sparse, semi-transparent\n },\n palm: {\n spring: [50, 160, 50, 255],\n summer: [20, 145, 20, 255],\n autumn: [55, 150, 30, 255],\n winter: [40, 130, 30, 255]\n },\n birch: {\n spring: [150, 210, 110, 255],\n summer: [80, 160, 60, 255],\n autumn: [230, 185, 40, 255],\n winter: [180, 180, 170, 90] // near-bare\n },\n cherry: {\n spring: [255, 180, 205, 255], // pink blossom\n summer: [50, 140, 50, 255],\n autumn: [200, 60, 40, 255],\n winter: [120, 90, 80, 110] // bare\n }\n};\n\n// ---------------------------------------------------------------------------\n// Pre-built unit-scale meshes (shared across all layer instances)\n// ---------------------------------------------------------------------------\n\nconst TRUNK_MESH = createTrunkMesh();\n\nconst CANOPY_MESHES: Record<TreeType, ReturnType<typeof createTrunkMesh>> = {\n pine: createPineCanopyMesh(3),\n oak: createOakCanopyMesh(),\n palm: createPalmCanopyMesh(),\n birch: createBirchCanopyMesh(),\n cherry: createCherryCanopyMesh()\n};\n\nconst ALL_TREE_TYPES: TreeType[] = ['pine', 'oak', 'palm', 'birch', 'cherry'];\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\ntype _TreeLayerProps<DataT> = {\n /** Source data. */\n data: DataT[];\n\n /** Longitude/latitude position of the tree base. */\n getPosition?: (d: DataT) => Position;\n\n /** Base elevation (metres above sea level). @default 0 */\n getElevation?: (d: DataT) => number;\n\n /**\n * Silhouette / species variant.\n * 'pine' \u2013 layered conical tiers (evergreen)\n * 'oak' \u2013 wide spherical canopy\n * 'palm' \u2013 tall thin trunk with flat crown\n * 'birch' \u2013 narrow oval canopy, pale bark\n * 'cherry' \u2013 round lush canopy, seasonal blossom\n * @default 'pine'\n */\n getTreeType?: (d: DataT) => TreeType;\n\n /**\n * Total tree height in metres.\n * @default 10\n */\n getHeight?: (d: DataT) => number;\n\n /**\n * Fraction of total height occupied by the trunk (0\u20131).\n * @default 0.35\n */\n getTrunkHeightFraction?: (d: DataT) => number;\n\n /**\n * Trunk base radius in metres.\n * @default 0.5\n */\n getTrunkRadius?: (d: DataT) => number;\n\n /**\n * Horizontal radius of the canopy in metres.\n * @default 3\n */\n getCanopyRadius?: (d: DataT) => number;\n\n /**\n * Explicit trunk colour [r, g, b, a].\n * When null the species default is used.\n * @default null\n */\n getTrunkColor?: (d: DataT) => Color | null;\n\n /**\n * Explicit canopy colour [r, g, b, a].\n * When null the species \u00D7 season default is used.\n * @default null\n */\n getCanopyColor?: (d: DataT) => Color | null;\n\n /**\n * Season used to pick the default canopy colour when no explicit colour is provided.\n * @default 'summer'\n */\n getSeason?: (d: DataT) => Season;\n\n /**\n * Number of cone tiers for pine trees (1\u20135).\n * Higher values produce a denser layered silhouette.\n * @default 3\n */\n getBranchLevels?: (d: DataT) => number;\n\n /**\n * Global size multiplier applied to all dimensions.\n * @default 1\n */\n sizeScale?: number;\n};\n\nexport type TreeLayerProps<DataT = unknown> = _TreeLayerProps<DataT> & LayerProps;\n\nconst defaultProps: DefaultProps<TreeLayerProps<unknown>> = {\n getPosition: {type: 'accessor', value: (d: any) => d.position},\n getElevation: {type: 'accessor', value: (_d: any) => 0},\n getTreeType: {type: 'accessor', value: (_d: any) => 'pine' as TreeType},\n getHeight: {type: 'accessor', value: (_d: any) => 10},\n getTrunkHeightFraction: {type: 'accessor', value: (_d: any) => 0.35},\n getTrunkRadius: {type: 'accessor', value: (_d: any) => 0.5},\n getCanopyRadius: {type: 'accessor', value: (_d: any) => 3},\n getTrunkColor: {type: 'accessor', value: (_d: any) => null},\n getCanopyColor: {type: 'accessor', value: (_d: any) => null},\n getSeason: {type: 'accessor', value: (_d: any) => 'summer' as Season},\n getBranchLevels: {type: 'accessor', value: (_d: any) => 3},\n sizeScale: {type: 'number', value: 1, min: 0}\n};\n\n// ---------------------------------------------------------------------------\n// State\n// ---------------------------------------------------------------------------\n\ntype TreeLayerState = {\n grouped: Record<TreeType, unknown[]>;\n pineMeshes: Record<number, ReturnType<typeof createPineCanopyMesh>>;\n};\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * **TreeLayer** \u2014 A parametric, Three.js-powered deck.gl layer that renders\n * richly configurable 3D trees at geographic positions.\n *\n * Each tree is composed of two `SimpleMeshLayer` instances: one for the trunk\n * (a tapered cylinder) and one for the canopy (silhouette depends on `getTreeType`).\n * All geometry is generated procedurally using Three.js `BufferGeometry` primitives\n * and converted to the `@loaders.gl/schema` `MeshGeometry` format accepted by\n * `SimpleMeshLayer`.\n *\n * Parametric controls include:\n * - Species / silhouette (`getTreeType`)\n * - Total height (`getHeight`) and trunk-to-canopy proportion (`getTrunkHeightFraction`)\n * - Trunk and canopy radii (`getTrunkRadius`, `getCanopyRadius`)\n * - Explicit or season-driven colours (`getTrunkColor`, `getCanopyColor`, `getSeason`)\n * - Pine tier density (`getBranchLevels`)\n * - Global scale factor (`sizeScale`)\n */\nexport class TreeLayer<DataT = unknown, ExtraPropsT extends {} = {}> extends CompositeLayer<\n ExtraPropsT & Required<_TreeLayerProps<DataT>>\n> {\n static layerName = 'TreeLayer';\n static defaultProps = defaultProps;\n\n declare state: TreeLayerState;\n\n initializeState() {\n this.state = {\n grouped: {pine: [], oak: [], palm: [], birch: [], cherry: []},\n pineMeshes: {}\n };\n }\n\n updateState({props, changeFlags}) {\n if (changeFlags.dataChanged || changeFlags.updateTriggersChanged) {\n const {data, getTreeType, getBranchLevels} = props;\n const grouped: Record<TreeType, DataT[]> = {\n pine: [],\n oak: [],\n palm: [],\n birch: [],\n cherry: []\n };\n\n // Build per-level pine mesh cache\n const pineMeshes: Record<number, ReturnType<typeof createPineCanopyMesh>> = {};\n\n for (const d of data as DataT[]) {\n const type = getTreeType(d) as TreeType;\n if (grouped[type]) grouped[type].push(d);\n if (type === 'pine') {\n const levels = Math.max(1, Math.min(5, Math.round(getBranchLevels(d) as number)));\n pineMeshes[levels] ??= createPineCanopyMesh(levels);\n }\n }\n\n this.setState({grouped, pineMeshes});\n }\n }\n\n /** Build a single canopy sub-layer for one tree type. */\n private _buildCanopyLayer(type: TreeType) {\n const {\n getPosition,\n getElevation,\n getHeight,\n getTrunkHeightFraction,\n getCanopyRadius,\n getCanopyColor,\n getSeason,\n sizeScale\n } = this.props; // eslint-disable-line max-len\n const {grouped, pineMeshes} = this.state;\n\n let mesh = CANOPY_MESHES[type];\n if (type === 'pine') {\n const firstLevel = Object.keys(pineMeshes)[0];\n if (firstLevel) mesh = pineMeshes[Number(firstLevel)];\n }\n\n return new SimpleMeshLayer(\n this.getSubLayerProps({\n id: `canopy-${type}`,\n data: grouped[type],\n mesh,\n getPosition: (d) => {\n const pos = getPosition(d);\n const elevation = getElevation(d) || 0;\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n return [pos[0], pos[1], elevation + h * f];\n },\n getScale: (d) => {\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const r = getCanopyRadius(d) * sizeScale;\n return [r, r, h * (1 - f)];\n },\n getColor: (d) => {\n const explicit = getCanopyColor(d);\n if (explicit) return explicit;\n const season = getSeason(d) || 'summer';\n return DEFAULT_CANOPY_COLORS[type][season];\n },\n pickable: this.props.pickable,\n material: {ambient: 0.4, diffuse: 0.7, shininess: 12}\n })\n );\n }\n\n renderLayers() {\n const {\n getPosition,\n getElevation,\n getTreeType,\n getHeight,\n getTrunkHeightFraction,\n getTrunkRadius,\n getTrunkColor,\n sizeScale\n } = this.props;\n\n const {grouped} = this.state;\n\n // -----------------------------------------------------------------------\n // 1. Trunk layer \u2014 one layer for ALL tree types, shared cylinder geometry\n // -----------------------------------------------------------------------\n const trunkLayer = new SimpleMeshLayer(\n this.getSubLayerProps({\n id: 'trunks',\n data: this.props.data,\n mesh: TRUNK_MESH,\n getPosition: (d) => {\n const pos = getPosition(d);\n return [pos[0], pos[1], getElevation(d) || 0];\n },\n getScale: (d) => {\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const r = getTrunkRadius(d) * sizeScale;\n return [r, r, h * f];\n },\n getColor: (d) => {\n const explicit = getTrunkColor(d);\n if (explicit) return explicit;\n const type = getTreeType(d) || 'pine';\n return DEFAULT_TRUNK_COLORS[type] ?? DEFAULT_TRUNK_COLORS.pine;\n },\n pickable: this.props.pickable,\n material: {ambient: 0.35, diffuse: 0.6, shininess: 8}\n })\n );\n\n // -----------------------------------------------------------------------\n // 2. Canopy layers \u2014 one per tree type, only for trees of that type\n // -----------------------------------------------------------------------\n const canopyLayers = ALL_TREE_TYPES.filter((type) => grouped[type].length > 0).map((type) =>\n this._buildCanopyLayer(type)\n );\n\n return [trunkLayer, ...canopyLayers];\n }\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {\n BufferGeometry,\n BufferAttribute,\n CylinderGeometry,\n ConeGeometry,\n SphereGeometry,\n Matrix4\n} from 'three';\n\n/**\n * Mesh format compatible with deck.gl SimpleMeshLayer.\n * All geometries are Z-up (deck.gl convention), unit scale (0..1 in Z = bottom to top).\n */\nexport type TreeMesh = {\n attributes: {\n POSITION: {value: Float32Array; size: 3};\n NORMAL: {value: Float32Array; size: 3};\n };\n indices: {value: Uint32Array; size: 1};\n topology: 'triangle-list';\n mode: 4;\n};\n\n/**\n * Rotation matrix that converts from Three.js Y-up to deck.gl Z-up.\n * Rotates -90 degrees around the X axis: Y -> Z, Z -> -Y.\n */\nconst Y_TO_Z_UP = new Matrix4().makeRotationX(-Math.PI / 2);\n\n/**\n * Extract a TreeMesh from a Three.js BufferGeometry.\n * Assumes the geometry has already been rotated to Z-up.\n */\nfunction extractMesh(geo: BufferGeometry): TreeMesh {\n geo.computeVertexNormals();\n const posAttr = geo.attributes.position as BufferAttribute;\n const norAttr = geo.attributes.normal as BufferAttribute;\n const idx = geo.index;\n\n return {\n attributes: {\n POSITION: {value: new Float32Array(posAttr.array), size: 3},\n NORMAL: {value: new Float32Array(norAttr.array), size: 3}\n },\n indices: {value: new Uint32Array(idx ? idx.array : new Uint32Array(0)), size: 1},\n topology: 'triangle-list',\n mode: 4\n };\n}\n\n/** Copy indices for one geometry slice, offsetting by the current vertex base. */\nfunction copyIndices(\n out: Uint32Array,\n outOffset: number,\n geo: BufferGeometry,\n vertexBase: number\n): number {\n if (geo.index) {\n const src = geo.index.array;\n for (let i = 0; i < src.length; i++) out[outOffset + i] = src[i] + vertexBase;\n return src.length;\n }\n const count = geo.attributes.position.count;\n for (let i = 0; i < count; i++) out[outOffset + i] = vertexBase + i;\n return count;\n}\n\n/**\n * Merge multiple Three.js BufferGeometries into a single geometry.\n * All input geometries must be indexed.\n */\nfunction mergeGeometries(geos: BufferGeometry[]): BufferGeometry {\n let totalVertices = 0;\n let totalIndices = 0;\n for (const geo of geos) {\n totalVertices += geo.attributes.position.count;\n totalIndices += geo.index ? geo.index.count : geo.attributes.position.count;\n }\n\n const positions = new Float32Array(totalVertices * 3);\n const normals = new Float32Array(totalVertices * 3);\n const indices = new Uint32Array(totalIndices);\n let vOffset = 0;\n let iOffset = 0;\n\n for (const geo of geos) {\n const count = geo.attributes.position.count;\n const srcNor = geo.attributes.normal ? (geo.attributes.normal.array as Float32Array) : null;\n positions.set(geo.attributes.position.array as Float32Array, vOffset * 3);\n if (srcNor) normals.set(srcNor, vOffset * 3);\n iOffset += copyIndices(indices, iOffset, geo, vOffset);\n vOffset += count;\n }\n\n const merged = new BufferGeometry();\n merged.setAttribute('position', new BufferAttribute(positions, 3));\n merged.setAttribute('normal', new BufferAttribute(normals, 3));\n merged.setIndex(new BufferAttribute(indices, 1));\n return merged;\n}\n\n/**\n * Unit trunk cylinder mesh: from z=0 (base) to z=1 (top), radius tapers from 1 to 0.7.\n * Scale via `getScale = [trunkRadius, trunkRadius, trunkHeight]`.\n */\nexport function createTrunkMesh(segments = 8): TreeMesh {\n const geo = new CylinderGeometry(0.7, 1.0, 1.0, segments);\n // Three.js CylinderGeometry is centered at origin, extends from y=-0.5 to y=0.5\n geo.applyMatrix4(Y_TO_Z_UP); // Rotate to Z-up: now z=-0.5 to z=0.5\n geo.translate(0, 0, 0.5); // Shift so base is at z=0, top at z=1\n return extractMesh(geo);\n}\n\n/**\n * Unit pine canopy mesh: multiple tiered cones creating a Christmas tree silhouette.\n * Extends from z=0 (base of canopy) to z=1 (tip).\n *\n * @param levels - number of cone tiers (1-5)\n * @param segments - polygon segments per cone\n */\nexport function createPineCanopyMesh(levels = 3, segments = 8): TreeMesh {\n const geos: BufferGeometry[] = [];\n const tierHeight = 0.55 / levels;\n\n for (let i = 0; i < levels; i++) {\n const t = i / (levels - 1 || 1);\n // Bottom tiers are wider, top tiers are narrower\n const radius = (1 - t * 0.5) * 0.85;\n // Tiers are staggered: each one starts 60% up the previous tier\n const zBase = t * (1 - tierHeight * 1.2);\n\n const cone = new ConeGeometry(radius, tierHeight, segments);\n cone.applyMatrix4(Y_TO_Z_UP);\n // ConeGeometry apex is at y=+height/2 -> z=+height/2 after rotation\n // Translate so apex points upward and base is at zBase\n cone.translate(0, 0, zBase + tierHeight);\n geos.push(cone);\n }\n\n // Sharp tip at top\n const tip = new ConeGeometry(0.12, 0.18, 6);\n tip.applyMatrix4(Y_TO_Z_UP);\n tip.translate(0, 0, 1.0);\n geos.push(tip);\n\n const merged = mergeGeometries(geos);\n merged.computeVertexNormals();\n return extractMesh(merged);\n}\n\n/**\n * Unit oak canopy mesh: a large sphere.\n * Extends from z=0 to z=1, center at z=0.5.\n */\nexport function createOakCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.5, 12, 8);\n // SphereGeometry is centered at origin, radius=0.5\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5); // center at z=0.5, extends z=0 to z=1\n return extractMesh(geo);\n}\n\n/**\n * Unit palm canopy mesh: a flat, wide disk crown typical of palm trees.\n * Extends from z=0 to z=0.35, radius=1.\n */\nexport function createPalmCanopyMesh(): TreeMesh {\n // Flattened sphere acting as a spread crown\n const geo = new SphereGeometry(0.7, 12, 5);\n const flatten = new Matrix4().makeScale(1.4, 0.35, 1.4);\n geo.applyMatrix4(flatten);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.18);\n return extractMesh(geo);\n}\n\n/**\n * Unit birch canopy mesh: a narrow oval / diamond shape.\n * Extends from z=0 to z=1, narrower than an oak.\n */\nexport function createBirchCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.42, 10, 8);\n // Elongate vertically (Z after rotation)\n const elongate = new Matrix4().makeScale(1, 1.45, 1);\n geo.applyMatrix4(elongate);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n\n/**\n * Unit cherry canopy mesh: a full, round sphere slightly larger than oak.\n * Extends from z=0 to z=1.1 (slightly wider than tall for a lush look).\n */\nexport function createCherryCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.52, 12, 8);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport type {TreeLayerProps, TreeType, Season, CropConfig} from './tree-layer/tree-layer';\nexport {TreeLayer} from './tree-layer/tree-layer';\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {CompositeLayer} from '@deck.gl/core';\nimport type {Color, DefaultProps, LayerProps, Position} from '@deck.gl/core';\nimport {SimpleMeshLayer} from '@deck.gl/mesh-layers';\nimport {\n createTrunkMesh,\n createPineCanopyMesh,\n createOakCanopyMesh,\n createPalmCanopyMesh,\n createBirchCanopyMesh,\n createCherryCanopyMesh,\n createCropMesh\n} from './tree-geometry';\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Tree species / silhouette variant. */\nexport type TreeType = 'pine' | 'oak' | 'palm' | 'birch' | 'cherry';\n\n/** Season that drives default canopy colour when no explicit colour is supplied. */\nexport type Season = 'spring' | 'summer' | 'autumn' | 'winter';\n\n/**\n * Crop configuration for a single tree.\n *\n * Pass this from `getCrop` to render small spherical crop points on the tree\n * and/or scattered on the ground around it. Works for fruit, nuts, or flowers\n * (flowering stage is expressed simply as a flower-coloured crop config).\n *\n * Positions are randomised deterministically from the tree's geographic\n * coordinates, so they are stable across re-renders.\n */\nexport type CropConfig = {\n /** Colour of each crop sphere [r, g, b, a]. */\n color: Color;\n /** Number of crop spheres placed in the outer canopy volume (live/in-tree crops). */\n count: number;\n /**\n * Number of crop spheres scattered on the ground within the canopy footprint\n * (dropped/fallen crops).\n * @default 0\n */\n droppedCount?: number;\n /** Radius of each individual crop sphere in metres. */\n radius: number;\n};\n\n// ---------------------------------------------------------------------------\n// Default colours\n// ---------------------------------------------------------------------------\n\n/** Default trunk colours per tree type [r, g, b, a]. */\nconst DEFAULT_TRUNK_COLORS: Record<TreeType, Color> = {\n pine: [80, 50, 20, 255],\n oak: [91, 57, 23, 255],\n palm: [140, 100, 55, 255],\n birch: [220, 215, 205, 255], // white-grey birch bark\n cherry: [100, 60, 40, 255]\n};\n\n/** Default canopy colours per (tree type, season) [r, g, b, a]. */\nconst DEFAULT_CANOPY_COLORS: Record<TreeType, Record<Season, Color>> = {\n pine: {\n spring: [34, 100, 34, 255],\n summer: [0, 64, 0, 255],\n autumn: [0, 64, 0, 255], // evergreen \u2014 no colour change\n winter: [0, 55, 0, 255]\n },\n oak: {\n spring: [100, 180, 80, 255],\n summer: [34, 120, 15, 255],\n autumn: [180, 85, 20, 255],\n winter: [100, 80, 60, 160] // sparse, semi-transparent\n },\n palm: {\n spring: [50, 160, 50, 255],\n summer: [20, 145, 20, 255],\n autumn: [55, 150, 30, 255],\n winter: [40, 130, 30, 255]\n },\n birch: {\n spring: [150, 210, 110, 255],\n summer: [80, 160, 60, 255],\n autumn: [230, 185, 40, 255],\n winter: [180, 180, 170, 90] // near-bare\n },\n cherry: {\n spring: [255, 180, 205, 255], // pink blossom\n summer: [50, 140, 50, 255],\n autumn: [200, 60, 40, 255],\n winter: [120, 90, 80, 110] // bare\n }\n};\n\n// ---------------------------------------------------------------------------\n// Pre-built unit-scale meshes (shared across all layer instances)\n// ---------------------------------------------------------------------------\n\nconst TRUNK_MESH = createTrunkMesh();\n\nconst CANOPY_MESHES: Record<TreeType, ReturnType<typeof createTrunkMesh>> = {\n pine: createPineCanopyMesh(3),\n oak: createOakCanopyMesh(),\n palm: createPalmCanopyMesh(),\n birch: createBirchCanopyMesh(),\n cherry: createCherryCanopyMesh()\n};\n\nconst CROP_MESH = createCropMesh();\n\nconst ALL_TREE_TYPES: TreeType[] = ['pine', 'oak', 'palm', 'birch', 'cherry'];\n\n/**\n * Fraction of canopy height by which the canopy mesh is lowered into the trunk.\n * Hides the trunk-top disk that would otherwise peek above the canopy base.\n * 0.22 means the canopy base sits 22% of canopy-height below the trunk top.\n */\nconst CANOPY_TRUNK_OVERLAP = 0.22;\n\n// ---------------------------------------------------------------------------\n// Crop helpers\n// ---------------------------------------------------------------------------\n\n/** splitmix32 \u2014 fast, high-quality seeded PRNG returning values in [0, 1). */\nfunction createRng(seed: number): () => number {\n let s = seed >>> 0;\n return (): number => {\n s = (s + 0x9e3779b9) | 0;\n let t = s ^ (s >>> 16);\n t = Math.imul(t, 0x21f0aaad);\n t ^= t >>> 15;\n t = Math.imul(t, 0x735a2d97);\n return ((t ^ (t >>> 15)) >>> 0) / 4294967296;\n };\n}\n\n/** Deterministic integer seed derived from a geographic position. */\nfunction positionSeed(lng: number, lat: number): number {\n return ((Math.round(lng * 10000) * 92821) ^ (Math.round(lat * 10000) * 65537)) >>> 0;\n}\n\nconst DEG_PER_METER_LAT = 1 / 111320;\n\nfunction lngDegreesPerMeter(latDeg: number): number {\n return 1 / (111320 * Math.cos((latDeg * Math.PI) / 180));\n}\n\n/** Internal flat record for a single rendered crop sphere. */\ntype CropPoint = {\n position: [number, number, number];\n color: Color;\n scale: number;\n};\n\n/**\n * Expand live crop positions so they straddle the canopy surface.\n *\n * The canopy mesh is a SphereGeometry(0.5, \u2026) which, after SimpleMeshLayer\n * applies getScale = [r, r, H], has a true XY radius of 0.5 * r and a true\n * Z half-height of 0.5 * H. Crops are placed at 85\u2013110 % of those real\n * dimensions so most of each sphere sits just outside the canopy surface.\n *\n * Positions are seeded from the tree's geographic coordinates so they are\n * stable across re-renders.\n */\nfunction expandLiveCropPoints(opts: {\n lng: number;\n lat: number;\n elevation: number;\n height: number;\n trunkFraction: number;\n canopyRadius: number;\n cropConfig: CropConfig;\n out: CropPoint[];\n}): void {\n const {lng, lat, elevation, height, trunkFraction, canopyRadius, cropConfig, out} = opts;\n if (cropConfig.count <= 0) return;\n\n // Actual canopy sphere radii after SimpleMeshLayer scaling.\n // SphereGeometry has unit radius 0.5, so world radius = 0.5 * getScale component.\n const rxy = canopyRadius * 0.5;\n const canopyH = height * (1 - trunkFraction);\n const rz = canopyH * 0.5;\n // Canopy position is lowered by CANOPY_TRUNK_OVERLAP to hide the trunk-top disk\n const canopyCenterZ = elevation + height * trunkFraction - canopyH * CANOPY_TRUNK_OVERLAP + rz;\n\n const dLng = lngDegreesPerMeter(lat);\n const rng = createRng(positionSeed(lng, lat));\n\n for (let i = 0; i < cropConfig.count; i++) {\n const theta = rng() * Math.PI * 2;\n // Exclude top and bottom caps so crops never crown the canopy or hang below.\n // cos(phi) in [-0.80, 0.80] \u2192 phi from ~37\u00B0 to ~143\u00B0 (equatorial band).\n const cosPhi = -0.8 + rng() * 1.6;\n const sinPhi = Math.sqrt(Math.max(0, 1 - cosPhi * cosPhi));\n // 90\u2013102 % of canopy radius: crops sit just at/inside the surface, tips barely poke out\n const radFrac = 0.9 + rng() * 0.12;\n\n const dx = rxy * radFrac * sinPhi * Math.cos(theta);\n const dy = rxy * radFrac * sinPhi * Math.sin(theta);\n const dz = rz * radFrac * cosPhi;\n\n out.push({\n position: [lng + dx * dLng, lat + dy * DEG_PER_METER_LAT, canopyCenterZ + dz],\n color: cropConfig.color,\n scale: cropConfig.radius\n });\n }\n}\n\n/**\n * Expand dropped crop positions uniformly across the ground disk within the\n * canopy footprint. Uses a separate seed offset from live crops so that\n * changing `count` does not affect dropped positions.\n */\nfunction expandDroppedCropPoints(opts: {\n lng: number;\n lat: number;\n elevation: number;\n canopyRadius: number;\n cropConfig: CropConfig;\n out: CropPoint[];\n}): void {\n const {lng, lat, elevation, canopyRadius, cropConfig, out} = opts;\n const droppedCount = cropConfig.droppedCount ?? 0;\n if (droppedCount <= 0) return;\n\n // Actual canopy footprint radius (see note in expandLiveCropPoints)\n const footprintRadius = canopyRadius * 0.5;\n const dLng = lngDegreesPerMeter(lat);\n // XOR with a constant so the dropped sequence is independent of the live one\n const rng = createRng(positionSeed(lng, lat) ^ 0x1a2b3c4d);\n\n // Dropped crops are semi-transparent so they read as fallen/decaying\n const c = cropConfig.color as number[];\n const droppedColor: Color = [c[0], c[1], c[2], Math.round((c[3] ?? 255) * 0.45)] as Color;\n\n for (let i = 0; i < droppedCount; i++) {\n const theta = rng() * Math.PI * 2;\n // sqrt for uniform-area disk sampling\n const dist = Math.sqrt(rng()) * footprintRadius;\n\n const dx = dist * Math.cos(theta);\n const dy = dist * Math.sin(theta);\n\n out.push({\n position: [lng + dx * dLng, lat + dy * DEG_PER_METER_LAT, elevation + 0.05],\n color: droppedColor,\n scale: cropConfig.radius\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\ntype _TreeLayerProps<DataT> = {\n /** Source data. */\n data: DataT[];\n\n /** Longitude/latitude position of the tree base. */\n getPosition?: (d: DataT) => Position;\n\n /** Base elevation (metres above sea level). @default 0 */\n getElevation?: (d: DataT) => number;\n\n /**\n * Silhouette / species variant.\n * 'pine' \u2013 layered conical tiers (evergreen)\n * 'oak' \u2013 wide spherical canopy\n * 'palm' \u2013 tall thin trunk with flat crown\n * 'birch' \u2013 narrow oval canopy, pale bark\n * 'cherry' \u2013 round lush canopy, seasonal blossom\n * @default 'pine'\n */\n getTreeType?: (d: DataT) => TreeType;\n\n /**\n * Total tree height in metres.\n * @default 10\n */\n getHeight?: (d: DataT) => number;\n\n /**\n * Fraction of total height occupied by the trunk (0\u20131).\n * @default 0.35\n */\n getTrunkHeightFraction?: (d: DataT) => number;\n\n /**\n * Trunk base radius in metres.\n * @default 0.5\n */\n getTrunkRadius?: (d: DataT) => number;\n\n /**\n * Horizontal radius of the canopy in metres.\n * @default 3\n */\n getCanopyRadius?: (d: DataT) => number;\n\n /**\n * Explicit trunk colour [r, g, b, a].\n * When null the species default is used.\n * @default null\n */\n getTrunkColor?: (d: DataT) => Color | null;\n\n /**\n * Explicit canopy colour [r, g, b, a].\n * When null the species \u00D7 season default is used.\n * @default null\n */\n getCanopyColor?: (d: DataT) => Color | null;\n\n /**\n * Season used to pick the default canopy colour when no explicit colour is provided.\n * @default 'summer'\n */\n getSeason?: (d: DataT) => Season;\n\n /**\n * Number of cone tiers for pine trees (1\u20135).\n * Higher values produce a denser layered silhouette.\n * @default 3\n */\n getBranchLevels?: (d: DataT) => number;\n\n /**\n * Optional crop configuration for this tree.\n *\n * Return a `CropConfig` to render small spherical crop points in the outer\n * canopy volume (live crops) and/or scattered on the ground around the trunk\n * (dropped crops). Return `null` to show no crops for this tree.\n *\n * The same accessor can express fruit, nuts, or flowering stage \u2014 pass\n * flower-coloured points (e.g. `[255, 200, 220, 255]`) for a blossom effect.\n *\n * Crop positions are randomised deterministically from the tree's geographic\n * coordinates; they are stable across re-renders.\n *\n * @default null (no crops)\n */\n getCrop?: (d: DataT) => CropConfig | null;\n\n /**\n * Global size multiplier applied to all dimensions.\n * @default 1\n */\n sizeScale?: number;\n};\n\nexport type TreeLayerProps<DataT = unknown> = _TreeLayerProps<DataT> & LayerProps;\n\nconst defaultProps: DefaultProps<TreeLayerProps<unknown>> = {\n getPosition: {type: 'accessor', value: (d: any) => d.position},\n getElevation: {type: 'accessor', value: (_d: any) => 0},\n getTreeType: {type: 'accessor', value: (_d: any) => 'pine' as TreeType},\n getHeight: {type: 'accessor', value: (_d: any) => 10},\n getTrunkHeightFraction: {type: 'accessor', value: (_d: any) => 0.35},\n getTrunkRadius: {type: 'accessor', value: (_d: any) => 0.5},\n getCanopyRadius: {type: 'accessor', value: (_d: any) => 3},\n getTrunkColor: {type: 'accessor', value: (_d: any) => null},\n getCanopyColor: {type: 'accessor', value: (_d: any) => null},\n getSeason: {type: 'accessor', value: (_d: any) => 'summer' as Season},\n getBranchLevels: {type: 'accessor', value: (_d: any) => 3},\n getCrop: {type: 'accessor', value: (_d: any) => null},\n sizeScale: {type: 'number', value: 1, min: 0}\n};\n\n// ---------------------------------------------------------------------------\n// State\n// ---------------------------------------------------------------------------\n\ntype TreeLayerState = {\n grouped: Record<TreeType, unknown[]>;\n pineMeshes: Record<number, ReturnType<typeof createPineCanopyMesh>>;\n liveCropPoints: CropPoint[];\n droppedCropPoints: CropPoint[];\n};\n\n// ---------------------------------------------------------------------------\n// Layer\n// ---------------------------------------------------------------------------\n\n/**\n * **TreeLayer** \u2014 A parametric, Three.js-powered deck.gl layer that renders\n * richly configurable 3D trees at geographic positions.\n *\n * Each tree is composed of two `SimpleMeshLayer` instances: one for the trunk\n * (a tapered cylinder) and one for the canopy (silhouette depends on `getTreeType`).\n * All geometry is generated procedurally using Three.js `BufferGeometry` primitives\n * and converted to the `@loaders.gl/schema` `MeshGeometry` format accepted by\n * `SimpleMeshLayer`.\n *\n * Parametric controls include:\n * - Species / silhouette (`getTreeType`)\n * - Total height (`getHeight`) and trunk-to-canopy proportion (`getTrunkHeightFraction`)\n * - Trunk and canopy radii (`getTrunkRadius`, `getCanopyRadius`)\n * - Explicit or season-driven colours (`getTrunkColor`, `getCanopyColor`, `getSeason`)\n * - Pine tier density (`getBranchLevels`)\n * - Crop / fruit / flower visualisation (`getCrop`)\n * - Global scale factor (`sizeScale`)\n */\nexport class TreeLayer<DataT = unknown, ExtraPropsT extends {} = {}> extends CompositeLayer<\n ExtraPropsT & Required<_TreeLayerProps<DataT>>\n> {\n static layerName = 'TreeLayer';\n static defaultProps = defaultProps;\n\n declare state: TreeLayerState;\n\n initializeState() {\n this.state = {\n grouped: {pine: [], oak: [], palm: [], birch: [], cherry: []},\n pineMeshes: {},\n liveCropPoints: [],\n droppedCropPoints: []\n };\n }\n\n updateState({props, oldProps, changeFlags}) {\n if (changeFlags.dataChanged || changeFlags.propsChanged || changeFlags.updateTriggersChanged) {\n const {\n data,\n getTreeType,\n getBranchLevels,\n getCrop,\n getPosition,\n getElevation,\n getHeight,\n getTrunkHeightFraction,\n getCanopyRadius,\n sizeScale\n } = props;\n\n const grouped: Record<TreeType, DataT[]> = {\n pine: [],\n oak: [],\n palm: [],\n birch: [],\n cherry: []\n };\n\n const pineMeshes: Record<number, ReturnType<typeof createPineCanopyMesh>> = {};\n const liveCropPoints: CropPoint[] = [];\n const droppedCropPoints: CropPoint[] = [];\n\n for (const d of data as DataT[]) {\n const type = getTreeType(d) as TreeType;\n if (grouped[type]) grouped[type].push(d);\n\n if (type === 'pine') {\n const levels = Math.max(1, Math.min(5, Math.round(getBranchLevels(d) as number)));\n pineMeshes[levels] ??= createPineCanopyMesh(levels);\n }\n\n const cropConfig = getCrop(d);\n if (cropConfig) {\n const pos = getPosition(d);\n const lng = pos[0];\n const lat = pos[1];\n const elev = getElevation(d) || 0;\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const r = getCanopyRadius(d) * sizeScale;\n\n // Scale crop radius in lock-step with all other dimensions\n const scaledCropConfig: CropConfig = {\n ...cropConfig,\n radius: cropConfig.radius * sizeScale\n };\n expandLiveCropPoints({\n lng,\n lat,\n elevation: elev,\n height: h,\n trunkFraction: f,\n canopyRadius: r,\n cropConfig: scaledCropConfig,\n out: liveCropPoints\n });\n expandDroppedCropPoints({\n lng,\n lat,\n elevation: elev,\n canopyRadius: r,\n cropConfig: scaledCropConfig,\n out: droppedCropPoints\n });\n }\n }\n\n this.setState({grouped, pineMeshes, liveCropPoints, droppedCropPoints});\n }\n }\n\n /**\n * Build a single canopy sub-layer.\n *\n * Takes explicit `mesh`, `data`, and `layerId` so that pine trees can be\n * split into one sub-layer per level count (each with its own mesh).\n */\n private _buildCanopyLayer(\n type: TreeType,\n mesh: ReturnType<typeof createTrunkMesh>,\n data: unknown[],\n layerId: string\n ): SimpleMeshLayer {\n const {\n getPosition,\n getElevation,\n getHeight,\n getTrunkHeightFraction,\n getCanopyRadius,\n getCanopyColor,\n getSeason,\n sizeScale\n } = this.props;\n\n return new SimpleMeshLayer(\n this.getSubLayerProps({\n id: layerId,\n data,\n mesh,\n getPosition: (d) => {\n const pos = getPosition(d);\n const elevation = getElevation(d) || 0;\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const canopyH = h * (1 - f);\n return [pos[0], pos[1], elevation + h * f - canopyH * CANOPY_TRUNK_OVERLAP];\n },\n getScale: (d) => {\n const pos = getPosition(d);\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const r = getCanopyRadius(d) * sizeScale;\n // Per-tree asymmetric XY scale from position hash \u2014 no two canopies\n // are the same oval, giving organic variety with zero extra draw calls.\n const seed = positionSeed(pos[0], pos[1]);\n const sx = 1 + ((seed & 0xffff) / 65535 - 0.5) * 0.3;\n const sy = 1 + (((seed >>> 16) & 0xffff) / 65535 - 0.5) * 0.3;\n return [r * sx, r * sy, h * (1 - f)];\n },\n getOrientation: (d) => {\n // Random bearing per tree: yaw (index 1) rotates around the vertical\n // Z axis in deck.gl's [pitch, yaw, roll] convention.\n // Pine tiers face different compass directions; bumpy canopies present\n // a unique silhouette from every viewing angle.\n const pos = getPosition(d);\n const seed = positionSeed(pos[0], pos[1]);\n const angle = (((seed ^ (seed >>> 13)) & 0xffff) / 65535) * 360;\n return [0, angle, 0];\n },\n getColor: (d) => {\n const explicit = getCanopyColor(d);\n if (explicit) return explicit;\n const season = getSeason(d) || 'summer';\n return DEFAULT_CANOPY_COLORS[type][season];\n },\n pickable: this.props.pickable,\n material: {ambient: 0.55, diffuse: 0.55, shininess: 0},\n updateTriggers: {\n getPosition: sizeScale,\n getScale: sizeScale,\n getOrientation: sizeScale\n }\n })\n );\n }\n\n renderLayers() {\n const {\n getPosition,\n getElevation,\n getTreeType,\n getHeight,\n getTrunkHeightFraction,\n getTrunkRadius,\n getTrunkColor,\n getBranchLevels,\n sizeScale\n } = this.props;\n\n const {grouped, pineMeshes, liveCropPoints, droppedCropPoints} = this.state;\n\n // -----------------------------------------------------------------------\n // 1. Trunk layer \u2014 one layer for ALL tree types, shared cylinder geometry\n // -----------------------------------------------------------------------\n const trunkLayer = new SimpleMeshLayer(\n this.getSubLayerProps({\n id: 'trunks',\n data: this.props.data,\n mesh: TRUNK_MESH,\n getPosition: (d) => {\n const pos = getPosition(d);\n return [pos[0], pos[1], getElevation(d) || 0];\n },\n getScale: (d) => {\n const h = getHeight(d) * sizeScale;\n const f = getTrunkHeightFraction(d);\n const r = getTrunkRadius(d) * sizeScale;\n return [r, r, h * f];\n },\n getColor: (d) => {\n const explicit = getTrunkColor(d);\n if (explicit) return explicit;\n const type = getTreeType(d) || 'pine';\n return DEFAULT_TRUNK_COLORS[type] ?? DEFAULT_TRUNK_COLORS.pine;\n },\n pickable: this.props.pickable,\n material: {ambient: 0.45, diffuse: 0.55, shininess: 4},\n updateTriggers: {getScale: sizeScale}\n })\n );\n\n // -----------------------------------------------------------------------\n // 2. Canopy layers\n // Non-pine: one sub-layer per species.\n // Pine: one sub-layer per branch-level count, each using its own mesh,\n // so trees with 2/3/4 tiers never share a mismatched mesh.\n // -----------------------------------------------------------------------\n const nonPineCanopies = ALL_TREE_TYPES.filter((t) => t !== 'pine' && grouped[t].length > 0).map(\n (t) => this._buildCanopyLayer(t, CANOPY_MESHES[t], grouped[t], `canopy-${t}`)\n );\n\n const pineCanopies = Object.entries(pineMeshes).flatMap(([levelStr, mesh]) => {\n const levels = Number(levelStr);\n const pineData = grouped.pine.filter(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (d) => Math.max(1, Math.min(5, Math.round(getBranchLevels(d as any)))) === levels\n );\n return pineData.length > 0\n ? [this._buildCanopyLayer('pine', mesh, pineData, `canopy-pine-${levels}`)]\n : [];\n });\n\n const canopyLayers = [...nonPineCanopies, ...pineCanopies];\n\n // -----------------------------------------------------------------------\n // 3. Crop layers \u2014 live (in canopy) and dropped (on ground)\n // -----------------------------------------------------------------------\n const cropLayers = [];\n\n if (liveCropPoints.length > 0) {\n cropLayers.push(\n new SimpleMeshLayer(\n this.getSubLayerProps({\n id: 'live-crops',\n data: liveCropPoints,\n mesh: CROP_MESH,\n getPosition: (d: CropPoint) => d.position,\n getScale: (d: CropPoint) => [d.scale, d.scale, d.scale],\n getColor: (d: CropPoint) => d.color,\n pickable: false,\n material: {ambient: 0.5, diffuse: 0.8, shininess: 40}\n })\n )\n );\n }\n\n if (droppedCropPoints.length > 0) {\n cropLayers.push(\n new SimpleMeshLayer(\n this.getSubLayerProps({\n id: 'dropped-crops',\n data: droppedCropPoints,\n mesh: CROP_MESH,\n getPosition: (d: CropPoint) => d.position,\n getScale: (d: CropPoint) => [d.scale, d.scale, d.scale],\n getColor: (d: CropPoint) => d.color,\n pickable: false,\n material: {ambient: 0.6, diffuse: 0.5, shininess: 10}\n })\n )\n );\n }\n\n return [trunkLayer, ...canopyLayers, ...cropLayers];\n }\n}\n", "// deck.gl-community\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {\n BufferGeometry,\n BufferAttribute,\n CylinderGeometry,\n ConeGeometry,\n SphereGeometry,\n IcosahedronGeometry,\n Matrix4\n} from 'three';\n\n/**\n * Mesh format compatible with deck.gl SimpleMeshLayer.\n * All geometries are Z-up (deck.gl convention), unit scale (0..1 in Z = bottom to top).\n */\nexport type TreeMesh = {\n attributes: {\n POSITION: {value: Float32Array; size: 3};\n NORMAL: {value: Float32Array; size: 3};\n };\n indices: {value: Uint32Array; size: 1};\n topology: 'triangle-list';\n mode: 4;\n};\n\n/**\n * Rotation matrix that converts from Three.js Y-up to deck.gl Z-up.\n * Rotates -90 degrees around the X axis: Y -> Z, Z -> -Y.\n */\nconst Y_TO_Z_UP = new Matrix4().makeRotationX(-Math.PI / 2);\n\n/**\n * Perturb each vertex radially using a sum of low-frequency sinusoidal waves\n * evaluated at the vertex's surface direction. Adjacent vertices receive\n * smoothly-varying displacements so there are no gaps or cracks in the mesh.\n * Applied once at module init \u2014 zero runtime cost.\n *\n * @param geo Three.js BufferGeometry to modify in-place (before Y_TO_Z_UP rotation)\n * @param magnitude Fractional displacement amplitude, e.g. 0.15 = \u00B115 % of radius\n * @param seed Integer seed \u2014 each species gets a distinct blob shape\n */\nfunction jitterSmooth(geo: BufferGeometry, magnitude: number, seed: number): void {\n let s = seed >>> 0;\n const rng = () => {\n s = (s + 0x9e3779b9) | 0;\n let t = s ^ (s >>> 16);\n t = Math.imul(t, 0x21f0aaad);\n t ^= t >>> 15;\n t = Math.imul(t, 0x735a2d97);\n return ((t ^ (t >>> 15)) >>> 0) / 4294967296;\n };\n // 4 low-frequency waves (2\u20135 bumps across the sphere) \u2014 smooth, no cracks\n const waves = Array.from({length: 4}, () => ({\n fx: 2 + rng() * 3,\n fy: 2 + rng() * 3,\n fz: 2 + rng() * 3,\n phase: rng() * Math.PI * 2\n }));\n\n const pos = geo.attributes.position.array as Float32Array;\n for (let i = 0; i < pos.length; i += 3) {\n const x = pos[i];\n const y = pos[i + 1];\n const z = pos[i + 2];\n const r = Math.sqrt(x * x + y * y + z * z);\n if (r !== 0) {\n const nx = x / r;\n const ny = y / r;\n const nz = z / r;\n let noise = 0;\n for (const w of waves) {\n noise += Math.sin(nx * w.fx + ny * w.fy + nz * w.fz + w.phase);\n }\n noise /= 4; // normalise to ~ [-1, 1]\n const scale = 1 + noise * magnitude;\n pos[i] = x * scale;\n pos[i + 1] = y * scale;\n pos[i + 2] = z * scale;\n }\n }\n}\n\n/**\n * Extract a TreeMesh from a Three.js BufferGeometry.\n * Assumes the geometry has already been rotated to Z-up.\n */\nfunction extractMesh(geo: BufferGeometry): TreeMesh {\n geo.computeVertexNormals();\n const posAttr = geo.attributes.position as BufferAttribute;\n const norAttr = geo.attributes.normal as BufferAttribute;\n const idx = geo.index;\n\n return {\n attributes: {\n POSITION: {value: new Float32Array(posAttr.array), size: 3},\n NORMAL: {value: new Float32Array(norAttr.array), size: 3}\n },\n indices: {value: new Uint32Array(idx ? idx.array : new Uint32Array(0)), size: 1},\n topology: 'triangle-list',\n mode: 4\n };\n}\n\n/** Copy indices for one geometry slice, offsetting by the current vertex base. */\nfunction copyIndices(\n out: Uint32Array,\n outOffset: number,\n geo: BufferGeometry,\n vertexBase: number\n): number {\n if (geo.index) {\n const src = geo.index.array;\n for (let i = 0; i < src.length; i++) out[outOffset + i] = src[i] + vertexBase;\n return src.length;\n }\n const count = geo.attributes.position.count;\n for (let i = 0; i < count; i++) out[outOffset + i] = vertexBase + i;\n return count;\n}\n\n/**\n * Merge multiple Three.js BufferGeometries into a single geometry.\n * All input geometries must be indexed.\n */\nfunction mergeGeometries(geos: BufferGeometry[]): BufferGeometry {\n let totalVertices = 0;\n let totalIndices = 0;\n for (const geo of geos) {\n totalVertices += geo.attributes.position.count;\n totalIndices += geo.index ? geo.index.count : geo.attributes.position.count;\n }\n\n const positions = new Float32Array(totalVertices * 3);\n const normals = new Float32Array(totalVertices * 3);\n const indices = new Uint32Array(totalIndices);\n let vOffset = 0;\n let iOffset = 0;\n\n for (const geo of geos) {\n const count = geo.attributes.position.count;\n const srcNor = geo.attributes.normal ? (geo.attributes.normal.array as Float32Array) : null;\n positions.set(geo.attributes.position.array as Float32Array, vOffset * 3);\n if (srcNor) normals.set(srcNor, vOffset * 3);\n iOffset += copyIndices(indices, iOffset, geo, vOffset);\n vOffset += count;\n }\n\n const merged = new BufferGeometry();\n merged.setAttribute('position', new BufferAttribute(positions, 3));\n merged.setAttribute('normal', new BufferAttribute(normals, 3));\n merged.setIndex(new BufferAttribute(indices, 1));\n return merged;\n}\n\n/**\n * Unit trunk cylinder mesh: from z=0 (base) to z=1 (top), radius tapers from 1 to 0.7.\n * Scale via `getScale = [trunkRadius, trunkRadius, trunkHeight]`.\n */\nexport function createTrunkMesh(segments = 8): TreeMesh {\n const geo = new CylinderGeometry(0.7, 1.0, 1.0, segments);\n // Three.js CylinderGeometry is centered at origin, extends from y=-0.5 to y=0.5\n geo.applyMatrix4(Y_TO_Z_UP); // Rotate to Z-up: now z=-0.5 to z=0.5\n geo.translate(0, 0, 0.5); // Shift so base is at z=0, top at z=1\n return extractMesh(geo);\n}\n\n/**\n * Unit pine canopy mesh: multiple tiered cones creating a Christmas tree silhouette.\n * Extends from z=0 (base of canopy) to z=1 (tip).\n *\n * @param levels - number of cone tiers (1-5)\n * @param segments - polygon segments per cone\n */\nexport function createPineCanopyMesh(levels = 3, segments = 8): TreeMesh {\n const geos: BufferGeometry[] = [];\n\n // Deterministic per-levels RNG so each level count gets its own organic shape.\n let s = (levels * 2654435761) >>> 0;\n const rng = () => {\n s = (s + 0x9e3779b9) | 0;\n let t = s ^ (s >>> 16);\n t = Math.imul(t, 0x21f0aaad);\n t ^= t >>> 15;\n t = Math.imul(t, 0x735a2d97);\n return ((t ^ (t >>> 15)) >>> 0) / 4294967296;\n };\n\n // Base tier height for 50 % overlap filling z = 0..0.8.\n const tierHeight = 1.6 / (levels + 1);\n const step = tierHeight / 2;\n\n let zCursor = 0;\n for (let i = 0; i < levels; i++) {\n const t = i / (levels - 1 || 1);\n\n // Width narrows toward the top; each tier gets \u00B120 % random variation.\n const baseRadius = (1 - t * 0.5) * 0.85;\n const radius = baseRadius * (0.8 + rng() * 0.4);\n\n // Height varies \u00B115 % per tier for uneven silhouette.\n const tierH = tierHeight * (0.85 + rng() * 0.3);\n\n const cone = new ConeGeometry(radius, tierH, segments);\n cone.applyMatrix4(Y_TO_Z_UP);\n\n // Drift increases from 0 at the bottom tier to \u00B10.10 at the top tier.\n // Bottom tier stays centred so it always connects cleanly to the trunk.\n const driftScale = levels > 1 ? i / (levels - 1) : 0;\n const driftX = (rng() - 0.5) * 0.2 * driftScale;\n const driftY = (rng() - 0.5) * 0.2 * driftScale;\n cone.translate(driftX, driftY, zCursor + tierH / 2);\n geos.push(cone);\n\n zCursor += step;\n }\n\n // Slender tip with slight lean.\n const tip = new ConeGeometry(0.08, 0.22, 6);\n tip.applyMatrix4(Y_TO_Z_UP);\n tip.translate((rng() - 0.5) * 0.08, (rng() - 0.5) * 0.08, zCursor + 0.05);\n geos.push(tip);\n\n const merged = mergeGeometries(geos);\n return extractMesh(merged);\n}\n\n/**\n * Unit oak canopy mesh: high-segment sphere, smooth pole, no shading stripe.\n *\n * IcosahedronGeometry (detail \u2265 1) always creates a single vertex at the\n * sphere's north pole via subdivision (normalized midpoint of the top edge),\n * giving 5 triangles meeting at the apex \u2014 the visible spike. Rotating the\n * icosahedron only moves WHICH original vertex becomes the apex; all 12 base\n * vertices are 5-connected, so the artifact persists.\n *\n * SphereGeometry(24, 16) places 24 tiny triangles at the pole instead of 5,\n * which is invisible at normal viewing distances. The earlier \"shading stripe\"\n * with the low-poly sphere (12 \u00D7 8 = 22.5\u00B0 bands) was coarse Gouraud banding,\n * not a UV-seam issue. At 24 \u00D7 16 (7.5\u00B0 bands) the shading is smooth.\n * Extends z = 0 (base) to z = 1 (top).\n */\nexport function createOakCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.5, 14, 10);\n jitterSmooth(geo, 0.18, 1);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n\n/**\n * Unit palm canopy mesh: a flat, wide disk crown typical of palm trees.\n * Extends from z=0 to z=0.35, radius=1.\n */\nexport function createPalmCanopyMesh(): TreeMesh {\n // Flattened sphere acting as a spread crown\n const geo = new SphereGeometry(0.7, 12, 5);\n jitterSmooth(geo, 0.1, 4);\n const flatten = new Matrix4().makeScale(1.4, 0.35, 1.4);\n geo.applyMatrix4(flatten);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.18);\n return extractMesh(geo);\n}\n\n/**\n * Unit birch canopy mesh: a narrow oval / diamond shape.\n * Extends from z=0 to z=1, narrower than an oak.\n */\nexport function createBirchCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.42, 10, 8);\n jitterSmooth(geo, 0.14, 2);\n // Elongate vertically (Z after rotation)\n const elongate = new Matrix4().makeScale(1, 1.45, 1);\n geo.applyMatrix4(elongate);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n\n/**\n * Unit cherry canopy mesh: a full, round sphere slightly larger than oak.\n * Extends from z=0 to z=1.1 (slightly wider than tall for a lush look).\n */\nexport function createCherryCanopyMesh(): TreeMesh {\n const geo = new SphereGeometry(0.52, 12, 8);\n jitterSmooth(geo, 0.2, 3);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n\n/**\n * Unit crop sphere mesh for rendering individual fruits, nuts, or flowers.\n * Deliberately low-polygon (24 triangles) so hundreds of instances remain cheap.\n * Scale uniformly via getScale = [r, r, r] to set the world-space radius in metres.\n */\nexport function createCropMesh(): TreeMesh {\n const geo = new SphereGeometry(0.5, 6, 4);\n geo.applyMatrix4(Y_TO_Z_UP);\n geo.translate(0, 0, 0.5);\n return extractMesh(geo);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export type { TreeLayerProps, TreeType, Season } from "./tree-layer/tree-layer.js";
|
|
1
|
+
export type { TreeLayerProps, TreeType, Season, CropConfig } from "./tree-layer/tree-layer.js";
|
|
2
2
|
export { TreeLayer } from "./tree-layer/tree-layer.js";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAC,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAC,mCAAgC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAC,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAC,mCAAgC;AAC1F,OAAO,EAAC,SAAS,EAAC,mCAAgC"}
|
|
@@ -34,8 +34,19 @@ export declare function createTrunkMesh(segments?: number): TreeMesh;
|
|
|
34
34
|
*/
|
|
35
35
|
export declare function createPineCanopyMesh(levels?: number, segments?: number): TreeMesh;
|
|
36
36
|
/**
|
|
37
|
-
* Unit oak canopy mesh:
|
|
38
|
-
*
|
|
37
|
+
* Unit oak canopy mesh: high-segment sphere, smooth pole, no shading stripe.
|
|
38
|
+
*
|
|
39
|
+
* IcosahedronGeometry (detail ≥ 1) always creates a single vertex at the
|
|
40
|
+
* sphere's north pole via subdivision (normalized midpoint of the top edge),
|
|
41
|
+
* giving 5 triangles meeting at the apex — the visible spike. Rotating the
|
|
42
|
+
* icosahedron only moves WHICH original vertex becomes the apex; all 12 base
|
|
43
|
+
* vertices are 5-connected, so the artifact persists.
|
|
44
|
+
*
|
|
45
|
+
* SphereGeometry(24, 16) places 24 tiny triangles at the pole instead of 5,
|
|
46
|
+
* which is invisible at normal viewing distances. The earlier "shading stripe"
|
|
47
|
+
* with the low-poly sphere (12 × 8 = 22.5° bands) was coarse Gouraud banding,
|
|
48
|
+
* not a UV-seam issue. At 24 × 16 (7.5° bands) the shading is smooth.
|
|
49
|
+
* Extends z = 0 (base) to z = 1 (top).
|
|
39
50
|
*/
|
|
40
51
|
export declare function createOakCanopyMesh(): TreeMesh;
|
|
41
52
|
/**
|
|
@@ -53,4 +64,10 @@ export declare function createBirchCanopyMesh(): TreeMesh;
|
|
|
53
64
|
* Extends from z=0 to z=1.1 (slightly wider than tall for a lush look).
|
|
54
65
|
*/
|
|
55
66
|
export declare function createCherryCanopyMesh(): TreeMesh;
|
|
67
|
+
/**
|
|
68
|
+
* Unit crop sphere mesh for rendering individual fruits, nuts, or flowers.
|
|
69
|
+
* Deliberately low-polygon (24 triangles) so hundreds of instances remain cheap.
|
|
70
|
+
* Scale uniformly via getScale = [r, r, r] to set the world-space radius in metres.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createCropMesh(): TreeMesh;
|
|
56
73
|
//# sourceMappingURL=tree-geometry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-geometry.d.ts","sourceRoot":"","sources":["../../src/tree-layer/tree-geometry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tree-geometry.d.ts","sourceRoot":"","sources":["../../src/tree-layer/tree-geometry.ts"],"names":[],"mappings":"AAcA;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,UAAU,EAAE;QACV,QAAQ,EAAE;YAAC,KAAK,EAAE,YAAY,CAAC;YAAC,IAAI,EAAE,CAAC,CAAA;SAAC,CAAC;QACzC,MAAM,EAAE;YAAC,KAAK,EAAE,YAAY,CAAC;YAAC,IAAI,EAAE,CAAC,CAAA;SAAC,CAAC;KACxC,CAAC;IACF,OAAO,EAAE;QAAC,KAAK,EAAE,WAAW,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAC,CAAC;IACvC,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAmIF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,SAAI,GAAG,QAAQ,CAMtD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAI,EAAE,QAAQ,SAAI,GAAG,QAAQ,CAmDvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAM9C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,QAAQ,CAS/C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,QAAQ,CAShD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,QAAQ,CAMjD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAKzC"}
|
|
@@ -7,6 +7,55 @@ import { BufferGeometry, BufferAttribute, CylinderGeometry, ConeGeometry, Sphere
|
|
|
7
7
|
* Rotates -90 degrees around the X axis: Y -> Z, Z -> -Y.
|
|
8
8
|
*/
|
|
9
9
|
const Y_TO_Z_UP = new Matrix4().makeRotationX(-Math.PI / 2);
|
|
10
|
+
/**
|
|
11
|
+
* Perturb each vertex radially using a sum of low-frequency sinusoidal waves
|
|
12
|
+
* evaluated at the vertex's surface direction. Adjacent vertices receive
|
|
13
|
+
* smoothly-varying displacements so there are no gaps or cracks in the mesh.
|
|
14
|
+
* Applied once at module init — zero runtime cost.
|
|
15
|
+
*
|
|
16
|
+
* @param geo Three.js BufferGeometry to modify in-place (before Y_TO_Z_UP rotation)
|
|
17
|
+
* @param magnitude Fractional displacement amplitude, e.g. 0.15 = ±15 % of radius
|
|
18
|
+
* @param seed Integer seed — each species gets a distinct blob shape
|
|
19
|
+
*/
|
|
20
|
+
function jitterSmooth(geo, magnitude, seed) {
|
|
21
|
+
let s = seed >>> 0;
|
|
22
|
+
const rng = () => {
|
|
23
|
+
s = (s + 0x9e3779b9) | 0;
|
|
24
|
+
let t = s ^ (s >>> 16);
|
|
25
|
+
t = Math.imul(t, 0x21f0aaad);
|
|
26
|
+
t ^= t >>> 15;
|
|
27
|
+
t = Math.imul(t, 0x735a2d97);
|
|
28
|
+
return ((t ^ (t >>> 15)) >>> 0) / 4294967296;
|
|
29
|
+
};
|
|
30
|
+
// 4 low-frequency waves (2–5 bumps across the sphere) — smooth, no cracks
|
|
31
|
+
const waves = Array.from({ length: 4 }, () => ({
|
|
32
|
+
fx: 2 + rng() * 3,
|
|
33
|
+
fy: 2 + rng() * 3,
|
|
34
|
+
fz: 2 + rng() * 3,
|
|
35
|
+
phase: rng() * Math.PI * 2
|
|
36
|
+
}));
|
|
37
|
+
const pos = geo.attributes.position.array;
|
|
38
|
+
for (let i = 0; i < pos.length; i += 3) {
|
|
39
|
+
const x = pos[i];
|
|
40
|
+
const y = pos[i + 1];
|
|
41
|
+
const z = pos[i + 2];
|
|
42
|
+
const r = Math.sqrt(x * x + y * y + z * z);
|
|
43
|
+
if (r !== 0) {
|
|
44
|
+
const nx = x / r;
|
|
45
|
+
const ny = y / r;
|
|
46
|
+
const nz = z / r;
|
|
47
|
+
let noise = 0;
|
|
48
|
+
for (const w of waves) {
|
|
49
|
+
noise += Math.sin(nx * w.fx + ny * w.fy + nz * w.fz + w.phase);
|
|
50
|
+
}
|
|
51
|
+
noise /= 4; // normalise to ~ [-1, 1]
|
|
52
|
+
const scale = 1 + noise * magnitude;
|
|
53
|
+
pos[i] = x * scale;
|
|
54
|
+
pos[i + 1] = y * scale;
|
|
55
|
+
pos[i + 2] = z * scale;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
10
59
|
/**
|
|
11
60
|
* Extract a TreeMesh from a Three.js BufferGeometry.
|
|
12
61
|
* Assumes the geometry has already been rotated to Z-up.
|
|
@@ -90,38 +139,66 @@ export function createTrunkMesh(segments = 8) {
|
|
|
90
139
|
*/
|
|
91
140
|
export function createPineCanopyMesh(levels = 3, segments = 8) {
|
|
92
141
|
const geos = [];
|
|
93
|
-
|
|
142
|
+
// Deterministic per-levels RNG so each level count gets its own organic shape.
|
|
143
|
+
let s = (levels * 2654435761) >>> 0;
|
|
144
|
+
const rng = () => {
|
|
145
|
+
s = (s + 0x9e3779b9) | 0;
|
|
146
|
+
let t = s ^ (s >>> 16);
|
|
147
|
+
t = Math.imul(t, 0x21f0aaad);
|
|
148
|
+
t ^= t >>> 15;
|
|
149
|
+
t = Math.imul(t, 0x735a2d97);
|
|
150
|
+
return ((t ^ (t >>> 15)) >>> 0) / 4294967296;
|
|
151
|
+
};
|
|
152
|
+
// Base tier height for 50 % overlap filling z = 0..0.8.
|
|
153
|
+
const tierHeight = 1.6 / (levels + 1);
|
|
154
|
+
const step = tierHeight / 2;
|
|
155
|
+
let zCursor = 0;
|
|
94
156
|
for (let i = 0; i < levels; i++) {
|
|
95
157
|
const t = i / (levels - 1 || 1);
|
|
96
|
-
//
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const
|
|
158
|
+
// Width narrows toward the top; each tier gets ±20 % random variation.
|
|
159
|
+
const baseRadius = (1 - t * 0.5) * 0.85;
|
|
160
|
+
const radius = baseRadius * (0.8 + rng() * 0.4);
|
|
161
|
+
// Height varies ±15 % per tier for uneven silhouette.
|
|
162
|
+
const tierH = tierHeight * (0.85 + rng() * 0.3);
|
|
163
|
+
const cone = new ConeGeometry(radius, tierH, segments);
|
|
101
164
|
cone.applyMatrix4(Y_TO_Z_UP);
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
|
|
165
|
+
// Drift increases from 0 at the bottom tier to ±0.10 at the top tier.
|
|
166
|
+
// Bottom tier stays centred so it always connects cleanly to the trunk.
|
|
167
|
+
const driftScale = levels > 1 ? i / (levels - 1) : 0;
|
|
168
|
+
const driftX = (rng() - 0.5) * 0.2 * driftScale;
|
|
169
|
+
const driftY = (rng() - 0.5) * 0.2 * driftScale;
|
|
170
|
+
cone.translate(driftX, driftY, zCursor + tierH / 2);
|
|
105
171
|
geos.push(cone);
|
|
172
|
+
zCursor += step;
|
|
106
173
|
}
|
|
107
|
-
//
|
|
108
|
-
const tip = new ConeGeometry(0.
|
|
174
|
+
// Slender tip with slight lean.
|
|
175
|
+
const tip = new ConeGeometry(0.08, 0.22, 6);
|
|
109
176
|
tip.applyMatrix4(Y_TO_Z_UP);
|
|
110
|
-
tip.translate(0, 0,
|
|
177
|
+
tip.translate((rng() - 0.5) * 0.08, (rng() - 0.5) * 0.08, zCursor + 0.05);
|
|
111
178
|
geos.push(tip);
|
|
112
179
|
const merged = mergeGeometries(geos);
|
|
113
|
-
merged.computeVertexNormals();
|
|
114
180
|
return extractMesh(merged);
|
|
115
181
|
}
|
|
116
182
|
/**
|
|
117
|
-
* Unit oak canopy mesh:
|
|
118
|
-
*
|
|
183
|
+
* Unit oak canopy mesh: high-segment sphere, smooth pole, no shading stripe.
|
|
184
|
+
*
|
|
185
|
+
* IcosahedronGeometry (detail ≥ 1) always creates a single vertex at the
|
|
186
|
+
* sphere's north pole via subdivision (normalized midpoint of the top edge),
|
|
187
|
+
* giving 5 triangles meeting at the apex — the visible spike. Rotating the
|
|
188
|
+
* icosahedron only moves WHICH original vertex becomes the apex; all 12 base
|
|
189
|
+
* vertices are 5-connected, so the artifact persists.
|
|
190
|
+
*
|
|
191
|
+
* SphereGeometry(24, 16) places 24 tiny triangles at the pole instead of 5,
|
|
192
|
+
* which is invisible at normal viewing distances. The earlier "shading stripe"
|
|
193
|
+
* with the low-poly sphere (12 × 8 = 22.5° bands) was coarse Gouraud banding,
|
|
194
|
+
* not a UV-seam issue. At 24 × 16 (7.5° bands) the shading is smooth.
|
|
195
|
+
* Extends z = 0 (base) to z = 1 (top).
|
|
119
196
|
*/
|
|
120
197
|
export function createOakCanopyMesh() {
|
|
121
|
-
const geo = new SphereGeometry(0.5,
|
|
122
|
-
|
|
198
|
+
const geo = new SphereGeometry(0.5, 14, 10);
|
|
199
|
+
jitterSmooth(geo, 0.18, 1);
|
|
123
200
|
geo.applyMatrix4(Y_TO_Z_UP);
|
|
124
|
-
geo.translate(0, 0, 0.5);
|
|
201
|
+
geo.translate(0, 0, 0.5);
|
|
125
202
|
return extractMesh(geo);
|
|
126
203
|
}
|
|
127
204
|
/**
|
|
@@ -131,6 +208,7 @@ export function createOakCanopyMesh() {
|
|
|
131
208
|
export function createPalmCanopyMesh() {
|
|
132
209
|
// Flattened sphere acting as a spread crown
|
|
133
210
|
const geo = new SphereGeometry(0.7, 12, 5);
|
|
211
|
+
jitterSmooth(geo, 0.1, 4);
|
|
134
212
|
const flatten = new Matrix4().makeScale(1.4, 0.35, 1.4);
|
|
135
213
|
geo.applyMatrix4(flatten);
|
|
136
214
|
geo.applyMatrix4(Y_TO_Z_UP);
|
|
@@ -143,6 +221,7 @@ export function createPalmCanopyMesh() {
|
|
|
143
221
|
*/
|
|
144
222
|
export function createBirchCanopyMesh() {
|
|
145
223
|
const geo = new SphereGeometry(0.42, 10, 8);
|
|
224
|
+
jitterSmooth(geo, 0.14, 2);
|
|
146
225
|
// Elongate vertically (Z after rotation)
|
|
147
226
|
const elongate = new Matrix4().makeScale(1, 1.45, 1);
|
|
148
227
|
geo.applyMatrix4(elongate);
|
|
@@ -156,6 +235,18 @@ export function createBirchCanopyMesh() {
|
|
|
156
235
|
*/
|
|
157
236
|
export function createCherryCanopyMesh() {
|
|
158
237
|
const geo = new SphereGeometry(0.52, 12, 8);
|
|
238
|
+
jitterSmooth(geo, 0.2, 3);
|
|
239
|
+
geo.applyMatrix4(Y_TO_Z_UP);
|
|
240
|
+
geo.translate(0, 0, 0.5);
|
|
241
|
+
return extractMesh(geo);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Unit crop sphere mesh for rendering individual fruits, nuts, or flowers.
|
|
245
|
+
* Deliberately low-polygon (24 triangles) so hundreds of instances remain cheap.
|
|
246
|
+
* Scale uniformly via getScale = [r, r, r] to set the world-space radius in metres.
|
|
247
|
+
*/
|
|
248
|
+
export function createCropMesh() {
|
|
249
|
+
const geo = new SphereGeometry(0.5, 6, 4);
|
|
159
250
|
geo.applyMatrix4(Y_TO_Z_UP);
|
|
160
251
|
geo.translate(0, 0, 0.5);
|
|
161
252
|
return extractMesh(geo);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-geometry.js","sourceRoot":"","sources":["../../src/tree-layer/tree-geometry.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,+BAA+B;AAC/B,oCAAoC;AAEpC,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,cAAc,
|
|
1
|
+
{"version":3,"file":"tree-geometry.js","sourceRoot":"","sources":["../../src/tree-layer/tree-geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,40 @@
|
|
|
1
1
|
import { CompositeLayer } from '@deck.gl/core';
|
|
2
2
|
import type { Color, DefaultProps, LayerProps, Position } from '@deck.gl/core';
|
|
3
|
-
import { SimpleMeshLayer } from '@deck.gl/mesh-layers';
|
|
4
3
|
import { createPineCanopyMesh } from "./tree-geometry.js";
|
|
5
4
|
/** Tree species / silhouette variant. */
|
|
6
5
|
export type TreeType = 'pine' | 'oak' | 'palm' | 'birch' | 'cherry';
|
|
7
6
|
/** Season that drives default canopy colour when no explicit colour is supplied. */
|
|
8
7
|
export type Season = 'spring' | 'summer' | 'autumn' | 'winter';
|
|
8
|
+
/**
|
|
9
|
+
* Crop configuration for a single tree.
|
|
10
|
+
*
|
|
11
|
+
* Pass this from `getCrop` to render small spherical crop points on the tree
|
|
12
|
+
* and/or scattered on the ground around it. Works for fruit, nuts, or flowers
|
|
13
|
+
* (flowering stage is expressed simply as a flower-coloured crop config).
|
|
14
|
+
*
|
|
15
|
+
* Positions are randomised deterministically from the tree's geographic
|
|
16
|
+
* coordinates, so they are stable across re-renders.
|
|
17
|
+
*/
|
|
18
|
+
export type CropConfig = {
|
|
19
|
+
/** Colour of each crop sphere [r, g, b, a]. */
|
|
20
|
+
color: Color;
|
|
21
|
+
/** Number of crop spheres placed in the outer canopy volume (live/in-tree crops). */
|
|
22
|
+
count: number;
|
|
23
|
+
/**
|
|
24
|
+
* Number of crop spheres scattered on the ground within the canopy footprint
|
|
25
|
+
* (dropped/fallen crops).
|
|
26
|
+
* @default 0
|
|
27
|
+
*/
|
|
28
|
+
droppedCount?: number;
|
|
29
|
+
/** Radius of each individual crop sphere in metres. */
|
|
30
|
+
radius: number;
|
|
31
|
+
};
|
|
32
|
+
/** Internal flat record for a single rendered crop sphere. */
|
|
33
|
+
type CropPoint = {
|
|
34
|
+
position: [number, number, number];
|
|
35
|
+
color: Color;
|
|
36
|
+
scale: number;
|
|
37
|
+
};
|
|
9
38
|
type _TreeLayerProps<DataT> = {
|
|
10
39
|
/** Source data. */
|
|
11
40
|
data: DataT[];
|
|
@@ -66,6 +95,22 @@ type _TreeLayerProps<DataT> = {
|
|
|
66
95
|
* @default 3
|
|
67
96
|
*/
|
|
68
97
|
getBranchLevels?: (d: DataT) => number;
|
|
98
|
+
/**
|
|
99
|
+
* Optional crop configuration for this tree.
|
|
100
|
+
*
|
|
101
|
+
* Return a `CropConfig` to render small spherical crop points in the outer
|
|
102
|
+
* canopy volume (live crops) and/or scattered on the ground around the trunk
|
|
103
|
+
* (dropped crops). Return `null` to show no crops for this tree.
|
|
104
|
+
*
|
|
105
|
+
* The same accessor can express fruit, nuts, or flowering stage — pass
|
|
106
|
+
* flower-coloured points (e.g. `[255, 200, 220, 255]`) for a blossom effect.
|
|
107
|
+
*
|
|
108
|
+
* Crop positions are randomised deterministically from the tree's geographic
|
|
109
|
+
* coordinates; they are stable across re-renders.
|
|
110
|
+
*
|
|
111
|
+
* @default null (no crops)
|
|
112
|
+
*/
|
|
113
|
+
getCrop?: (d: DataT) => CropConfig | null;
|
|
69
114
|
/**
|
|
70
115
|
* Global size multiplier applied to all dimensions.
|
|
71
116
|
* @default 1
|
|
@@ -76,6 +121,8 @@ export type TreeLayerProps<DataT = unknown> = _TreeLayerProps<DataT> & LayerProp
|
|
|
76
121
|
type TreeLayerState = {
|
|
77
122
|
grouped: Record<TreeType, unknown[]>;
|
|
78
123
|
pineMeshes: Record<number, ReturnType<typeof createPineCanopyMesh>>;
|
|
124
|
+
liveCropPoints: CropPoint[];
|
|
125
|
+
droppedCropPoints: CropPoint[];
|
|
79
126
|
};
|
|
80
127
|
/**
|
|
81
128
|
* **TreeLayer** — A parametric, Three.js-powered deck.gl layer that renders
|
|
@@ -93,6 +140,7 @@ type TreeLayerState = {
|
|
|
93
140
|
* - Trunk and canopy radii (`getTrunkRadius`, `getCanopyRadius`)
|
|
94
141
|
* - Explicit or season-driven colours (`getTrunkColor`, `getCanopyColor`, `getSeason`)
|
|
95
142
|
* - Pine tier density (`getBranchLevels`)
|
|
143
|
+
* - Crop / fruit / flower visualisation (`getCrop`)
|
|
96
144
|
* - Global scale factor (`sizeScale`)
|
|
97
145
|
*/
|
|
98
146
|
export declare class TreeLayer<DataT = unknown, ExtraPropsT extends {} = {}> extends CompositeLayer<ExtraPropsT & Required<_TreeLayerProps<DataT>>> {
|
|
@@ -100,13 +148,19 @@ export declare class TreeLayer<DataT = unknown, ExtraPropsT extends {} = {}> ext
|
|
|
100
148
|
static defaultProps: DefaultProps<TreeLayerProps<unknown>>;
|
|
101
149
|
state: TreeLayerState;
|
|
102
150
|
initializeState(): void;
|
|
103
|
-
updateState({ props, changeFlags }: {
|
|
151
|
+
updateState({ props, oldProps, changeFlags }: {
|
|
104
152
|
props: any;
|
|
153
|
+
oldProps: any;
|
|
105
154
|
changeFlags: any;
|
|
106
155
|
}): void;
|
|
107
|
-
/**
|
|
156
|
+
/**
|
|
157
|
+
* Build a single canopy sub-layer.
|
|
158
|
+
*
|
|
159
|
+
* Takes explicit `mesh`, `data`, and `layerId` so that pine trees can be
|
|
160
|
+
* split into one sub-layer per level count (each with its own mesh).
|
|
161
|
+
*/
|
|
108
162
|
private _buildCanopyLayer;
|
|
109
|
-
renderLayers():
|
|
163
|
+
renderLayers(): any[];
|
|
110
164
|
}
|
|
111
165
|
export {};
|
|
112
166
|
//# sourceMappingURL=tree-layer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree-layer.d.ts","sourceRoot":"","sources":["../../src/tree-layer/tree-layer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,cAAc,EAAC,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAC,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"tree-layer.d.ts","sourceRoot":"","sources":["../../src/tree-layer/tree-layer.ts"],"names":[],"mappings":"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"}
|