Sheet Set Detection#

from pathlib import Path
from dessia_drawing.core import Drawing
from drawing_tools.config.default_language_configs import DEFAULT_ENGLISH_CONFIG, DEFAULT_FRENCH_CONFIG
from drawing_tools.featured_drawing import FeaturedDrawing
from drawing_tools.sheet.set.detection import SheetSet, detect_sets_by_subdivision_lines, find_set_symbols

_DATA_DIR = Path("data/json")
_drawing_sets = Drawing.from_json(str(_DATA_DIR / "sheet_with_sets_1.json"))
featured_drawing = FeaturedDrawing(_drawing_sets, language_configs=[DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG])
sheet = _drawing_sets.sheets[0]
featured_sheet = featured_drawing.sheets[0]
sets = detect_sets_by_subdivision_lines(sheet)
sheet_set = sets[0] if sets else None
featured_sheet.plot_data_sets().plot()

The sheet set detection module identifies named rectangular regions (called sets) within a technical drawing sheet. A set corresponds to, either the complete area of a sheet, or a sub-area of the sheet delimited by subdivision lines and identified by a SET marker symbol.

Purpose#

Some technical drawings pack multiple independent assemblies or parts onto a single sheet, separated by visible subdivision lines. Each region is labeled with a SET marker symbol (e.g., “000”, “001”) that names that portion of the sheet.

This module provides:

  • SheetSet dataclass: Represents a named rectangular region with spatial query methods

  • Automatic detection: Finds subdivision lines and SET markers to create SheetSet instances

  • View-to-set mapping: Determines which views belong to which set

  • Visualization: SheetSetsSketcher for color-coded region overlays

These capabilities are useful for:

  • Analyzing multi-part sheets where each set has its own views

  • Filtering views by set for targeted processing

  • Validating that the correct annotations appear in each set region

Overview#

The detection algorithm in detect_sets_by_subdivision_lines() recursively partitions the content zone (a guillotine partition). It first does a one-off setup, then runs an iterative partitioning loop.

Setup (computed once):

  1. Take the sheet’s grid content zone as the initial zone.

  2. Extract all horizontal and vertical line segments from that content and merge them by collinearity (same direction and position) — so a divider drawn as several fragments (e.g. a full-height line broken in two pieces by an adjacent view) is reconstructed into one line.

  3. Extract all SET marker symbols (frame.entity_type == "SET") on the sheet.

Iterative partitioning: maintain a work-list of zones, seeded with the initial content zone. While the list is not empty, take a zone and:

  • look for divider lines that cross the zone over at least min_length_ratio (default 95%) of its width or height;

  • if one is found, split the zone into two at that line and re-queue both halves (the other dividers are rediscovered in the sub-zones);

  • otherwise the zone is final — for each SET symbol whose center lies in the zone, create one SheetSet assigned to that zone, named from the symbol’s text content.

Regions with no SET symbol (an isometric-view column, a title block, …) are naturally excluded rather than absorbed into a neighbouring set. If a sheet has SET markers but no dividers, the whole content zone stays as a single final zone — each marker then yields one set sharing that region.

The collinear-merge step (setup 2) is what makes a partial or fragmented divider usable:

------  ------       merge collinear        ---------------
piece A piece B   ------------------->      single divider (full span recognized)

The generic segment and rectangle operations (reconstruct, crop, span test, split) live in the reusable drawing_tools.helpers.axis_partition module.

Visualization Example#

The following interactive visualization shows a sheet with two detected sets “001” and “000”, highlighted with color-coded overlays produced by SheetSetsSketcher:

Source: sheet_with_sets_1.json, sheet [0]

Note

Multiple SET symbols per region

A single rectangular region can contain more than one SET marker symbol. In that case, one SheetSet is created per symbol, all sharing the same bounding_rectangle. When visualized, this produces overlapping colored rectangles in the same area — one per set. The following visualization illustrates this case with 3 sets (003, 004, 005) sharing the same region:

Source: sheet_with_sets_3.json, sheet [2]

Worked Example: a Multi-region Sheet#

Sheet PL.04 of the condor LIGHT ASSY drawing stacks two sets vertically in its left column, while a full-height isometric view and the title block occupy the right column. The divider between the two sets is a horizontal line that stops where the right column begins, and the column separator is itself drawn as two collinear pieces:

content zone (PL.04)
+----------------------------+------------------+
|  Set 002  (VUE DE GAUCHE)  |                  |
+----------------------------+   no set         |
|  Set 003  (VUE DE DESSUS)  |  (isometric view |
|                            |   + title block) |
+----------------------------+------------------+
                             ^
    vertical divider x~=804 (reconstructed from two collinear pieces)
    horizontal divider y~=499 then splits the left column in two

Reconstructing the fragmented dividers lets the recursive partition (1) isolate the right column as a leaf containing no SET symbol — so it is excluded — and (2) split the left column into the two stacked sets 002 (top) and 003 (bottom). Each set is bounded to the left column instead of wrongly spanning the full sheet width.

Source: condor_TEMP_332P641283_--A_DRW01_FORMATION_LIGHT_ASSY.json, sheet [3] (PL.04)

SheetSet Dataclass#

SheetSet is a dataclass representing one named region:

from drawing_tools.sheet.set.detection import SheetSet

