Snapshot 2: Move/Rotate-Performance + Hatch↔Curve bidirektional
Performance-Optimierungen für User-Transform-Commands (_Move/_Rotate/etc): - CommandBegin/End-Listener: Mein Code schläft während Rhinos Transform läuft komplett (Replace-Handler early-return). Beim CommandEnd vergleicht ein batch-Pass den Pre-Transform-Snapshot mit dem aktuellen State und macht EINEN konfliktfreien Sync-Regen pro betroffener Wand. Kein "Unable to transform"-Konflikt mehr, deutlich snappier. - Sub-Volumen non-destruktiv: doc.Objects.Replace statt Delete+AddBrep wenn die Anzahl gleich bleibt (= häufiger Fall bei Brüstung/Höhe/XY-Drag). - Migrate-Skip bei reinem Z-Drag: spart die Pass durch alle Öffnungen wenn XY unverändert ist. - Sync-Regen-Deduplizierung im Batch via _dossier_skip_sync_regen Flag. - Display-Suppress während des gesamten CommandEnd-Batch (kein sichtbares „Aufbauen" von Sub-Volumen). Plus Gestaltung-Fix: - Hatch→Curve Reverse-Sync via Hatch.Get3dCurves(outer=True): User kann die Hatch alleine verschieben/rotieren/skalieren → Curve folgt mit derselben Transform. Vorher nur Curve→Hatch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+213
-21
@@ -4021,14 +4021,28 @@ def _regenerate_element_body(doc, element_id, src_obj, meta, geom, geschoss_name
|
|||||||
layer_breps = new_layer_breps
|
layer_breps = new_layer_breps
|
||||||
|
|
||||||
# Oeffnungs-Sub-Volumina (Rahmen+Sims+Glas) erzeugen.
|
# Oeffnungs-Sub-Volumina (Rahmen+Sims+Glas) erzeugen.
|
||||||
|
# Nicht-destruktiv wenn moeglich: wenn die Anzahl der Sub-Volumen gleich
|
||||||
|
# bleibt (z.B. bei Bruestung-/Hoehe-/XY-Aenderung), nutzen wir
|
||||||
|
# `doc.Objects.Replace` auf die existierenden IDs statt Delete+AddBrep.
|
||||||
|
# Damit kollidiert ein laufender `_Move`-Command nicht mehr mit dem
|
||||||
|
# Wand-Regen → kein „Unable to transform"-Fehler mehr. Bei Anzahl-
|
||||||
|
# Aenderung (z.B. Fluegel-Wechsel) Fallback auf Delete+Add.
|
||||||
op_layer = _ensure_layer(doc, _layer_path_volume(doc, geschoss_name))
|
op_layer = _ensure_layer(doc, _layer_path_volume(doc, geschoss_name))
|
||||||
for op_meta, pt_loc, op_uk in opening_jobs:
|
for op_meta, pt_loc, op_uk in opening_jobs:
|
||||||
for o, _m in _find_objects_by_wall_id(doc, op_meta["id"],
|
old_objs = list(_find_objects_by_wall_id(doc, op_meta["id"],
|
||||||
"oeffnung_volume"):
|
"oeffnung_volume"))
|
||||||
try: doc.Objects.Delete(o.Id, True)
|
|
||||||
except Exception as ex: print("[ELEMENTE] del old oeff vol:", ex)
|
|
||||||
pieces = _make_oeffnung_pieces(geom, pt_loc, meta["dicke"],
|
pieces = _make_oeffnung_pieces(geom, pt_loc, meta["dicke"],
|
||||||
op_meta, op_uk)
|
op_meta, op_uk)
|
||||||
|
if len(old_objs) == len(pieces) and len(pieces) > 0:
|
||||||
|
for (old_obj, _old_meta), pbrep in zip(old_objs, pieces):
|
||||||
|
try: doc.Objects.Replace(old_obj.Id, pbrep)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] replace oeff vol:", ex)
|
||||||
|
continue
|
||||||
|
# Fallback: Anzahl hat sich geaendert → alte loeschen + neue adden.
|
||||||
|
for o, _m in old_objs:
|
||||||
|
try: doc.Objects.Delete(o.Id, True)
|
||||||
|
except Exception as ex: print("[ELEMENTE] del old oeff vol:", ex)
|
||||||
for pbrep in pieces:
|
for pbrep in pieces:
|
||||||
op_attrs = Rhino.DocObjects.ObjectAttributes()
|
op_attrs = Rhino.DocObjects.ObjectAttributes()
|
||||||
op_attrs.LayerIndex = op_layer
|
op_attrs.LayerIndex = op_layer
|
||||||
@@ -6860,16 +6874,10 @@ def _apply_oeffnung_constraint(new_obj, meta, old_obj=None):
|
|||||||
attrs.SetUserString(_KEY_OEFF_BRUEST, "{:.6f}".format(new_bruest))
|
attrs.SetUserString(_KEY_OEFF_BRUEST, "{:.6f}".format(new_bruest))
|
||||||
doc.Objects.ModifyAttributes(current.Id, attrs, True)
|
doc.Objects.ModifyAttributes(current.Id, attrs, True)
|
||||||
if doc is not None and parent_id:
|
if doc is not None and parent_id:
|
||||||
# Defer-Regel: NUR Tueren im User-`_Move` brauchen Defer (Sync kollidiert
|
# Skip Sync-Regen wenn wir gerade in einer Batch-Verarbeitung sind
|
||||||
# mit Rhinos Move-Operation → „Unable to transform"). Fenster und Grip-
|
# (Command-End): dort macht der Caller EINEN Sync-Regen pro Wand
|
||||||
# Drag laufen sync — bei diesen tritt der Konflikt nicht auf und der
|
# am Schluss → spart Mehrfach-Regen bei mehreren Öffnungen pro Wand.
|
||||||
# Sync-Regen gibt sofortiges Feedback ohne sichtbares Aufladen.
|
if not sc.sticky.get("_dossier_skip_sync_regen"):
|
||||||
skip_ids = sc.sticky.get("_elemente_replace_selected_ids") or set()
|
|
||||||
is_user_move = str(new_obj.Id) in skip_ids
|
|
||||||
is_tuer = (meta.get("oeff_typ") == "tuer")
|
|
||||||
if is_user_move and is_tuer:
|
|
||||||
_queue_regen(parent_id)
|
|
||||||
else:
|
|
||||||
try: _regenerate_element(doc, parent_id)
|
try: _regenerate_element(doc, parent_id)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
print("[ELEMENTE] sync regen oeffnung:", ex)
|
print("[ELEMENTE] sync regen oeffnung:", ex)
|
||||||
@@ -6886,6 +6894,11 @@ def _on_object_replaced(sender, e):
|
|||||||
die regenerierten Volumen liegen.
|
die regenerierten Volumen liegen.
|
||||||
"""
|
"""
|
||||||
if sc.sticky.get(_REGEN_BUSY): return
|
if sc.sticky.get(_REGEN_BUSY): return
|
||||||
|
# Wenn ein User-Transform-Command (_Move/_Rotate/etc.) aktiv ist: GAR
|
||||||
|
# NICHTS hier tun. Rhinos Move soll konfliktfrei durchlaufen. Nach
|
||||||
|
# CommandEnd vergleichen wir Snapshot vs. aktuellen State + machen den
|
||||||
|
# ganzen Update in einem konfliktfreien Batch.
|
||||||
|
if sc.sticky.get(_UT_ACTIVE_KEY): return
|
||||||
doc = Rhino.RhinoDoc.ActiveDoc
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
# Snapshot der aktuell selektierten IDs — damit Migrate die Objekte
|
# Snapshot der aktuell selektierten IDs — damit Migrate die Objekte
|
||||||
# skippen kann die Rhinos Move/Rotate gerade transformiert (sonst
|
# skippen kann die Rhinos Move/Rotate gerade transformiert (sonst
|
||||||
@@ -7683,6 +7696,175 @@ def _on_idle_selection(sender, e):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Welche Rhino-Commands transformieren mehrere Objekte gleichzeitig — bei
|
||||||
|
# diesen lassen wir Rhinos Move/Rotate KOMPLETT durchlaufen und feuern den
|
||||||
|
# Wand-Regen erst NACH CommandEnd. So gibt's keine „Unable to transform"-
|
||||||
|
# Kollision mehr zwischen meinem Sync-Regen und Rhinos pending Transforms.
|
||||||
|
_USER_TRANSFORM_CMDS = frozenset((
|
||||||
|
"Move", "Rotate", "Rotate3D", "Mirror", "Scale", "Scale1D", "Scale2D",
|
||||||
|
"Drag", "Gumball", "Orient", "Orient3Pt", "RemapCPlane", "Transform",
|
||||||
|
))
|
||||||
|
|
||||||
|
_UT_ACTIVE_KEY = "_dossier_user_transform_active"
|
||||||
|
_UT_SNAPSHOT_KEY = "_dossier_pre_transform_snapshot"
|
||||||
|
|
||||||
|
|
||||||
|
def _snapshot_source_positions(doc):
|
||||||
|
"""Schnappschuss aller Source-Geometrien — gerade vor einem User-Transform.
|
||||||
|
Wird in _on_command_end gegen aktuelle Positionen verglichen, um Brüstung-
|
||||||
|
Mitnahme + Migration zu rechnen ohne mit Rhinos noch laufender Move-
|
||||||
|
Operation zu kollidieren."""
|
||||||
|
snap = {}
|
||||||
|
if doc is None: return snap
|
||||||
|
for obj in doc.Objects:
|
||||||
|
try:
|
||||||
|
m = _read_meta(obj)
|
||||||
|
if not m: continue
|
||||||
|
t = m.get("type")
|
||||||
|
if t not in SOURCE_TYPES: continue
|
||||||
|
geom = obj.Geometry
|
||||||
|
if hasattr(geom, "Location"):
|
||||||
|
p = geom.Location
|
||||||
|
snap[m["id"]] = {"type": t, "pos": (p.X, p.Y, p.Z)}
|
||||||
|
elif isinstance(geom, rg.Curve):
|
||||||
|
s = geom.PointAtStart; e = geom.PointAtEnd
|
||||||
|
snap[m["id"]] = {"type": t,
|
||||||
|
"start": (s.X, s.Y, s.Z),
|
||||||
|
"end": (e.X, e.Y, e.Z)}
|
||||||
|
except Exception: pass
|
||||||
|
return snap
|
||||||
|
|
||||||
|
|
||||||
|
def _on_command_begin(sender, e):
|
||||||
|
try:
|
||||||
|
name = getattr(e, "CommandEnglishName", "") or ""
|
||||||
|
except Exception: name = ""
|
||||||
|
if name not in _USER_TRANSFORM_CMDS: return
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return
|
||||||
|
sc.sticky[_UT_SNAPSHOT_KEY] = _snapshot_source_positions(doc)
|
||||||
|
sc.sticky[_UT_ACTIVE_KEY] = name
|
||||||
|
|
||||||
|
|
||||||
|
def _on_command_end(sender, e):
|
||||||
|
name = sc.sticky.get(_UT_ACTIVE_KEY)
|
||||||
|
if not name: return
|
||||||
|
sc.sticky[_UT_ACTIVE_KEY] = None
|
||||||
|
snapshot = sc.sticky.get(_UT_SNAPSHOT_KEY) or {}
|
||||||
|
sc.sticky[_UT_SNAPSHOT_KEY] = None
|
||||||
|
doc = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
if doc is None: return
|
||||||
|
|
||||||
|
# Pseudo-Object Wrapper damit _apply_oeffnung_constraint pt_old.Location
|
||||||
|
# lesen kann ohne den echten alten RhinoObject zu kennen.
|
||||||
|
class _PseudoOld(object):
|
||||||
|
def __init__(self, pt): self.Geometry = rg.Point(pt)
|
||||||
|
|
||||||
|
affected_walls = set()
|
||||||
|
_was_busy = sc.sticky.get(_REGEN_BUSY, False)
|
||||||
|
sc.sticky[_REGEN_BUSY] = True
|
||||||
|
# Skip-Flag: in der Schleife wuerde jeder Constraint einen eigenen Sync-
|
||||||
|
# Regen ausloesen → mehrere Regens pro Wand. Wir machen am Schluss EINEN
|
||||||
|
# Regen pro affected_wall — viel schneller bei mehreren Oeffnungen.
|
||||||
|
sc.sticky["_dossier_skip_sync_regen"] = True
|
||||||
|
# Display-Updates komplett suppressen waehrend der Batch — Rhino zeichnet
|
||||||
|
# sonst nach jedem Brep-Add/Replace neu, was bei mehreren Sub-Volumen
|
||||||
|
# sichtbares „Aufbauen" verursacht. Ein einziger Redraw am Ende reicht.
|
||||||
|
prev_redraw = doc.Views.RedrawEnabled
|
||||||
|
doc.Views.RedrawEnabled = False
|
||||||
|
try:
|
||||||
|
for obj in list(doc.Objects):
|
||||||
|
try:
|
||||||
|
m = _read_meta(obj)
|
||||||
|
if not m: continue
|
||||||
|
t = m.get("type")
|
||||||
|
if t not in SOURCE_TYPES: continue
|
||||||
|
old = snapshot.get(m["id"])
|
||||||
|
if old is None: continue
|
||||||
|
if t == "wand_axis":
|
||||||
|
geom = obj.Geometry
|
||||||
|
if not isinstance(geom, rg.Curve): continue
|
||||||
|
os = old.get("start"); oe = old.get("end")
|
||||||
|
# Migrate NUR wenn XY tatsaechlich geaendert. Bei reinem
|
||||||
|
# Z-Drag (XY identisch) waere Migrate ein no-op-Loop ueber
|
||||||
|
# alle Oeffnungen mit Replace-Ops je Punkt — spart die
|
||||||
|
# ganze Pass + die nachfolgenden Replace-Events.
|
||||||
|
if os and oe:
|
||||||
|
xy_changed = (
|
||||||
|
abs(geom.PointAtStart.X - os[0]) > 1e-6 or
|
||||||
|
abs(geom.PointAtStart.Y - os[1]) > 1e-6 or
|
||||||
|
abs(geom.PointAtEnd.X - oe[0]) > 1e-6 or
|
||||||
|
abs(geom.PointAtEnd.Y - oe[1]) > 1e-6
|
||||||
|
)
|
||||||
|
if xy_changed:
|
||||||
|
try:
|
||||||
|
old_line = rg.LineCurve(
|
||||||
|
rg.Point3d(os[0], os[1], os[2]),
|
||||||
|
rg.Point3d(oe[0], oe[1], oe[2]))
|
||||||
|
_migrate_openings_to_new_axis(m["id"], old_line, geom)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] post-cmd migrate:", ex)
|
||||||
|
# Z-Drag detect + Brüstungs-Mitnahme (= setzt sticky-delta
|
||||||
|
# den der Idle-Pfad spaeter applied — aber wir koennen
|
||||||
|
# gleich hier syncen)
|
||||||
|
_apply_wand_z_drag_constraint(obj, m)
|
||||||
|
z_entry = sc.sticky.get("_elemente_wand_z_delta")
|
||||||
|
if isinstance(z_entry, tuple) and len(z_entry) == 2 \
|
||||||
|
and z_entry[0] == m["id"]:
|
||||||
|
z_delta = float(z_entry[1])
|
||||||
|
sc.sticky["_elemente_wand_z_delta"] = None
|
||||||
|
# Brüstungen aller Öffnungen mit anpassen
|
||||||
|
if abs(z_delta) >= 1e-6:
|
||||||
|
for op_obj, op_meta in _find_openings_for_wall(doc, m["id"]):
|
||||||
|
cur_b = op_meta.get("oeff_brueest")
|
||||||
|
try:
|
||||||
|
cur_b_val = float(cur_b) if cur_b not in (None, "") else 0.0
|
||||||
|
except (ValueError, TypeError): cur_b_val = 0.0
|
||||||
|
new_b = max(0.0, cur_b_val + z_delta)
|
||||||
|
try:
|
||||||
|
attrs = op_obj.Attributes.Duplicate()
|
||||||
|
attrs.SetUserString(_KEY_OEFF_BRUEST,
|
||||||
|
"{:.6f}".format(new_b))
|
||||||
|
doc.Objects.ModifyAttributes(op_obj.Id, attrs, True)
|
||||||
|
pt_geom = op_obj.Geometry
|
||||||
|
if hasattr(pt_geom, "Location"):
|
||||||
|
pt = pt_geom.Location
|
||||||
|
doc.Objects.Replace(op_obj.Id,
|
||||||
|
rg.Point(rg.Point3d(pt.X, pt.Y, new_b)))
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] post-cmd brueest:", ex)
|
||||||
|
affected_walls.add(m["id"])
|
||||||
|
elif t == "oeffnung_point":
|
||||||
|
op_pos = old.get("pos")
|
||||||
|
if op_pos is None: continue
|
||||||
|
pseudo = _PseudoOld(rg.Point3d(op_pos[0], op_pos[1], op_pos[2]))
|
||||||
|
_apply_oeffnung_constraint(obj, m, pseudo)
|
||||||
|
pid = m.get("oeff_parent")
|
||||||
|
if pid: affected_walls.add(pid)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] post-cmd source:", ex)
|
||||||
|
finally:
|
||||||
|
sc.sticky[_REGEN_BUSY] = _was_busy
|
||||||
|
sc.sticky["_dossier_skip_sync_regen"] = None
|
||||||
|
|
||||||
|
# Sync-Regen aller betroffenen Wände — Move ist sauber abgeschlossen,
|
||||||
|
# kein Konflikt mehr moeglich. EIN Regen pro Wand (nicht pro Oeffnung).
|
||||||
|
# Display bleibt suppressed bis ALLE Wände durch sind → kein „Aufbauen".
|
||||||
|
try:
|
||||||
|
for wid in affected_walls:
|
||||||
|
try: _regenerate_element(doc, wid)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[ELEMENTE] post-cmd regen:", ex)
|
||||||
|
finally:
|
||||||
|
doc.Views.RedrawEnabled = prev_redraw
|
||||||
|
try: doc.Views.Redraw()
|
||||||
|
except Exception: pass
|
||||||
|
b = sc.sticky.get("elemente_bridge")
|
||||||
|
if b is not None:
|
||||||
|
try: b._send_state()
|
||||||
|
except Exception: pass
|
||||||
|
|
||||||
|
|
||||||
def _install_listeners(bridge):
|
def _install_listeners(bridge):
|
||||||
"""Listener-Registrierung mit Re-Reload-Schutz.
|
"""Listener-Registrierung mit Re-Reload-Schutz.
|
||||||
|
|
||||||
@@ -7722,21 +7904,31 @@ def _install_listeners(bridge):
|
|||||||
try:
|
try:
|
||||||
if old_refs.get("idle"): Rhino.RhinoApp.Idle -= old_refs["idle"]
|
if old_refs.get("idle"): Rhino.RhinoApp.Idle -= old_refs["idle"]
|
||||||
except Exception: pass
|
except Exception: pass
|
||||||
|
try:
|
||||||
|
if old_refs.get("cmd_begin"): Rhino.Commands.Command.BeginCommand -= old_refs["cmd_begin"]
|
||||||
|
except Exception: pass
|
||||||
|
try:
|
||||||
|
if old_refs.get("cmd_end"): Rhino.Commands.Command.EndCommand -= old_refs["cmd_end"]
|
||||||
|
except Exception: pass
|
||||||
Rhino.RhinoDoc.ReplaceRhinoObject += _on_object_replaced
|
Rhino.RhinoDoc.ReplaceRhinoObject += _on_object_replaced
|
||||||
Rhino.RhinoDoc.AddRhinoObject += _on_object_added
|
Rhino.RhinoDoc.AddRhinoObject += _on_object_added
|
||||||
Rhino.RhinoDoc.DeleteRhinoObject += _on_object_deleted
|
Rhino.RhinoDoc.DeleteRhinoObject += _on_object_deleted
|
||||||
Rhino.RhinoDoc.SelectObjects += _on_select_objects
|
Rhino.RhinoDoc.SelectObjects += _on_select_objects
|
||||||
Rhino.RhinoDoc.DeselectObjects += _on_deselect_objects
|
Rhino.RhinoDoc.DeselectObjects += _on_deselect_objects
|
||||||
Rhino.RhinoApp.Idle += _on_idle_selection
|
Rhino.RhinoApp.Idle += _on_idle_selection
|
||||||
|
Rhino.Commands.Command.BeginCommand += _on_command_begin
|
||||||
|
Rhino.Commands.Command.EndCommand += _on_command_end
|
||||||
sc.sticky[refs_key] = {
|
sc.sticky[refs_key] = {
|
||||||
"replace": _on_object_replaced,
|
"replace": _on_object_replaced,
|
||||||
"add": _on_object_added,
|
"add": _on_object_added,
|
||||||
"delete": _on_object_deleted,
|
"delete": _on_object_deleted,
|
||||||
"select": _on_select_objects,
|
"select": _on_select_objects,
|
||||||
"deselect": _on_deselect_objects,
|
"deselect": _on_deselect_objects,
|
||||||
"idle": _on_idle_selection,
|
"idle": _on_idle_selection,
|
||||||
|
"cmd_begin": _on_command_begin,
|
||||||
|
"cmd_end": _on_command_end,
|
||||||
}
|
}
|
||||||
print("[ELEMENTE] Listener aktiv (Replace + Add + Delete + Select + Idle)")
|
print("[ELEMENTE] Listener aktiv (Replace + Add + Delete + Select + Idle + Cmd)")
|
||||||
|
|
||||||
|
|
||||||
def _bridge_factory():
|
def _bridge_factory():
|
||||||
|
|||||||
+37
-1
@@ -1374,11 +1374,47 @@ def _install_selection_listener(bridge):
|
|||||||
except Exception: pass
|
except Exception: pass
|
||||||
|
|
||||||
def on_replace(sender, args):
|
def on_replace(sender, args):
|
||||||
"""Hatch der zugehoerigen Curve mitziehen wenn Curve veraendert wird."""
|
"""Sync Curve↔Hatch bei Move/Replace:
|
||||||
|
- Curve hat _FILL_KEY (= hatch_id) → Hatch via Hatch.Create neu auf die
|
||||||
|
aktuelle Curve aufsetzen (existierender Pfad).
|
||||||
|
- Hatch hat _FILL_OWNER_KEY (= curve_id) → Curve um den gleichen
|
||||||
|
Vektor mit-translaten (User hat Hatch alleine verschoben).
|
||||||
|
"""
|
||||||
new_obj = args.NewRhinoObject
|
new_obj = args.NewRhinoObject
|
||||||
if new_obj is None or new_obj.Id in _processing:
|
if new_obj is None or new_obj.Id in _processing:
|
||||||
return
|
return
|
||||||
a = new_obj.Attributes
|
a = new_obj.Attributes
|
||||||
|
# Reverse-Direction: Hatch verschoben/rotiert/skaliert → Curve mitnehmen.
|
||||||
|
# Wir nehmen die Outer-Boundary direkt aus der (bereits transformed)
|
||||||
|
# Hatch — funktioniert fuer Move, Rotate, Scale, beliebige Transforms.
|
||||||
|
if isinstance(new_obj.Geometry, rg.Hatch):
|
||||||
|
owner_id_str = a.GetUserString(_FILL_OWNER_KEY)
|
||||||
|
if not owner_id_str:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
owner_id = System.Guid(owner_id_str)
|
||||||
|
except Exception:
|
||||||
|
return
|
||||||
|
doc2 = Rhino.RhinoDoc.ActiveDoc
|
||||||
|
owner_obj = doc2.Objects.FindId(owner_id)
|
||||||
|
if owner_obj is None or owner_obj.IsDeleted:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
new_curves = new_obj.Geometry.Get3dCurves(True)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[GESTALTUNG] hatch.Get3dCurves:", ex)
|
||||||
|
return
|
||||||
|
if not new_curves or len(new_curves) == 0:
|
||||||
|
return
|
||||||
|
new_curve = new_curves[0]
|
||||||
|
_processing.add(owner_id)
|
||||||
|
try:
|
||||||
|
doc2.Objects.Replace(owner_id, new_curve)
|
||||||
|
except Exception as ex:
|
||||||
|
print("[GESTALTUNG] hatch→curve replace:", ex)
|
||||||
|
finally:
|
||||||
|
_processing.discard(owner_id)
|
||||||
|
return
|
||||||
hatch_id_str = a.GetUserString(_FILL_KEY)
|
hatch_id_str = a.GetUserString(_FILL_KEY)
|
||||||
if not hatch_id_str:
|
if not hatch_id_str:
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user