feat: 简化自动布线共线冗余点

dev
Zhaowenlong 3 weeks ago
parent 088302771a
commit 967ad4b7f1

@ -206,6 +206,79 @@ def _append_orthogonal(points, target_point, preferred_axis=None):
_append_unique(points, point)
def _collinear_points(first, middle, last):
ax = float(middle.x) - float(first.x)
ay = float(middle.y) - float(first.y)
az = float(middle.z) - float(first.z)
bx = float(last.x) - float(middle.x)
by = float(last.y) - float(middle.y)
bz = float(last.z) - float(middle.z)
cross_x = ay * bz - az * by
cross_y = az * bx - ax * bz
cross_z = ax * by - ay * bx
dot = ax * bx + ay * by + az * bz
return (
abs(cross_x) <= 0.000001
and abs(cross_y) <= 0.000001
and abs(cross_z) <= 0.000001
and dot >= -0.000001
)
def _route_point_key(point, tolerance=0.001):
scale = 1.0 / float(tolerance or 0.001)
return (
int(round(float(point.x) * scale)),
int(round(float(point.y) * scale)),
int(round(float(point.z) * scale)),
)
def _simplify_collinear_points(points, preserved_point_keys=None):
normalized = [_vector(point) for point in points or [] if _is_finite_point(_vector(point))]
if len(normalized) <= 2:
return normalized
preserved_indices = {0, 1, len(normalized) - 2, len(normalized) - 1}
preserved_point_keys = set(preserved_point_keys or [])
simplified = [normalized[0]]
simplified_indices = [0]
for index, point in enumerate(normalized[1:], start=1):
_append_unique(simplified, point)
if len(simplified_indices) < len(simplified):
simplified_indices.append(index)
while len(simplified) >= 3 and _collinear_points(
simplified[-3],
simplified[-2],
simplified[-1],
):
if (
simplified_indices[-2] in preserved_indices
or _route_point_key(simplified[-2]) in preserved_point_keys
):
break
simplified.pop(-2)
simplified_indices.pop(-2)
return simplified
def _important_route_node_keys(network, path_keys, path_result):
edges = network.get("edges", {}) if isinstance(network, dict) else {}
important = {
key
for key in path_keys or []
if len(edges.get(key, []) or []) != 2
}
segments = path_result.get("segments", []) if isinstance(path_result, dict) else []
for index in range(1, len(path_keys or []) - 1):
previous_segment = segments[index - 1] if index - 1 < len(segments) else {}
next_segment = segments[index] if index < len(segments) else {}
previous_carrier = (previous_segment.get("carrier") or {}).get("name", "")
next_carrier = (next_segment.get("carrier") or {}).get("name", "")
if previous_carrier != next_carrier:
important.add(path_keys[index])
return important
def _offset(point, direction, distance):
return App.Vector(
float(point.x) + float(direction.x) * float(distance),
@ -716,6 +789,10 @@ def build_network_route(start_terminal, end_terminal, route_index=0, options=Non
_append_unique(points, point)
_append_orthogonal(points, end_exit)
_append_unique(points, end_origin)
points = _simplify_collinear_points(
points,
preserved_point_keys=_important_route_node_keys(network, path_keys, path_result),
)
return {
"algorithm": "network-dijkstra-v1",

@ -332,6 +332,39 @@ class AutoRoutingTest(unittest.TestCase):
self.assertEqual(2, third_payload["lane"]["index"])
self.assertEqual(-12.0, third_payload["lane"]["offset_mm"])
def test_network_eplan_connection_route_removes_collinear_network_points(self):
_install_fake_freecad()
terminal_objects, _wiring_objects, routing_network, auto_routing = _reload_modules()
app = sys.modules["FreeCAD"]
doc = FakeDocument()
terminal_objects.ensure_root_group(doc, "project-1")
start = _terminal(doc, terminal_objects, "TerminalStart", "terminal-start", app.Vector(0, 0, 0))
end = _terminal(doc, terminal_objects, "TerminalEnd", "terminal-end", app.Vector(100, 0, 0))
routing_network.create_route_carrier(
doc,
[
app.Vector(0, 0, 20),
app.Vector(50, 0, 20),
app.Vector(100, 0, 20),
],
project_uuid="project-1",
kind="WireDuct",
)
result = auto_routing.route_eplan_connection_between_terminals(doc, start, end)
point_tuples = [(point.x, point.y, point.z) for point in result["points"]]
self.assertNotIn((50.0, 0.0, 20.0), point_tuples)
self.assertEqual(
[
(0.0, 0.0, 0.0),
(0.0, 0.0, 20.0),
(100.0, 0.0, 20.0),
(100.0, 0.0, 0.0),
],
point_tuples,
)
def test_eplan_connection_route_replaces_existing_wire_uuid_when_endpoints_change(self):
_install_fake_freecad()
terminal_objects, wiring_objects, routing_network, auto_routing = _reload_modules()

Loading…
Cancel
Save