EM-Workbench-for-FreeCAD/EM_FHNode.py
Enrico Di Lorenzo - FastFieldSolvers S.R.L 741336a74a * Support for FHPath (multiple segments along a path)
- Added FHPath object
- Implemented proper path and filename for exporting FastHenry file
  from FHSolver/FHInputFile object
- Fixed coordinate display to show the actual point coordinates
  when the user is selecting a point on the GUI
- Added option to display / hide the internal nodes of a FHPlane
- Added command to add/remove nodes/holes from an already
  existing FHPlane
- Moved common functions / variables to EM_Globals.py
- Completed all function description texts
- Multiple bug fixes (load FreeCAD file, import EM, etc.)
2018-12-30 00:20:41 +01:00

317 lines
14 KiB
Python

#***************************************************************************
#* *
#* Copyright (c) 2018 *
#* Efficient Power Conversion Corporation, Inc. http://epc-co.com *
#* *
#* Developed by FastFieldSolvers S.R.L. under contract by EPC *
#* http://www.fastfieldsolvers.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__="FreeCAD E.M. Workbench FastHenry Node Class"
__author__ = "FastFieldSolvers S.R.L."
__url__ = "http://www.fastfieldsolvers.com"
# defines
#
EMFHNODE_DEF_NODESIZE = 10
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
from FreeCAD import Vector
from EM_Globals import EMFHNODE_DEF_NODECOLOR
if FreeCAD.GuiUp:
import FreeCADGui
from PySide import QtCore, QtGui
from DraftTools import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt,txt, utf8_decode=False):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
__dir__ = os.path.dirname(__file__)
iconPath = os.path.join( __dir__, 'Resources' )
def makeFHNode(baseobj=None,X=0.0,Y=0.0,Z=0.0,color=None,size=None,name='FHNode'):
''' Creates a FastHenry node ('N' statement in FastHenry)
'baseobj' is the point object whose position is used as base for the FNNode.
It has priority over X,Y,Z.
If no 'baseobj' is given, X,Y,Z are used as coordinates
'X' x coordinate of the node, in absolute coordinate system
'Y' y coordinate of the node, in absolute coordinate system
'Z' z coordinate of the node, in absolute coordinate system
'color' node color, e.g. a tuple (1.0,0.0,0.0).
Defaults to EMFHNODE_DEF_NODECOLOR
'size' node size. Defaults to EMFHNODE_DEF_NODESIZE
'name' is the name of the object
Example:
node = makeFHNode(X=1.0,Y=2.0,Z=0.0)
'''
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
obj.Label = translate("EM", name)
# this adds the relevant properties to the object
#'obj' (e.g. 'Base' property) making it a _FHNode
_FHNode(obj)
# manage ViewProvider object
if FreeCAD.GuiUp:
_ViewProviderFHNode(obj.ViewObject)
# set base ViewObject properties to user-selected values (if any)
if color:
obj.ViewObject.PointColor = color
else:
obj.ViewObject.PointColor = EMFHNODE_DEF_NODECOLOR
if size:
obj.ViewObject.PointSize = size
else:
obj.ViewObject.PointSize = EMFHNODE_DEF_NODESIZE
# check if 'baseobj' is a point (only base object allowed)
if baseobj:
if Draft.getType(baseobj) == "Point":
# get the absolute coordinates of the Point
X = baseobj.Shape.Point.x
Y = baseobj.Shape.Point.y
Z = baseobj.Shape.Point.z
else:
FreeCAD.Console.PrintWarning(translate("EM","FHNodes can only take the position from Point objects"))
# set the node coordinates
obj.Proxy.setAbsCoord(Vector(X,Y,Z))
# return the newly created Python object
return obj
class _FHNode:
'''The EM FastHenry Node object'''
def __init__(self, obj):
''' Add properties '''
obj.addProperty("App::PropertyDistance","X","EM",QT_TRANSLATE_NOOP("App::Property","X Location"))
obj.addProperty("App::PropertyDistance","Y","EM",QT_TRANSLATE_NOOP("App::Property","Y Location"))
obj.addProperty("App::PropertyDistance","Z","EM",QT_TRANSLATE_NOOP("App::Property","Z Location"))
obj.Proxy = self
self.Type = "FHNode"
# save the object in the class, to store or retrieve specific data from it
# from within the class
self.Object = obj
def execute(self, obj):
''' this method is mandatory. It is called on Document.recompute()
'''
#FreeCAD.Console.PrintWarning("_FHNode execute\n") #debug
# set the shape as a Vertex at relative position obj.X, obj.Y, obj.Z
# The vertex will then be adjusted according to the FHNode Placement
obj.Shape = Part.Vertex(self.getRelCoord())
#FreeCAD.Console.PrintWarning("_FHNode execute ends\n") #debug
def onChanged(self, obj, prop):
''' take action if an object property 'prop' changed
'''
#FreeCAD.Console.PrintWarning("_FHNode onChanged(" + str(prop)+")\n") #debug
if not hasattr(self,"Object"):
# on restore, self.Object is not there anymore (JSON does not serialize complex objects
# members of the class, so __getstate__() and __setstate__() skip them);
# so we must "re-attach" (re-create) the 'self.Object'
self.Object = obj
#FreeCAD.Console.PrintWarning("_FHNode onChanged(" + str(prop)+") ends\n") #debug
def serialize(self,fid,extension=""):
''' Serialize the object to the 'fid' file descriptor
'fid': the file descriptor
'extension': any extension to add to the node name, in case of a node
belonging to a conductive plane. If not empty, it also changes
the way the node is serialized, according to the plane node definition.
Defaults to an empty string.
'''
pos = self.getAbsCoord()
if extension == "":
fid.write("N" + self.Object.Label)
fid.write(" x=" + str(pos.x) + " y=" + str(pos.y) + " z=" + str(pos.z))
else:
fid.write("+ N" + self.Object.Label + extension)
fid.write(" (" + str(pos.x) + "," + str(pos.y) + "," + str(pos.z) + ")")
fid.write("\n")
def getAbsCoord(self):
''' Get a FreeCAD.Vector containing the node coordinates
in the absolute reference system
'''
# should be "self.Object.Placement.multVec(Vector(self.Object.X, self.Object.Y, self.Object.Z))"
# but as the shape is always a Vertex, this is a shortcut - but works only if there is execute() first!
# as it must update the shape
#return self.Object.Shape.Point
return self.Object.Placement.multVec(Vector(self.Object.X, self.Object.Y, self.Object.Z))
def getRelCoord(self):
''' Get a FreeCAD.Vector containing the node coordinates relative to the FHNode Placement
These coordinates correspond to (self.Object.X, self.Object.Y, self.Object.Z),
that are the same as self.Object.Placement.inverse().multVec(self.Object.Shape.Point))
'''
return Vector(self.Object.X,self.Object.Y,self.Object.Z)
def setRelCoord(self,rel_coord,placement=None):
''' Sets the node position relative to the placement
'rel_coord': FreeCAD.Vector containing the node coordinates relative to the FHNode Placement
'placement': a new FHNode placement. If 'None', the placement is not changed
Remark: the function will not recalculate() the object (i.e. the change of position is not
immediately visible by just calling this function)
'''
if placement:
# validation of the parameter
if isinstance(placement, FreeCAD.Placement):
self.Object.Placement = placement
self.Object.X = rel_coord.x
self.Object.Y = rel_coord.y
self.Object.Z = rel_coord.z
def setAbsCoord(self,abs_coord,placement=None):
''' Sets the absolute node position, considering the object placement, and in case forcing a new placement
'abs_coord': FreeCAD.Vector containing the node coordinates in the absolute reference system
'placement': a new placement. If 'None', the placement is not changed
Remark: the function will not recalculate() the object (i.e. the change of position is not
immediately visible by just calling this function)
'''
if placement:
# validation of the parameter
if isinstance(placement, FreeCAD.Placement):
self.Object.Placement = placement
placement = self.Object.Placement.copy()
rel_coord = placement.inverse().multVec(abs_coord)
self.Object.X = rel_coord.x
self.Object.Y = rel_coord.y
self.Object.Z = rel_coord.z
def __getstate__(self):
return self.Type
def __setstate__(self,state):
if state:
self.Type = state
class _ViewProviderFHNode:
def __init__(self, obj):
''' Set this object to the proxy object of the actual view provider '''
obj.Proxy = self
self.Object = obj.Object
def attach(self, obj):
''' Setup the scene sub-graph of the view provider, this method is mandatory '''
# on restore, self.Object is not there anymore (JSON does not serialize complex objects
# members of the class, so __getstate__() and __setstate__() skip them);
# so we must "re-attach" (re-create) the 'self.Object'
self.Object = obj.Object
return
def updateData(self, fp, prop):
''' If a property of the handled feature has changed we have the chance to handle this here '''
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n") # debug
return
def getDefaultDisplayMode(self):
''' Return the name of the default display mode. It must be defined in getDisplayModes. '''
return "Flat Lines"
def onChanged(self, vp, prop):
''' If the 'prop' property changed for the ViewProvider 'vp' '''
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n") # debug
def getIcon(self):
''' Return the icon which will appear in the tree view. This method is optional
and if not defined a default icon is shown.
'''
return os.path.join(iconPath, 'node_icon.svg')
def __getstate__(self):
return None
def __setstate__(self,state):
return None
class _CommandFHNode:
''' The EM FastHenry Node (FHNode) command definition
'''
def GetResources(self):
return {'Pixmap' : os.path.join(iconPath, 'node_icon.svg') ,
'MenuText': QT_TRANSLATE_NOOP("EM_FHNode","FHNode"),
'Accel': "E, N",
'ToolTip': QT_TRANSLATE_NOOP("EM_FHNode","Creates a FastHenry Node object from scratch or from a selected object (point)")}
def IsActive(self):
return not FreeCAD.ActiveDocument is None
def Activated(self):
# get the selected object(s)
sel = FreeCADGui.Selection.getSelectionEx()
done = False
# if selection is not empty
if sel:
# automatic mode
import Draft
if Draft.getType(sel[0].Object) == "Point":
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
FreeCADGui.addModule("EM")
for selobj in sel:
FreeCADGui.doCommand('obj=EM.makeFHNode(FreeCAD.ActiveDocument.'+selobj.Object.Name+')')
# autogrouping, for later on
#FreeCADGui.addModule("Draft")
#FreeCADGui.doCommand("Draft.autogroup(obj)")
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
done = True
# if no selection, or nothing good in the selected objects
if not done:
FreeCAD.DraftWorkingPlane.setup()
# get a 3D point via Snapper, setting the callback functions
FreeCADGui.Snapper.getPoint(callback=self.getPoint,movecallback=self.move)
def getPoint(self,point=None,obj=None):
'''This function is called by the Snapper when it has a 3D point'''
if point == None:
return
coord = point
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
FreeCADGui.addModule("EM")
FreeCADGui.doCommand('obj=EM.makeFHNode(X='+str(coord.x)+',Y='+str(coord.y)+',Z='+str(coord.z)+')')
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
# might improve in the future with continue command
#if self.continueCmd:
# self.Activated()
# this is used to display the global point position information
# in the Snapper user interface. By default it would display the relative
# point position on the DraftWorkingPlane (see DraftSnap.py, move() member).
# This would be different from the behavior of Draft.Point command.
def move(self,point=None,snapInfo=None):
if FreeCADGui.Snapper.ui:
FreeCADGui.Snapper.ui.displayPoint(point)
if FreeCAD.GuiUp:
FreeCADGui.addCommand('EM_FHNode',_CommandFHNode())