import { memo, useMemo } from 'react' import type { Plant } from '@/lib/plants' import type { EditorPlanting } from '@/lib/plantings' import { PlopMarker, SEMANTIC_FAR } from './PlopMarker' import { DIMMED_OPACITY, objectTransform } from './shared' import type { EditorObject } from './types' /** The most common plant color among an object's plops (for the far-zoom tint). */ function dominantColor(plops: EditorPlanting[], plantsById: Map): string | null { const freq = new Map() for (const p of plops) { const c = plantsById.get(p.plantId)?.color if (c) freq.set(c, (freq.get(c) ?? 0) + 1) } let best: string | null = null let bestN = 0 for (const [c, n] of freq) { if (n > bestN) { best = c bestN = n } } return best } /** * Renders every active plop, grouped under its parent object's translate+rotate * transform so plops track the bed when it's moved/rotated. Far zoom adds a * dominant-plant tint over each planted object so beds read at a glance. Focus * mode dims plops on non-focused objects. */ export const PlopLayer = memo(function PlopLayer({ objects, plantings, plantsById, scale, focusedObjectId, selectedPlantingId, onSelectPlop, }: { objects: EditorObject[] plantings: EditorPlanting[] plantsById: Map scale: number focusedObjectId: number | null selectedPlantingId: number | null onSelectPlop: (id: number) => void }) { // Group plops by object once per plops change (not on every pan/zoom frame). const byObject = useMemo(() => { const m = new Map() for (const p of plantings) { const arr = m.get(p.objectId) if (arr) arr.push(p) else m.set(p.objectId, [p]) } return m }, [plantings]) const far = scale < SEMANTIC_FAR return ( <> {objects.map((o) => { const plops = byObject.get(o.id) if (!plops || plops.length === 0) return null const dimmed = focusedObjectId != null && focusedObjectId !== o.id const halfW = o.widthCm / 2 const halfH = o.heightCm / 2 const tint = far ? dominantColor(plops, plantsById) : null return ( {tint && (o.shape === 'circle' ? ( ) : ( ))} {plops.map((p) => ( ))} ) })} ) })