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: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ uv sync --extra test
Run the demo using the platform's default graphics backend:

```bash
uv run python -m layout_viewer
uv run python -m layout_viewer_core
```

On macOS, `--backend metal` is also available; on Windows, use `--backend d3d11`.
Expand Down Expand Up @@ -91,7 +91,7 @@ The tests in `tests/` currently smoke-test Qt theme resources and compiled shade
packs. Add focused tests for geometry, picking, and scene invariants when changing
those areas.

Shader source files live in `src/layout_viewer/qt/render/shaders/`. After editing
Shader source files live in `src/layout_viewer_core/qt/render/shaders/`. After editing
them, regenerate the local packs:

```bash
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# layout-viewer
# layout-viewer-core

Interactive 2D PCB layout viewer built on PySide6 and Qt RHI (Direct3D 11, Vulkan, Metal).

The package renders detached `LayoutScene` values and is independent of source applications.

```python
from layout_viewer import show_scene
from layout_viewer.demo import demo_scene
from layout_viewer_core import show_scene
from layout_viewer_core.demo import demo_scene

show_scene(demo_scene())
```
Expand All @@ -16,7 +16,7 @@ show_scene(demo_scene())
Build a scene from layers and geometry, then pass it to `show_scene`:

```python
from layout_viewer import LayoutScene, SceneLayer, ScenePolygon, SceneTrace, show_scene
from layout_viewer_core import LayoutScene, SceneLayer, ScenePolygon, SceneTrace, show_scene

