Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,7 @@ next-env.d.ts
/package-lock.json

/native/spacemouse/build/

# Local preview launchers
/desktop/local-v090.cjs
/desktop/scripts/launch-v090.cjs
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@

Features, improvements, fixes and compatibility notes for LO2S - OpticMesh.

## [0.9.0 Beta] - 2026-09-10

A preview release for testing Technical Plots. Keep a separate project copy before opening it in an older version. The hosted web app remains at v0.7.0.

- Reviewed the manual and shortcut guide around the Pixel Map → 3D Simulation → Technical Plots workflow. Added illustrated Plots topics for layouts, templates, maps, specifications, framing, text and delivery; refreshed workspace images and corrected outdated saving, export and feature-limit descriptions.

- Matched Technical Plots scrollbars to the dark inspector styling and kept the page layout stable when notes are expanded. Fixed spaces being intercepted while typing in text areas.

- Added text-frame formatting controls for bold, italic, underline and alignment, preserved in projects, templates, duplicates and printed/PDF pages.

- Added a Technical Plots workspace with fixed A3 landscape sheets, editable frame layouts, reusable templates and user-supplied logos and title blocks.
- Added input/output maps, screen detail grids, and specification tables covering pixel pitch, raster, aspect ratios, physical dimensions and mapping coordinates. Long tables continue onto additional sheets.
- Added orthographic and isometric scene views with shaded/wireframe choices and optional stage geometry.
- Added desktop PDF export, an OpticMesh print preview with page navigation and printer selection, project persistence, and Undo/Redo for plot layouts.
- Added cursor-based frame placement, adjustable grid snapping, and drag-and-drop sheet reordering.
- Added paper fonts and frame text sizes, multiline footer notes, and arithmetic fields with the shared adjustment controls and Shift steps of 10.
- Scene frames refresh automatically when their settings change, follow camera names until renamed, and support independent pan and zoom.
- Screen Specifications and Screen Detail use the configured physical panel sizes and panel raster; map labels wrap instead of disappearing on narrow slices.
- Screen Detail reserves space for horizontal and vertical pixel dimensions and wrapped panel notes, preventing overlap with the grid or frame edges.
- Technical Plots uses the shared amber notifications and its own Tools menu label.
- Added Ctrl+D, Duplicate frame and Alt-drag frame copying, preserving settings with independent editing, snapped placement and Undo/Redo.
- Added bottom-right frame resizing, live dimensions, edge/centre alignment guides and equal-spacing snapping with millimetre measurements.
- Input and output maps show each slice’s corresponding X/Y origin, pixel dimensions and physical size. Narrow slices keep wrapped labels inside whenever they fit; numbered callouts are reserved for insufficient space or overlapping/warped geometry.
- Improved scene framing with an immediate drag/zoom preview and a sharp update after interaction. Removed the shifting view-update footer control.
- Changing a plot frame’s camera view now resets its pan and zoom to Fit View; custom titles remain unchanged.
- New view titles use capitalized camera names and can be renamed directly, without an automatic-title checkbox.

## [0.8.0 Beta] - 2026-09-09

Version 0.8.0 is a beta preview. Version 0.7.0 remains the stable Windows release and hosted web app.
Expand Down
43 changes: 30 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@

# LO2S - OpticMesh

**Build your LED layout. Check the pixel map. Preview your visuals on stage.**
**Check your pixel map. Simulate your stage. Deliver clear technical plots.**

OpticMesh is a free, open-source companion for visual artists, LED technicians and production designers. Turn a Resolume Advanced Output map into physically sized LED surfaces, place them within an imported stage design, and preview patterns or live visuals from your chosen camera angle. You can also create calibration patterns and export the maps and geometry needed by the rest of your workflow.
OpticMesh is a free, open-source companion for visual artists, LED technicians and production designers. Turn a Resolume Advanced Output map into physically sized LED surfaces, place them within an imported stage design, and preview patterns or live visuals from your chosen camera angle. Prepare branded A3 technical plots from that verified layout, with coordinates, dimensions, panel specifications and scene views. Patterns remains a supporting mode for calibration and content checks.

**New preview: v0.8.0 Beta · Windows x64**
**New preview: v0.9.0 Beta · Windows x64**

