Files
hydro-builder-app/README.md
2026-06-13 17:36:44 -07:00

6.0 KiB
Raw Blame History

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

Quick start

npm install
npm run dev        # → http://localhost:5173
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.