@deck.gl-community/three 9.2.5 → 9.3.1-alpha.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.
@@ -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 unknown as number[];\n const droppedColor: Color = [\n c[0],\n c[1],\n c[2],\n Math.round((c[3] ?? 255) * 0.45)\n ] as unknown 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.6;\n const sy = 1 + (((seed >>> 16) & 0xffff) / 65535 - 0.5) * 0.6;\n return [r * sx, r * sy, h * (1 - f)];\n },\n getOrientation: (d) => {\n // Random bearing + slight pitch per tree so spherical canopies show\n // visible lean variety at typical farm viewing angles (30\u201360\u00B0 pitch).\n // pitch [0]: \u00B112\u00B0, yaw [1]: full 360\u00B0, roll [2]: 0\n const pos = getPosition(d);\n const seed = positionSeed(pos[0], pos[1]);\n const yaw = (((seed ^ (seed >>> 13)) & 0xffff) / 65535) * 360;\n const pitch = (((seed ^ (seed >>> 7)) & 0xff) / 255 - 0.5) * 24;\n return [pitch, yaw, 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
@@ -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;AAC9E,OAAO,EAAC,SAAS,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: a large sphere.
38
- * Extends from z=0 to z=1, center at z=0.5.
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":"AAaA;;;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;AAgFF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,SAAI,GAAG,QAAQ,CAMtD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAI,EAAE,QAAQ,SAAI,GAAG,QAAQ,CA4BvE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAM9C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,QAAQ,CAQ/C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,QAAQ,CAQhD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,QAAQ,CAKjD"}
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
- const tierHeight = 0.55 / levels;
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
- // Bottom tiers are wider, top tiers are narrower
97
- const radius = (1 - t * 0.5) * 0.85;
98
- // Tiers are staggered: each one starts 60% up the previous tier
99
- const zBase = t * (1 - tierHeight * 1.2);
100
- const cone = new ConeGeometry(radius, tierHeight, segments);
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
- // ConeGeometry apex is at y=+height/2 -> z=+height/2 after rotation
103
- // Translate so apex points upward and base is at zBase
104
- cone.translate(0, 0, zBase + tierHeight);
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
- // Sharp tip at top
108
- const tip = new ConeGeometry(0.12, 0.18, 6);
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, 1.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: a large sphere.
118
- * Extends from z=0 to z=1, center at z=0.5.
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, 12, 8);
122
- // SphereGeometry is centered at origin, radius=0.5
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); // center at z=0.5, extends z=0 to z=1
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":"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"}
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
- /** Build a single canopy sub-layer for one tree type. */
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(): SimpleMeshLayer<any, {}>[];
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;AAC7E,OAAO,EAAC,eAAe,EAAC,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAEL,oBAAoB,EAKrB,2BAAwB;AAMzB,yCAAyC;AACzC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpE,oFAAoF;AACpF,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAqE/D,KAAK,eAAe,CAAC,KAAK,IAAI;IAC5B,mBAAmB;IACnB,IAAI,EAAE,KAAK,EAAE,CAAC;IAEd,oDAAoD;IACpD,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,QAAQ,CAAC;IAErC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEpC;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,QAAQ,CAAC;IAErC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEjC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAE9C;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEtC;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEvC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,KAAK,GAAG,IAAI,CAAC;IAE3C;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,KAAK,GAAG,IAAI,CAAC;IAE5C;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,KAAK,MAAM,CAAC;IAEvC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC;AAqBlF,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,CAAC;CACrE,CAAC;AAMF;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,SAAS,CAAC,KAAK,GAAG,OAAO,EAAE,WAAW,SAAS,EAAE,GAAG,EAAE,CAAE,SAAQ,cAAc,CACzF,WAAW,GAAG,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAC/C;IACC,MAAM,CAAC,SAAS,SAAe;IAC/B,MAAM,CAAC,YAAY,wCAAgB;IAE3B,KAAK,EAAE,cAAc,CAAC;IAE9B,eAAe;IAOf,WAAW,CAAC,EAAC,KAAK,EAAE,WAAW,EAAC;;;KAAA;IA2BhC,yDAAyD;IACzD,OAAO,CAAC,iBAAiB;IAiDzB,YAAY;CAoDb"}
1
+ {"version":3,"file":"tree-layer.d.ts","sourceRoot":"","sources":["../../src/tree-layer/tree-layer.ts"],"names":[],"mappings":"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"}