New tool: Array from shape

Now Draft arrays can be ripped and repeated as many times as necessary
=)
This commit is contained in:
DeepSOIC 2015-10-29 01:44:21 +03:00
parent e805a2834e
commit a80b04aacf
3 changed files with 301 additions and 14 deletions

View File

@ -1,23 +1,23 @@
#*************************************************************************** #***************************************************************************
#* * #* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) * #* copyright (c) 2015 - victor titov (deepsoic) *
#* <vv.titov@gmail.com> * #* <vv.titov@gmail.com> *
#* * #* *
#* This program is free software; you can redistribute it and/or modify * #* this program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) * #* it under the terms of the gnu lesser general public license (lgpl) *
#* as published by the Free Software Foundation; either version 2 of * #* as published by the free software foundation; either version 2 of *
#* the License, or (at your option) any later version. * #* the license, or (at your option) any later version. *
#* for detail see the LICENCE text file. * #* for detail see the licence text file. *
#* * #* *
#* This program is distributed in the hope that it will be useful, * #* this program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of * #* but without any warranty; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * #* merchantability or fitness for a particular purpose. see the *
#* GNU Library General Public License for more details. * #* gnu library general public license for more details. *
#* * #* *
#* You should have received a copy of the GNU Library General Public * #* you should have received a copy of the gnu library general public *
#* License along with this program; if not, write to the Free Software * #* license along with this program; if not, write to the free software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 *
#* USA * #* usa *
#* * #* *
#*************************************************************************** #***************************************************************************
@ -51,6 +51,8 @@ class LatticeWorkbench (Workbench):
cmdsArrayTools = cmdsArrayTools + latticePlacement.exportedCommands cmdsArrayTools = cmdsArrayTools + latticePlacement.exportedCommands
import latticePolarArray import latticePolarArray
cmdsArrayTools = cmdsArrayTools + latticePolarArray.exportedCommands cmdsArrayTools = cmdsArrayTools + latticePolarArray.exportedCommands
import latticeArrayFromShape
cmdsArrayTools = cmdsArrayTools + latticeArrayFromShape.exportedCommands
import latticeApply import latticeApply
cmdsArrayTools = cmdsArrayTools + latticeApply.exportedCommands cmdsArrayTools = cmdsArrayTools + latticeApply.exportedCommands
import latticeCompose import latticeCompose

216
latticeArrayFromShape.py Normal file
View File

