From a205ee549f84e3692191dbf63365d7371200d315 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Tue, 29 Aug 2023 16:25:42 +0200 Subject: [PATCH 1/2] Draft: Fix Draft_Mirror preview --- src/Mod/Draft/DraftGeomUtils.py | 1 + src/Mod/Draft/draftgeoutils/geometry.py | 28 +++++++++++++++++ src/Mod/Draft/draftguitools/gui_mirror.py | 35 ++++++++------------- src/Mod/Draft/draftguitools/gui_trackers.py | 28 +++++++++++++++-- 4 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 725f8e7c11..4df2d73818 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -78,6 +78,7 @@ from draftgeoutils.geometry import (findPerpendicular, mirror, are_coplanar, is_straight_line, + mirror_matrix, uv_vectors_from_face, placement_from_face, placement_from_points) diff --git a/src/Mod/Draft/draftgeoutils/geometry.py b/src/Mod/Draft/draftgeoutils/geometry.py index 4b5fd89d2d..f25f1eb62e 100644 --- a/src/Mod/Draft/draftgeoutils/geometry.py +++ b/src/Mod/Draft/draftgeoutils/geometry.py @@ -517,6 +517,32 @@ def mirror(point, edge): return None +def mirror_matrix(mtx, pos, nor): + """Return a mirrored copy of a matrix. + + Parameters + ---------- + mtx: Base::Matrix + Matrix. + pos: Base::Vector3 + Point on mirror plane. + nor: Base::Vector3 + Normal of mirror plane. + + Return + ------ + Base::Matrix + """ + # Code by Jolbas: + # https://forum.freecad.org/viewtopic.php?p=702793#p702793 + mtx_copy = App.Matrix(mtx) + mtx_copy.move(-pos) + mtx_copy.scale(-1) + mtx_copy = App.Rotation(nor, 180) * mtx_copy + mtx_copy.move(pos) + return mtx_copy + + def uv_vectors_from_face(face, vec_z=App.Vector(0, 0, 1), tol=-1): """Return the u and v vectors of a planar face. @@ -525,6 +551,8 @@ def uv_vectors_from_face(face, vec_z=App.Vector(0, 0, 1), tol=-1): If the u vector matches +/-vec_z, or the v vector matches -vec_z, the vectors are rotated to ensure the v vector matches +vec_z. + Parameters + ---------- face: Part.Face Face. vec_z: Base::Vector3, optional diff --git a/src/Mod/Draft/draftguitools/gui_mirror.py b/src/Mod/Draft/draftguitools/gui_mirror.py index f0f7f01dec..5c6744cd26 100644 --- a/src/Mod/Draft/draftguitools/gui_mirror.py +++ b/src/Mod/Draft/draftguitools/gui_mirror.py @@ -40,9 +40,11 @@ from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD as App import FreeCADGui as Gui import Draft_rc +import DraftGeomUtils import DraftVecUtils import WorkingPlane import draftguitools.gui_base_original as gui_base_original +import draftguitools.gui_trackers as trackers import draftguitools.gui_tool_utils as gui_tool_utils from draftutils.messages import _msg @@ -86,8 +88,7 @@ class Mirror(gui_base_original.Modifier): self.ui.pointUi(title=translate("draft", self.featureName), icon="Draft_Mirror") self.ui.xValue.setFocus() self.ui.xValue.selectAll() - # self.ghost = trackers.ghostTracker(self.sel) - # TODO: solve this (see below) + self.ghost = trackers.ghostTracker(self.sel, mirror=True) self.call = self.view.addEventCallback("SoEvent", self.action) _msg(translate("draft", "Pick start point of mirror line")) self.ui.isCopy.hide() @@ -133,29 +134,19 @@ class Mirror(gui_base_original.Modifier): if arg["Key"] == "ESCAPE": self.finish() elif arg["Type"] == "SoLocation2Event": # mouse movement detection - (self.point, - ctrlPoint, info) = gui_tool_utils.getPoint(self, arg) + self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg) if len(self.node) > 0: last = self.node[-1] if self.ghost: - if self.point != last: - # TODO: the following doesn't work at the moment - mu = self.point.sub(last).normalize() - # This part used to test for the GUI to obtain - # the camera view but this is unnecessary - # as this command is always launched in the GUI. - _view = Gui.ActiveDocument.ActiveView - mv = _view.getViewDirection().negative() - mw = mv.cross(mu) - _plane = WorkingPlane.plane(u=mu, v=mv, w=mw, - pos=last) - tm = _plane.getPlacement().toMatrix() - m = self.ghost.getMatrix() - m = m.multiply(tm.inverse()) - m.scale(App.Vector(1, 1, -1)) - m = m.multiply(tm) - m.scale(App.Vector(-1, 1, 1)) - self.ghost.setMatrix(m) + tol = 1e-7 + if self.point.sub(last).Length > tol: + # The normal of the mirror plane must be same as in mirror.py. + nor = self.point.sub(last).cross(self.wp.axis) + if nor.Length > tol: + nor.normalize() + mtx = DraftGeomUtils.mirror_matrix(App.Matrix(), last, nor) + self.ghost.setMatrix(mtx) # Ignores the position of the matrix. + self.ghost.move(App.Vector(mtx.col(3)[:3])) if self.extendedCopy: if not gui_tool_utils.hasMod(arg, gui_tool_utils.MODALT): self.finish() diff --git a/src/Mod/Draft/draftguitools/gui_trackers.py b/src/Mod/Draft/draftguitools/gui_trackers.py index 45d6609a89..9cf06de6f9 100644 --- a/src/Mod/Draft/draftguitools/gui_trackers.py +++ b/src/Mod/Draft/draftguitools/gui_trackers.py @@ -666,7 +666,7 @@ class ghostTracker(Tracker): You can pass it an object or a list of objects, or a shape. """ - def __init__(self, sel, dotted=False, scolor=None, swidth=None): + def __init__(self, sel, dotted=False, scolor=None, swidth=None, mirror=False): self.trans = coin.SoTransform() self.trans.translation.setValue([0, 0, 0]) self.children = [self.trans] @@ -691,6 +691,8 @@ class ghostTracker(Tracker): selnode.addChild(self.marker) node.addChild(selnode) rootsep.addChild(node) + if mirror is True: + self._flip(rootsep) self.children.append(rootsep) super().__init__(dotted, scolor, swidth, children=self.children, name="ghostTracker") @@ -776,13 +778,35 @@ class ghostTracker(Tracker): return FreeCAD.Matrix() def setMatrix(self, matrix): - """Set the transformation matrix.""" + """Set the transformation matrix. + + The 4th column of the matrix (the position) is ignored. + """ m = coin.SbMatrix(matrix.A11, matrix.A12, matrix.A13, matrix.A14, matrix.A21, matrix.A22, matrix.A23, matrix.A24, matrix.A31, matrix.A32, matrix.A33, matrix.A34, matrix.A41, matrix.A42, matrix.A43, matrix.A44) self.trans.setMatrix(m) + def _flip(self, root): + """Flip the normals of the coin faces.""" + # Code by wmayer: + # https://forum.freecad.org/viewtopic.php?p=702640#p702640 + search = coin.SoSearchAction() + search.setType(coin.SoIndexedFaceSet.getClassTypeId()) + search.apply(root) + path = search.getPath() + if path: + node = path.getTail() + index = node.coordIndex.getValues() + if len(index) % 4 == 0: + for i in range(0, len(index), 4): + tmp = index[i] + index[i] = index[i+1] + index[i+1] = tmp + + node.coordIndex.setValues(index) + class editTracker(Tracker): """A node edit tracker.""" From ccbc52affdd2e8fe0955d967c6461126984d76e5 Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Tue, 29 Aug 2023 18:59:40 +0200 Subject: [PATCH 2/2] Draft: Remove unused import WorkingPlane --- src/Mod/Draft/draftguitools/gui_mirror.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mod/Draft/draftguitools/gui_mirror.py b/src/Mod/Draft/draftguitools/gui_mirror.py index 5c6744cd26..27dd6da252 100644 --- a/src/Mod/Draft/draftguitools/gui_mirror.py +++ b/src/Mod/Draft/draftguitools/gui_mirror.py @@ -42,7 +42,6 @@ import FreeCADGui as Gui import Draft_rc import DraftGeomUtils import DraftVecUtils -import WorkingPlane import draftguitools.gui_base_original as gui_base_original import draftguitools.gui_trackers as trackers import draftguitools.gui_tool_utils as gui_tool_utils