Draft: Added mirror tool (more graphical version of Part Mirror) - fixes #2215
This commit is contained in:
parent
cf3e69de08
commit
2e91633da9
|
@ -91,7 +91,8 @@ class ArchWorkbench(Workbench):
|
|||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
|
||||
"Draft_Clone"]
|
||||
self.draftextratools = ["Draft_WireToBSpline","Draft_AddPoint","Draft_DelPoint","Draft_ShapeString","Draft_PathArray"]
|
||||
self.draftextratools = ["Draft_WireToBSpline","Draft_AddPoint","Draft_DelPoint","Draft_ShapeString",
|
||||
"Draft_PathArray","Draft_Mirror"]
|
||||
self.draftcontexttools = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup",
|
||||
"Draft_SelectGroup","Draft_SelectPlane",
|
||||
"Draft_ShowSnapBar","Draft_ToggleGrid","Draft_UndoLine",
|
||||
|
|
|
@ -2456,6 +2456,41 @@ def getCloneBase(obj,strict=False):
|
|||
return False
|
||||
return obj
|
||||
|
||||
|
||||
def mirror(objlist,p1,p2):
|
||||
'''mirror(objlist,p1,p2,[clone]): creates a mirrored version of the given object(s)
|
||||
along an axis that passes through the two vectors p1 and p2.'''
|
||||
|
||||
if not objlist:
|
||||
FreeCAD.Console.PrintError(translate("draft","No object given\n"))
|
||||
return
|
||||
if p1 == p2:
|
||||
FreeCAD.Console.PrintError(translate("draft","The two points are coincident\n"))
|
||||
return
|
||||
if not isinstance(objlist,list):
|
||||
objlist = [objlist]
|
||||
|
||||
result = []
|
||||
|
||||
for obj in objlist:
|
||||
mir = FreeCAD.ActiveDocument.addObject("Part::Mirroring","mirror")
|
||||
mir.Label = "Mirror of "+obj.Label
|
||||
mir.Source = obj
|
||||
if gui:
|
||||
norm = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
|
||||
else:
|
||||
norm = FreeCAD.Vector(0,0,1)
|
||||
pnorm = p2.sub(p1).cross(norm).normalize()
|
||||
mir.Base = p1
|
||||
mir.Normal = pnorm
|
||||
formatObject(mir,obj)
|
||||
result.append(mir)
|
||||
|
||||
if len(result) == 1:
|
||||
result = result[0]
|
||||
return result
|
||||
|
||||
|
||||
def heal(objlist=None,delete=True,reparent=True):
|
||||
'''heal([objlist],[delete],[reparent]) - recreates Draft objects that are damaged,
|
||||
for example if created from an earlier version. If delete is True,
|
||||
|
|
|
@ -4409,6 +4409,129 @@ class VisGroup():
|
|||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
|
||||
class Mirror(Modifier):
|
||||
"The Draft_Mirror FreeCAD command definition"
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'Draft_Mirror',
|
||||
'Accel' : "M, I",
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft_Mirror", "Mirror"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft_Mirror", "Mirrors the selected objects along a line defined by two points")}
|
||||
|
||||
def Activated(self):
|
||||
self.name = translate("draft","Mirror").decode("utf8")
|
||||
Modifier.Activated(self,self.name)
|
||||
self.ghost = None
|
||||
if self.ui:
|
||||
if not FreeCADGui.Selection.getSelection():
|
||||
self.ui.selectUi()
|
||||
msg(translate("draft", "Select an object to mirror\n"))
|
||||
self.call = self.view.addEventCallback("SoEvent",selectObject)
|
||||
else:
|
||||
self.proceed()
|
||||
|
||||
def proceed(self):
|
||||
if self.call: self.view.removeEventCallback("SoEvent",self.call)
|
||||
self.sel = FreeCADGui.Selection.getSelection()
|
||||
self.ui.pointUi(self.name)
|
||||
self.ui.modUi()
|
||||
self.ui.xValue.setFocus()
|
||||
self.ui.xValue.selectAll()
|
||||
#self.ghost = ghostTracker(self.sel) TODO: solve this (see below)
|
||||
self.call = self.view.addEventCallback("SoEvent",self.action)
|
||||
msg(translate("draft", "Pick start point of mirror line:\n"))
|
||||
self.ui.isCopy.hide()
|
||||
|
||||
def finish(self,closed=False,cont=False):
|
||||
if self.ghost:
|
||||
self.ghost.finalize()
|
||||
Modifier.finish(self)
|
||||
if cont and self.ui:
|
||||
if self.ui.continueMode:
|
||||
FreeCADGui.Selection.clearSelection()
|
||||
self.Activated()
|
||||
|
||||
def mirror(self,p1,p2,copy=False):
|
||||
"mirroring the real shapes"
|
||||
sel = '['
|
||||
for o in self.sel:
|
||||
if len(sel) > 1:
|
||||
sel += ','
|
||||
sel += 'FreeCAD.ActiveDocument.'+o.Name
|
||||
sel += ']'
|
||||
FreeCADGui.addModule("Draft")
|
||||
self.commit(translate("draft","Mirror"),
|
||||
['Draft.mirror('+sel+','+DraftVecUtils.toString(p1)+','+DraftVecUtils.toString(p2)+')',
|
||||
'FreeCAD.ActiveDocument.recompute()'])
|
||||
|
||||
def action(self,arg):
|
||||
"scene event handler"
|
||||
if arg["Type"] == "SoKeyboardEvent":
|
||||
if arg["Key"] == "ESCAPE":
|
||||
self.finish()
|
||||
elif arg["Type"] == "SoLocation2Event": #mouse movement detection
|
||||
self.point,ctrlPoint,info = 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()
|
||||
if FreeCAD.GuiUp:
|
||||
mv = FreeCADGui.ActiveDocument.ActiveView.getViewDirection().negative()
|
||||
else:
|
||||
mv = FreeCAD.Vector(0,0,1)
|
||||
mw = mv.cross(mu)
|
||||
import WorkingPlane
|
||||
tm = WorkingPlane.plane(u=mu,v=mv,w=mw,pos=last).getPlacement().toMatrix()
|
||||
m = self.ghost.getMatrix()
|
||||
m = m.multiply(tm.inverse())
|
||||
m.scale(FreeCAD.Vector(1,1,-1))
|
||||
m = m.multiply(tm)
|
||||
m.scale(FreeCAD.Vector(-1,1,1))
|
||||
self.ghost.setMatrix(m)
|
||||
if self.extendedCopy:
|
||||
if not hasMod(arg,MODALT): self.finish()
|
||||
elif arg["Type"] == "SoMouseButtonEvent":
|
||||
if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"):
|
||||
if self.point:
|
||||
self.ui.redraw()
|
||||
if (self.node == []):
|
||||
self.node.append(self.point)
|
||||
self.ui.isRelative.show()
|
||||
if self.ghost:
|
||||
self.ghost.on()
|
||||
msg(translate("draft", "Pick end point of mirror line:\n"))
|
||||
if self.planetrack:
|
||||
self.planetrack.set(self.point)
|
||||
else:
|
||||
last = self.node[0]
|
||||
if self.ui.isCopy.isChecked() or hasMod(arg,MODALT):
|
||||
self.mirror(last,self.point,True)
|
||||
else:
|
||||
self.mirror(last,self.point)
|
||||
if hasMod(arg,MODALT):
|
||||
self.extendedCopy = True
|
||||
else:
|
||||
self.finish(cont=True)
|
||||
|
||||
def numericInput(self,numx,numy,numz):
|
||||
"this function gets called by the toolbar when valid x, y, and z have been entered there"
|
||||
self.point = Vector(numx,numy,numz)
|
||||
if not self.node:
|
||||
self.node.append(self.point)
|
||||
if self.ghost:
|
||||
self.ghost.on()
|
||||
msg(translate("draft", "Pick end point of mirror line:\n"))
|
||||
else:
|
||||
last = self.node[-1]
|
||||
if self.ui.isCopy.isChecked():
|
||||
self.mirror(last,self.point,True)
|
||||
else:
|
||||
self.mirror(last,self.point)
|
||||
self.finish()
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Snap tools
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -4620,6 +4743,7 @@ FreeCADGui.addCommand('Draft_Clone',Draft_Clone())
|
|||
FreeCADGui.addCommand('Draft_PathArray',PathArray())
|
||||
FreeCADGui.addCommand('Draft_Heal',Heal())
|
||||
FreeCADGui.addCommand('Draft_VisGroup',VisGroup())
|
||||
FreeCADGui.addCommand('Draft_Mirror',Mirror())
|
||||
|
||||
# context commands
|
||||
FreeCADGui.addCommand('Draft_FinishLine',FinishLine())
|
||||
|
|
|
@ -611,6 +611,27 @@ class ghostTracker(Tracker):
|
|||
except:
|
||||
print("Error retrieving coin node")
|
||||
return sep
|
||||
|
||||
def getMatrix(self):
|
||||
r = FreeCADGui.ActiveDocument.ActiveView.getViewer().getSoRenderManager().getViewportRegion()
|
||||
v = coin.SoGetMatrixAction(r)
|
||||
m = self.trans.getMatrix(v)
|
||||
if m:
|
||||
m = m.getValue()
|
||||
return FreeCAD.Matrix(m[0][0],m[0][1],m[0][2],m[0][3],
|
||||
m[1][0],m[1][1],m[1][2],m[1][3],
|
||||
m[2][0],m[2][1],m[2][2],m[2][3],
|
||||
m[3][0],m[3][1],m[3][2],m[3][3])
|
||||
else:
|
||||
return FreeCAD.Matrix()
|
||||
|
||||
def setMatrix(self,matrix):
|
||||
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)
|
||||
|
||||
|
||||
class editTracker(Tracker):
|
||||
"A node edit tracker"
|
||||
|
|
|
@ -112,7 +112,7 @@ class DraftWorkbench (Workbench):
|
|||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Edit","Draft_WireToBSpline","Draft_AddPoint",
|
||||
"Draft_DelPoint","Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
|
||||
"Draft_PathArray","Draft_Clone","Draft_Drawing"]
|
||||
"Draft_PathArray","Draft_Clone","Draft_Drawing","Draft_Mirror"]
|
||||
self.treecmdList = ["Draft_ApplyStyle","Draft_ToggleDisplayMode","Draft_AddToGroup",
|
||||
"Draft_SelectGroup","Draft_SelectPlane",
|
||||
"Draft_ShowSnapBar","Draft_ToggleGrid"]
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
<file>icons/Draft_ShapeString.svg</file>
|
||||
<file>icons/Draft_Facebinder.svg</file>
|
||||
<file>icons/Draft_FlipDimension.svg</file>
|
||||
<file>icons/Draft_Mirror.svg</file>
|
||||
<file>patterns/concrete.svg</file>
|
||||
<file>patterns/cross.svg</file>
|
||||
<file>patterns/line.svg</file>
|
||||
|
|
147
src/Mod/Draft/Resources/icons/Draft_Mirror.svg
Normal file
147
src/Mod/Draft/Resources/icons/Draft_Mirror.svg
Normal file
|
@ -0,0 +1,147 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2766"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="Draft_Mirror.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
<defs
|
||||
id="defs2768">
|
||||
<linearGradient
|
||||
id="linearGradient3787">
|
||||
<stop
|
||||
id="stop3789"
|
||||
offset="0"
|
||||
style="stop-color:#0619c0;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3791"
|
||||
offset="1"
|
||||
style="stop-color:#379cfb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3864">
|
||||
<stop
|
||||
id="stop3866"
|
||||
offset="0"
|
||||
style="stop-color:#0619c0;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop3868"
|
||||
offset="1"
|
||||
style="stop-color:#379cfb;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2774" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient4195"
|
||||
x1="33.863636"
|
||||
y1="62.454544"
|
||||
x2="33.590908"
|
||||
y2="2.0909081"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-2.1818182,-0.18181818)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3787"
|
||||
id="linearGradient3524-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="581.26331"
|
||||
y1="126.79625"
|
||||
x2="609.54919"
|
||||
y2="100.10708"
|
||||
gradientTransform="matrix(1.0614931,0,0,1,-617.05296,-100.11156)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3864"
|
||||
id="linearGradient4229"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.0663257,0,0,0.63511735,-694.0785,-10.579799)"
|
||||
x1="660.59497"
|
||||
y1="94.477272"
|
||||
x2="669.62323"
|
||||
y2="36.649147" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3787"
|
||||
id="linearGradient4229-7"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-1.0663257,0,0,0.63511735,758.22943,-10.579799)"
|
||||
x1="660.59497"
|
||||
y1="94.477272"
|
||||
x2="669.62323"
|
||||
y2="36.649147" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.7781746"
|
||||
inkscape:cx="42.445645"
|
||||
inkscape:cy="22.613414"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1053"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata2771">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient4195);stroke-width:5.5999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:5.5999999,5.5999999;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 31.454546,1.2727273 0,61.2727277"
|
||||
id="path4187"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4229);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.06983113;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 3.6583237,49.424356 20.0910513,0 0,-0.121565 0.03139,0 0,-36.60608 z"
|
||||
id="rect4197"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4229-7);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.06983113;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 60.4926,49.424356 -20.091052,0 0,-0.121565 -0.03139,0 0,-36.60608 z"
|
||||
id="rect4197-8"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
Loading…
Reference in New Issue
Block a user