Attributes:

  • name (str): The set identifier (e.g., “000”, “001”)

  • bounding_rectangle (BoundingRectangle): The rectangular boundary

  • symbol (Symbol | None): The SET marker symbol that identifies this set

Spatial Query Methods#

# Check if a point is inside the set
sheet_set.contains_point(point_2d)  # -> bool

# Fraction (0-1) of a rectangle's area lying within the set
sheet_set.containment_ratio(rectangle)  # -> float

# How many of the rectangle's 4 edges do not overflow the set (0-4)
sheet_set.contained_edge_count(rectangle)  # -> int

# Check if an entity's center is inside the set
sheet_set.contains_entity(entity)  # -> bool

Detection Functions#

detect_sets_by_subdivision_lines#

Main detection function. Analyzes the sheet’s background view to find subdivision lines and SET markers:

from drawing_tools.sheet.set.detection import detect_sets_by_subdivision_lines

sets = detect_sets_by_subdivision_lines(sheet)
# sets: list[SheetSet]

Parameters:

  • sheet (Sheet): The sheet to analyze

  • min_length_ratio (float, default 0.95): Minimum fraction of the current zone’s width or height a line must span to subdivide it

find_set_symbols#

Public helper that finds all SET marker symbols on a sheet:

from drawing_tools.sheet.set.detection import find_set_symbols

symbols = find_set_symbols(sheet)
print(f"Found {len(symbols)} SET symbols")

FeaturedSheet Integration#

FeaturedSheet exposes set detection through convenient properties and methods. Detection is lazy-cached: the first access to sets triggers the detection algorithm, and subsequent accesses return the cached result.

Properties#

  • setslist[SheetSet]: All detected sets (lazy-cached)

  • has_setsbool: Whether the sheet has any detected sets

  • views_by_setdict[str, list[FeaturedView]]: Views organized by set name

Methods#

  • get_set_for_view(view, min_containment_ratio=0.9, min_contained_edges=2)SheetSet | None: Find which set a view belongs to. A view is attributed to the set covering the largest fraction of its area, provided that fraction reaches min_containment_ratio and at least min_contained_edges of the view’s four edges do not overflow the set. Returns None otherwise (genuinely straddling views, full-frame views such as the background view). Pass min_containment_ratio=1.0 for strict full containment.

  • get_views_in_set(sheet_set, min_containment_ratio=0.9, min_contained_edges=2)list[FeaturedView]: Get all views attributed to a given set, using the same rule.

    The defaults live in drawing_tools.sheet.featured_sheet as DEFAULT_MIN_CONTAINMENT_RATIO and DEFAULT_MIN_CONTAINED_EDGES. Lowering the ratio tolerates views that slightly overflow their set; the edge guard keeps full-frame views (which overflow on all four sides) out of every set.

Usage Examples#

Basic Detection#

from pathlib import Path
from dessia_drawing.core import Drawing
from drawing_tools import FeaturedDrawing
from drawing_tools.config.default_language_configs import DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG

drawing = Drawing.from_json(filepath=Path("drawing.json"))
featured_drawing = FeaturedDrawing(
    drawing=drawing,
    language_configs=[DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG]
)

for sheet in featured_drawing.sheets:
    print(f"Sheet: {sheet.name}")
    print(f"Has sets: {sheet.has_sets}")
    print(f"Sets count: {len(sheet.sets)}")

    for sheet_set in sheet.sets:
        br = sheet_set.bounding_rectangle
        print(f"\nSet '{sheet_set.name}':")
        print(f"  Bounds: x=[{br.xmin:.1f}, {br.xmax:.1f}], y=[{br.ymin:.1f}, {br.ymax:.1f}]")

Querying Views by Set#

for sheet in featured_drawing.sheets:
    # Get views organized by set
    for set_name, views in sheet.views_by_set.items():
        print(f"Set '{set_name}': {[v.name for v in views]}")

    # Find which set each view belongs to
    for view in sheet.views:
        found_set = sheet.get_set_for_view(view)
        if found_set:
            print(f"  {view.name} -> Set '{found_set.name}'")
        else:
            print(f"  {view.name} -> no set (straddles sets or full-frame view)")

Finding SET Symbols#

from drawing_tools.sheet.set.detection import find_set_symbols

for sheet in featured_drawing.sheets:
    symbols = find_set_symbols(sheet)
    print(f"Found {len(symbols)} SET symbols:")
    for symbol in symbols:
        print(f"  - '{symbol.get_text_content().strip()}'")

Script#

The script scripts/sheet_sets_detector.py demonstrates the full detection pipeline including SET symbol discovery, set detection, view-to-set mapping, and visualization with SheetSetsSketcher.

Visualization#

Use the @plot_data_view method on FeaturedSheet to visualize detected sets:

featured_sheet.plot_data_sets().plot()

In addition to the color-coded set regions (each labelled Set <name> (<n> views)), every view is outlined in the color of the set it is assigned to; views assigned to no set are outlined in red dashed. Each view outline has a tooltip showing the view name, its set, and the deciding metrics versus their thresholds, e.g. containment_ratio=0.91>0.9=min, contained_edges=3>2=min.

This method is also available on FeaturedDrawing to visualize all sheets at once. See Display & Report Methods for an interactive example.

See Also#