meshlite 0.2.0__tar.gz
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.
- meshlite-0.2.0/.gitignore +32 -0
- meshlite-0.2.0/CHANGELOG.md +70 -0
- meshlite-0.2.0/CONTRIBUTING.md +81 -0
- meshlite-0.2.0/LICENSE +33 -0
- meshlite-0.2.0/PKG-INFO +173 -0
- meshlite-0.2.0/README.md +135 -0
- meshlite-0.2.0/assets/fonts/codicon.ttf +0 -0
- meshlite-0.2.0/assets/shaders/mesh.frag +43 -0
- meshlite-0.2.0/assets/shaders/mesh.vert +18 -0
- meshlite-0.2.0/main.py +27 -0
- meshlite-0.2.0/pyproject.toml +92 -0
- meshlite-0.2.0/src/meshlite/__init__.py +3 -0
- meshlite-0.2.0/src/meshlite/app.py +123 -0
- meshlite-0.2.0/src/meshlite/app_state/__init__.py +58 -0
- meshlite-0.2.0/src/meshlite/app_state/command_bus.py +285 -0
- meshlite-0.2.0/src/meshlite/app_state/document.py +114 -0
- meshlite-0.2.0/src/meshlite/app_state/events.py +137 -0
- meshlite-0.2.0/src/meshlite/app_state/history.py +161 -0
- meshlite-0.2.0/src/meshlite/app_state/node.py +58 -0
- meshlite-0.2.0/src/meshlite/app_state/preferences.py +127 -0
- meshlite-0.2.0/src/meshlite/app_state/selection_model.py +93 -0
- meshlite-0.2.0/src/meshlite/app_state/task_runner.py +164 -0
- meshlite-0.2.0/src/meshlite/app_state/transform.py +43 -0
- meshlite-0.2.0/src/meshlite/config/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/domain/__init__.py +24 -0
- meshlite-0.2.0/src/meshlite/domain/mesh_data.py +88 -0
- meshlite-0.2.0/src/meshlite/domain/mesh_info.py +81 -0
- meshlite-0.2.0/src/meshlite/domain/mesh_io.py +92 -0
- meshlite-0.2.0/src/meshlite/domain/mrm_shim.py +214 -0
- meshlite-0.2.0/src/meshlite/ops/__init__.py +44 -0
- meshlite-0.2.0/src/meshlite/ops/_dev/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/_dev/counter_op.py +64 -0
- meshlite-0.2.0/src/meshlite/ops/_manifest.py +25 -0
- meshlite-0.2.0/src/meshlite/ops/base.py +269 -0
- meshlite-0.2.0/src/meshlite/ops/boolean/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/boolean/boolean_op.py +138 -0
- meshlite-0.2.0/src/meshlite/ops/inspect/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/inspect/find_self_intersections.py +109 -0
- meshlite-0.2.0/src/meshlite/ops/io/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/io/load_mesh.py +97 -0
- meshlite-0.2.0/src/meshlite/ops/io/save_mesh.py +83 -0
- meshlite-0.2.0/src/meshlite/ops/registry.py +230 -0
- meshlite-0.2.0/src/meshlite/ops/repair/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/repair/auto_repair.py +188 -0
- meshlite-0.2.0/src/meshlite/ops/repair/fill_holes.py +135 -0
- meshlite-0.2.0/src/meshlite/ops/repair/remove_duplicates.py +84 -0
- meshlite-0.2.0/src/meshlite/ops/simplify/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/simplify/decimate.py +189 -0
- meshlite-0.2.0/src/meshlite/ops/simplify/remesh.py +121 -0
- meshlite-0.2.0/src/meshlite/ops/simplify/subdivide.py +130 -0
- meshlite-0.2.0/src/meshlite/ops/smooth/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/smooth/laplacian.py +83 -0
- meshlite-0.2.0/src/meshlite/ops/transform/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ops/transform/mirror.py +91 -0
- meshlite-0.2.0/src/meshlite/ops/transform/rotate.py +114 -0
- meshlite-0.2.0/src/meshlite/ops/transform/scale.py +106 -0
- meshlite-0.2.0/src/meshlite/ops/transform/translate.py +52 -0
- meshlite-0.2.0/src/meshlite/render/__init__.py +21 -0
- meshlite-0.2.0/src/meshlite/render/axes.py +71 -0
- meshlite-0.2.0/src/meshlite/render/camera.py +186 -0
- meshlite-0.2.0/src/meshlite/render/gpu_mesh.py +88 -0
- meshlite-0.2.0/src/meshlite/render/gpu_upload.py +65 -0
- meshlite-0.2.0/src/meshlite/render/renderer.py +202 -0
- meshlite-0.2.0/src/meshlite/render/shader_loader.py +83 -0
- meshlite-0.2.0/src/meshlite/ui/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ui/fonts.py +63 -0
- meshlite-0.2.0/src/meshlite/ui/icons.py +209 -0
- meshlite-0.2.0/src/meshlite/ui/panels/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ui/panels/activity_bar.py +76 -0
- meshlite-0.2.0/src/meshlite/ui/panels/base_panel.py +93 -0
- meshlite-0.2.0/src/meshlite/ui/panels/bottom/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ui/panels/bottom/console.py +20 -0
- meshlite-0.2.0/src/meshlite/ui/panels/bottom/mesh_info.py +106 -0
- meshlite-0.2.0/src/meshlite/ui/panels/properties.py +216 -0
- meshlite-0.2.0/src/meshlite/ui/panels/sidebar.py +66 -0
- meshlite-0.2.0/src/meshlite/ui/panels/sidebar_operations.py +62 -0
- meshlite-0.2.0/src/meshlite/ui/panels/sidebar_outliner.py +94 -0
- meshlite-0.2.0/src/meshlite/ui/panels/sidebar_search.py +96 -0
- meshlite-0.2.0/src/meshlite/ui/panels/sidebar_settings.py +149 -0
- meshlite-0.2.0/src/meshlite/ui/panels/status_bar.py +71 -0
- meshlite-0.2.0/src/meshlite/ui/panels/top_toolbar.py +335 -0
- meshlite-0.2.0/src/meshlite/ui/panels/viewport.py +156 -0
- meshlite-0.2.0/src/meshlite/ui/runner.py +704 -0
- meshlite-0.2.0/src/meshlite/ui/theme.py +180 -0
- meshlite-0.2.0/src/meshlite/ui/widgets/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/ui/widgets/command_palette.py +268 -0
- meshlite-0.2.0/src/meshlite/ui/widgets/info_cache.py +38 -0
- meshlite-0.2.0/src/meshlite/ui/widgets/param_widgets.py +144 -0
- meshlite-0.2.0/src/meshlite/utils/__init__.py +0 -0
- meshlite-0.2.0/src/meshlite/utils/async_task.py +240 -0
- meshlite-0.2.0/src/meshlite/utils/file_dialog.py +79 -0
- meshlite-0.2.0/src/meshlite/utils/fuzzy.py +108 -0
- meshlite-0.2.0/src/meshlite/utils/paths.py +36 -0
- meshlite-0.2.0/tests/__init__.py +0 -0
- meshlite-0.2.0/tests/conftest.py +29 -0
- meshlite-0.2.0/tests/fixtures/cube.stl +0 -0
- meshlite-0.2.0/tests/fixtures/open_cylinder.stl +0 -0
- meshlite-0.2.0/tests/test_architecture.py +85 -0
- meshlite-0.2.0/tests/test_command_bus.py +230 -0
- meshlite-0.2.0/tests/test_fill_holes.py +194 -0
- meshlite-0.2.0/tests/test_load_save_ops.py +200 -0
- meshlite-0.2.0/tests/test_mesh_info.py +98 -0
- meshlite-0.2.0/tests/test_mesh_io.py +151 -0
- meshlite-0.2.0/tests/test_ops_repair.py +101 -0
- meshlite-0.2.0/tests/test_ops_simplify.py +92 -0
- meshlite-0.2.0/tests/test_ops_transform.py +102 -0
- meshlite-0.2.0/tests/test_preferences.py +82 -0
- meshlite-0.2.0/tests/test_registry.py +189 -0
- meshlite-0.2.0/tests/test_undo.py +265 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.eggs/
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.ruff_cache/
|
|
12
|
+
.coverage
|
|
13
|
+
htmlcov/
|
|
14
|
+
|
|
15
|
+
# Virtual environments
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
env/
|
|
19
|
+
|
|
20
|
+
# IDE
|
|
21
|
+
.vscode/
|
|
22
|
+
.idea/
|
|
23
|
+
*.swp
|
|
24
|
+
*.swo
|
|
25
|
+
|
|
26
|
+
# hello_imgui — runtime layout/window state, not source
|
|
27
|
+
*.ini
|
|
28
|
+
!assets/layouts/*.ini
|
|
29
|
+
|
|
30
|
+
# OS
|
|
31
|
+
.DS_Store
|
|
32
|
+
Thumbs.db
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.2.0 (2026-04-12)
|
|
4
|
+
|
|
5
|
+
Production readiness release.
|
|
6
|
+
|
|
7
|
+
### New Features
|
|
8
|
+
|
|
9
|
+
- **User preferences** — all settings (viewport sensitivity, colors, lighting, camera FOV, undo limits) are persisted between sessions via hello_imgui's user pref API
|
|
10
|
+
- **Settings panel** — live sliders and color pickers in the sidebar for viewport, rendering, camera, and history settings with "Reset to Defaults"
|
|
11
|
+
- **Search panel** — fuzzy search across document nodes and operations in the sidebar
|
|
12
|
+
- **Recent files** — tracked automatically on mesh load; accessible from File > Open Recent and the command palette
|
|
13
|
+
- **Drag-and-drop** — drop mesh files onto the window to load them (GLFW backend, Linux)
|
|
14
|
+
- **Cross-platform CI** — GitHub Actions matrix now covers Linux, Windows, and macOS with Python 3.11/3.12/3.13
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
- **active_task_count leak** — counter was never decremented after task completion
|
|
19
|
+
- **GPU upload retry spam** — failed uploads no longer retry every frame at 60fps flooding the log
|
|
20
|
+
- **Unnecessary mesh clone** — removed a redundant `.clone()` in CommandBus finalize, saving memory per undoable operation
|
|
21
|
+
- **Falsy zero check in Decimate** — setting max_deleted_vertices to 0 no longer silently defaults to unlimited
|
|
22
|
+
- **BasePanel error dedup** — distinct panel errors are now logged instead of silencing everything after the first
|
|
23
|
+
- **Auto repair partial failure** — result message now clearly says "PARTIAL" when self-intersection fix fails
|
|
24
|
+
- **Debug button in production** — removed the CounterOp debug button and `_dev` import from the viewport
|
|
25
|
+
|
|
26
|
+
### Improvements
|
|
27
|
+
|
|
28
|
+
- Extracted `FLT_MAX` constant — replaced 5 duplicated 19-digit float literals across decimate/remesh/subdivide
|
|
29
|
+
- Named byte estimation constants in undo history with derivation comments
|
|
30
|
+
- Asset path resolver (`utils/paths.py`) — works in dev, pip-installed, and frozen bundle contexts
|
|
31
|
+
- Operation manifest (`ops/_manifest.py`) — explicit module list for frozen-bundle discovery
|
|
32
|
+
- Shader loader and font loader now use the centralized path resolver
|
|
33
|
+
|
|
34
|
+
### Technical
|
|
35
|
+
|
|
36
|
+
- 85 automated tests (77 original + 8 new preferences tests)
|
|
37
|
+
- Cross-platform CI: ubuntu-latest, windows-latest, macos-latest
|
|
38
|
+
- `selected_color` field on `RenderItem` — selection color now driven by preferences
|
|
39
|
+
|
|
40
|
+
## v0.1.0 (2026-04-12)
|
|
41
|
+
|
|
42
|
+
Initial public release.
|
|
43
|
+
|
|
44
|
+
### Features
|
|
45
|
+
|
|
46
|
+
- **15 mesh operations** with full MeshLib parameter exposure:
|
|
47
|
+
- File: Open Mesh, Save Mesh As
|
|
48
|
+
- Repair: Fill Holes, Auto Repair, Remove Duplicates
|
|
49
|
+
- Inspect: Fix Self-Intersections (Local + Voxel-based)
|
|
50
|
+
- Mesh Edit: Decimate, Remesh, Subdivide, Laplacian Smooth
|
|
51
|
+
- Boolean: Union / Intersection / Difference (with node picker)
|
|
52
|
+
- Transform: Translate, Rotate, Scale, Mirror
|
|
53
|
+
- **VSCode-style UI** with activity bar, dockable panels, dark theme, codicons
|
|
54
|
+
- **MeshInspector-style top toolbar** with 26 tools across 7 groups
|
|
55
|
+
- **Command palette** (Ctrl+Shift+P) with fuzzy search
|
|
56
|
+
- **Snapshot-based undo/redo** (Ctrl+Z / Ctrl+Shift+Z)
|
|
57
|
+
- **Mesh Info panel** with comprehensive statistics
|
|
58
|
+
- **Properties panel** with auto-rendered parameter widgets
|
|
59
|
+
- **Outliner** with visibility toggles and right-click context menus
|
|
60
|
+
- **Status bar** with mesh count, selection, FPS
|
|
61
|
+
- **Async worker dispatch** — operations run on background threads
|
|
62
|
+
- **One-file operation pattern** — adding a new op requires zero edits elsewhere
|
|
63
|
+
|
|
64
|
+
### Technical
|
|
65
|
+
|
|
66
|
+
- 5-layer architecture: domain / ops / render / app_state / ui
|
|
67
|
+
- MeshLib 3.1.1.211 backend
|
|
68
|
+
- imgui_bundle 1.92.601 UI
|
|
69
|
+
- moderngl 5.12.0 renderer
|
|
70
|
+
- Python 3.11+ required
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Contributing to meshlite
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing! meshlite is an open-source 3D mesh processing desktop app.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/syedjameel/meshlite.git
|
|
9
|
+
cd meshlite
|
|
10
|
+
uv venv --python 3.13 .venv
|
|
11
|
+
source .venv/bin/activate
|
|
12
|
+
uv pip install -e ".[dev]"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running Tests
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
PYTHONPATH= pytest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> **Note:** If your shell exports `PYTHONPATH` (e.g. for ROS or FreeCAD), clear it before running pytest to avoid plugin conflicts.
|
|
22
|
+
|
|
23
|
+
## Running the App
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
python main.py
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Adding a New Operation
|
|
30
|
+
|
|
31
|
+
Adding an operation to meshlite is a **one-file change**. Here's the pattern:
|
|
32
|
+
|
|
33
|
+
1. Create a file under `src/meshlite/ops/<category>/your_op.py`
|
|
34
|
+
2. Subclass `Operation`, decorate with `@register_operation`
|
|
35
|
+
3. Define `id`, `label`, `category`, `schema` (params), and `run()`
|
|
36
|
+
4. That's it — the op auto-discovers and appears in the command palette, sidebar, properties panel, and toolbar
|
|
37
|
+
|
|
38
|
+
See `src/meshlite/ops/smooth/laplacian.py` for a clean example.
|
|
39
|
+
|
|
40
|
+
If you plan to build a standalone bundle (PyInstaller etc.), also add the module path to `src/meshlite/ops/_manifest.py` so it's discovered in frozen mode.
|
|
41
|
+
|
|
42
|
+
## Architecture Rules
|
|
43
|
+
|
|
44
|
+
meshlite has 5 layers with strict dependency rules:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
ui ───> app_state ──> ops ──> domain
|
|
48
|
+
| | |
|
|
49
|
+
└─> render ─┘ └──> domain
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **domain/** — pure MeshLib wrapper. Only place `meshlib.*` is imported (via `mrm_shim.py`)
|
|
53
|
+
- **ops/** — operations. May import `meshlib` directly for Settings struct construction
|
|
54
|
+
- **render/** — moderngl only. No meshlib, no ImGui
|
|
55
|
+
- **app_state/** — Document, CommandBus, events, preferences. No meshlib, no GL, no ImGui
|
|
56
|
+
- **ui/** — ImGui panels. Imports from `app_state` (not `domain` directly)
|
|
57
|
+
|
|
58
|
+
Run `pytest tests/test_architecture.py` to verify these rules.
|
|
59
|
+
|
|
60
|
+
## User Preferences
|
|
61
|
+
|
|
62
|
+
User-configurable settings live in `app_state/preferences.py` (a plain dataclass with JSON serialization). To make a new value configurable:
|
|
63
|
+
|
|
64
|
+
1. Add the field to `Preferences` with a sensible default
|
|
65
|
+
2. Read from `self._app.preferences.<field>` in the consumer code
|
|
66
|
+
3. Add a widget in `ui/panels/sidebar_settings.py` if it should be user-facing
|
|
67
|
+
4. Preferences are auto-saved on exit and auto-loaded on startup
|
|
68
|
+
|
|
69
|
+
## Code Style
|
|
70
|
+
|
|
71
|
+
- We use `ruff` for linting
|
|
72
|
+
- Type hints on all public functions
|
|
73
|
+
- Docstrings on all public classes and functions
|
|
74
|
+
|
|
75
|
+
## Pull Request Process
|
|
76
|
+
|
|
77
|
+
1. Fork + branch from `main`
|
|
78
|
+
2. Make your changes
|
|
79
|
+
3. Run `PYTHONPATH= pytest` — all tests must pass
|
|
80
|
+
4. Run `ruff check src/ tests/` — no errors
|
|
81
|
+
5. Open a PR with a clear description
|
meshlite-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
GNU GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 29 June 2007
|
|
3
|
+
|
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
|
6
|
+
of this license document, but changing it is not allowed.
|
|
7
|
+
|
|
8
|
+
Preamble
|
|
9
|
+
|
|
10
|
+
The GNU General Public License is a free, copyleft license for
|
|
11
|
+
software and other kinds of works.
|
|
12
|
+
|
|
13
|
+
The licenses for most software and other practical works are designed
|
|
14
|
+
to take away your freedom to share and change the works. By contrast,
|
|
15
|
+
the GNU General Public License is intended to guarantee your freedom to
|
|
16
|
+
share and change all versions of a program--to make sure it remains free
|
|
17
|
+
software for all its users. We, the Free Software Foundation, use the
|
|
18
|
+
GNU General Public License for most of our software; it applies also to
|
|
19
|
+
any other work released this way by its authors. You can apply it to
|
|
20
|
+
your programs, too.
|
|
21
|
+
|
|
22
|
+
For the complete license text, see <https://www.gnu.org/licenses/gpl-3.0.txt>
|
|
23
|
+
|
|
24
|
+
meshforge — Copyright (C) 2026 Syed Jameel
|
|
25
|
+
This program is free software: you can redistribute it and/or modify
|
|
26
|
+
it under the terms of the GNU General Public License as published by
|
|
27
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
28
|
+
(at your option) any later version.
|
|
29
|
+
|
|
30
|
+
This program is distributed in the hope that it will be useful,
|
|
31
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
32
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
33
|
+
GNU General Public License for more details.
|
meshlite-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meshlite
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: VSCode-style 3D mesh processing desktop app, powered by MeshLib + ImGui.
|
|
5
|
+
Project-URL: Homepage, https://github.com/syedjameel/meshlite
|
|
6
|
+
Project-URL: Repository, https://github.com/syedjameel/meshlite
|
|
7
|
+
Project-URL: Issues, https://github.com/syedjameel/meshlite/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/syedjameel/meshlite/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Syed Jameel
|
|
10
|
+
License: GPL-3.0-or-later
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: 3d,imgui,mesh,mesh-processing,meshlib,moderngl,obj,ply,stl,viewer
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: X11 Applications
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
18
|
+
Classifier: Operating System :: MacOS
|
|
19
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
20
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
21
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Requires-Dist: imgui-bundle<2.0.0,>=1.6.3
|
|
29
|
+
Requires-Dist: meshlib>=3.0.0
|
|
30
|
+
Requires-Dist: moderngl<6.0.0,>=5.12.0
|
|
31
|
+
Requires-Dist: numpy<3.0.0,>=2.0.0
|
|
32
|
+
Requires-Dist: pyglm<3.0.0,>=2.8.2
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# meshlite
|
|
40
|
+
|
|
41
|
+
A desktop 3D mesh processing application with a VSCode-inspired interface, powered by [MeshLib](https://meshlib.io/), [moderngl](https://github.com/moderngl/moderngl), and [Dear ImGui](https://github.com/pthom/imgui_bundle).
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
**15 mesh operations** with full parameter control:
|
|
46
|
+
|
|
47
|
+
| Category | Operations |
|
|
48
|
+
|---|---|
|
|
49
|
+
| **File** | Open (STL / OBJ / PLY / GLB / OFF / 3MF), Save As |
|
|
50
|
+
| **Repair** | Fill Holes, Auto Repair, Remove Duplicates |
|
|
51
|
+
| **Inspect** | Mesh Info, Find Self-Intersections (Local + Voxel) |
|
|
52
|
+
| **Mesh Edit** | Decimate, Remesh, Subdivide, Laplacian Smooth |
|
|
53
|
+
| **Boolean** | Union, Intersection, Difference |
|
|
54
|
+
| **Transform** | Translate, Rotate, Scale, Mirror |
|
|
55
|
+
|
|
56
|
+
**Professional UI:**
|
|
57
|
+
|
|
58
|
+
- MeshInspector-style top toolbar with grouped icon buttons
|
|
59
|
+
- Activity bar + collapsible sidebar (Outliner, Operations, Search, Settings)
|
|
60
|
+
- Properties panel with auto-rendered parameter widgets
|
|
61
|
+
- Command palette with fuzzy search (`Ctrl+Shift+P`)
|
|
62
|
+
- Mesh Info panel with topology, geometry, and bounding box statistics
|
|
63
|
+
- Snapshot-based undo/redo (`Ctrl+Z` / `Ctrl+Shift+Z`)
|
|
64
|
+
- VSCode "Dark+" theme with codicon glyphs
|
|
65
|
+
- Drag-and-drop file loading (GLFW backend)
|
|
66
|
+
- Recent files list (File menu + command palette)
|
|
67
|
+
- Persistent user preferences (viewport, rendering, camera, history)
|
|
68
|
+
|
|
69
|
+
**Architecture:**
|
|
70
|
+
|
|
71
|
+
- Adding a new operation is a **single-file change** — zero edits to UI, toolbar, or registry
|
|
72
|
+
- 5-layer separation: `domain` / `ops` / `render` / `app_state` / `ui`
|
|
73
|
+
- Async worker dispatch — operations run on background threads
|
|
74
|
+
- 85 automated tests with architecture rule enforcement
|
|
75
|
+
- Cross-platform CI (Linux, Windows, macOS)
|
|
76
|
+
|
|
77
|
+
## Install
|
|
78
|
+
|
|
79
|
+
### From PyPI
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install meshlite
|
|
83
|
+
meshlite
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### From source
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/syedjameel/meshlite.git
|
|
90
|
+
cd meshlite
|
|
91
|
+
uv venv --python 3.13 .venv && source .venv/bin/activate
|
|
92
|
+
uv pip install -e ".[dev]"
|
|
93
|
+
python main.py
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Keyboard Shortcuts
|
|
97
|
+
|
|
98
|
+
| Shortcut | Action |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `Ctrl+Shift+P` | Command palette |
|
|
101
|
+
| `Ctrl+O` | Open mesh |
|
|
102
|
+
| `Ctrl+S` | Save mesh as |
|
|
103
|
+
| `Ctrl+Z` | Undo |
|
|
104
|
+
| `Ctrl+Shift+Z` | Redo |
|
|
105
|
+
| `F` | Frame all (fit viewport) |
|
|
106
|
+
| Drag file onto window | Load mesh |
|
|
107
|
+
|
|
108
|
+
## User Preferences
|
|
109
|
+
|
|
110
|
+
All settings are persisted automatically between sessions:
|
|
111
|
+
|
|
112
|
+
- **Viewport:** Rotate / zoom / pan sensitivity
|
|
113
|
+
- **Rendering:** Background color, mesh color, selected color, lighting (ambient, specular)
|
|
114
|
+
- **Camera:** Field of view
|
|
115
|
+
- **History:** Undo depth, memory cap
|
|
116
|
+
|
|
117
|
+
Access via the **Settings** panel in the sidebar (gear icon in the activity bar).
|
|
118
|
+
|
|
119
|
+
## Adding a New Operation
|
|
120
|
+
|
|
121
|
+
meshlite's architecture makes it trivial to add operations. Create one file:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
# src/meshlite/ops/smooth/laplacian.py
|
|
125
|
+
|
|
126
|
+
@register_operation
|
|
127
|
+
class LaplacianSmoothOperation(Operation):
|
|
128
|
+
id = "smooth.laplacian"
|
|
129
|
+
label = "Laplacian Smooth"
|
|
130
|
+
category = "Mesh Edit"
|
|
131
|
+
schema = ParamSchema((
|
|
132
|
+
Param("iterations", "int", "Iterations", default=3, min=1, max=100),
|
|
133
|
+
Param("force", "float", "Force", default=0.5, min=0.01, max=1.0),
|
|
134
|
+
))
|
|
135
|
+
|
|
136
|
+
def run(self, mesh, params, ctx):
|
|
137
|
+
# ... meshlib calls ...
|
|
138
|
+
return OperationResult(mesh=mesh, message="Smoothed")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The operation automatically appears in the command palette, sidebar, properties panel, and toolbar — with zero edits to any other file.
|
|
142
|
+
|
|
143
|
+
## Architecture
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
ui ───> app_state ──> ops ──> domain
|
|
147
|
+
| | |
|
|
148
|
+
└─> render ─┘ └──> domain
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Layer | Responsibility | Dependencies |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `domain/` | MeshLib wrapper (`mrm_shim.py`), mesh data, I/O | meshlib, numpy |
|
|
154
|
+
| `ops/` | Operations framework, registry, auto-discovery | domain |
|
|
155
|
+
| `render/` | moderngl renderer, GPU mesh, camera, shaders | domain (read-only) |
|
|
156
|
+
| `app_state/` | Document, CommandBus, undo, events, preferences | domain, ops |
|
|
157
|
+
| `ui/` | ImGui panels, theme, toolbar, command palette | all layers (except domain directly) |
|
|
158
|
+
|
|
159
|
+
## Tech Stack
|
|
160
|
+
|
|
161
|
+
- **Python 3.11+**
|
|
162
|
+
- [MeshLib](https://meshlib.io/) — mesh processing engine (GPL-3)
|
|
163
|
+
- [imgui_bundle](https://github.com/pthom/imgui_bundle) — Dear ImGui + hello_imgui
|
|
164
|
+
- [moderngl](https://github.com/moderngl/moderngl) — OpenGL rendering
|
|
165
|
+
- [numpy](https://numpy.org/) + [PyGLM](https://github.com/Zuzu-Typ/PyGLM)
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, code style, and how to add operations.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
[GPL-3.0-or-later](./LICENSE) — matches MeshLib's license.
|
meshlite-0.2.0/README.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# meshlite
|
|
2
|
+
|
|
3
|
+
A desktop 3D mesh processing application with a VSCode-inspired interface, powered by [MeshLib](https://meshlib.io/), [moderngl](https://github.com/moderngl/moderngl), and [Dear ImGui](https://github.com/pthom/imgui_bundle).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
**15 mesh operations** with full parameter control:
|
|
8
|
+
|
|
9
|
+
| Category | Operations |
|
|
10
|
+
|---|---|
|
|
11
|
+
| **File** | Open (STL / OBJ / PLY / GLB / OFF / 3MF), Save As |
|
|
12
|
+
| **Repair** | Fill Holes, Auto Repair, Remove Duplicates |
|
|
13
|
+
| **Inspect** | Mesh Info, Find Self-Intersections (Local + Voxel) |
|
|
14
|
+
| **Mesh Edit** | Decimate, Remesh, Subdivide, Laplacian Smooth |
|
|
15
|
+
| **Boolean** | Union, Intersection, Difference |
|
|
16
|
+
| **Transform** | Translate, Rotate, Scale, Mirror |
|
|
17
|
+
|
|
18
|
+
**Professional UI:**
|
|
19
|
+
|
|
20
|
+
- MeshInspector-style top toolbar with grouped icon buttons
|
|
21
|
+
- Activity bar + collapsible sidebar (Outliner, Operations, Search, Settings)
|
|
22
|
+
- Properties panel with auto-rendered parameter widgets
|
|
23
|
+
- Command palette with fuzzy search (`Ctrl+Shift+P`)
|
|
24
|
+
- Mesh Info panel with topology, geometry, and bounding box statistics
|
|
25
|
+
- Snapshot-based undo/redo (`Ctrl+Z` / `Ctrl+Shift+Z`)
|
|
26
|
+
- VSCode "Dark+" theme with codicon glyphs
|
|
27
|
+
- Drag-and-drop file loading (GLFW backend)
|
|
28
|
+
- Recent files list (File menu + command palette)
|
|
29
|
+
- Persistent user preferences (viewport, rendering, camera, history)
|
|
30
|
+
|
|
31
|
+
**Architecture:**
|
|
32
|
+
|
|
33
|
+
- Adding a new operation is a **single-file change** — zero edits to UI, toolbar, or registry
|
|
34
|
+
- 5-layer separation: `domain` / `ops` / `render` / `app_state` / `ui`
|
|
35
|
+
- Async worker dispatch — operations run on background threads
|
|
36
|
+
- 85 automated tests with architecture rule enforcement
|
|
37
|
+
- Cross-platform CI (Linux, Windows, macOS)
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
### From PyPI
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install meshlite
|
|
45
|
+
meshlite
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### From source
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/syedjameel/meshlite.git
|
|
52
|
+
cd meshlite
|
|
53
|
+
uv venv --python 3.13 .venv && source .venv/bin/activate
|
|
54
|
+
uv pip install -e ".[dev]"
|
|
55
|
+
python main.py
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Keyboard Shortcuts
|
|
59
|
+
|
|
60
|
+
| Shortcut | Action |
|
|
61
|
+
|---|---|
|
|
62
|
+
| `Ctrl+Shift+P` | Command palette |
|
|
63
|
+
| `Ctrl+O` | Open mesh |
|
|
64
|
+
| `Ctrl+S` | Save mesh as |
|
|
65
|
+
| `Ctrl+Z` | Undo |
|
|
66
|
+
| `Ctrl+Shift+Z` | Redo |
|
|
67
|
+
| `F` | Frame all (fit viewport) |
|
|
68
|
+
| Drag file onto window | Load mesh |
|
|
69
|
+
|
|
70
|
+
## User Preferences
|
|
71
|
+
|
|
72
|
+
All settings are persisted automatically between sessions:
|
|
73
|
+
|
|
74
|
+
- **Viewport:** Rotate / zoom / pan sensitivity
|
|
75
|
+
- **Rendering:** Background color, mesh color, selected color, lighting (ambient, specular)
|
|
76
|
+
- **Camera:** Field of view
|
|
77
|
+
- **History:** Undo depth, memory cap
|
|
78
|
+
|
|
79
|
+
Access via the **Settings** panel in the sidebar (gear icon in the activity bar).
|
|
80
|
+
|
|
81
|
+
## Adding a New Operation
|
|
82
|
+
|
|
83
|
+
meshlite's architecture makes it trivial to add operations. Create one file:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# src/meshlite/ops/smooth/laplacian.py
|
|
87
|
+
|
|
88
|
+
@register_operation
|
|
89
|
+
class LaplacianSmoothOperation(Operation):
|
|
90
|
+
id = "smooth.laplacian"
|
|
91
|
+
label = "Laplacian Smooth"
|
|
92
|
+
category = "Mesh Edit"
|
|
93
|
+
schema = ParamSchema((
|
|
94
|
+
Param("iterations", "int", "Iterations", default=3, min=1, max=100),
|
|
95
|
+
Param("force", "float", "Force", default=0.5, min=0.01, max=1.0),
|
|
96
|
+
))
|
|
97
|
+
|
|
98
|
+
def run(self, mesh, params, ctx):
|
|
99
|
+
# ... meshlib calls ...
|
|
100
|
+
return OperationResult(mesh=mesh, message="Smoothed")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The operation automatically appears in the command palette, sidebar, properties panel, and toolbar — with zero edits to any other file.
|
|
104
|
+
|
|
105
|
+
## Architecture
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
ui ───> app_state ──> ops ──> domain
|
|
109
|
+
| | |
|
|
110
|
+
└─> render ─┘ └──> domain
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Layer | Responsibility | Dependencies |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `domain/` | MeshLib wrapper (`mrm_shim.py`), mesh data, I/O | meshlib, numpy |
|
|
116
|
+
| `ops/` | Operations framework, registry, auto-discovery | domain |
|
|
117
|
+
| `render/` | moderngl renderer, GPU mesh, camera, shaders | domain (read-only) |
|
|
118
|
+
| `app_state/` | Document, CommandBus, undo, events, preferences | domain, ops |
|
|
119
|
+
| `ui/` | ImGui panels, theme, toolbar, command palette | all layers (except domain directly) |
|
|
120
|
+
|
|
121
|
+
## Tech Stack
|
|
122
|
+
|
|
123
|
+
- **Python 3.11+**
|
|
124
|
+
- [MeshLib](https://meshlib.io/) — mesh processing engine (GPL-3)
|
|
125
|
+
- [imgui_bundle](https://github.com/pthom/imgui_bundle) — Dear ImGui + hello_imgui
|
|
126
|
+
- [moderngl](https://github.com/moderngl/moderngl) — OpenGL rendering
|
|
127
|
+
- [numpy](https://numpy.org/) + [PyGLM](https://github.com/Zuzu-Typ/PyGLM)
|
|
128
|
+
|
|
129
|
+
## Contributing
|
|
130
|
+
|
|
131
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, code style, and how to add operations.
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
[GPL-3.0-or-later](./LICENSE) — matches MeshLib's license.
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#version 330
|
|
2
|
+
|
|
3
|
+
// Lighting model aligned with MeshInspector/MeshLib's approach:
|
|
4
|
+
// simple Phong (ambient + diffuse + specular) with configurable strengths.
|
|
5
|
+
// No fill light, no rim light — clean and predictable.
|
|
6
|
+
|
|
7
|
+
in vec3 v_normal;
|
|
8
|
+
in vec3 v_position;
|
|
9
|
+
|
|
10
|
+
out vec4 f_color;
|
|
11
|
+
|
|
12
|
+
uniform vec3 light_pos;
|
|
13
|
+
uniform vec3 view_pos;
|
|
14
|
+
uniform vec3 object_color;
|
|
15
|
+
|
|
16
|
+
// Configurable lighting params - sent from the renderer each frame.
|
|
17
|
+
// Defaults match MeshInspector's typical look.
|
|
18
|
+
uniform float ambient_strength; // default 0.2
|
|
19
|
+
uniform float specular_strength; // default 0.4
|
|
20
|
+
uniform float specular_exponent; // default 35.0
|
|
21
|
+
|
|
22
|
+
void main() {
|
|
23
|
+
vec3 N = normalize(v_normal);
|
|
24
|
+
vec3 L = normalize(light_pos - v_position);
|
|
25
|
+
vec3 V = normalize(view_pos - v_position);
|
|
26
|
+
|
|
27
|
+
// Diffuse (Lambertian)
|
|
28
|
+
float NdotL = max(dot(N, L), 0.0);
|
|
29
|
+
|
|
30
|
+
// Specular (Phong reflection)
|
|
31
|
+
vec3 R = reflect(-L, N);
|
|
32
|
+
float spec = pow(max(dot(R, V), 0.0), specular_exponent);
|
|
33
|
+
|
|
34
|
+
// Combine
|
|
35
|
+
vec3 ambient = ambient_strength * vec3(1.0);
|
|
36
|
+
vec3 diffuse = NdotL * vec3(1.0);
|
|
37
|
+
vec3 specular = specular_strength * spec * vec3(1.0);
|
|
38
|
+
|
|
39
|
+
vec3 result = (ambient + diffuse + specular) * object_color;
|
|
40
|
+
result = clamp(result, 0.0, 1.0);
|
|
41
|
+
|
|
42
|
+
f_color = vec4(result, 1.0);
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#version 330
|
|
2
|
+
|
|
3
|
+
in vec3 in_position;
|
|
4
|
+
in vec3 in_normal;
|
|
5
|
+
|
|
6
|
+
out vec3 v_normal;
|
|
7
|
+
out vec3 v_position;
|
|
8
|
+
|
|
9
|
+
uniform mat4 model;
|
|
10
|
+
uniform mat4 view;
|
|
11
|
+
uniform mat4 projection;
|
|
12
|
+
uniform mat3 normal_matrix;
|
|
13
|
+
|
|
14
|
+
void main() {
|
|
15
|
+
v_normal = normal_matrix * in_normal;
|
|
16
|
+
v_position = vec3(model * vec4(in_position, 1.0));
|
|
17
|
+
gl_Position = projection * view * model * vec4(in_position, 1.0);
|
|
18
|
+
}
|
meshlite-0.2.0/main.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""meshlite entry point.
|
|
2
|
+
|
|
3
|
+
Run with:
|
|
4
|
+
|
|
5
|
+
python main.py
|
|
6
|
+
|
|
7
|
+
or, after ``pip install -e .``:
|
|
8
|
+
|
|
9
|
+
meshlite
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# Allow ``python main.py`` from the project root without installing the package.
|
|
18
|
+
# When installed via ``pip install -e .`` the package is on sys.path already and
|
|
19
|
+
# this insert is harmless.
|
|
20
|
+
_SRC = Path(__file__).resolve().parent / "src"
|
|
21
|
+
if _SRC.is_dir() and str(_SRC) not in sys.path:
|
|
22
|
+
sys.path.insert(0, str(_SRC))
|
|
23
|
+
|
|
24
|
+
from meshlite.app import main # noqa: E402
|
|
25
|
+
|
|
26
|
+
if __name__ == "__main__":
|
|
27
|
+
main()
|