From a0eeba7b6096a0d5aa1b67685c32a5d1dec3a353 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Sat, 19 Dec 2015 03:42:26 +0300 Subject: [PATCH] New feature: ParaSeries Experimental; not complete, but does something cool already. --- InitGui.py | 1 + Lattice2CompoundFeatures.py | 3 +- lattice2BaseFeature.py | 2 + lattice2ParaSeries.py | 240 ++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 lattice2ParaSeries.py diff --git a/InitGui.py b/InitGui.py index e3f7a73..ee5d04f 100644 --- a/InitGui.py +++ b/InitGui.py @@ -68,6 +68,7 @@ class Lattice2Workbench (Workbench): + Lattice2.CompoundFeatures.FuseCompound.exportedCommands + Lattice2.CompoundFeatures.BoundBox.exportedCommands + Lattice2.CompoundFeatures.ShapeString.exportedCommands + + Lattice2.CompoundFeatures.ParaSeries.exportedCommands ) self.appendToolbar('Lattice2CompoundFeatures', cmdsCompoundTools) self.appendMenu('Lattice2', cmdsCompoundTools) diff --git a/Lattice2CompoundFeatures.py b/Lattice2CompoundFeatures.py index fc677a9..99b6dd6 100644 --- a/Lattice2CompoundFeatures.py +++ b/Lattice2CompoundFeatures.py @@ -3,4 +3,5 @@ import FuseCompound2 as FuseCompound import lattice2Downgrade as Downgrade import lattice2BoundBox as BoundBox import lattice2ShapeString as ShapeString -import lattice2MakeCompound as MakeCompound \ No newline at end of file +import lattice2MakeCompound as MakeCompound +import lattice2ParaSeries as ParaSeries \ No newline at end of file diff --git a/lattice2BaseFeature.py b/lattice2BaseFeature.py index f6d8af1..5d44268 100644 --- a/lattice2BaseFeature.py +++ b/lattice2BaseFeature.py @@ -123,6 +123,8 @@ class LatticeFeature(): plms = self.derivedExecute(obj) if plms is not None: + if plms == "suppress": + return obj.NumElements = len(plms) shapes = [] markerSize = obj.MarkerSize diff --git a/lattice2ParaSeries.py b/lattice2ParaSeries.py new file mode 100644 index 0000000..6734b44 --- /dev/null +++ b/lattice2ParaSeries.py @@ -0,0 +1,240 @@ +#*************************************************************************** +#* * +#* Copyright (c) 2015 - Victor Titov (DeepSOIC) * +#* * +#* * +#* 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 ParaSeries feature" +__author__ = "DeepSOIC" +__url__ = "" +__doc__ = "Lattice ParaSeries feature: generates series of shapes by modifying a parameter" + +import math + +import FreeCAD as App +import Part + +from lattice2Common import * +import lattice2BaseFeature +import lattice2Executer +import lattice2Markers as markers + +# -------------------------- document object -------------------------------------------------- + +def makeLatticeParaSeries(name): + '''makeLatticeParaSeries(name): makes a LatticeParaSeries object.''' + return lattice2BaseFeature.makeLatticeFeature(name, LatticeParaSeries, ViewProviderLatticeParaSeries) + +class LatticeParaSeries(lattice2BaseFeature.LatticeFeature): + "The Lattice ParaSeries object" + + def derivedInit(self,obj): + self.Type = "LatticeParaSeries" + + obj.addProperty("App::PropertyLink","Object","Lattice ParaSeries","Object to make series from. Can be any generic shape, as well as an array of placements.") + + obj.addProperty("App::PropertyEnumeration","ParameterType","Lattice ParaSeries","Data type of parameter to vary.") + obj.ParameterType = ['float','int','string'] + + obj.addProperty("App::PropertyString","ParameterRef","Lattice ParaSeries","Reference to the parameter to vary. Use expression. Example: Box.Height") + + obj.addProperty("App::PropertyEnumeration","ValuesSource","Lattice ParaSeries","Select where to take the values for parameter from.") + obj.ValuesSource = ["Values Property","Spreadsheet", "Generator"] + + obj.addProperty("App::PropertyStringList","Values","Lattice ParaSeries","List of values for series") + + obj.addProperty("App::PropertyEnumeration","Recomputing","Lattice ParaSeries","Sets recomputing policy.") + obj.Recomputing = ["Disabled", "Recompute Once", "Enabled"] + obj.Recomputing = "Disabled" # recomputing ParaSeries can be very long, so disable it by default + + + def derivedExecute(self,selfobj): + if selfobj.Recomputing == "Disabled": + raise ValueError("Recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it.") + try: + #collect values + values = [] + if selfobj.ValuesSource == "Values Property": + pass + else: + raise ValueError(selfobj.Name + ": ValuesSource = "+selfobj.ValuesSource+" is not yet implemented") + + #convert values to type + for strv in selfobj.Values: + if len(strv) == 0: continue + if selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int': + if len(strv.strip()) == 0: continue + v = float(strv.replace(",",".")) + if selfobj.ParameterType == 'int': + v = int(round(v)) + elif selfobj.ParameterType == 'string': + v = strv.strip() + else: + raise ValueError(selfobj.Name + ": ParameterType option not implemented: "+selfobj.ParameterType) + values.append(v) + + if len(values) == 0: + scale = 1.0 + try: + if not selfobj.Object.Shape.isNull(): + scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps)) + except Exception: + pass + if scale < DistConfusion * 100: + scale = 1.0 + selfobj.Shape = markers.getNullShapeShape(scale) + raise ValueError(selfobj.Name + ": list of values is empty.") + + doc1 = selfobj.Document + doc2 = App.newDocument() + object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error + try: + object_in_doc2 = doc2.copyObject(selfobj.Object, True) + refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"] + pieces = refstr.split(".") + objname = pieces[0] + obj_to_modify = doc2.getObject(objname) + output_shapes = [] + for val in values: + if obj_to_modify.isDerivedFrom("Spreadsheet::Sheet"): + if len(pieces) != 2: + raise ValueError(selfobj.Name + ": failed to parse parameter reference: "+refstr ) + obj_to_modify.set(pieces[1], str(val)) + elif obj_to_modify.isDerivedFrom("Sketcher::SketchObject") and pieces[1] == "Constraints": + if len(pieces) != 3: + raise ValueError(selfobj.Name + ": failed to parse parameter reference: "+refstr ) + obj_to_modify.setDatum(pieces[2],val) + else: + if len(pieces) != 2: + raise ValueError(selfobj.Name + ": failed to parse parameter reference: "+refstr ) + setattr(obj_to_modify, pieces[1], val) + + doc2.recompute() + + shape = None + for obj in doc2.Objects: + if 'Invalid' in obj.State: + latticeExecuter.error(obj,"Recomputing shape for parameter value of "+val+" failed.") + + scale = 1.0 + try: + if not selfobj.Object.Shape.isNull(): + scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps)) + except Exception: + pass + if scale < DistConfusion * 100: + scale = 1.0 + shape = markers.getNullShapeShape(scale) + if shape is None: + shape = object_in_doc2.Shape.copy() + output_shapes.append(shape) + + + finally: + #delete all references, before destroying the document. Probably not required, but to be sure... + del(object_in_doc2) + doc2_name = doc2.Name + del(doc2) + App.closeDocument(doc2_name) + + selfobj.Shape = Part.makeCompound(output_shapes) + + output_is_lattice = lattice2BaseFeature.isObjectLattice(selfobj.Object) + if 'Auto' in selfobj.isLattice: + new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' + if selfobj.isLattice != new_isLattice:#check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) + selfobj.isLattice = new_isLattice + finally: + if selfobj.Recomputing == "Recompute Once": + selfobj.Recomputing = "Disabled" + return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB. + +class ViewProviderLatticeParaSeries(lattice2BaseFeature.ViewProviderLatticeFeature): + + def getIcon(self): + return getIconPath("Lattice2_ParaSeries.svg") + + def claimChildren(self): + return [self.Object.Object] + +# -------------------------- /document object -------------------------------------------------- + +# -------------------------- Gui command -------------------------------------------------- + + + +def CreateLatticeParaSeries(name, shapeObj): + '''utility function; sharing common code for all populate-copies commands''' + FreeCADGui.addModule("lattice2ParaSeries") + FreeCADGui.addModule("lattice2Executer") + + #fill in properties + FreeCADGui.doCommand("f = lattice2ParaSeries.makeLatticeParaSeries(name='"+name+"')") + FreeCADGui.doCommand("f.Object = App.ActiveDocument."+shapeObj.Name) + + #execute + FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") + + #hide something + FreeCADGui.doCommand("f.Object.ViewObject.hide()") + + #finalize + FreeCADGui.doCommand("Gui.Selection.addSelection(f)") + FreeCADGui.doCommand("f = None") + +def cmdCreateSeries(): + sel = FreeCADGui.Selection.getSelectionEx() + if len(sel) == 1 : + FreeCAD.ActiveDocument.openTransaction("Populate with copies") + CreateLatticeParaSeries("ParaSeries",sel[0].Object) + deselect(sel) + FreeCAD.ActiveDocument.commitTransaction() + else: + raise SelectionError("Bad selection","Please select an object to generate series, first.") + +class _CommandLatticeParaSeries: + "Command to create LatticeParaSeries feature" + def GetResources(self): + return {'Pixmap' : getIconPath("Lattice2_ParaSeries.svg"), + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_ParaSeries","ParaSeries"), + 'Accel': "", + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_ParaSeries","ParaSeries: generate an array of shapes by varying a design parameter")} + + def Activated(self): + try: + if len(FreeCADGui.Selection.getSelection())==0: + infoMessage("ParaSeries", + "ParaSeries command. Generates an array of shapes by varying a design parameter.\n\n"+ + "Please select an object to generate array from. Then invoke the command. After that, set up the series in properties of ParaSeries feature created, and change Recomputing property to get a result.\n\n"+ + "Setting up the series involves: specifying the parameter to modify (ParameterRef property - set up an expression), and setting up the value list.") + return + cmdCreateSeries() + except Exception as err: + msgError(err) + + def IsActive(self): + if FreeCAD.ActiveDocument: + return True + else: + return False + +FreeCADGui.addCommand('Lattice2_ParaSeries', _CommandLatticeParaSeries()) + +exportedCommands = ['Lattice2_ParaSeries']