@ -0,0 +1,216 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="Lattice ArrayFromShape object: creates an array of placements from a compound."
__author__ = "DeepSOIC"
__url__ = ""
import math
import FreeCAD as App
import Part
from latticeCommon import *
import latticeBaseFeature
import latticeCompoundExplorer as LCE
import latticeGeomUtils as Utils
# -------------------------- document object --------------------------------------------------
def makeLatticeArrayFromShape(name):
'''makeLatticeArrayFromShape(name): makes a LatticeArrayFromShape object.'''
return latticeBaseFeature.makeLatticeFeature(name, LatticeArrayFromShape,'Lattice_ArrayFromShape.svg')
class LatticeArrayFromShape(latticeBaseFeature.LatticeFeature):
"The Lattice ArrayFromShape object"
def derivedInit(self,obj):
self.Type = "LatticeArrayFromShape"
obj.addProperty("App::PropertyLink","Base","Lattice ArrayFromShape","Object to generate array of placements from. Should be a compound. If not, single placement will be created.")
obj.addProperty("App::PropertyBool","FlattenBaseHierarchy","Lattice ArrayFromShape","Unpack subcompounds, to use all shapes, not just direct children.")
obj.FlattenBaseHierarchy = True
obj.addProperty("App::PropertyEnumeration","TranslateMode","Lattice ArrayFromShape","Method of deriving translation part of output placements")
obj.TranslateMode = ['(none)', 'base', 'child', 'child.CenterOfMass','child.CenterOfBoundBox','child.Vertex']
obj.TranslateMode = 'child'
obj.addProperty("App::PropertyInteger","TranslateElementIndex","Lattice ArrayFromShape","Index of vertex used for translation calculation.")
obj.addProperty("App::PropertyEnumeration","OrientMode","Lattice ArrayFromShape","Method of deriving orientation part of output placements")
obj.OrientMode = ['(none)', 'base', 'child', 'child.InertiaAxes','child.Edge', 'child.FaceAxis']
obj.OrientMode = 'child'
obj.addProperty("App::PropertyInteger","OrientElementIndex","Lattice ArrayFromShape","Index of subelement used for orientation calculation.")
def derivedExecute(self,obj):
# cache stuff
base = obj.Base.Shape
if base.ShapeType != 'Compound':
base = Part.makeCompound([base])
if obj.FlattenBaseHierarchy:
baseChildren = LCE.AllLeaves(base)
else:
baseChildren = base.childShapes()
#cache mode comparisons, for speed
posIsNone = obj.TranslateMode == '(none)'
posIsBase = obj.TranslateMode == 'base'
posIsChild = obj.TranslateMode == 'child'
posIsCenterM = obj.TranslateMode == 'child.CenterOfMass'
posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox'
posIsVertex = obj.TranslateMode == 'child.Vertex'
oriIsNone = obj.OrientMode == '(none)'
oriIsBase = obj.OrientMode == 'base'
oriIsChild = obj.OrientMode == 'child'
oriIsInertial = obj.OrientMode == 'child.InertiaAxes'
oriIsEdge = obj.OrientMode == 'child.Edge'
oriIsFace = obj.OrientMode == 'child.FaceAxis'
# initialize output containers and loop variables
outputPlms = [] #list of placements
# the essence
for child in baseChildren:
pos = App.Vector()
ori = App.Rotation()
if posIsNone:
pass
elif posIsBase:
pos = base.Placement.Base
elif posIsChild:
pos = child.Placement.Base
elif posIsCenterM:
leaves = LCE.AllLeaves(child)
totalW = 0
weightAttrib = {"Vertex":"",
"Edge":"Length",
"Wire":"Length",
"Face":"Area",
"Shell":"Area",
"Solid":"Volume",
"CompSolid":""}
#Center of mass of a compound is a weghted average of centers
# of mass of individual objects.
for leaf in leaves:
w = 1.0 if not weightAttrib else (getattr(leaf, weightAttrib))
if child.ShapeType == 'Vertex':
childCM = child.Point
#elif child.ShapeType == 'CompSolid':
#todo
else:
childCM = child.CenterOfMass
pos += childCM * w
totalW += w
pos = pos * (1.0/totalW)
elif posIsCenterBB:
import latticeBoundBox
bb = latticeBoundBox.getPrecisionBoundBox(child)
pos = bb.Center
elif posIsVertex:
v = child.Vertexes[obj.TranslateElementIndex - 1]
else:
raise ValueError("latticePolarArrayFromShape: translation mode not implemented: "+obj.TranslateMode)
if oriIsNone:
pass
elif oriIsBase:
ori = base.Placement.Rotation
elif oriIsChild:
ori = child.Placement.Rotation
elif oriIsInertial:
leaves = LCE.AllLeaves(child)
if len(leaves)>1:
raise ValueError("latticePolarArrayFromShape: calculation of principal axes of compounds is not supported yet")
props = leaves[0].PrincipalProperties
XAx = props['FirstAxisOfInertia']
ZAx = props['ThirdAxisOfInertia']
ori = Utils.makeOrientationFromLocalAxes(ZAx, XAx)
elif oriIsEdge:
edge = child.Edges[obj.OrientElementIndex - 1]
XAx = edge.Curve.tangent(edge.Curve.FirstParameter)[0]
ori1 = Utils.makeOrientationFromLocalAxes(ZAx= XAx)
ori2 = Utils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0),XAx= App.Vector(0,0,1))
ori = ori1.multiply(ori2)
elif oriIsFace:
face = child.Faces[obj.OrientElementIndex - 1]
ZAx = face.Surface.Axis
else:
raise ValueError("latticePolarArrayFromShape: rientation mode not implemented: "+obj.OrientMode)
plm = App.Placement(pos, ori)
outputPlms.append(plm)
if (posIsNone or posIsBase) and (oriIsNone or oriIsBase):
break #output just one placement if modes are set so that placement does not depend on current child
return outputPlms
# -------------------------- /document object --------------------------------------------------
# -------------------------- Gui command --------------------------------------------------
def CreateLatticeArrayFromShape(name):
sel = FreeCADGui.Selection.getSelectionEx()
FreeCAD.ActiveDocument.openTransaction("Create LatticeArrayFromShape")
FreeCADGui.addModule("latticeArrayFromShape")
FreeCADGui.doCommand("f = latticeArrayFromShape.makeLatticeArrayFromShape(name='"+name+"')")
FreeCADGui.doCommand("f.Base = App.ActiveDocument."+sel[0].ObjectName)
FreeCADGui.doCommand("for child in f.ViewObject.Proxy.claimChildren():\n"+
" child.ViewObject.hide()")
FreeCADGui.doCommand("f.Proxy.execute(f)")
FreeCADGui.doCommand("f.purgeTouched()")
FreeCADGui.doCommand("f = None")
FreeCAD.ActiveDocument.commitTransaction()
class _CommandLatticeArrayFromShape:
"Command to create LatticeArrayFromShape feature"
def GetResources(self):
return {'Pixmap' : getIconPath("Lattice_ArrayFromShape.svg"),
'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice_ArrayFromShape","Make lattice from compound"),
'Accel': "",
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice_ArrayFromShape","Lattice ArrayFromShape: make placements array from shapes in a compound.")}
def Activated(self):
if len(FreeCADGui.Selection.getSelection()) == 1 :
CreateLatticeArrayFromShape(name = "ArrayFromShape")
else:
mb = QtGui.QMessageBox()
mb.setIcon(mb.Icon.Warning)
mb.setText(translate("Lattice_ArrayFromShape", "Please select one object, first.", None))
mb.setWindowTitle(translate("Lattice_ArrayFromShape","Bad selection", None))
mb.exec_()
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
FreeCADGui.addCommand('Lattice_ArrayFromShape', _CommandLatticeArrayFromShape())
exportedCommands = ['Lattice_ArrayFromShape']
# -------------------------- /Gui command --------------------------------------------------

69
latticeGeomUtils.py Normal file
View File

@ -0,0 +1,69 @@
#***************************************************************************
#* *
#* Copyright (c) 2015 - Victor Titov (DeepSOIC) *
#* <vv.titov@gmail.com> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD as App
from latticeCommon import *
__title__="Geometric utility routines for Lattice workbench for FreeCAD"
__author__ = "DeepSOIC"
__url__ = ""
def makeOrientationFromLocalAxes(ZAx, XAx = None):
'''
makeOrientationFromLocalAxes(ZAx, XAx): constructs App.Rotation to get into
alignment with given local Z and X axes. Z axis is followed strictly; X axis
is a guide and can be not strictly perpendicular to Z axis; it will be
corrected and modified
'''
if XAx is None:
XAx = App.Vector(0,0,1) #Why Z? Because I prefer local X axis to be aligned so that local XZ plane is parallel to global Z axis.
#First, compute all three axes.
ZAx.normalize() #just to be sure; it's important to have the matrix normalized
YAx = ZAx.cross(XAx) # construct Y axis
if YAx.Length < ParaConfusion*10.0:
#failed, try some other X axis direction hint
XAx = App.Vector(0,0,1)
YAx = ZAx.cross(XAx)
if YAx.Length < ParaConfusion*10.0:
#failed again. Now, we can tell, that local Z axis is along global
# Z axis
XAx = App.Vector(1,0,0)
YAx = ZAx.cross(XAx)
YAx.normalize()
XAx = YAx.cross(ZAx) # force X perpendicular
#hacky way of constucting rotation to a local coordinate system:
# make matrix,
m = App.Matrix()
m.A = list(XAx)+[0.0]+list(YAx)+[0.0]+list(ZAx)+[0.0]+[0.0]*3+[1.0]
m.transpose() # local axes vectors are columns of matrix, but we put them in as rwos, because it is convenient, and then transpose it.
# make placement out of matrix,
tmpplm = App.Placement(m)
# and extract rotation from placement.
ori = tmpplm.Rotation
return ori