pipeworks_mud_mapper.components.map_view

Plotly-based map visualization component for the MUD Mapper.

This module provides the core visualization functions for rendering MUD zone maps using Plotly. It creates interactive 2D map views that display rooms as nodes and exits as connecting lines, with support for:

  • Flattened multi-level display: All Z-levels shown on a single 2D plane

  • Visual Z-level differentiation: Size and color distinguish floor levels

  • Room selection highlighting: Selected room shown in red

  • Exit visualization: Lines for cardinal directions, labels for up/down, and triangle markers for cross-zone exits

  • Interactive pan, zoom, and hover tooltips

Design Principles

  1. Flattened View: All rooms rendered on one plane regardless of Z

  2. Z-Ordered Rendering: Rooms drawn back-to-front so ground level is clickable

  3. Separation of Concerns: Base figure creation separate from room rendering

  4. Graceful Degradation: Handles missing data, empty rooms, invalid exits

  5. Deterministic Output: Same inputs produce identical figures

Coordinate System

The map follows standard cartographic conventions:

  • X-axis: East (+) / West (-), displayed left-to-right

  • Y-axis: North (+) / South (-), displayed bottom-to-top

  • Z-axis: Represented by visual styling (size/color), not position

The coordinate range is -20 to +20 on both axes, with gridlines every 5 units.

Visual Design

Rooms are styled by Z-level for easy identification:

  • z=-1 (Down): Black filled, 14px (smallest) - cellars, basements

  • z=+1 (Up): White with black border, 18px (medium) - towers, attics

  • z=0 (Ground): Blue filled, 20px (largest) - main floor

  • Selected: Red, regardless of Z-level

Exit connections:

  • Cardinal exits (N/E/S/W): Gray lines between rooms on same Z-level

  • Vertical exits (U/D): Dashed lines between stacked rooms plus U/D labels

  • Zone exits: Triangle markers pointing in the exit direction

  • Crosshair: Dashed gray lines at origin for orientation

Constants

Z_LEVEL_STYLESdict[int, dict]

Visual styling for each Z-level (size, color, border)

Z_RENDER_ORDERlist[int]

Order in which Z-levels are rendered (back to front)

SELECTED_ROOM_COLORstr

Color for highlighted/selected rooms

Functions

create_map_figure(title) -> go.Figure

Create empty map canvas with grid and crosshair

create_map_figure_with_rooms(rooms, visible_z_levels, selected_room) -> go.Figure

Create map with rooms and exits rendered across multiple Z-levels

Internal Functions

_group_rooms_by_z_level(rooms, visible_z_levels) -> dict

Group rooms into buckets by their Z coordinate

_find_stacked_positions(rooms, visible_z_levels) -> dict

Find X,Y positions with rooms at multiple Z-levels

_draw_all_exit_lines(fig, rooms, visible_z_levels) -> None

Draw connecting lines for cardinal exits

_draw_vertical_exit_lines(fig, rooms, visible_z_levels) -> None

Draw dashed lines for up/down exits

_draw_zone_exit_markers(fig, rooms, visible_z_levels) -> None

Draw directional markers for cross-zone exits

_draw_rooms_at_z_level(fig, rooms_at_level, z_level, selected_room) -> None

Render rooms for a single Z-level as a Scatter trace

_add_vertical_exit_labels(fig, stacked_positions, rooms) -> None

Add “U/D” labels near stacked rooms with vertical exits

Usage

Create an empty map canvas:

>>> from pipeworks_mud_mapper.components.map_view import create_map_figure
>>> fig = create_map_figure()
>>> fig.show()  # Opens in browser

Render rooms from all Z-levels:

>>> from pipeworks_mud_mapper.components.map_view import (
...     create_map_figure_with_rooms
... )
>>> rooms = {
...     "ground": {"name": "Ground", "coords": [0, 0, 0], "exits": {"down": "cellar"}},
...     "cellar": {"name": "Cellar", "coords": [0, 0, -1], "exits": {"up": "ground"}},
... }
>>> fig = create_map_figure_with_rooms(rooms, selected_room="ground")
>>> fig.show()

Filter to specific Z-levels:

>>> fig = create_map_figure_with_rooms(rooms, visible_z_levels=[0])  # Ground only

Integration with Dash:

>>> import dash
>>> from dash import dcc
>>> fig = create_map_figure_with_rooms(rooms)
>>> graph = dcc.Graph(figure=fig, id="map-graph")

Architecture

The module uses a layered rendering approach:

  1. Base Figure (create_map_figure) - Creates the canvas with grid, crosshair, and axis labels - Fixed dimensions (700x650) to prevent layout thrashing

  2. Exit Lines (_draw_all_exit_lines) - Drawn first so rooms appear on top - Only cardinal directions on same Z-level

  3. Vertical Exit Lines (_draw_vertical_exit_lines) - Dashed connectors between stacked rooms with U/D exits

  4. Zone Exit Markers (_draw_zone_exit_markers) - Directional triangles for cross-zone exits

  5. Room Nodes (_draw_rooms_at_z_level) - Rendered in Z-order: z=-1 first, z=+1 second, z=0 last - Ground level on top ensures it receives clicks

  6. Vertical Labels (_add_vertical_exit_labels) - Added last as annotations - Show U/D for stacked rooms with vertical exits

