# Hydro Builder An interactive, browser-based 3D builder for hydroponic / water-flow systems. Design lattice towers, grow walls, pipe networks, pumps, reservoirs and custom water paths — with live, approximate flow simulation. Built with **React + TypeScript + Vite**, **React Three Fiber / Drei**, **Zustand**, and **TailwindCSS**. ![Hydro Builder](docs/screenshot.png) ## Quick start ```bash npm install npm run dev # → http://localhost:5173 ``` ```bash npm run build # typecheck + production build (dist/) npm run preview # serve the production build npm run typecheck # tsc only ``` On first launch the app loads a working demo: reservoir → pump → riser → overhead return → emitter spraying back into the tank, plus a grow tower, tray and lattice to play with. Your work autosaves to the browser and is restored on the next visit. ## Using the builder | Action | How | |---|---| | Place a part | Click it in the left library, then click the ground (Shift-click stamps copies). Or drag it from the library onto the canvas. | | Move a part | Drag it in the scene. In Front/Side views, dragging moves vertically too. | | Raise / lower | `E` / `Q` (or edit Y in the inspector) | | Rotate | `R` (90° around Y), Shift-`R` reverse, or inspector fields/buttons | | Duplicate / delete | `D` / `Delete`, or inspector buttons | | Undo / redo | `⌘Z` / `⇧⌘Z` (or toolbar) | | Measure | Toolbar 📏, then click two points | | Snap to grid | Toolbar ⌗ (0.25 ft grid) | | Connect pipes | Drop a part near another part's connector — it snaps. Green dot = joined, amber = open. | | Views | Toolbar: 3D orbit / Top / Front / Side (2D orthographic) | | Water animation | Toolbar 💧 Flow | | Save / Load | Toolbar — named projects in browser storage | | Export / Import | Toolbar — design JSON file | ## How the simulation works The system is treated as a **graph**: - **Nodes** — connection points; connectors that touch are merged (union-find). Every flow-carrying part also gets an internal node so 3-way tees, tanks, towers etc. work uniformly. - **Edges** — part bodies (pipes, elbows, valves, …), each with a hydraulic resistance. - **Pump** — pressure source. It checks its inlet reaches a reservoir, then traverses downstream collecting resistance, static head (highest point above the pump), open ends, dead ends and loops. Approximate delivered flow: ``` Q = rated_GPH × headFactor × resistanceFactor headFactor = clamp(1 − head / maxHead, 0, 1) resistanceFactor = 1 / (1 + R / 30) R(pipe) ≈ length / diameter⁴ R(elbow) ≈ 1.2 × (angle/90) / diameter² R(valve) ≈ 0.3 + restriction(% open); closed = blocks flow ``` Diagnostics surfaced in the bottom panel: missing source, disconnected pump, pump too weak for the elevation (head ≥ max head), bottlenecks, open pipe ends (leaks), dead ends, closed valves, loops. Click a diagnostic to select the offending part. Flow direction is rendered as animated arrows; flowing pipes tint blue and the rate scales arrow speed. This is deliberately simple steady-state math — not CFD — and is designed to be swapped out later (see below). ## Project structure ``` src/ ├── types.ts # Shared domain types (parts, sim, project file) ├── parts/ │ └── catalog.ts # ★ Part catalog: defaults, params, connectors ├── store/ │ └── builderStore.ts # Zustand store + undo/redo (BuilderState) ├── simulation/ │ └── flowSimulator.ts # Graph build + pump physics + warnings ├── utils/ │ ├── connectors.ts # World-space connector math, snap logic │ ├── serializer.ts # Save/load/export/import (ProjectSerializer) │ ├── demoProject.ts # Starter scene │ └── dragState.ts # Transient drag context ├── components/ │ ├── SceneCanvas.tsx # R3F canvas, cameras, grid, drag plane, measure │ ├── PartMesh.tsx # Geometry for every part type + selection/drag │ ├── FlowArrows.tsx # Animated flow-direction arrows │ ├── Toolbar.tsx # Project actions, tools, view presets │ ├── PartLibrary.tsx # Left sidebar │ ├── PropertiesPanel.tsx # Right sidebar inspector │ └── StatusPanel.tsx # Bottom simulation status ├── App.tsx # Layout, shortcuts, autosave, drag-drop └── main.tsx ``` Conventions: world units are **feet** (1 grid cell = 0.5 ft), pipe diameters in **inches**, flow in **GPH** (shown with L/h). Rotations are radians (XYZ euler) in state, degrees in the UI. ## How to extend **Add a new part type** (e.g. a UV filter): 1. Add `'uvFilter'` to `PartType` in `src/types.ts`. 2. Add a catalog entry in `src/parts/catalog.ts` — defaults, inspector params and `getConnectors()`. Add it to a category in `CATEGORIES` and (if it carries water) to `FLOW_PARTS`. 3. Add a mesh case in `src/components/PartMesh.tsx`. 4. Give it a resistance in `partResistance()` in `src/simulation/flowSimulator.ts`. That's it — placement, snapping, drag, save/load, inspector and simulation pick it up automatically. **Improve the physics**: everything lives in `flowSimulator.ts`. The graph build is separate from the flow estimate, so you can replace the single-pass estimate with e.g. Hardy-Cross iteration or a linear solver over the same graph without touching the UI. **Planned extension points** (the architecture already supports them): - *Nutrient dosing* — add a `doser` part + per-edge concentration tracking in the simulator (it already knows flow per part). - *Plant growth zones / lighting / timers* — new part categories; timers can gate `partResistance()` (a valve already shows how blocking works). - *Bill of materials* — iterate `useBuilder.getState().parts` and group by type/params; the catalog has labels and units. - *Parts marketplace / AI assistant* — the project JSON (`ProjectFile`) is a complete, validated serialization format for sharing and generation.