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
SheetSetinstancesView-to-set mapping: Determines which views belong to which set
Visualization:
SheetSetsSketcherfor 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):
Take the sheet’s grid content zone as the initial zone.
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.
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
SheetSetassigned 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 boundarysymbol(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 analyzemin_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#
sets→list[SheetSet]: All detected sets (lazy-cached)has_sets→bool: Whether the sheet has any detected setsviews_by_set→dict[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 reachesmin_containment_ratioand at leastmin_contained_edgesof the view’s four edges do not overflow the set. ReturnsNoneotherwise (genuinely straddling views, full-frame views such as the background view). Passmin_containment_ratio=1.0for 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_sheetasDEFAULT_MIN_CONTAINMENT_RATIOandDEFAULT_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#
Featured Classes Guide — FeaturedSheet class reference
Display & Report Methods — Interactive display examples
scripts/sheet_sets_detector.py— Demonstration script