scene = LayoutScene(
layers=(SceneLayer("F.Cu", (0.95, 0.61, 0.20, 1.0)),),
Expand All @@ -38,12 +38,12 @@ show_scene(scene)
Run the synthetic demo board:

```bash
python -m layout_viewer
python -m layout_viewer_core
```

## Architecture

- Core scene and geometry logic lives in `src/layout_viewer/` (`scene.py`, `mesh.py`,
- Core scene and geometry logic lives in `src/layout_viewer_core/` (`scene.py`, `mesh.py`,
`picking.py`, and `camera.py`) and does not depend on Qt.
- `qt/app.py` bootstraps the application. Qt UI code is grouped in `qt/ui/`;
canvas and GPU rendering code is grouped in `qt/render/`.
Expand All @@ -62,7 +62,7 @@ Install the checkout and its runtime dependencies into the active environment:
python -m pip install -e .
```

Wheel and editable builds compile the RHI shaders in `src/layout_viewer/qt/render/shaders`
Wheel and editable builds compile the RHI shaders in `src/layout_viewer_core/qt/render/shaders`
with the Qt `qsb` tool shipped by PySide6 (or the executable named by `LAYOUT_VIEWER_QSB`).
After editing shader sources in a checkout, refresh the local packs with:

Expand Down
6 changes: 3 additions & 3 deletions _build_backend/layout_viewer_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
root = Path.cwd().resolve()
destination = str(Path(wheel_directory).resolve())
metadata = str(Path(metadata_directory).resolve()) if metadata_directory is not None else None
with tempfile.TemporaryDirectory(prefix="layout-viewer-wheel-") as temporary:
with tempfile.TemporaryDirectory(prefix="layout-viewer-core-wheel-") as temporary:
staging = Path(temporary)
shutil.copytree(
root / "src/layout_viewer",
staging / "src/layout_viewer",
root / "src/layout_viewer_core",
staging / "src/layout_viewer_core",
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.qsb"),
)
for name in ("pyproject.toml", "README.md", "LICENSE"):
Expand Down
4 changes: 2 additions & 2 deletions _build_backend/layout_viewer_shader_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import subprocess
import tempfile

SHADER_DIRECTORY = Path("src/layout_viewer/qt/render/shaders")
SHADER_DIRECTORY = Path("src/layout_viewer_core/qt/render/shaders")
QSB_REQUIREMENT = "PySide6>=6.8.3,<7"


Expand Down Expand Up @@ -43,7 +43,7 @@ def bake_shaders(directory):
if not sources:
raise RuntimeError(f"No layout shader sources found in {directory}")
qsb, env = _compiler()
with tempfile.TemporaryDirectory(prefix="layout-viewer-shaders-") as temporary:
with tempfile.TemporaryDirectory(prefix="layout-viewer-core-shaders-") as temporary:
temporary = Path(temporary)
for source in sources:
subprocess.run(
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ include = ["_build_backend", "README.md", "LICENSE"]
exclude = ["_build_backend/__pycache__"]

[tool.flit.module]
name = "layout_viewer"
name = "layout_viewer_core"

[project]
name = "layout_viewer"
name = "layout_viewer_core"
dynamic = ["version", "description"]
license = "Apache-2.0"
requires-python = ">=3.10"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

from typing import TYPE_CHECKING, Any

from layout_viewer.picking import PickedObject
from layout_viewer.scene import (
from layout_viewer_core.picking import PickedObject
from layout_viewer_core.scene import (
LayoutScene,
SceneComponent,
SceneJoin,
Expand All @@ -18,7 +18,7 @@
)

if TYPE_CHECKING:
from layout_viewer.qt.ui.window import LayoutWindow
from layout_viewer_core.qt.ui.window import LayoutWindow

__version__ = "0.1.0"

Expand All @@ -37,9 +37,9 @@


def show_scene(scene: LayoutScene | None = None, **options: Any) -> LayoutWindow:
"""Open a layout window; see :func:`layout_viewer.qt.app.show_scene` for options."""
"""Open a layout window; see :func:`layout_viewer_core.qt.app.show_scene` for options."""
try:
from layout_viewer.qt.app import show_scene as show
from layout_viewer_core.qt.app import show_scene as show
except ImportError as exc:
raise ImportError("The layout viewer requires PySide6 and mapbox-earcut: pip install layout-viewer.") from exc
raise ImportError("The layout viewer requires PySide6 and mapbox-earcut: pip install layout-viewer-core.") from exc
return show(scene, **options)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

"""Launch the synthetic demo board with ``python -m layout_viewer``."""
"""Launch the synthetic demo board with ``python -m layout_viewer_core``."""

from __future__ import annotations

Expand All @@ -17,8 +17,8 @@ def main(argv: Sequence[str] | None = None) -> None:
)
parser.add_argument("--color-by-layer", action="store_false", dest="color_by_net", help="Use source layer colors")
args = parser.parse_args(argv)
from layout_viewer import show_scene
from layout_viewer.demo import demo_scene
from layout_viewer_core import show_scene
from layout_viewer_core.demo import demo_scene

show_scene(demo_scene(), color_by_net=args.color_by_net, backend=args.backend)

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class CaptureResult:

def merge_scene(previous, patch, changed):
"""Replace only changed source records, preserving untouched value identities."""
from layout_viewer.mesh import scene_network_names
from layout_viewer_core.mesh import scene_network_names

updates = {}
for name in ("polygons", "traces", "joins", "pads", "object_info"):
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import math

from layout_viewer.scene import LayoutScene, SceneComponent, SceneLayer, ScenePad, ScenePolygon, SceneTrace
from layout_viewer_core.scene import LayoutScene, SceneComponent, SceneLayer, ScenePad, ScenePolygon, SceneTrace


def _rectangle(x0: float, y0: float, x1: float, y1: float) -> tuple[tuple[float, float], ...]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def prepare_incremental(scene, previous, parent, patch, changed):
Removed/replaced ranges become unreachable through drawing and picking.
Periodic compaction bounds tombstones and retained immutable chunks.
"""
from layout_viewer.mesh import prepare_scene, scene_network_names
from layout_viewer.picking import OverlayPickIndex
from layout_viewer_core.mesh import prepare_scene, scene_network_names
from layout_viewer_core.picking import OverlayPickIndex

if not changed:
return parent
Expand Down
8 changes: 4 additions & 4 deletions src/layout_viewer/mesh.py → src/layout_viewer_core/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import numpy as np

from layout_viewer.scene import (
from layout_viewer_core.scene import (
Bounds,
LayoutScene,
SceneComponent,
Expand Down Expand Up @@ -270,8 +270,8 @@ def _prepare_sdf(


def _pickable(prepared: PreparedScene, scene: LayoutScene, sources: np.ndarray) -> PreparedScene:
from layout_viewer.incremental import outline_data
from layout_viewer.picking import PickIndex
from layout_viewer_core.incremental import outline_data
from layout_viewer_core.picking import PickIndex

sources.setflags(write=False)
ranges = {}
Expand Down Expand Up @@ -325,7 +325,7 @@ def _prepare_polygon_buffers(
try:
import mapbox_earcut
except ImportError as exc:
raise ImportError("Triangulation requires: pip install layout-viewer.") from exc
raise ImportError("Triangulation requires: pip install layout-viewer-core.") from exc
vertices, indices, batches, vertex_nets = [], [], [], []
net_ids = {net: index for index, net in enumerate(scene_network_names(scene))}
polygon_groups = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import numpy as np

from layout_viewer.scene import (
from layout_viewer_core.scene import (
LayoutScene,
Point,
SceneJoin,
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from PySide6.QtCore import QCoreApplication, QEventLoop, Qt, QThread
from PySide6.QtWidgets import QApplication

from layout_viewer.qt.ui.window import LayoutWindow
from layout_viewer.scene import LayoutScene
from layout_viewer_core.qt.ui.window import LayoutWindow
from layout_viewer_core.scene import LayoutScene

# Check triangulation support before creating QApplication.
import_module("mapbox_earcut")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
from PySide6.QtCore import QRectF, Qt
from PySide6.QtGui import QColor, QFont, QImage, QPainter, QPainterPath, QPen, QRhi, QRhiBuffer, QRhiTexture

from layout_viewer.colors import COMPONENT_OUTLINE_COLOR
from layout_viewer.mesh import split_coordinates
from layout_viewer_core.colors import COMPONENT_OUTLINE_COLOR
from layout_viewer_core.mesh import split_coordinates

if TYPE_CHECKING:
from layout_viewer.camera import Camera
from layout_viewer.mesh import ComponentBatch
from layout_viewer.scene import SceneComponent
from layout_viewer_core.camera import Camera
from layout_viewer_core.mesh import ComponentBatch
from layout_viewer_core.scene import SceneComponent


class ComponentLabels:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy as np
from PySide6.QtGui import QImage, QRhiBuffer, QRhiTexture

from layout_viewer.colors import NET_COLORS
from layout_viewer_core.colors import NET_COLORS


class SdfCoverage:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@
import numpy as np
from PySide6.QtGui import QRhiBuffer, QRhiColorAttachment, QRhiTextureRenderTargetDescription

from layout_viewer.colors import (
from layout_viewer_core.colors import (
COMPONENT_COLORS,
DRILL_COLOR,
HIGHLIGHT_KEYLINE_COLOR,
HOVER_COLOR,
SELECTION_COLOR,
net_color,
)
from layout_viewer.qt.render.component_labels import ComponentLabels
from layout_viewer.qt.render.coverage import SdfCoverage
from layout_viewer.qt.render.grid import GridRenderer
from layout_viewer.qt.render.rhi import Device, Target, camera_uniforms
from layout_viewer.scene import ScenePolygon
from layout_viewer_core.qt.render.component_labels import ComponentLabels
from layout_viewer_core.qt.render.coverage import SdfCoverage
from layout_viewer_core.qt.render.grid import GridRenderer
from layout_viewer_core.qt.render.rhi import Device, Target, camera_uniforms
from layout_viewer_core.scene import ScenePolygon


class Renderer:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from PySide6.QtWidgets import QRhiWidget
from PySide6.support import VoidPtr

from layout_viewer.mesh import split_coordinates
from layout_viewer_core.mesh import split_coordinates

UNIFORM_SIZE = 256
SHADER_DIRECTORY = Path(__file__).resolve().parent / "shaders"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from PySide6.QtCore import QRectF, Qt
from PySide6.QtGui import QColor, QIcon, QPainter, QPainterPath, QPen, QPixmap

from layout_viewer.qt.ui.theme import COLORS
from layout_viewer_core.qt.ui.theme import COLORS


def layer_icon_path(layer_count: int = 2) -> QPainterPath:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
QWidget,
)

from layout_viewer.picking import inspector_rows
from layout_viewer.qt.ui.icons import workspace_icon
from layout_viewer.qt.ui.theme import COLORS
from layout_viewer_core.picking import inspector_rows
from layout_viewer_core.qt.ui.icons import workspace_icon
from layout_viewer_core.qt.ui.theme import COLORS

if TYPE_CHECKING:
from layout_viewer.picking import PickHit
from layout_viewer_core.picking import PickHit


class ObjectInspector(QFrame):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from PySide6.QtCore import QElapsedTimer, Qt, QTimer, Slot
from PySide6.QtWidgets import QFrame, QHBoxLayout, QLabel, QProgressBar, QVBoxLayout, QWidget

from layout_viewer.qt.ui.theme import COLORS
from layout_viewer_core.qt.ui.theme import COLORS


class LoadingPanel(QFrame):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@
)
from PySide6.QtWidgets import QFrame, QGraphicsDropShadowEffect, QHBoxLayout, QToolButton

from layout_viewer.qt.ui.icons import layer_icon_path
from layout_viewer.qt.ui.theme import COLORS
from layout_viewer_core.qt.ui.icons import layer_icon_path
from layout_viewer_core.qt.ui.theme import COLORS

if TYPE_CHECKING:
from layout_viewer.qt.view.canvas import LayoutView
from layout_viewer_core.qt.view.canvas import LayoutView


def navigation_icon(name: str) -> QIcon:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
QWidget,
)

from layout_viewer.colors import net_color
from layout_viewer.mesh import scene_network_names
from layout_viewer.qt.ui.icons import workspace_icon
from layout_viewer.qt.ui.theme import COLORS
from layout_viewer_core.colors import net_color
from layout_viewer_core.mesh import scene_network_names
from layout_viewer_core.qt.ui.icons import workspace_icon
from layout_viewer_core.qt.ui.theme import COLORS

if TYPE_CHECKING:
from layout_viewer.scene import LayoutScene, SceneLayer
from layout_viewer_core.scene import LayoutScene, SceneLayer


class ElidedLabel(QLabel):
Expand Down
File renamed without changes.
Loading
Loading