Draft: Added mirror tool (more graphical version of Part Mirror) - fixes #2215

This commit is contained in:
Yorik van Havre 2015-12-05 14:19:35 -02:00
parent cf3e69de08
commit 2e91633da9
7 changed files with 331 additions and 2 deletions

View File

@ -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",

View File

@ -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,

View File

@ -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())

View File

@ -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"

View File

@ -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"]

View File

@ -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>

View 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