- tech draw draft v2
This commit is contained in:
@@ -243,7 +243,16 @@ class DrawingCanvas(QWidget):
|
||||
t = self._render_result.view_transforms.get(view_id)
|
||||
if t is None or t[0] <= 0:
|
||||
return None
|
||||
scale, ox, oy = t
|
||||
scale, ox, oy = t[0], t[1], t[2]
|
||||
if len(t) >= 6:
|
||||
# sheet(p) = s·R(θ)(p − c) + o → invert around the centre.
|
||||
th = math.radians(t[3])
|
||||
cos_t, sin_t = math.cos(th), math.sin(th)
|
||||
sx, sy = (pt[0] - ox) / scale, (pt[1] - oy) / scale
|
||||
return (
|
||||
sx * cos_t + sy * sin_t + t[4],
|
||||
-sx * sin_t + sy * cos_t + t[5],
|
||||
)
|
||||
return ((pt[0] - ox) / scale, (pt[1] - oy) / scale)
|
||||
|
||||
def _pick_feature(self, pos: QPointF) -> Optional[dict]:
|
||||
@@ -283,12 +292,19 @@ class DrawingCanvas(QWidget):
|
||||
"p2": m2,
|
||||
}
|
||||
elif prim.kind == "circle" and prim.center and prim.radius:
|
||||
d = abs(
|
||||
math.hypot(
|
||||
pt[0] - prim.center[0], pt[1] - prim.center[1]
|
||||
)
|
||||
- prim.radius
|
||||
)
|
||||
dc = math.hypot(pt[0] - prim.center[0], pt[1] - prim.center[1])
|
||||
if dc <= tol:
|
||||
# Hit the circle's centre mark: a point feature — circle
|
||||
# centres are first-class dimension references.
|
||||
mc = self._sheet_to_model(prim.view_id, prim.center)
|
||||
if mc:
|
||||
return {
|
||||
"kind": "point",
|
||||
"view_id": prim.view_id,
|
||||
"point": mc,
|
||||
"radius": prim.radius / scale,
|
||||
}
|
||||
d = abs(dc - prim.radius)
|
||||
if d < best_d:
|
||||
best_d = d
|
||||
mc = self._sheet_to_model(prim.view_id, prim.center)
|
||||
@@ -506,19 +522,11 @@ class TechnicalDrawingWidget(QWidget):
|
||||
(
|
||||
"distance",
|
||||
"Distance",
|
||||
"Pick two edges — the dimension line is placed "
|
||||
"perpendicular to them, measuring the distance between",
|
||||
),
|
||||
(
|
||||
"diameter",
|
||||
"Diameter",
|
||||
"Pick a circle — its diameter is added",
|
||||
),
|
||||
(
|
||||
"angle",
|
||||
"Angle",
|
||||
"Pick two edges — the angle between them is added",
|
||||
"Pick two edges, an edge and a circle centre, or two "
|
||||
"circle centres — measures the distance between them",
|
||||
),
|
||||
("diameter", "Diameter", "Pick a circle — its diameter is added"),
|
||||
("angle", "Angle", "Pick two edges — the angle between them is added"),
|
||||
):
|
||||
btn = QPushButton(label)
|
||||
btn.setCheckable(True)
|
||||
@@ -818,7 +826,7 @@ class TechnicalDrawingWidget(QWidget):
|
||||
self._first_pick = None
|
||||
self._canvas.set_pick_mode(tool)
|
||||
prompts = {
|
||||
"distance": "Distance: click the first edge",
|
||||
"distance": "Distance: click an edge or a circle centre",
|
||||
"diameter": "Diameter: click a circle",
|
||||
"angle": "Angle: click the first edge",
|
||||
}
|
||||
@@ -883,6 +891,40 @@ class TechnicalDrawingWidget(QWidget):
|
||||
elif mode in ("distance", "angle"):
|
||||
self._on_edge_pick(info, mode)
|
||||
|
||||
@staticmethod
|
||||
def _pick_point(info: dict) -> Optional[Tuple[float, float]]:
|
||||
"""The measurable point of a pick: a point pick is its point, a
|
||||
circle pick counts as its centre; a bare segment is None."""
|
||||
if info.get("kind") == "point":
|
||||
return info["point"]
|
||||
if info.get("kind") == "circle":
|
||||
return info["center"]
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _distance_anchors(
|
||||
cls, first: dict, second: dict
|
||||
) -> Tuple[Tuple[float, float], Tuple[float, float]]:
|
||||
"""Anchor pair for a distance between two picks (model coords).
|
||||
|
||||
A pick may be a segment (edge), a circle (measured at its centre)
|
||||
or a point (a picked circle centre). Point/segment mixes use the
|
||||
closest point on the segment so the dimension lands perpendicular
|
||||
to the edge, ISO style.
|
||||
"""
|
||||
fp = cls._pick_point(first)
|
||||
sp = cls._pick_point(second)
|
||||
if fp is not None and sp is not None:
|
||||
return fp, sp
|
||||
if fp is not None:
|
||||
return fp, _closest_point_on_segment(fp, second["p1"], second["p2"])
|
||||
if sp is not None:
|
||||
return sp, _closest_point_on_segment(sp, first["p1"], first["p2"])
|
||||
q1, q2, _d = _closest_points_on_segments(
|
||||
first["p1"], first["p2"], second["p1"], second["p2"]
|
||||
)
|
||||
return q1, q2
|
||||
|
||||
def _on_edge_pick(self, info: dict, mode: str) -> None:
|
||||
if (
|
||||
self._first_pick is None
|
||||
@@ -891,16 +933,16 @@ class TechnicalDrawingWidget(QWidget):
|
||||
# First edge (or picked in a different view: restart there).
|
||||
self._first_pick = info
|
||||
self._status_label.setText(
|
||||
"Select the second edge in the same view (Esc cancels)"
|
||||
"Select the second feature in the same view (Esc cancels)"
|
||||
)
|
||||
return
|
||||
|
||||
a1, a2 = self._first_pick["p1"], self._first_pick["p2"]
|
||||
b1, b2 = info["p1"], info["p2"]
|
||||
first = self._first_pick
|
||||
view_id = info["view_id"]
|
||||
|
||||
if mode == "distance":
|
||||
q1, q2, dist = _closest_points_on_segments(a1, a2, b1, b2)
|
||||
p1, p2 = self._distance_anchors(first, info)
|
||||
dist = math.hypot(p2[0] - p1[0], p2[1] - p1[1])
|
||||
if dist < 0.01:
|
||||
self._status_label.setText(
|
||||
"The two edges coincide — no distance to measure"
|
||||
@@ -908,12 +950,20 @@ class TechnicalDrawingWidget(QWidget):
|
||||
return
|
||||
self._add_manual_dimension(
|
||||
"length",
|
||||
anchors=(q1, q2),
|
||||
anchors=(p1, p2),
|
||||
view_id=view_id,
|
||||
direction=((q2[0] - q1[0]) / dist, (q2[1] - q1[1]) / dist),
|
||||
direction=((p2[0] - p1[0]) / dist, (p2[1] - p1[1]) / dist),
|
||||
done_msg=f"Distance dimension added: {dist:.2f}",
|
||||
)
|
||||
else: # angle
|
||||
if first.get("kind") != "segment" or info.get("kind") != "segment":
|
||||
self._status_label.setText(
|
||||
"Angle needs two edges — cancel and pick edge lines"
|
||||
)
|
||||
self._first_pick = None
|
||||
return
|
||||
a1, a2 = first["p1"], first["p2"]
|
||||
b1, b2 = info["p1"], info["p2"]
|
||||
vertex = _line_intersection(a1, a2, b1, b2)
|
||||
if vertex is None:
|
||||
self._status_label.setText(
|
||||
@@ -937,6 +987,17 @@ class TechnicalDrawingWidget(QWidget):
|
||||
)
|
||||
|
||||
def _on_diameter_pick(self, info: dict) -> None:
|
||||
if info.get("kind") == "point" and info.get("radius"):
|
||||
# Picked the centre mark of a circle.
|
||||
cx, cy = info["point"]
|
||||
r = info["radius"]
|
||||
self._add_manual_dimension(
|
||||
"diameter",
|
||||
anchors=((cx - r, cy), (cx + r, cy)),
|
||||
view_id=info["view_id"],
|
||||
done_msg=f"Diameter dimension added: Ø{2 * r:.2f}",
|
||||
)
|
||||
return
|
||||
if info.get("kind") != "circle":
|
||||
return
|
||||
cx, cy = info["center"]
|
||||
|
||||
Reference in New Issue
Block a user