[Download v0.8.0 Beta](https://github.com/johnjjdave/opticmesh/releases/tag/v0.8.0) · [Changelog](CHANGELOG.md)
[Download v0.9.0 Beta](https://github.com/johnjjdave/opticmesh/releases/tag/v0.9.0) · [Changelog](CHANGELOG.md)

![OpticMesh 3D workspace with LED screens](public/manual/3d-overview.png)

## One workflow, three workspaces
## One workflow, four workspaces

The production flow is **Resolume Pixel Map → 3D Simulation → Technical Plots**. Use Patterns whenever you need standalone calibration images.

### Patterns — prepare and calibrate your display

Expand Down Expand Up @@ -42,40 +44,55 @@ Build a physically scaled preview from your Resolume slices and add an imported
- **Live sources:** patterns, video devices, NDI and Spout, with scene-wide or per-slice routing. Native NDI/Spout I/O requires Windows.
- **Scene exports:** GLB, glTF, OBJ, MVR mesh packages, STL and USDZ. Preservation of hierarchy, materials and units depends on the format.

### Technical Plots — prepare the handover

Create fixed **A3 landscape** sheets with your own logo and title block. Combine input/output maps, screen details, specifications, orthographic or isometric stage views, and delivery notes.

- **Mapping information:** slice names and IDs, input or output coordinates, pixel dimensions, physical sizes, pixel pitch and panel sizes/raster.
- **Page layout:** place, drag, resize and copy frames with grid snapping, alignment guides and millimetre spacing. Reorder sheets and save reusable templates.
- **View framing:** choose the camera and render style, then pan or zoom inside each frame. Each view refreshes when its settings change.
- **Typography:** adjustable fonts and sizes, with bold, italic, underline and alignment for note frames.
- **Delivery:** export PDF or use the A3 print preview. Long specification tables continue onto additional pages.

Scene views use parallel projection and are labelled **Not to scale**. Review the printed measurements rather than measuring stage geometry from the page.

## Floating Preview — keep a preview above your show software

In 3D mode, **Output → Floating Preview** opens an **always-on-top, camera-only preview** that stays live while you work elsewhere. Drag its top-left handle to position it and the bottom-right corner to resize it. Inside the preview, left-drag orbits, right-drag pans and the wheel zooms. You cannot accidentally edit objects there.

The preview keeps its own camera while receiving scene, material, map-style and live-source updates. It stays open when you switch to Patterns or Pixel Map. **Pause main viewport** stops drawing the main 3D view while the preview continues.
The preview keeps its own camera while receiving scene, material, map-style and live-source updates. It stays open when you switch to Patterns, Pixel Map or Plots. **Pause main viewport** stops drawing the main 3D view while the preview continues.

## Save, share and troubleshoot

Save your work in a **`.lo2s` project**, including imported model geometry. **File → Compile Project** collects the project, Resolume XML and input/output PNG maps into a named folder.
Save your work in a **`.lo2s` project**, including imported model geometry and editable plot sheets. **File → Compile Project** collects the project, Resolume XML and input/output PNG maps into a named folder. Export the finished plots separately as PDF.

The Windows app maintains startup and recovery saves. Use **File → Open Recent** to reopen one of your six most recently used projects. OpticMesh asks before replacing unsaved work and offers **Save**, **Quit without saving**, or **Cancel** when closing. Projects reopen on Pattern Generator so you can reconnect live inputs deliberately.

**Performance** distinguishes interface FPS, viewport redraws and scene/drawn triangle counts. The Windows app adds CPU and memory readings, with GPU/VRAM telemetry on supported NVIDIA systems. Load colours help identify pressure; see the manual for each reading's scope.

## Getting started

1. Install the [v0.8.0 Beta](https://github.com/johnjjdave/opticmesh/releases/tag/v0.8.0)
1. Install the [v0.9.0 Beta](https://github.com/johnjjdave/opticmesh/releases/tag/v0.9.0)
2. Create a pattern or import a Resolume Advanced Output XML file in **Pixel Map**.
3. Set your LED product's pixel pitch and check its physical dimensions.
4. Switch to **3D**, import stage geometry if needed, and position your screens.
5. Choose your source, frame the preview, and save a named project before exporting.
5. Choose your source and verify the 3D arrangement.
6. Open **Plots**, prepare your sheets, and check labels, dimensions and view framing.
7. Save a named project, then export the PDF and any required map or 3D files.

Each release includes its own searchable, illustrated guide. Open **Guide** or **Help → OpticMesh Manual** inside the app for instructions matching your installed version. Keyboard shortcuts are included in the same guide, with adjustable text size and enlarged screenshots.

## Windows and web availability

Development from v0.8.0 onward focuses on Windows. The hosted web app remains at v0.7.0; it will not receive the new desktop features.

| Capability | Windows v0.8.0 Beta | Hosted web v0.7.0 |
| Capability | Windows v0.9.0 Beta | Hosted web v0.7.0 |
| --- | --- | --- |
| Patterns, Resolume maps and LED scene layout | Yes | Yes |
| Imported stage models and material controls | Yes | Not in the hosted stable release |
| NDI / Spout input and output | Yes | No |
| Always-on-top Floating Preview | Yes | No |
| Technical Plots, templates and A3 PDF/print | Yes | No |
| Managed startup and recovery saves | Yes | No — save projects manually |

The Windows x64 installer includes the official NDI Runtime prerequisite when a compatible runtime is absent; NDI Tools is not required. The installer is unsigned. Windows may show an unknown-publisher prompt; use the official release and its `SHA256SUMS.txt` to verify your download.
Expand All @@ -89,12 +106,12 @@ Documents\OpticMesh\
└── Test Patterns\
```

**Projects** holds named projects and recovery files; **Exports** is the default for 3D exports; **Test Patterns** is the default for PNG maps and patterns. Ctrl+S updates a named project, or opens Save As for the startup project.
**Projects** holds named projects and recovery files; **Exports** is the default for 3D exports and Technical Plots PDFs; **Test Patterns** is the default for PNG maps and patterns. Ctrl+S updates a named project, or opens Save As for the startup project.

## Compatibility and scope

- v0.8.0 is a beta preview. Keep a separate copy of projects you need to reopen in v0.7.0; projects containing imported models require v0.8.0 or later.
- Imported models are static geometry. Fixture lighting simulation, animation playback, polygon reduction and Technical Plots are not included in this release.
- v0.9.0 is a beta preview for testing. Keep a separate copy of projects you need to reopen in older versions; Technical Plots requires v0.9.0 or later.
- Imported models are static geometry. Fixture lighting simulation, animation playback, polygon reduction are not included in this release.
- Import units and format capabilities matter. OBJ and STL require a source-unit choice; MVR uses its defined units. Review the manual before exchanging files with another application.
- 3D NDI/Spout output is 1920 × 1080, targeting 30 fps; achievable performance depends on the scene, sources and system. PNG exports retain their configured native resolution.
- Projects and media processing stay local. Desktop update checks contact GitHub; NDI exchanges frames over the network. See the [privacy and release policy](CODE_SIGNING_POLICY.md).
Expand Down
58 changes: 58 additions & 0 deletions app/expression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export function evaluateExpression(source: string, allowSigned = false): number | null {
const text = source.replace(/[×x]/gi, "*").replace(/÷/g, "/").replace(/,/g, "").trim();
if (!text || !/^[\d.+\-*/()\s]+$/.test(text)) return null;
let index = 0;
const skip = () => {
while (/\s/.test(text[index] ?? "")) index += 1;
};
const expression = (): number => {
let value = term();
while (true) {
skip();
const op = text[index];
if (op !== "+" && op !== "-") break;
index += 1;
const next = term();
value = op === "+" ? value + next : value - next;
}
return value;
};
const term = (): number => {
let value = factor();
while (true) {
skip();
const op = text[index];
if (op !== "*" && op !== "/") break;
index += 1;
const next = factor();
value = op === "*" ? value * next : value / next;
}
return value;
};
const factor = (): number => {
skip();
if (text[index] === "+" || text[index] === "-") {
const sign = text[index++] === "-" ? -1 : 1;
return sign * factor();
}
if (text[index] === "(") {
index += 1;
const value = expression();
skip();
if (text[index] !== ")") throw new Error("Missing parenthesis");
index += 1;
return value;
}
const match = text.slice(index).match(/^(?:\d+\.?\d*|\.\d+)/);
if (!match) throw new Error("Expected number");
index += match[0].length;
return Number(match[0]);
};
try {
const result = expression();
skip();
return index === text.length && Number.isFinite(result) && (allowSigned || result > 0) ? result : null;
} catch {
return null;
}
}
Loading
Loading