Performance Considerations

  • Rooms batched into single Scatter trace per Z-level

  • Exit lines deduplicated (bidirectional exits draw once)

  • Zone exit markers render as a single Scatter trace

  • For zones with 100+ rooms, consider filter controls

  • Figure creation is synchronous and typically < 50ms

Attributes

Z_LEVEL_STYLES

Visual styling configuration for each Z-level.

Z_RENDER_ORDER

Order in which Z-levels are rendered (back to front).

SELECTED_ROOM_COLOR

Color used for the currently selected room.

Z_LEVEL_VISUAL_OFFSET

Visual X,Y offset applied to rooms based on Z-level.

ZONE_EXIT_MARKER_COLOR

Color used for cross-zone exit markers.

ZONE_EXIT_MARKER_OFFSETS

Offsets for positioning zone exit markers relative to the room node.

ZONE_EXIT_MARKER_SYMBOLS

Plotly marker symbols for zone exits by direction.

Functions

create_map_figure([title])

Create the base map figure with crosshair and grid.

create_map_figure_with_rooms([rooms, ...])

Create map figure with all rooms flattened onto a single 2D plane.

Module Contents

pipeworks_mud_mapper.components.map_view.Z_LEVEL_STYLES: dict[int, dict]

Visual styling configuration for each Z-level.

Each Z-level maps to a dictionary containing:

  • size: Marker diameter in pixels

  • color: Fill color (rgba string)

  • border_color: Marker outline color

  • border_width: Marker outline width in pixels

  • label: Human-readable label for legends/tooltips

The size hierarchy (14 < 18 < 20) ensures that when rooms overlap at the same X,Y position, the ground-level room (largest) is most visible and receives clicks first due to render order.

pipeworks_mud_mapper.components.map_view.Z_RENDER_ORDER: list[int]

Order in which Z-levels are rendered (back to front).

Rooms are drawn in this order, meaning:

  1. z=-1 (Down) is drawn first, appearing at the back

  2. z=+1 (Up) is drawn second

  3. z=0 (Ground) is drawn last, appearing on top

This ensures that when rooms overlap at the same X,Y position (stacked vertically), the ground-level room is on top and receives mouse clicks. Users can use the Z-level filter to hide ground level when they need to select a room below or above.

pipeworks_mud_mapper.components.map_view.SELECTED_ROOM_COLOR: str = 'rgba(255, 100, 100, 1)'

Color used for the currently selected room.

This red color overrides the Z-level-based color when a room is selected, providing clear visual feedback regardless of which floor the selected room is on. The color is intentionally bright and distinct from all Z-level colors.

pipeworks_mud_mapper.components.map_view.Z_LEVEL_VISUAL_OFFSET: dict[int, tuple[float, float]]

Visual X,Y offset applied to rooms based on Z-level.

When rooms are stacked at the same logical X,Y position but different Z-levels, this offset separates them visually so they don’t overlap. The diagonal offset creates an intuitive “stacking” effect:

  • z=-1 (Down): Shifted left and down, appearing “behind”

  • z=0 (Ground): No offset, the reference position

  • z=+1 (Up): Shifted right and up, appearing “in front”

The offset value (0.4) is small enough that stacked rooms are clearly at the “same position” but large enough to be visually distinct and separately clickable.

Note: This is purely visual. The logical coordinates used for U/D connections remain unchanged.

pipeworks_mud_mapper.components.map_view.ZONE_EXIT_MARKER_COLOR: str = 'rgba(255, 165, 0, 0.85)'

Color used for cross-zone exit markers.

Zone exits are visually distinct from local exits so authors can quickly spot which directions lead out of the current zone. The orange hue reads as a “handoff” indicator without overpowering room nodes.

pipeworks_mud_mapper.components.map_view.ZONE_EXIT_MARKER_OFFSETS: dict[str, tuple[float, float]]

Offsets for positioning zone exit markers relative to the room node.

Each offset is expressed in map units and is applied after the Z-level visual offset. This keeps markers near their originating room but prevents overlap with the room node itself.

pipeworks_mud_mapper.components.map_view.ZONE_EXIT_MARKER_SYMBOLS: dict[str, str]

Plotly marker symbols for zone exits by direction.

pipeworks_mud_mapper.components.map_view.create_map_figure(title=None)[source]

Create the base map figure with crosshair and grid.

Creates an empty Plotly figure configured as a 2D map canvas with:

  • Cartesian grid with 5-unit spacing

  • Crosshair at origin for orientation

  • Compass-labeled axes (N/S/E/W at extremes)

  • Fixed dimensions to prevent resize loops in Dash

This function creates only the canvas - use create_map_figure_with_rooms to add room visualizations.

Parameters:

title (str | None, optional) – Title text to display above the map (default: None). If None, no title is shown. This is the default for flattened views where a Z-level title would be misleading.

Returns:

