|
|
|
|
@ -588,11 +588,7 @@ def _create_carrier_geometry(doc, name, points):
|
|
|
|
|
return obj
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_route_carrier(doc, points, label="", project_uuid="", kind=ROUTE_CARRIER_KIND, capacity=1):
|
|
|
|
|
"""Create a routable carrier from ordered 3D points."""
|
|
|
|
|
if doc is None:
|
|
|
|
|
raise RoutingNetworkError("No FreeCAD document is available.")
|
|
|
|
|
|
|
|
|
|
def _normalized_route_points(points):
|
|
|
|
|
normalized = []
|
|
|
|
|
for point in points or []:
|
|
|
|
|
vector = _vector(point)
|
|
|
|
|
@ -600,19 +596,47 @@ def create_route_carrier(doc, points, label="", project_uuid="", kind=ROUTE_CARR
|
|
|
|
|
continue
|
|
|
|
|
if not normalized or _distance(normalized[-1], vector) > DEFAULT_NODE_TOLERANCE:
|
|
|
|
|
normalized.append(vector)
|
|
|
|
|
return normalized
|
|
|
|
|
|
|
|
|
|
if len(normalized) < 2:
|
|
|
|
|
raise RoutingNetworkError("A route carrier requires at least two distinct points.")
|
|
|
|
|
|
|
|
|
|
name = _unique_name(doc, "QETRouteCarrier")
|
|
|
|
|
carrier = _create_carrier_geometry(doc, name, normalized)
|
|
|
|
|
carrier.Label = label or "QET Route Carrier"
|
|
|
|
|
def _set_route_carrier_points(carrier, points):
|
|
|
|
|
_ensure_vector_list_property(
|
|
|
|
|
carrier,
|
|
|
|
|
"Points",
|
|
|
|
|
"Ordered centerline points used by the 3D router",
|
|
|
|
|
)
|
|
|
|
|
carrier.Points = list(normalized)
|
|
|
|
|
carrier.Points = list(points)
|
|
|
|
|
try:
|
|
|
|
|
import Part
|
|
|
|
|
|
|
|
|
|
carrier.Shape = Part.makePolygon(points)
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _update_route_carrier(carrier, points, project_uuid="", kind=ROUTE_CARRIER_KIND, capacity=1):
|
|
|
|
|
normalized = _normalized_route_points(points)
|
|
|
|
|
if len(normalized) < 2:
|
|
|
|
|
return False
|
|
|
|
|
_set_route_carrier_points(carrier, normalized)
|
|
|
|
|
_set_route_carrier_semantics(carrier, project_uuid=project_uuid, kind=kind, capacity=capacity)
|
|
|
|
|
_style_route_carrier(carrier, kind)
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_route_carrier(doc, points, label="", project_uuid="", kind=ROUTE_CARRIER_KIND, capacity=1):
|
|
|
|
|
"""Create a routable carrier from ordered 3D points."""
|
|
|
|
|
if doc is None:
|
|
|
|
|
raise RoutingNetworkError("No FreeCAD document is available.")
|
|
|
|
|
|
|
|
|
|
normalized = _normalized_route_points(points)
|
|
|
|
|
if len(normalized) < 2:
|
|
|
|
|
raise RoutingNetworkError("A route carrier requires at least two distinct points.")
|
|
|
|
|
|
|
|
|
|
name = _unique_name(doc, "QETRouteCarrier")
|
|
|
|
|
carrier = _create_carrier_geometry(doc, name, normalized)
|
|
|
|
|
carrier.Label = label or "QET Route Carrier"
|
|
|
|
|
_set_route_carrier_points(carrier, normalized)
|
|
|
|
|
_set_route_carrier_semantics(carrier, project_uuid=project_uuid, kind=kind, capacity=capacity)
|
|
|
|
|
|
|
|
|
|
group = WiringObjects.ensure_carrier_group(doc, project_uuid)
|
|
|
|
|
@ -1396,6 +1420,40 @@ def _wire_duct_centerline_from_bbox(bbox, margin=DEFAULT_WIRE_DUCT_MARGIN, min_a
|
|
|
|
|
).get("centerline", [])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _sync_wire_duct_source_carriers(doc, source, spec, project_uuid="", capacity=1):
|
|
|
|
|
carriers = _live_source_carriers(doc, source)
|
|
|
|
|
if not carriers:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
desired = [
|
|
|
|
|
(spec.get("centerline", []), ROUTE_CARRIER_KIND_WIRE_DUCT),
|
|
|
|
|
]
|
|
|
|
|
desired.extend(
|
|
|
|
|
(points, ROUTE_CARRIER_KIND_WIRE_DUCT_OPEN_END)
|
|
|
|
|
for points in (spec.get("open_ends", []) or [])
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
updated = []
|
|
|
|
|
for carrier, desired_item in zip(carriers, desired):
|
|
|
|
|
points, kind = desired_item
|
|
|
|
|
if _update_route_carrier(
|
|
|
|
|
carrier,
|
|
|
|
|
points,
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
kind=kind,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
):
|
|
|
|
|
updated.append(carrier)
|
|
|
|
|
|
|
|
|
|
if updated:
|
|
|
|
|
_mark_wire_duct_source(source, updated[0], updated)
|
|
|
|
|
try:
|
|
|
|
|
doc.recompute()
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _wiring_cut_out_points_from_bbox(bbox):
|
|
|
|
|
extents = _bbox_extents(bbox)
|
|
|
|
|
if not extents:
|
|
|
|
|
@ -1436,7 +1494,57 @@ def _wire_duct_sources_from_selection(selection_ex):
|
|
|
|
|
return sources
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _mark_wire_duct_source(source, carrier):
|
|
|
|
|
def _route_source_carrier_names(source):
|
|
|
|
|
names = []
|
|
|
|
|
try:
|
|
|
|
|
raw = (getattr(source, "QetRouteCarrierNamesJson", "") or "").strip()
|
|
|
|
|
if raw:
|
|
|
|
|
parsed = json.loads(raw)
|
|
|
|
|
if isinstance(parsed, list):
|
|
|
|
|
names.extend(str(item).strip() for item in parsed if str(item).strip())
|
|
|
|
|
except Exception:
|
|
|
|
|
names = []
|
|
|
|
|
carrier_name = (getattr(source, "QetRouteCarrierName", "") or "").strip()
|
|
|
|
|
if carrier_name:
|
|
|
|
|
names.insert(0, carrier_name)
|
|
|
|
|
result = []
|
|
|
|
|
seen = set()
|
|
|
|
|
for name in names:
|
|
|
|
|
if name in seen:
|
|
|
|
|
continue
|
|
|
|
|
seen.add(name)
|
|
|
|
|
result.append(name)
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _live_source_carriers(doc, source):
|
|
|
|
|
if doc is None or source is None:
|
|
|
|
|
return []
|
|
|
|
|
carriers = []
|
|
|
|
|
for carrier_name in _route_source_carrier_names(source):
|
|
|
|
|
carrier = doc.getObject(carrier_name)
|
|
|
|
|
if carrier is not None and is_route_carrier(carrier):
|
|
|
|
|
carriers.append(carrier)
|
|
|
|
|
return carriers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _remember_source_carriers(source, carriers):
|
|
|
|
|
live_names = [
|
|
|
|
|
getattr(carrier, "Name", "")
|
|
|
|
|
for carrier in (carriers or [])
|
|
|
|
|
if carrier is not None and getattr(carrier, "Name", "")
|
|
|
|
|
]
|
|
|
|
|
if live_names:
|
|
|
|
|
TerminalObjects.ensure_string_property(
|
|
|
|
|
source,
|
|
|
|
|
"QetRouteCarrierNamesJson",
|
|
|
|
|
PROPERTY_GROUP,
|
|
|
|
|
"Generated route carriers for this source",
|
|
|
|
|
json.dumps(live_names, ensure_ascii=False),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _mark_wire_duct_source(source, carrier, carriers=None):
|
|
|
|
|
if source is None:
|
|
|
|
|
return
|
|
|
|
|
try:
|
|
|
|
|
@ -1449,6 +1557,7 @@ def _mark_wire_duct_source(source, carrier):
|
|
|
|
|
"Generated route carrier for this source",
|
|
|
|
|
getattr(carrier, "Name", ""),
|
|
|
|
|
)
|
|
|
|
|
_remember_source_carriers(source, carriers or ([carrier] if carrier is not None else []))
|
|
|
|
|
except Exception:
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
@ -1508,13 +1617,8 @@ def _mark_terminal_access_source(source, carrier):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _live_source_carrier(doc, source):
|
|
|
|
|
carrier_name = (getattr(source, "QetRouteCarrierName", "") or "").strip()
|
|
|
|
|
if not carrier_name or doc is None:
|
|
|
|
|
return None
|
|
|
|
|
carrier = doc.getObject(carrier_name)
|
|
|
|
|
if carrier is not None and is_route_carrier(carrier):
|
|
|
|
|
return carrier
|
|
|
|
|
return None
|
|
|
|
|
carriers = _live_source_carriers(doc, source)
|
|
|
|
|
return carriers[0] if carriers else None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def detect_wire_duct_sources(doc, min_aspect=DEFAULT_AUTO_WIRE_DUCT_MIN_ASPECT):
|
|
|
|
|
@ -1610,8 +1714,6 @@ def create_wire_duct_carriers_from_document(
|
|
|
|
|
"""Auto-detect wire duct objects in the document and create WireDuct centerlines."""
|
|
|
|
|
created = []
|
|
|
|
|
for index, source in enumerate(detect_wire_duct_sources(doc, min_aspect=min_aspect), start=1):
|
|
|
|
|
if _live_source_carrier(doc, source) is not None:
|
|
|
|
|
continue
|
|
|
|
|
bbox = _bound_box_from_object(source)
|
|
|
|
|
if bbox is None:
|
|
|
|
|
continue
|
|
|
|
|
@ -1625,6 +1727,14 @@ def create_wire_duct_carriers_from_document(
|
|
|
|
|
continue
|
|
|
|
|
label = getattr(source, "Label", "") or getattr(source, "Name", "") or "Wire Duct"
|
|
|
|
|
capacity = _route_carrier_capacity_value(source, default=1)
|
|
|
|
|
if _sync_wire_duct_source_carriers(
|
|
|
|
|
doc,
|
|
|
|
|
source,
|
|
|
|
|
spec,
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
):
|
|
|
|
|
continue
|
|
|
|
|
carrier = create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
points,
|
|
|
|
|
@ -1633,21 +1743,22 @@ def create_wire_duct_carriers_from_document(
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
_mark_wire_duct_source(source, carrier)
|
|
|
|
|
source_created = [carrier]
|
|
|
|
|
created.append(carrier)
|
|
|
|
|
for end_index, open_end_points in enumerate(spec.get("open_ends", []) or [], start=1):
|
|
|
|
|
if len(open_end_points) < 2:
|
|
|
|
|
continue
|
|
|
|
|
created.append(
|
|
|
|
|
create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
open_end_points,
|
|
|
|
|
label="QET Auto Wire Duct Open End {0} {1}".format(label, end_index),
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT_OPEN_END,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
open_end_carrier = create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
open_end_points,
|
|
|
|
|
label="QET Auto Wire Duct Open End {0} {1}".format(label, end_index),
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT_OPEN_END,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
source_created.append(open_end_carrier)
|
|
|
|
|
created.append(open_end_carrier)
|
|
|
|
|
_mark_wire_duct_source(source, carrier, source_created)
|
|
|
|
|
return created
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1916,8 +2027,6 @@ def create_wire_duct_carriers_from_selection(
|
|
|
|
|
"""Create WireDuct centerline carriers from selected duct-like solids."""
|
|
|
|
|
created = []
|
|
|
|
|
for index, source in enumerate(_wire_duct_sources_from_selection(selection_ex), start=1):
|
|
|
|
|
if _live_source_carrier(doc, source) is not None:
|
|
|
|
|
continue
|
|
|
|
|
bbox = _bound_box_from_object(source)
|
|
|
|
|
if bbox is None:
|
|
|
|
|
continue
|
|
|
|
|
@ -1931,6 +2040,14 @@ def create_wire_duct_carriers_from_selection(
|
|
|
|
|
continue
|
|
|
|
|
label = getattr(source, "Label", "") or getattr(source, "Name", "") or "Wire Duct"
|
|
|
|
|
capacity = _route_carrier_capacity_value(source, default=1)
|
|
|
|
|
if _sync_wire_duct_source_carriers(
|
|
|
|
|
doc,
|
|
|
|
|
source,
|
|
|
|
|
spec,
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
):
|
|
|
|
|
continue
|
|
|
|
|
carrier = create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
points,
|
|
|
|
|
@ -1939,21 +2056,22 @@ def create_wire_duct_carriers_from_selection(
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
_mark_wire_duct_source(source, carrier)
|
|
|
|
|
source_created = [carrier]
|
|
|
|
|
created.append(carrier)
|
|
|
|
|
for end_index, open_end_points in enumerate(spec.get("open_ends", []) or [], start=1):
|
|
|
|
|
if len(open_end_points) < 2:
|
|
|
|
|
continue
|
|
|
|
|
created.append(
|
|
|
|
|
create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
open_end_points,
|
|
|
|
|
label="QET Wire Duct Open End {0} {1}".format(label, end_index),
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT_OPEN_END,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
open_end_carrier = create_route_carrier(
|
|
|
|
|
doc,
|
|
|
|
|
open_end_points,
|
|
|
|
|
label="QET Wire Duct Open End {0} {1}".format(label, end_index),
|
|
|
|
|
project_uuid=project_uuid,
|
|
|
|
|
kind=ROUTE_CARRIER_KIND_WIRE_DUCT_OPEN_END,
|
|
|
|
|
capacity=capacity,
|
|
|
|
|
)
|
|
|
|
|
source_created.append(open_end_carrier)
|
|
|
|
|
created.append(open_end_carrier)
|
|
|
|
|
_mark_wire_duct_source(source, carrier, source_created)
|
|
|
|
|
return created
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|