- tech draw draft v2
This commit is contained in:
@@ -14,7 +14,7 @@ Exact public API:
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Dict, List, Optional, Sequence, Tuple
|
||||
|
||||
import numpy as np
|
||||
@@ -68,6 +68,9 @@ class DrawingPrimitive:
|
||||
center: Optional[Tuple[float, float]] = None
|
||||
radius: Optional[float] = None
|
||||
dash_pattern: Tuple[float, ...] = ()
|
||||
# View this primitive belongs to (geometry primitives); used for
|
||||
# hit-testing in the drawing workbench.
|
||||
view_id: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -80,6 +83,9 @@ class DrawingRenderResult:
|
||||
unresolved_annotation_ids: Tuple[str, ...]
|
||||
source_fingerprint: str
|
||||
warnings: Tuple[str, ...]
|
||||
# Per-view model→sheet transform: view_id → (scale, offset_x, offset_y)
|
||||
# with sheet(x, y) = (x*scale + offset_x, y*scale + offset_y).
|
||||
view_transforms: Dict[str, Tuple[float, float, float]] = field(default_factory=dict)
|
||||
|
||||
|
||||
# ── View presets ───────────────────────────────────────────────────────────
|
||||
@@ -319,6 +325,7 @@ def _assemble_view(
|
||||
view: DrawingView,
|
||||
slot: Optional[Tuple[float, float, float, float]] = None,
|
||||
scale_override: Optional[float] = None,
|
||||
transforms: Optional[Dict[str, Tuple[float, float, float]]] = None,
|
||||
) -> Tuple[Tuple[DrawingPrimitive, ...], Tuple[DrawingCandidate, ...], Tuple[str, ...]]:
|
||||
"""Fit projected edges into *slot* and emit primitives + candidates.
|
||||
|
||||
@@ -326,6 +333,8 @@ def _assemble_view(
|
||||
bottom-left, +y up). When omitted the projection is fitted to the
|
||||
whole sheet. *scale_override* forces a specific model→sheet scale
|
||||
(used to keep all orthographic views at one shared scale).
|
||||
When *transforms* is given, the resolved
|
||||
``(scale, offset_x, offset_y)`` is recorded under the view id.
|
||||
"""
|
||||
warnings = list(warnings)
|
||||
primitives: List[DrawingPrimitive] = []
|
||||
@@ -366,6 +375,11 @@ def _assemble_view(
|
||||
offset_x = left + (avail_w - geom_w * scale) / 2.0 - min_x * scale
|
||||
offset_y = bottom + (avail_h - geom_h * scale) / 2.0 - min_y * scale
|
||||
|
||||
# Use kind as view_id for readability (UUID is opaque to users).
|
||||
view_id = view.kind if view.kind in _STANDARD_VIEWS else (view.name or view.id)
|
||||
if transforms is not None:
|
||||
transforms[view_id] = (scale, offset_x, offset_y)
|
||||
|
||||
def _to_sheet(x: float, y: float) -> Tuple[float, float]:
|
||||
return (x * scale + offset_x, y * scale + offset_y)
|
||||
|
||||
@@ -383,6 +397,7 @@ def _assemble_view(
|
||||
style=style,
|
||||
center=_to_sheet(cx, cy),
|
||||
radius=radius * scale,
|
||||
view_id=view_id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -391,12 +406,11 @@ def _assemble_view(
|
||||
kind="line",
|
||||
points=(_to_sheet(*p1), _to_sheet(*p2)),
|
||||
style=style,
|
||||
view_id=view_id,
|
||||
)
|
||||
)
|
||||
|
||||
# ── Dimension candidate extraction ──────────────────────────────
|
||||
# Use kind as view_id for readability (UUID is opaque to users).
|
||||
view_id = view.kind if view.kind in _STANDARD_VIEWS else (view.name or view.id)
|
||||
|
||||
# 1. Overall extents (bounding-box width/height), anchored at real
|
||||
# bbox corners so extension lines can start at feature extremes.
|
||||
@@ -1011,8 +1025,11 @@ def _generate_dimension_primitives(
|
||||
|
||||
Linear/extent candidates become extension lines + an offset dimension
|
||||
line with arrowheads + centred text (dimension line broken for the
|
||||
label). Diameters become a 45° leader with a horizontal tail. Angles
|
||||
become a small vertex arc + text.
|
||||
label). Anchors on one feature line yield a dimension line
|
||||
perpendicular to the measurement direction; anchors that are the
|
||||
closest points of two parallel edges (distance dimensions) yield a
|
||||
dimension line parallel to it. Diameters become a 45° leader with a
|
||||
horizontal tail. Angles become a small vertex arc + text.
|
||||
"""
|
||||
# Keep dimension lines inside the sheet (with a small margin).
|
||||
_sheet_min_x, _sheet_max_x = 6.0, _A3_WIDTH_MM - 6.0
|
||||
@@ -1084,65 +1101,129 @@ def _generate_dimension_primitives(
|
||||
d = (vx / vl, vy / vl) if vl > 1e-9 else (1.0, 0.0)
|
||||
|
||||
mx, my = (p1[0] + p2[0]) / 2.0, (p1[1] + p2[1]) / 2.0
|
||||
# Half the anchor spread along the measurement direction: the
|
||||
# dimension line lands this far (plus the offset) beyond the
|
||||
# far feature line.
|
||||
spread = abs((p2[0] - p1[0]) * d[0] + (p2[1] - p1[1]) * d[1]) / 2.0
|
||||
offset = spread + _DIM_OFFSET_MM
|
||||
# Place the dimension line on the side of the anchor pair away
|
||||
# from the view centre (outside the feature).
|
||||
sign = (
|
||||
-1.0
|
||||
if (view_center[0] - mx) * d[0] + (view_center[1] - my) * d[1] > 0
|
||||
else 1.0
|
||||
)
|
||||
cdim = _clamp_sheet((mx + sign * d[0] * offset, my + sign * d[1] * offset))
|
||||
# Decompose the anchor pair: separation along the measurement
|
||||
# direction d and perpendicular to it. cross ≈ 0 means the
|
||||
# anchors are the closest points on two parallel edges (a
|
||||
# distance dimension); otherwise they sit on one feature line.
|
||||
wdx, wdy = p2[0] - p1[0], p2[1] - p1[1]
|
||||
along = wdx * d[0] + wdy * d[1]
|
||||
cross = abs(wdx * d[1] - wdy * d[0])
|
||||
|
||||
# Progressive stacking: push the dimension line away from
|
||||
# already-placed parallel lines whose foot spans overlap, so
|
||||
# parallel dimensions stay readable (standard CAD behaviour).
|
||||
for _ in range(8):
|
||||
if cross > 0.5:
|
||||
# ── Feature-line case ─────────────────────────────────
|
||||
# The dimension line is perpendicular to d, offset away
|
||||
# from the view centre (half the anchor spread plus the
|
||||
# standard offset, so it lands beyond the far feature).
|
||||
spread = abs(along) / 2.0
|
||||
offset = spread + _DIM_OFFSET_MM
|
||||
sign = (
|
||||
-1.0
|
||||
if (view_center[0] - mx) * d[0] + (view_center[1] - my) * d[1] > 0
|
||||
else 1.0
|
||||
)
|
||||
cdim = _clamp_sheet((mx + sign * d[0] * offset, my + sign * d[1] * offset))
|
||||
|
||||
# Progressive stacking: push the dimension line away from
|
||||
# already-placed parallel lines whose foot spans overlap, so
|
||||
# parallel dimensions stay readable (standard CAD behaviour).
|
||||
for _ in range(8):
|
||||
t1 = (cdim[0] - p1[0]) * d[0] + (cdim[1] - p1[1]) * d[1]
|
||||
t2 = (cdim[0] - p2[0]) * d[0] + (cdim[1] - p2[1]) * d[1]
|
||||
e1 = (p1[0] + d[0] * t1, p1[1] + d[1] * t1)
|
||||
e2 = (p2[0] + d[0] * t2, p2[1] + d[1] * t2)
|
||||
half = math.hypot(e2[0] - e1[0], e2[1] - e1[1]) / 2.0
|
||||
if half <= 0.5:
|
||||
break
|
||||
u = (
|
||||
(e2[0] - e1[0]) / (2.0 * half),
|
||||
(e2[1] - e1[1]) / (2.0 * half),
|
||||
)
|
||||
pushed = False
|
||||
for (ux0, uy0, qx0, qy0, tmin0, tmax0) in placed_dim_lines:
|
||||
if abs(u[0] * ux0 + u[1] * uy0) < 0.98:
|
||||
continue # not parallel
|
||||
ta1 = (e1[0] - qx0) * ux0 + (e1[1] - qy0) * uy0
|
||||
ta2 = (e2[0] - qx0) * ux0 + (e2[1] - qy0) * uy0
|
||||
if max(ta1, ta2) < tmin0 or min(ta1, ta2) > tmax0:
|
||||
continue # spans do not overlap
|
||||
sep = (cdim[0] - qx0) * d[0] + (cdim[1] - qy0) * d[1]
|
||||
if abs(sep) < _DIM_STANDOFF_MM:
|
||||
prev = cdim
|
||||
cdim = (
|
||||
cdim[0] + sign * d[0] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
cdim[1] + sign * d[1] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
)
|
||||
cdim = _clamp_sheet(cdim)
|
||||
if cdim == prev:
|
||||
# Pushed against the sheet edge — stop stacking.
|
||||
break
|
||||
pushed = True
|
||||
break
|
||||
if not pushed:
|
||||
break
|
||||
|
||||
# Final feet of the (possibly stacked) dimension line.
|
||||
t1 = (cdim[0] - p1[0]) * d[0] + (cdim[1] - p1[1]) * d[1]
|
||||
t2 = (cdim[0] - p2[0]) * d[0] + (cdim[1] - p2[1]) * d[1]
|
||||
e1 = (p1[0] + d[0] * t1, p1[1] + d[1] * t1)
|
||||
e2 = (p2[0] + d[0] * t2, p2[1] + d[1] * t2)
|
||||
half = math.hypot(e2[0] - e1[0], e2[1] - e1[1]) / 2.0
|
||||
if half <= 0.5:
|
||||
break
|
||||
u = (
|
||||
(e2[0] - e1[0]) / (2.0 * half),
|
||||
(e2[1] - e1[1]) / (2.0 * half),
|
||||
ext_dir = (sign * d[0], sign * d[1])
|
||||
else:
|
||||
# ── Inter-edge distance case ──────────────────────────
|
||||
# The measured distance runs along d; the dimension line
|
||||
# is parallel to d, offset from the picked line on the
|
||||
# side away from the view centre.
|
||||
n = (-d[1], d[0])
|
||||
sign = (
|
||||
-1.0
|
||||
if (view_center[0] - mx) * n[0] + (view_center[1] - my) * n[1] > 0
|
||||
else 1.0
|
||||
)
|
||||
pushed = False
|
||||
for (ux0, uy0, qx0, qy0, tmin0, tmax0) in placed_dim_lines:
|
||||
if abs(u[0] * ux0 + u[1] * uy0) < 0.98:
|
||||
continue # not parallel
|
||||
ta1 = (e1[0] - qx0) * ux0 + (e1[1] - qy0) * uy0
|
||||
ta2 = (e2[0] - qx0) * ux0 + (e2[1] - qy0) * uy0
|
||||
if max(ta1, ta2) < tmin0 or min(ta1, ta2) > tmax0:
|
||||
continue # spans do not overlap
|
||||
sep = (cdim[0] - qx0) * d[0] + (cdim[1] - qy0) * d[1]
|
||||
if abs(sep) < _DIM_STANDOFF_MM:
|
||||
prev = cdim
|
||||
cdim = (
|
||||
cdim[0] + sign * d[0] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
cdim[1] + sign * d[1] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
)
|
||||
cdim = _clamp_sheet(cdim)
|
||||
if cdim == prev:
|
||||
# Pushed against the sheet edge — stop stacking.
|
||||
break
|
||||
pushed = True
|
||||
break
|
||||
if not pushed:
|
||||
break
|
||||
cdim = _clamp_sheet(
|
||||
(
|
||||
mx + sign * n[0] * _DIM_OFFSET_MM,
|
||||
my + sign * n[1] * _DIM_OFFSET_MM,
|
||||
)
|
||||
)
|
||||
half = abs(along) / 2.0
|
||||
u_dir = (d[0], d[1]) if along >= 0 else (-d[0], -d[1])
|
||||
|
||||
# Final feet of the (possibly stacked) dimension line.
|
||||
t1 = (cdim[0] - p1[0]) * d[0] + (cdim[1] - p1[1]) * d[1]
|
||||
t2 = (cdim[0] - p2[0]) * d[0] + (cdim[1] - p2[1]) * d[1]
|
||||
e1 = (p1[0] + d[0] * t1, p1[1] + d[1] * t1)
|
||||
e2 = (p2[0] + d[0] * t2, p2[1] + d[1] * t2)
|
||||
half = math.hypot(e2[0] - e1[0], e2[1] - e1[1]) / 2.0
|
||||
# Progressive stacking (same rules, pushing along n).
|
||||
for _ in range(8):
|
||||
if half <= 0.5:
|
||||
break
|
||||
e1 = (cdim[0] - u_dir[0] * half, cdim[1] - u_dir[1] * half)
|
||||
e2 = (cdim[0] + u_dir[0] * half, cdim[1] + u_dir[1] * half)
|
||||
pushed = False
|
||||
for (ux0, uy0, qx0, qy0, tmin0, tmax0) in placed_dim_lines:
|
||||
if abs(u_dir[0] * ux0 + u_dir[1] * uy0) < 0.98:
|
||||
continue # not parallel
|
||||
ta1 = (e1[0] - qx0) * ux0 + (e1[1] - qy0) * uy0
|
||||
ta2 = (e2[0] - qx0) * ux0 + (e2[1] - qy0) * uy0
|
||||
if max(ta1, ta2) < tmin0 or min(ta1, ta2) > tmax0:
|
||||
continue # spans do not overlap
|
||||
sep = (cdim[0] - qx0) * n[0] + (cdim[1] - qy0) * n[1]
|
||||
if abs(sep) < _DIM_STANDOFF_MM:
|
||||
prev = cdim
|
||||
cdim = (
|
||||
cdim[0] + sign * n[0] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
cdim[1] + sign * n[1] * (_DIM_STANDOFF_MM - abs(sep)),
|
||||
)
|
||||
cdim = _clamp_sheet(cdim)
|
||||
if cdim == prev:
|
||||
# Pushed against the sheet edge — stop stacking.
|
||||
break
|
||||
pushed = True
|
||||
break
|
||||
if not pushed:
|
||||
break
|
||||
|
||||
e1 = (cdim[0] - u_dir[0] * half, cdim[1] - u_dir[1] * half)
|
||||
e2 = (cdim[0] + u_dir[0] * half, cdim[1] + u_dir[1] * half)
|
||||
ext_dir = (sign * n[0], sign * n[1])
|
||||
|
||||
# ── Shared rendering of the positioned dimension line ─────
|
||||
if half > 0.5:
|
||||
u = ((e2[0] - e1[0]) / (2.0 * half), (e2[1] - e1[1]) / (2.0 * half))
|
||||
s1 = (e1[0] - cdim[0]) * u[0] + (e1[1] - cdim[1]) * u[1]
|
||||
@@ -1151,10 +1232,10 @@ def _generate_dimension_primitives(
|
||||
(u[0], u[1], cdim[0], cdim[1], min(s1, s2), max(s1, s2))
|
||||
)
|
||||
# Extension lines: anchor → 2mm past the dimension line.
|
||||
for anchor, t in ((p1, t1), (p2, t2)):
|
||||
for anchor, foot in ((p1, e1), (p2, e2)):
|
||||
end = (
|
||||
anchor[0] + d[0] * (t + sign * _DIM_EXT_OVERSHOOT_MM),
|
||||
anchor[1] + d[1] * (t + sign * _DIM_EXT_OVERSHOOT_MM),
|
||||
foot[0] + ext_dir[0] * _DIM_EXT_OVERSHOOT_MM,
|
||||
foot[1] + ext_dir[1] * _DIM_EXT_OVERSHOOT_MM,
|
||||
)
|
||||
prims.append(
|
||||
DrawingPrimitive(
|
||||
@@ -1279,6 +1360,108 @@ def _generate_dimension_primitives(
|
||||
return prims
|
||||
|
||||
|
||||
_MANUAL_DIMENSION_KINDS = ("length", "diameter", "angle")
|
||||
|
||||
|
||||
def build_manual_candidates(
|
||||
drawing: TechnicalDrawing,
|
||||
view_transforms: Dict[str, Tuple[float, float, float]],
|
||||
) -> Tuple[List[DrawingCandidate], List[str], List[str]]:
|
||||
"""Convert user-placed annotations into renderable dimension candidates.
|
||||
|
||||
Manual annotations store their feature geometry in view-plane model
|
||||
coordinates (see ``DrawingAnnotation.anchors``); here it is re-projected
|
||||
through the current view transforms so the dimensions re-lay out
|
||||
correctly whenever views, slots, or the shared scale change. Returns
|
||||
``(candidates, resolved_ids, unresolved_ids)``.
|
||||
"""
|
||||
candidates: List[DrawingCandidate] = []
|
||||
resolved: List[str] = []
|
||||
unresolved: List[str] = []
|
||||
|
||||
for ann in drawing.annotations:
|
||||
if not ann.visible or ann.dimension_kind not in _MANUAL_DIMENSION_KINDS:
|
||||
continue
|
||||
if len(ann.anchors) < 2:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
transform = view_transforms.get(ann.view_id)
|
||||
if transform is None:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
scale, offset_x, offset_y = transform
|
||||
|
||||
def to_sheet(pt: Tuple[float, float]) -> Tuple[float, float]:
|
||||
return (pt[0] * scale + offset_x, pt[1] * scale + offset_y)
|
||||
|
||||
a0, a1 = ann.anchors[0], ann.anchors[1]
|
||||
if ann.dimension_kind == "angle":
|
||||
if len(ann.anchors) < 3:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
vertex, arm1, arm2 = ann.anchors
|
||||
v1x, v1y = arm1[0] - vertex[0], arm1[1] - vertex[1]
|
||||
v2x, v2y = arm2[0] - vertex[0], arm2[1] - vertex[1]
|
||||
m1 = math.hypot(v1x, v1y)
|
||||
m2 = math.hypot(v2x, v2y)
|
||||
if m1 < 1e-9 or m2 < 1e-9:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
cos_a = (v1x * v2x + v1y * v2y) / (m1 * m2)
|
||||
value = math.degrees(math.acos(max(-1.0, min(1.0, cos_a))))
|
||||
if value < 0.5 or value > 179.5:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
candidates.append(
|
||||
DrawingCandidate(
|
||||
key=f"manual:{ann.id}",
|
||||
view_id=ann.view_id,
|
||||
kind="angle",
|
||||
references=(ann.id,),
|
||||
value=value,
|
||||
anchor_points=(to_sheet(vertex), to_sheet(arm1), to_sheet(arm2)),
|
||||
label=f"{value:.{_DISPLAY_PRECISION}f}°",
|
||||
)
|
||||
)
|
||||
resolved.append(ann.id)
|
||||
continue
|
||||
|
||||
value = math.dist(a0, a1)
|
||||
if value < 1e-6:
|
||||
unresolved.append(ann.id)
|
||||
continue
|
||||
if ann.dimension_kind == "length":
|
||||
if ann.direction:
|
||||
dx, dy = float(ann.direction[0]), float(ann.direction[1])
|
||||
if math.hypot(dx, dy) < 1e-9:
|
||||
dx, dy = a1[0] - a0[0], a1[1] - a0[1]
|
||||
else:
|
||||
dx, dy = a1[0] - a0[0], a1[1] - a0[1]
|
||||
# value >= 1e-6 guarantees the fallback vector is non-zero.
|
||||
mag = math.hypot(dx, dy)
|
||||
direction: Tuple[float, float] = (dx / mag, dy / mag)
|
||||
label = f"{value:.{_DISPLAY_PRECISION}f}"
|
||||
else: # diameter
|
||||
direction = ()
|
||||
label = f"Ø{value:.{_DISPLAY_PRECISION}f}"
|
||||
|
||||
candidates.append(
|
||||
DrawingCandidate(
|
||||
key=f"manual:{ann.id}",
|
||||
view_id=ann.view_id,
|
||||
kind=ann.dimension_kind,
|
||||
references=(ann.id,),
|
||||
value=value,
|
||||
anchor_points=(to_sheet(a0), to_sheet(a1)),
|
||||
label=label,
|
||||
direction=direction,
|
||||
)
|
||||
)
|
||||
resolved.append(ann.id)
|
||||
|
||||
return candidates, resolved, unresolved
|
||||
|
||||
|
||||
def generate_drawing(
|
||||
drawing: TechnicalDrawing,
|
||||
project: Project,
|
||||
@@ -1335,6 +1518,9 @@ def generate_drawing(
|
||||
if v.kind in _STANDARD_VIEWS and v.kind != "isometric"
|
||||
}
|
||||
|
||||
# Per-view model→sheet transforms, filled in by _assemble_view.
|
||||
transforms: Dict[str, Tuple[float, float, float]] = {}
|
||||
|
||||
for view in drawing.views:
|
||||
vid = _vid_of(view)
|
||||
slot = view_slots.get(vid)
|
||||
@@ -1343,14 +1529,25 @@ def generate_drawing(
|
||||
common_scale * view.scale if vid in ortho_vids and common_scale else None
|
||||
)
|
||||
prims, cands, vwarns = _assemble_view(
|
||||
edges, view_warnings, view, slot, scale_override
|
||||
edges, view_warnings, view, slot, scale_override, transforms
|
||||
)
|
||||
all_primitives.extend(prims)
|
||||
all_candidates.extend(cands)
|
||||
warnings.extend(vwarns)
|
||||
|
||||
# Auto-place dimensions from candidates (isometric views are never
|
||||
# dimensioned on real drawings).
|
||||
# User-placed dimensions: convert the model-space annotations into
|
||||
# sheet-space candidates so they re-lay out with the current views.
|
||||
manual_cands, manual_resolved, manual_unresolved = build_manual_candidates(
|
||||
drawing, transforms
|
||||
)
|
||||
manual_ids = {c.references[0] for c in manual_cands if c.references}
|
||||
resolved_ids: List[str] = list(manual_resolved)
|
||||
unresolved_ids: List[str] = list(manual_unresolved)
|
||||
|
||||
# Place dimensions per view: the auto selection (only while the user
|
||||
# has auto dimensions enabled) plus the manual dimensions, so the
|
||||
# stacking/overlap logic treats them alike. Isometric views are never
|
||||
# dimensioned on real drawings.
|
||||
for view in drawing.views:
|
||||
if view.kind == "isometric":
|
||||
continue
|
||||
@@ -1361,17 +1558,21 @@ def generate_drawing(
|
||||
if slot
|
||||
else (_A3_WIDTH_MM / 2.0, _A3_HEIGHT_MM / 2.0)
|
||||
)
|
||||
selected = _select_dimensions_for_placement(all_candidates, vid)
|
||||
selected: List[DrawingCandidate] = []
|
||||
if drawing.auto_dimensions:
|
||||
selected = _select_dimensions_for_placement(all_candidates, vid)
|
||||
selected.extend(c for c in manual_cands if c.view_id == vid)
|
||||
all_primitives.extend(_generate_dimension_primitives(selected, center))
|
||||
|
||||
# Match annotations to candidates.
|
||||
resolved_ids: List[str] = []
|
||||
unresolved_ids: List[str] = []
|
||||
# Match legacy (reference-based) annotations to candidates. Manual
|
||||
# dimensions were already rendered above as full dimension primitives.
|
||||
candidate_by_key: Dict[str, DrawingCandidate] = {c.key: c for c in all_candidates}
|
||||
|
||||
for ann in drawing.annotations:
|
||||
if not ann.visible:
|
||||
continue
|
||||
if ann.id in manual_ids:
|
||||
continue
|
||||
if ann.kind == "note":
|
||||
resolved_ids.append(ann.id)
|
||||
continue
|
||||
@@ -1405,6 +1606,7 @@ def generate_drawing(
|
||||
drawing.source_kind, drawing.source_id
|
||||
),
|
||||
warnings=tuple(warnings),
|
||||
view_transforms=transforms,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user