Plotly Figure configured as an interactive map canvas. The figure has:

  • Range: -20 to +20 on both axes

  • Grid: 5-unit spacing with light gray lines

  • Crosshair: Dashed gray lines at x=0 and y=0

  • Dimensions: 700x650 pixels (fixed)

Return type:

go.Figure

Examples

Create a map canvas with no title (default):

>>> fig = create_map_figure()
>>> fig.layout.title is None or fig.layout.title.text is None
True

Create a map canvas with a custom title:

>>> fig = create_map_figure(title="Crooked Pipe District")
>>> fig.layout.title.text
'Crooked Pipe District'

Notes

  • The figure uses fixed dimensions (autosize=False) to prevent infinite resize loops when embedded in Dash layouts

  • Axis labels show compass directions at the extremes: “W 20” at x=-20, “E 20” at x=+20, “S 20” at y=-20, “N 20” at y=+20

  • The scaleanchor constraint ensures 1:1 aspect ratio (square grid)

  • Pan and zoom are enabled (fixedrange=False)

See also

create_map_figure_with_rooms

Add rooms to the map canvas

pipeworks_mud_mapper.components.map_view.create_map_figure_with_rooms(rooms=None, visible_z_levels=None, selected_room=None, visual_offset_x=1.0, visual_offset_y=1.0)[source]

Create map figure with all rooms flattened onto a single 2D plane.

Renders a complete zone map showing rooms from multiple Z-levels simultaneously. Rooms are visually differentiated by Z-level using size and color, with the selected room highlighted in red.

The rendering order ensures that ground-level rooms (z=0) appear on top and receive clicks first when rooms overlap at the same X,Y position.

Parameters:
  • rooms (dict[str, dict] | None, optional) –

    Dictionary mapping room IDs to room data. Each room should have:

    • ”coords”: [x, y, z] list of coordinates

    • ”name”: Display name (falls back to room_id if missing)

    • ”exits”: Dict mapping direction to target room_id

    If None or empty, returns base map only.

  • visible_z_levels (list[int] | None, optional) – List of Z-levels to display (default: None, which shows all). Pass [-1, 0, 1] explicitly for all levels, or a subset like [0] to show only ground level, [-1, 0] to hide upper level.

  • selected_room (str | None, optional) – Room ID to highlight as selected (default: None). Selected room appears in red regardless of its Z-level.

  • visual_offset_x (float, optional) – X-axis scale factor for Z-level visual offset (default: 1.0).

  • visual_offset_y (float, optional) – Y-axis scale factor for Z-level visual offset (default: 1.0).

Returns:

  • go.Figure – Plotly Figure with rooms rendered in Z-order.

  • Visual Styling

  • --------------

  • Rooms are styled based on their Z coordinate

  • - **z=-1 (Down)** (Black filled, 14px - smallest, drawn first (back))

  • - **z=+1 (Up)** (White with black border, 18px - medium, drawn second)

  • - **z=0 (Ground)** (Blue filled, 20px - largest, drawn last (front))

  • - **Selected** (Always red, regardless of Z-level)

  • Exit connections

  • - **Cardinal (N/E/S/W)** (Gray lines between rooms on same Z-level)

  • - **Vertical (U/D)** ("U", "D", or "U/D" labels near stacked rooms)

Return type:

plotly.graph_objects.Figure

Examples

Render all rooms from a zone:

>>> rooms = {
...     "ground": {
...         "name": "Ground Floor",
...         "coords": [0, 0, 0],
...         "exits": {"down": "cellar", "north": "hall"}
...     },
...     "cellar": {
...         "name": "The Cellar",
...         "coords": [0, 0, -1],
...         "exits": {"up": "ground"}
...     },
...     "hall": {
...         "name": "Great Hall",
...         "coords": [0, 5, 0],
...         "exits": {"south": "ground"}
...     },
... }
>>> fig = create_map_figure_with_rooms(rooms)

Filter to ground level only:

>>> fig = create_map_figure_with_rooms(rooms, visible_z_levels=[0])

Highlight a selected room:

>>> fig = create_map_figure_with_rooms(
...     rooms, selected_room="cellar"
... )

Handle empty rooms gracefully:

>>> fig = create_map_figure_with_rooms(None)  # Returns base map
>>> fig = create_map_figure_with_rooms({})   # Returns base map

Notes

  • Rooms without “coords” key are treated as being at [0, 0, 0]

  • Cross-zone exits (containing ‘:’) are skipped during line drawing

  • Exit lines are drawn before room nodes (proper layering)

  • Vertical exits show text labels instead of lines

  • Hover text shows room name, ID, Z-level, and exit directions

  • When rooms overlap (stacked vertically), ground level is on top

When rooms share the same X,Y but different Z, they overlap visually. Due to render order, ground level (z=0) receives clicks first. To select a lower-level room:

  1. Uncheck “Ground (z=0)” in the layer filter

  2. Click the now-visible lower-level room

  3. Re-check ground level when done

See also

create_map_figure

Create empty map canvas

Z_LEVEL_STYLES

Visual configuration for each Z-level

Z_RENDER_ORDER

Rendering order for Z-levels