* 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.)
This commit is contained in:
parent
0d4f02c4c9
commit
741336a74a
2
EM.py
2
EM.py
|
@ -43,8 +43,10 @@ if FreeCAD.GuiUp:
|
|||
import FreeCADGui
|
||||
FreeCADGui.updateLocale()
|
||||
|
||||
from EM_Globals import *
|
||||
from EM_FHNode import *
|
||||
from EM_FHSegment import *
|
||||
from EM_FHPath import *
|
||||
from EM_FHPlaneHole import *
|
||||
from EM_FHPlane import *
|
||||
from EM_FHPort import *
|
||||
|
|
|
@ -36,7 +36,6 @@ __url__ = "http://www.fastfieldsolvers.com"
|
|||
EMFHEQUIV_LENTOL = 1e-12
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from FreeCAD import Vector
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -56,13 +55,14 @@ __dir__ = os.path.dirname(__file__)
|
|||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHEquiv(node1=None,node2=None,name='FHEquiv'):
|
||||
''' Creates a FastHenry node equivalence ('.equiv' statement in FastHenry)
|
||||
'''Creates a FastHenry node equivalence ('.equiv' statement in FastHenry)
|
||||
|
||||
'node1' is the first node to shortcut
|
||||
'node2' is the second node to shortcut
|
||||
'node1' is the first node to short-circuit
|
||||
'node2' is the second node to short-circuit
|
||||
'name' is the name of the object
|
||||
|
||||
Example:
|
||||
TBD
|
||||
equiv = makeFHEquiv(node1,node2)
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -88,8 +88,8 @@ class _FHEquiv:
|
|||
'''The EM FastHenry node Equivalence object'''
|
||||
def __init__(self, obj):
|
||||
''' Add properties '''
|
||||
obj.addProperty("App::PropertyLink","Node1","EM",QT_TRANSLATE_NOOP("App::Property","First FHNode to shortcut"))
|
||||
obj.addProperty("App::PropertyLink","Node2","EM",QT_TRANSLATE_NOOP("App::Property","Second FHNode to shortcut"))
|
||||
obj.addProperty("App::PropertyLink","Node1","EM",QT_TRANSLATE_NOOP("App::Property","First FHNode to short-circuit"))
|
||||
obj.addProperty("App::PropertyLink","Node2","EM",QT_TRANSLATE_NOOP("App::Property","Second FHNode to short-circuit"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHEquiv"
|
||||
# save the object in the class, to store or retrieve specific data from it
|
||||
|
@ -171,9 +171,8 @@ class _ViewProviderFHEquiv:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -181,11 +180,11 @@ class _ViewProviderFHEquiv:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' If the 'prop' property changed for the ViewProvider 'vp' '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n") # debug
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
''' Used to place other objects as children in the tree'''
|
||||
c = []
|
||||
if hasattr(self,"Object"):
|
||||
if hasattr(self.Object,"Node1"):
|
||||
|
@ -195,7 +194,7 @@ class _ViewProviderFHEquiv:
|
|||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'equiv_icon.svg')
|
||||
|
@ -219,8 +218,6 @@ class _CommandFHEquiv:
|
|||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# init properties (future)
|
||||
#self.Length = None
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
|
|
|
@ -30,8 +30,8 @@ __author__ = "FastFieldSolvers S.R.L."
|
|||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from FreeCAD import Vector
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FreeCADGui
|
||||
|
@ -50,14 +50,27 @@ __dir__ = os.path.dirname(__file__)
|
|||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHInputFile(doc=None,filename=None,folder=None):
|
||||
'''Creates a FastHenry input file
|
||||
'''Outputs a FastHenry input file based on the active document geometry
|
||||
|
||||
'doc' is the Document object that must contain at least one
|
||||
EM_FHSolver object and the relevant geometry.
|
||||
If no 'doc' is given, the active document is used, if any.
|
||||
'doc' is the Document object that must contain at least one
|
||||
EM_FHSolver object and the relevant geometry.
|
||||
If no 'doc' is given, the active document is used, if any.
|
||||
'filename' is the filename to use. If not passed as an argument,
|
||||
the 'Filename' property of the FHSolver object contained in the document
|
||||
will be used. If the 'Filename" string in the FHSolver object is empty,
|
||||
the function builds a filename concatenating the document name
|
||||
with the default extension EMFHSOLVER_DEF_FILENAME.
|
||||
Whatever the name, if a file with the same name exists in the target
|
||||
folder, the user is prompted to know if he/she wants to overwrite it.
|
||||
'folder' is the folder where the file will be stored. If not passed
|
||||
as an argument, the 'Folder' property of the FHSolver object
|
||||
contained in the document will be used. If the 'Folder' string
|
||||
in the FHSolver object is empty, the vunction defaults to the
|
||||
user's home path (e.g. in Windows "C:\Documents and Settings\
|
||||
username\My Documents", in Linux "/home/username")
|
||||
|
||||
Example:
|
||||
TBD
|
||||
makeFHInputFile()
|
||||
'''
|
||||
if not doc:
|
||||
doc = App.ActiveDocument
|
||||
|
@ -71,7 +84,8 @@ def makeFHInputFile(doc=None,filename=None,folder=None):
|
|||
FreeCAD.Console.PrintWarning(translate("EM","FHSolver object not found in the document. Aborting."))
|
||||
return
|
||||
else:
|
||||
# TBC warning: may warn the user if more that one solver is present per document
|
||||
if len(solver) > 1:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","More than one FHSolver object found in the document. Using the first one."))
|
||||
solver = solver[0]
|
||||
if not filename:
|
||||
# if 'filename' was not passed as an argument, retrieve it from the 'solver' object
|
||||
|
@ -84,16 +98,26 @@ def makeFHInputFile(doc=None,filename=None,folder=None):
|
|||
# otherwise, if the user passed a filename to the function, update it in the 'solver' object
|
||||
solver.Filename = filename
|
||||
if not folder:
|
||||
# if not specified, default to the user's home path
|
||||
# (e.g. in Windows "C:\Documents and Settings\username\My Documents", in Linux "/home/username")
|
||||
folder = FreeCAD.ConfigGet("UserHomePath")
|
||||
# if 'folder' was not passed as an argument, retrieve it from the 'solver' object
|
||||
# (this should be the standard way)
|
||||
if solver.Folder == "":
|
||||
# if not specified, default to the user's home path
|
||||
# (e.g. in Windows "C:\Documents and Settings\username\My Documents", in Linux "/home/username")
|
||||
solver.Folder = FreeCAD.ConfigGet("UserHomePath")
|
||||
folder = solver.Folder
|
||||
if not os.path.isdir(folder):
|
||||
# if 'folder' does not exists, create it
|
||||
os.mkdir(folder)
|
||||
# check if exists
|
||||
if os.path.isfile(folder + os.sep + filename):
|
||||
# filename already exists! Do not overwrite
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Filename already exists") + " '" + str(folder) + str(os.sep) + str(filename) + "'\n")
|
||||
return
|
||||
# filename already exists! Check if overwrite
|
||||
diag = QtGui.QMessageBox()
|
||||
diag.setText("File '" + str(filename) + "' exists in the folder '" + str(folder) + "'")
|
||||
diag.setInformativeText("Do you want to overwrite?")
|
||||
diag.setStandardButtons(QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok)
|
||||
ret = diag.exec_()
|
||||
if ret == QtGui.QMessageBox.Cancel:
|
||||
return
|
||||
FreeCAD.Console.PrintMessage(QT_TRANSLATE_NOOP("EM","Exporting to FastHenry file ") + "'" + folder + os.sep + filename + "'\n")
|
||||
with open(folder + os.sep + filename, 'w') as fid:
|
||||
# serialize the header
|
||||
|
@ -111,6 +135,13 @@ def makeFHInputFile(doc=None,filename=None,folder=None):
|
|||
for segment in segments:
|
||||
segment.Proxy.serialize(fid)
|
||||
fid.write("\n")
|
||||
# then the paths
|
||||
paths = [obj for obj in doc.Objects if Draft.getType(obj) == "FHPath"]
|
||||
if paths:
|
||||
fid.write("* Segments from paths\n")
|
||||
for path in paths:
|
||||
path.Proxy.serialize(fid)
|
||||
fid.write("\n")
|
||||
# then the planes
|
||||
planes = [obj for obj in doc.Objects if Draft.getType(obj) == "FHPlane"]
|
||||
if planes:
|
||||
|
@ -148,8 +179,6 @@ class _CommandFHInputFile:
|
|||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# init properties (future)
|
||||
#self.Length = None
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
|
@ -161,5 +190,3 @@ class _CommandFHInputFile:
|
|||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHInputFile',_CommandFHInputFile())
|
||||
|
||||
#pts = [obj for obj in FreeCAD.ActiveDocument.Objects if Draft.getType(obj) == "Point"]
|
||||
|
|
85
EM_FHNode.py
85
EM_FHNode.py
|
@ -31,12 +31,11 @@ __url__ = "http://www.fastfieldsolvers.com"
|
|||
|
||||
# defines
|
||||
#
|
||||
# default node color
|
||||
EMFHNODE_DEF_NODECOLOR = (1.0,0.0,0.0)
|
||||
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
|
||||
|
@ -55,13 +54,21 @@ __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)
|
||||
''' Creates a FastHenry node ('N' statement in FastHenry)
|
||||
|
||||
'baseobj' is the point object whose position is used as base for the FNNode
|
||||
If no 'baseobj' is given, X,Y,Z are used as coordinates
|
||||
'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:
|
||||
TBD
|
||||
node = makeFHNode(X=1.0,Y=2.0,Z=0.0)
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -91,8 +98,6 @@ def makeFHNode(baseobj=None,X=0.0,Y=0.0,Z=0.0,color=None,size=None,name='FHNode'
|
|||
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))
|
||||
# force recompute to show the Python object
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# return the newly created Python object
|
||||
return obj
|
||||
|
||||
|
@ -112,20 +117,22 @@ class _FHNode:
|
|||
def execute(self, obj):
|
||||
''' this method is mandatory. It is called on Document.recompute()
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHNode execute\n") #debug
|
||||
#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("\n_FHNode onChanged(" + str(prop)+")\n") #debug
|
||||
#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
|
||||
|
@ -134,7 +141,7 @@ class _FHNode:
|
|||
'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 empty string.
|
||||
Defaults to an empty string.
|
||||
'''
|
||||
pos = self.getAbsCoord()
|
||||
if extension == "":
|
||||
|
@ -146,14 +153,17 @@ class _FHNode:
|
|||
fid.write("\n")
|
||||
|
||||
def getAbsCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the absolute node coordinates
|
||||
''' 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
|
||||
return self.Object.Shape.Point
|
||||
# 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 relative node coordinates w.r.t. the Placement
|
||||
''' 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))
|
||||
|
@ -161,13 +171,13 @@ class _FHNode:
|
|||
return Vector(self.Object.X,self.Object.Y,self.Object.Z)
|
||||
|
||||
def setRelCoord(self,rel_coord,placement=None):
|
||||
''' Sets the relative node position w.r.t. the placement
|
||||
''' Sets the node position relative to the placement
|
||||
|
||||
'rel_coord': FreeCAD.Vector containing the relative node coordinates w.r.t. the Placement
|
||||
'placement': the new placement. If 'None', the placement is not changed
|
||||
'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. change of position is not visible
|
||||
just by calling this function)
|
||||
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
|
||||
|
@ -180,17 +190,18 @@ class _FHNode:
|
|||
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 absolute node coordinates
|
||||
'placement': the new placement. If 'None', the placement is not changed
|
||||
'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. change of position is not visible
|
||||
just by calling this function)
|
||||
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
|
||||
rel_coord = self.Object.Placement.inverse().multVec(abs_coord)
|
||||
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
|
||||
|
@ -217,9 +228,8 @@ class _ViewProviderFHNode:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -227,11 +237,11 @@ class _ViewProviderFHNode:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' 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 in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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')
|
||||
|
@ -275,15 +285,14 @@ class _CommandFHNode:
|
|||
done = True
|
||||
# if no selection, or nothing good in the selected objects
|
||||
if not done:
|
||||
#FreeCAD.DraftWorkingPlane.setup()
|
||||
FreeCAD.DraftWorkingPlane.setup()
|
||||
# get a 3D point via Snapper, setting the callback functions
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
|
||||
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"
|
||||
'''This function is called by the Snapper when it has a 3D point'''
|
||||
if point == None:
|
||||
return
|
||||
#coord = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
||||
coord = point
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
|
||||
FreeCADGui.addModule("EM")
|
||||
|
@ -294,6 +303,14 @@ class _CommandFHNode:
|
|||
#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())
|
||||
|
||||
|
|
475
EM_FHPath.py
Normal file
475
EM_FHPath.py
Normal file
|
@ -0,0 +1,475 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* 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 Path Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
EMFHPATH_DEF_SEGWIDTH = 0.2
|
||||
EMFHPATH_DEF_SEGHEIGHT = 0.2
|
||||
# default max number of segments into which a curve is discretized
|
||||
EMFHPATH_DEF_DISCR = 3
|
||||
# the coefficient to apply to the segment width (height) to get
|
||||
# the minimum radius of curvature allowed
|
||||
EMFHPATH_TIMESWIDTH = 3
|
||||
# imported defines
|
||||
from EM_Globals import EMFHSEGMENT_PARTOL, EMFHSEGMENT_LENTOL
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import DraftVecUtils
|
||||
from EM_Globals import getAbsCoordBodyPart, makeSegShape
|
||||
from FreeCAD import Vector
|
||||
|
||||
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 makeFHPath(baseobj=None,name='FHPath'):
|
||||
''' Creates a FastHenry Path (a set connected 'E' FastHenry statements)
|
||||
|
||||
'baseobj' is the object on which the path is based.
|
||||
If no 'baseobj' is given, the user must assign a base
|
||||
object later on, to be able to use this object.
|
||||
The 'baseobj' is mandatory, and can be any shape containing edges,
|
||||
even if the Path is designed to work best with the support of
|
||||
a sketch or a wire.
|
||||
'name' is the name of the object
|
||||
|
||||
Example:
|
||||
path = makeFHPath(myWire)
|
||||
'''
|
||||
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 _FHPath
|
||||
_FHPath(obj)
|
||||
# manage ViewProvider object
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderFHPath(obj.ViewObject)
|
||||
# set base ViewObject properties to user-selected values (if any)
|
||||
# check if 'baseobj' is a wire (only base object allowed), and only if not passed any node
|
||||
if baseobj:
|
||||
# if right type of base
|
||||
if not baseobj.isDerivedFrom("Part::Feature"):
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath can only be based on objects derived from Part::Feature"))
|
||||
return
|
||||
# check validity
|
||||
if baseobj.Shape.isNull():
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath base object shape is null"))
|
||||
return
|
||||
if not baseobj.Shape.isValid():
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath base object shape is invalid"))
|
||||
return
|
||||
obj.Base = baseobj
|
||||
# hide the base object
|
||||
if obj.Base and FreeCAD.GuiUp:
|
||||
obj.Base.ViewObject.hide()
|
||||
# return the newly created Python object
|
||||
return obj
|
||||
|
||||
class _FHPath:
|
||||
'''The EM FastHenry Path object'''
|
||||
def __init__(self, obj):
|
||||
''' Add properties '''
|
||||
obj.addProperty("App::PropertyLink", "Base", "EM", QT_TRANSLATE_NOOP("App::Property","The base object this component is built upon"))
|
||||
obj.addProperty("App::PropertyLinkList","Nodes","EM",QT_TRANSLATE_NOOP("App::Property","The list of FHNodes along the path. Not for direct user modification."))
|
||||
obj.addProperty("App::PropertyLength","Width","EM",QT_TRANSLATE_NOOP("App::Property","Path width ('w' segment parameter)"))
|
||||
obj.addProperty("App::PropertyLength","Height","EM",QT_TRANSLATE_NOOP("App::Property","Path height ('h' segment parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","Discr","EM",QT_TRANSLATE_NOOP("App::Property","Max number of segments into which curves will be discretized"))
|
||||
obj.addProperty("App::PropertyFloat","Sigma","EM",QT_TRANSLATE_NOOP("App::Property","Path conductivity ('sigma' segment parameter)"))
|
||||
obj.addProperty("App::PropertyVector","ww","EM",QT_TRANSLATE_NOOP("App::Property","Path cross-section direction along width at the start of the path ('wx', 'wy', 'wz' segment parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","nhinc","EM",QT_TRANSLATE_NOOP("App::Property","Number of filaments in the height direction ('nhinc' segment parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","nwinc","EM",QT_TRANSLATE_NOOP("App::Property","Number of filaments in the width direction ('nwinc' segment parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","rh","EM",QT_TRANSLATE_NOOP("App::Property","Ratio of adjacent filaments in the height direction ('rh' segment parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","rw","EM",QT_TRANSLATE_NOOP("App::Property","Ratio of adjacent filaments in the width direction ('rw' segment parameter)"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHPath"
|
||||
obj.Discr = EMFHPATH_DEF_DISCR
|
||||
# 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("_FHPath execute()\n") #debug
|
||||
# the Path needs a 'Base' object
|
||||
if not obj.Base:
|
||||
return
|
||||
# if right type of base
|
||||
if not obj.Base.isDerivedFrom("Part::Feature"):
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath can only be based on objects derived from Part::Feature"))
|
||||
return
|
||||
# check validity
|
||||
if obj.Base.Shape.isNull():
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath base object shape is null"))
|
||||
return
|
||||
if not obj.Base.Shape.isValid():
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPath base object shape is invalid"))
|
||||
return
|
||||
if obj.Width == None or obj.Width <= 0:
|
||||
obj.Width = EMFHPATH_DEF_SEGWIDTH
|
||||
if obj.Height == None or obj.Height <= 0:
|
||||
obj.Height = EMFHPATH_DEF_SEGHEIGHT
|
||||
# the FHPath has no Placement in itself; nodes positions will be in absolute
|
||||
# coordinates, as this is what FastHenry understands.
|
||||
# The FHSPath Placement is kept at zero, and the 'Base'
|
||||
# object Position will be used to find the absolute coordinates
|
||||
# of the vertexes, and the segments cross-section orientation will be
|
||||
# calculated in absolute coordinates from the Positions rotations.
|
||||
# This last part is different from FHSegment.
|
||||
if obj.Placement <> FreeCAD.Placement():
|
||||
obj.Placement = FreeCAD.Placement()
|
||||
# define nodes and segments
|
||||
edges_raw = []
|
||||
# checking TypeId; cannot check type(obj), too generic
|
||||
if obj.Base.TypeId == "Sketcher::SketchObject":
|
||||
if obj.Base.Shape.ShapeType == "Wire":
|
||||
edges_raw.extend(obj.Base.Shape.Edges)
|
||||
# compound
|
||||
elif obj.Base.TypeId == "Part::Compound":
|
||||
edges_raw.extend(obj.Base.Shape.Edges)
|
||||
# line or DWire (Draft Wire)
|
||||
elif obj.Base.TypeId == "Part::Part2DObjectPython":
|
||||
if obj.Base.Shape.ShapeType == "Wire" or obj.Base.Shape.ShapeType == "Edge":
|
||||
edges_raw.extend(obj.Base.Shape.Edges)
|
||||
# wire created by upgrading a set of (connected) edges
|
||||
elif obj.Base.TypeId == "Part::Feature":
|
||||
if obj.Base.Shape.ShapeType == "Wire":
|
||||
edges_raw.extend(obj.Base.Shape.Edges)
|
||||
# any other part, provided it has a 'Shape' attribute
|
||||
else:
|
||||
if hasattr(obj.Base, "Shape"):
|
||||
edges_raw.extend(obj.Base.Shape.Edges)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Unsupported base object type for FHPath"))
|
||||
return
|
||||
# sort the edges. Remark: the edge list might be disconnected (e.g. can happen with a compound
|
||||
# containing different edges / wires / sketches). We will join the dangling endpoints with segments later on
|
||||
edges = Part.__sortEdges__(edges_raw)
|
||||
if edges == []:
|
||||
return
|
||||
# get the max between the 'obj.Width' and the 'obj.Height'
|
||||
if obj.Width > obj.Height:
|
||||
geodim = obj.Width
|
||||
else:
|
||||
geodim = obj.Height
|
||||
# scan edges and derive node positions
|
||||
self.nodeCoords = []
|
||||
# initialize 'lastvertex' to the position of the first vertex,
|
||||
# (as if we had a previous segment)
|
||||
lastvertex = edges[0].valueAt(edges[0].FirstParameter)
|
||||
self.nodeCoords.append(lastvertex)
|
||||
for edge in edges:
|
||||
# might also rely on "edge.Curve.discretize(Deflection=geodim)"
|
||||
# where Deflection is the max distance between any point on the curve,
|
||||
# and the polygon approximating the curve
|
||||
if type(edge.Curve) == Part.Circle:
|
||||
# discretize only if required by the user, and if the curvature radius is not too small
|
||||
# vs. the max between the 'obj.Width' and the 'obj.Height'
|
||||
if obj.Discr <= 1 or edge.Curve.Radius < geodim*EMFHPATH_TIMESWIDTH:
|
||||
ddisc = 1
|
||||
else:
|
||||
ddisc = obj.Discr
|
||||
elif type(edge.Curve) == Part.Ellipse:
|
||||
# discretize
|
||||
if obj.Discr <= 1 or edge.Curve.MajorRadius < geodim*EMFHPATH_TIMESWIDTH or edge.Curve.MinorRadius < geodim*EMFHPATH_TIMESWIDTH:
|
||||
ddisc = 1
|
||||
else:
|
||||
ddisc = obj.Discr
|
||||
elif type(edge.Curve) == Part.Line:
|
||||
# if Part.Line, do not discretize
|
||||
ddisc = 1
|
||||
else:
|
||||
# if any other type of curve, discretize, no matter what.
|
||||
# It will be up to the user to decide if the discretization is ok.
|
||||
if obj.Discr <= 1:
|
||||
ddisc = 1
|
||||
else:
|
||||
ddisc = obj.Discr
|
||||
# check if the edge is not too short (could happen e.g. for Part.Line)
|
||||
# Note that we calculate the length from 'lastvertex', as we may have skipped also
|
||||
# some previous edges, if too short in their turn
|
||||
if edge.Length < geodim*EMFHPATH_TIMESWIDTH:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","An edge of the Base object supporting the FHPath is too short. FastHenry simulation may fail."))
|
||||
step = (edge.LastParameter - edge.FirstParameter) / ddisc
|
||||
# if same the last vertex of the previous edge is coincident
|
||||
# with the first vertex of the next edge, skip the vertex
|
||||
if (lastvertex-edge.valueAt(edge.FirstParameter)).Length < EMFHSEGMENT_LENTOL:
|
||||
start = 1
|
||||
else:
|
||||
start = 0
|
||||
for i in range(start, ddisc):
|
||||
# always skip last vertex, will add this at the end
|
||||
self.nodeCoords.append(edge.valueAt(edge.FirstParameter + i*step))
|
||||
# now add the very last vertex ('LastParameter' provides the exact position)
|
||||
lastvertex = edge.valueAt(edge.LastParameter)
|
||||
self.nodeCoords.append(lastvertex)
|
||||
if len(self.nodeCoords) < 2:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Less than two nodes found, cannot create the FHPath"))
|
||||
return
|
||||
# find the cross-section orientation of the first segment, according to the 'Base' object Placement.
|
||||
# If 'obj.ww' is not defined, use the FastHenry default (see makeSegShape() )
|
||||
self.ww = []
|
||||
if obj.ww.Length < EMFHSEGMENT_LENTOL:
|
||||
# this is zero anyway (i.e. below 'EMFHSEGMENT_LENTOL')
|
||||
self.ww = [Vector(0,0,0)]
|
||||
else:
|
||||
# transform 'obj.ww' according to the 'Base' Placement
|
||||
# (transation is don't care, we worry about rotation)
|
||||
self.ww = [obj.Base.Placement.multVec(obj.ww)]
|
||||
shapes = []
|
||||
# get node positions in absolute coordinates (at least two nodes exist, checked above)
|
||||
n1 = getAbsCoordBodyPart(obj.Base,self.nodeCoords[0])
|
||||
n2 = getAbsCoordBodyPart(obj.Base,self.nodeCoords[1])
|
||||
vNext = n2-n1
|
||||
for i in range(1, len(self.nodeCoords)):
|
||||
vPrev = vNext
|
||||
shape = makeSegShape(n1,n2,obj.Width,obj.Height,self.ww[-1])
|
||||
shapes.append(shape)
|
||||
# now we must calculate the cross-section orientation
|
||||
# of the next segment, i.e. update 'ww'
|
||||
if i < len(self.nodeCoords)-1:
|
||||
n1 = n2
|
||||
n2 = getAbsCoordBodyPart(obj.Base,self.nodeCoords[i+1])
|
||||
vNext = n2-n1
|
||||
# get angle in radians
|
||||
angle = vPrev.getAngle(vNext)
|
||||
# if the angle is actually greater than EMFHSEGMENT_PARTOL (i.e. the segments are not co-linear
|
||||
# or almost co-linear)
|
||||
if angle*FreeCAD.Units.Radian > EMFHSEGMENT_PARTOL:
|
||||
normal = vPrev.cross(vNext)
|
||||
# rotate 'ww'
|
||||
ww = DraftVecUtils.rotate(self.ww[-1],angle,normal)
|
||||
else:
|
||||
# otherwise, keep the previous orientation
|
||||
ww = self.ww[-1]
|
||||
self.ww.append(ww)
|
||||
shape = Part.makeCompound(shapes)
|
||||
# now create or assign FHNodes
|
||||
nodes = obj.Nodes
|
||||
numnodes = len(nodes)
|
||||
modified = False
|
||||
import EM_FHNode
|
||||
# if there are less FHNodes than required, extend them
|
||||
if numnodes < len(self.nodeCoords):
|
||||
modified = True
|
||||
for index in range(0,len(self.nodeCoords)-numnodes):
|
||||
# create a new FHNode at the nodeCoords position
|
||||
node = EM_FHNode.makeFHNode(X=self.nodeCoords[numnodes+index].x, Y=self.nodeCoords[numnodes+index].y, Z=self.nodeCoords[numnodes+index].z)
|
||||
# insert the new node before the last (the last node always stays the same,
|
||||
# to preserve FHPath attachments to other structures, if the FHPath shape changes)
|
||||
nodes.insert(-1,node)
|
||||
# if instead there are more FHNodes than required, must remove some of them
|
||||
elif numnodes > len(self.nodeCoords):
|
||||
# but do it only if there are more than two nodes left in the FHPath,
|
||||
# otherwise we assume this is a temporary change of FHPath shape,
|
||||
# and we preserve the end nodes (do not remove them)
|
||||
if numnodes > 2:
|
||||
modified = True
|
||||
# scan backwards, skipping the last node (last element is 'numnodes-1',
|
||||
# and range scans up to the last element before 'numnodes-len(self.nodeCoords)-1'
|
||||
for index in range(numnodes-2,len(self.nodeCoords)-2,-1):
|
||||
# remove the node from the 'nodes' list, but keeping the last node
|
||||
node = nodes[index]
|
||||
nodes.pop(index)
|
||||
# check if we can safely remove the extra nodes from the Document;
|
||||
# this can be done only if they do not belong to any other object.
|
||||
# So if the 'InList' member contains one element only, this is
|
||||
# the parent FHPath (we actually check for zero as well, even if
|
||||
# this should never happen), so we can remove the FHNode
|
||||
if len(node.InList) <= 1:
|
||||
node.Document.removeObject(node.Name)
|
||||
# and finally correct node positions
|
||||
for node, nodeCoord in zip(nodes, self.nodeCoords):
|
||||
# only if node position is not correct, change it
|
||||
if (node.Proxy.getAbsCoord()-nodeCoord).Length > EMFHSEGMENT_LENTOL:
|
||||
node.Proxy.setAbsCoord(nodeCoord)
|
||||
# only if we modified the list of nodes, re-assign it to the FHPath
|
||||
if modified:
|
||||
obj.Nodes = nodes
|
||||
# shape may be None, e.g. if endpoints coincide. Do not assign in this case
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
#FreeCAD.Console.PrintWarning("_FHPath execute() ends\n") #debug
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("_FHPath 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
|
||||
if not hasattr(self,"ww"):
|
||||
# on restore, self.ww is not there anymore; must recreate through execute(),
|
||||
# but first check we have all the needed attributes
|
||||
if hasattr(obj,"Base"):
|
||||
if hasattr(obj.Base,"Shape"):
|
||||
if not obj.Base.Shape.isNull():
|
||||
if obj.Base.Shape.isValid():
|
||||
self.execute(obj)
|
||||
#FreeCAD.Console.PrintWarning("_FHPath onChanged(" + str(prop)+") ends\n") #debug
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
'''
|
||||
if len(self.Object.Nodes) > 1:
|
||||
if len(self.Object.Nodes) == len(self.ww)+1:
|
||||
for index in range(0,len(self.Object.Nodes)-1):
|
||||
fid.write("E" + self.Object.Label + str(index) + " N" + self.Object.Nodes[index].Label + " N" + self.Object.Nodes[index+1].Label)
|
||||
fid.write(" w=" + str(self.Object.Width.Value) + " h=" + str(self.Object.Height.Value))
|
||||
if self.Object.Sigma > 0:
|
||||
fid.write(" sigma=" + str(self.Object.Sigma))
|
||||
if self.ww[index].Length >= EMFHSEGMENT_LENTOL:
|
||||
fid.write(" wx=" + str(self.ww[index].x) + " wy=" + str(self.ww[index].y) + " wz=" + str(self.ww[index].z))
|
||||
if self.Object.nhinc > 0:
|
||||
fid.write(" nhinc=" + str(self.Object.nhinc))
|
||||
if self.Object.nwinc > 0:
|
||||
fid.write(" nwinc=" + str(self.Object.nwinc))
|
||||
if self.Object.rh > 0:
|
||||
fid.write(" rh=" + str(self.Object.rh))
|
||||
if self.Object.rw > 0:
|
||||
fid.write(" rw=" + str(self.Object.rw))
|
||||
fid.write("\n")
|
||||
else:
|
||||
FreeCAD.Console.PrintError(translate("EM","Error when serializing FHPath. Number of nodes does not match number of segments + 1"))
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Cannot serialize FHPath. Less than two nodes found."))
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHPath:
|
||||
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 claimChildren(self):
|
||||
''' Used to place other objects as children in the tree'''
|
||||
c = []
|
||||
if hasattr(self,"Object"):
|
||||
if hasattr(self.Object,"Base"):
|
||||
c.append(self.Object.Base)
|
||||
if hasattr(self.Object,"Nodes"):
|
||||
c.extend(self.Object.Nodes)
|
||||
return c
|
||||
|
||||
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, 'path_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHPath:
|
||||
''' The EM FastHenry Path (FHPath) command definition
|
||||
'''
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : os.path.join(iconPath, 'path_icon.svg') ,
|
||||
'MenuText': QT_TRANSLATE_NOOP("EM_FHPath","FHPath"),
|
||||
'Accel': "E, T",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHPath","Creates a Path object (set of connected FastHenry segments) from a selected base object (sketch, wire or any shape containing edges)")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
# get the selected object(s)
|
||||
selection = FreeCADGui.Selection.getSelectionEx()
|
||||
# if selection is not empty
|
||||
done = False
|
||||
for selobj in selection:
|
||||
# automatic mode
|
||||
if selobj.Object.isDerivedFrom("Part::Feature"):
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHPath"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('obj=EM.makeFHPath(FreeCAD.ActiveDocument.'+selobj.Object.Name+')')
|
||||
# autogrouping, for later on
|
||||
#FreeCADGui.addModule("Draft")
|
||||
#FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# this is not a mistake. The double recompute() is needed to show the new FHNode object
|
||||
# that have been created by the first execute(), called upon the first recompute()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
done = True
|
||||
if done == False:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","No valid object found in the selection for the creation of a FHPath. Nothing done."))
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHPath',_CommandFHPath())
|
||||
|
214
EM_FHPlane.py
214
EM_FHPlane.py
|
@ -45,7 +45,7 @@ EMFHNODE_DEF_NODENAMEEXT = "p"
|
|||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import numpy as np
|
||||
from math import sqrt
|
||||
import EM
|
||||
from EM_Globals import EMFHNODE_DEF_NODECOLOR
|
||||
from FreeCAD import Vector
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -65,15 +65,28 @@ __dir__ = os.path.dirname(__file__)
|
|||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHPlane(baseobj=None,thickness=None,seg1=None,seg2=None,nodes=[],holes=[],name='FHPlane'):
|
||||
'''Creates a FastHenry uniform Plane ('G' statement in FastHenry)
|
||||
''' Creates a FastHenry uniform Plane ('G' statement in FastHenry)
|
||||
|
||||
'baseobj' is the object on which the node is based.
|
||||
This can be a Part::Box or a Draft::Rectangle.
|
||||
If no 'baseobj' is given, the user must assign a base
|
||||
object later on, to be able to use this object.
|
||||
'baseobj' is the object on which the node is based.
|
||||
This can be a Part::Box or a Draft::Rectangle.
|
||||
If no 'baseobj' is given, the user must assign a base
|
||||
object later on, to be able to use this object.
|
||||
'thickness' is a float defining the plane thickness. If not defines,
|
||||
it defaults to EMFHPLANE_DEF_THICKNESS
|
||||
'seg1' is an integer defining the number of segments
|
||||
along the x dimension of the plane
|
||||
('seg1' parameter in FastHenry)
|
||||
'seg2' is an integer defining the number of segments
|
||||
along the y dimension of the plane
|
||||
('seg2' parameter in FastHenry)
|
||||
'nodes' is an array of FHNode objects, specifying the nodes that
|
||||
will be adopted by the plane
|
||||
'holes' is an array of FHPlaneHole objects, specifying the holes that
|
||||
will be adopted by the plane
|
||||
'name' is the name of the object
|
||||
|
||||
Example:
|
||||
TBD
|
||||
plane = makeFHPlane(myDraftRect,thickness=1.0,seg1=15,seg2=15,[App.ActiveDocument.Node001])
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -152,9 +165,11 @@ class _FHPlane:
|
|||
obj.addProperty("App::PropertyLinkList","Nodes","EM",QT_TRANSLATE_NOOP("App::Property","Nodes for connections to the plane"))
|
||||
obj.addProperty("App::PropertyLinkList","Holes","EM",QT_TRANSLATE_NOOP("App::Property","Holes in the plane"))
|
||||
obj.addProperty("App::PropertyBool","FineMesh","Component",QT_TRANSLATE_NOOP("App::Property","Specifies if this the plane fine mesh is shown (i.e. composing segments)"))
|
||||
obj.addProperty("App::PropertyBool","ShowNodes","Component",QT_TRANSLATE_NOOP("App::Property","Show the internal node grid supporting the plane"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHPlane"
|
||||
self.FineMesh = False
|
||||
self.ShowNodes = True
|
||||
# save the object in the class, to store or retrieve specific data from it
|
||||
# from within the class
|
||||
self.Object = obj
|
||||
|
@ -267,8 +282,20 @@ class _FHPlane:
|
|||
# relative to the plane coordinate system
|
||||
abs_pos = node.Proxy.getAbsCoord()
|
||||
node.Proxy.setAbsCoord(abs_pos,self.Object.Base.Placement)
|
||||
# set the Z relative coordinate to zero (node will be on the plane)
|
||||
node.Z = 0.0
|
||||
# and change its color to mark that this is a plane node
|
||||
node.ViewObject.PointColor = EMFHNODE_DEF_PLANENODECOLOR
|
||||
|
||||
def removeNode(self,node):
|
||||
''' Remove a node from the FHPlane
|
||||
|
||||
'node': FHNode object
|
||||
|
||||
The color of the node will be reverted to the default node color
|
||||
'''
|
||||
node.ViewObject.PointColor = EMFHNODE_DEF_NODECOLOR
|
||||
|
||||
def adoptHole(self,hole):
|
||||
''' Adopt a hole in the FHPlane
|
||||
|
||||
|
@ -280,24 +307,33 @@ class _FHPlane:
|
|||
# relative to the plane coordinate system
|
||||
abs_pos = hole.Proxy.getAbsCoord()
|
||||
hole.Proxy.setAbsCoord(abs_pos,self.Object.Base.Placement)
|
||||
# set the Z relative coordinate to zero (node will be on the plane)
|
||||
hole.Z = 0.0
|
||||
|
||||
def removeHole(self,hole):
|
||||
''' Remove a hole from the FHPlane
|
||||
|
||||
'hole': FHPlaneHole object
|
||||
'''
|
||||
return
|
||||
|
||||
def makeFinePlane(self,obj,length,width,thickness,segwid1,segwid2):
|
||||
''' Compute a fine mesh plane shape given:
|
||||
|
||||
TBD
|
||||
TBD
|
||||
'obj' the FHPlane object
|
||||
'length' the length of the plane (along the x dimension)
|
||||
'width' the width of the plane (along the y dimension)
|
||||
'thickness' the thickness of the plane (along the z dimension)
|
||||
'segwid1' the width of the segments along the x dimension
|
||||
'segwid2' the width of the segments along the y dimension
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'width': segment width
|
||||
'height': segment height
|
||||
'ww': cross-section direction (along width)
|
||||
The function returns a Shape object defining the plane.
|
||||
|
||||
The plane is assumed to lie in the standard default position (default Placement)
|
||||
(with the Placement.Base in the origin, no rotation, and length along x, width along y, thickness along z)
|
||||
Its placement will be moved and rotated by the caller.
|
||||
'''
|
||||
segments=[]
|
||||
shapes=[]
|
||||
# prepare the array of the internal nodes of the plane. 'True' means that the node
|
||||
# exists, i.e. has not been removed due to holes in the plane.
|
||||
# The number of nodes is equal to the number of segments along the edge plus one;
|
||||
|
@ -307,13 +343,11 @@ class _FHPlane:
|
|||
seg1len=length/obj.seg1
|
||||
seg2len=width/obj.seg2
|
||||
#
|
||||
# debug
|
||||
for seg1 in range(obj.seg1+1):
|
||||
for seg2 in range(obj.seg2+1):
|
||||
shape = Part.Vertex(Vector(seg1len*seg1,seg2len*seg2,-0.1))
|
||||
segments.append(shape)
|
||||
# end debug
|
||||
#
|
||||
if obj.ShowNodes == True:
|
||||
for seg1 in range(obj.seg1+1):
|
||||
for seg2 in range(obj.seg2+1):
|
||||
shape = Part.Vertex(Vector(seg1len*seg1,seg2len*seg2,-0.1))
|
||||
shapes.append(shape)
|
||||
# now process the holes
|
||||
for hole in obj.Holes:
|
||||
if hole.Type == 'Point':
|
||||
|
@ -369,7 +403,7 @@ class _FHPlane:
|
|||
if nodes[seg1][seg2] and nodes[seg1+1][seg2]:
|
||||
# makeBox(length, width, height, point, direction)
|
||||
boxshape = Part.makeBox(seg1len,segwid1,thickness,Vector(seg1len*seg1,-segwid1/2+seg2len*seg2,0))
|
||||
segments.append(boxshape)
|
||||
shapes.append(boxshape)
|
||||
# layout segments along plane width
|
||||
for seg1 in range(obj.seg1+1):
|
||||
for seg2 in range(obj.seg2):
|
||||
|
@ -377,16 +411,19 @@ class _FHPlane:
|
|||
if nodes[seg1][seg2] and nodes[seg1][seg2+1]:
|
||||
# makeBox(length, width, height, point, direction)
|
||||
boxshape = Part.makeBox(segwid2,seg2len,thickness,Vector(-segwid2/2+seg1len*seg1,seg2len*seg2,0))
|
||||
segments.append(boxshape)
|
||||
shape = Part.makeCompound(segments)
|
||||
shapes.append(boxshape)
|
||||
shape = Part.makeCompound(shapes)
|
||||
return shape
|
||||
|
||||
def findNearestNode(self,x_coord,y_coord,obj,seg1len,seg2len):
|
||||
''' find the plane node nearest to the given point (in local plane coordinates)
|
||||
''' Find the plane node nearest to the given point (in local plane coordinates)
|
||||
|
||||
'x_coord' and 'y_coord' are the point coordinates, of type Base.Quantity
|
||||
'obj' is the FHPlane object
|
||||
'seg1len' and 'seg2len' are the lengths of the segments along the lenght and width, respectively
|
||||
'x_coord' and 'y_coord' are the point coordinates, of type Base.Quantity
|
||||
'obj' is the FHPlane object
|
||||
'seg1len' and 'seg2len' are the lengths of the segments along the lenght and width, respectively
|
||||
|
||||
The function returns a tuple containing two integers corresponding to the node
|
||||
position within the plane array of internal nodes.
|
||||
'''
|
||||
# as we cast to int, + 0.5 is used to approximate to the next larger int
|
||||
# if greater than x.5 and to the previous smaller int otherwise
|
||||
|
@ -403,6 +440,14 @@ class _FHPlane:
|
|||
nodeY = obj.seg2
|
||||
return (nodeX,nodeY)
|
||||
|
||||
def onBeforeChange(self, obj, prop):
|
||||
''' take action before the 'obj' object 'prop' will change
|
||||
'''
|
||||
# save current list of nodes and holes, before the change,
|
||||
# to be able to see which nodes/holes have been added or removed
|
||||
self.Nodes = obj.Nodes
|
||||
self.Holes = obj.Holes
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
|
@ -413,11 +458,27 @@ class _FHPlane:
|
|||
# so we must "re-attach" (re-create) the 'self.Object'
|
||||
self.Object = obj
|
||||
if prop == "Nodes":
|
||||
# check for new nodes
|
||||
for node in obj.Nodes:
|
||||
self.adoptNode(node)
|
||||
# if the node has been just added to the plane, adopt it
|
||||
if not node in self.Nodes:
|
||||
self.adoptNode(node)
|
||||
# check for removed nodes
|
||||
for node in self.Nodes:
|
||||
# if the node is not present any more in the plane, remove it
|
||||
if not node in obj.Nodes:
|
||||
self.removeNode(node)
|
||||
if prop == "Holes":
|
||||
# check for new holes
|
||||
for hole in obj.Holes:
|
||||
self.adoptHole(hole)
|
||||
# if the hole has been just added to the plane, adopt it
|
||||
if not hole in self.Holes:
|
||||
self.adoptHole(hole)
|
||||
# check for removed holes
|
||||
for hole in self.Holes:
|
||||
# if the hole is not present any more in the plane, remove it
|
||||
if not hole in obj.Holes:
|
||||
self.removeHole(hole)
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
|
@ -508,9 +569,8 @@ class _ViewProviderFHPlane:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -518,8 +578,8 @@ class _ViewProviderFHPlane:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' If the 'prop' property changed for the ViewProvider 'vp' '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n") # debug
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
|
@ -534,7 +594,7 @@ class _ViewProviderFHPlane:
|
|||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'plane_icon.svg')
|
||||
|
@ -552,7 +612,7 @@ class _CommandFHPlane:
|
|||
return {'Pixmap' : os.path.join(iconPath, 'plane_icon.svg') ,
|
||||
'MenuText': QT_TRANSLATE_NOOP("EM_FHPlane","FHPlane"),
|
||||
'Accel': "E, P",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHPlane","Creates a FastHenry uniform Plane object from scratch or from a selected base object (Part::Box or Draft::Rectangle)")}
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHPlane","Creates a FastHenry uniform Plane object from a selected base object (Part::Box or Draft::Rectangle)")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
@ -602,5 +662,85 @@ class _CommandFHPlane:
|
|||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","No base Part::Box or Draft::Rectangle selected. Cannot create a FHPlane.\n"))
|
||||
|
||||
class _CommandFHPlaneAddRemoveNodeHole:
|
||||
''' The EM FastHenry uniform Plane (FHPlane) add, or remove, Node, or Hole, command definition
|
||||
'''
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : os.path.join(iconPath, 'plane_addremovenodehole_icon.svg') ,
|
||||
'MenuText': QT_TRANSLATE_NOOP("EM_FHPlaneAddRemoveNodeHole","FHPlaneAddRemoveNodeHole"),
|
||||
'Accel': "E, A",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHPlaneAddRemoveNodeHole","Add/remove FHNodes or FHPlaneHoles to/from a FastHenry uniform Plane object")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
# get the selected object(s)
|
||||
selection = FreeCADGui.Selection.getSelectionEx()
|
||||
plane = None
|
||||
nodes = []
|
||||
holes = []
|
||||
# if selection is not empty
|
||||
for selobj in selection:
|
||||
# find the FHPlane
|
||||
if Draft.getType(selobj.Object) == "FHPlane":
|
||||
plane = selobj.Object
|
||||
elif Draft.getType(selobj.Object) == "FHNode":
|
||||
nodes.append(selobj.Object)
|
||||
elif Draft.getType(selobj.Object) == "FHPlaneHole":
|
||||
holes.append(selobj.Object)
|
||||
# whether or not a plane is selected, nodes/holes to be removed belong to a plane.
|
||||
# Let's start with them.
|
||||
addnodes = []
|
||||
for node in nodes:
|
||||
found = False
|
||||
for parent in node.InList:
|
||||
if Draft.getType(parent) == "FHPlane":
|
||||
# parent found, now remove the node
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Remove FHPlane Node"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('nodes = FreeCAD.ActiveDocument.'+parent.Name+'.Nodes')
|
||||
FreeCADGui.doCommand('nodes.remove(FreeCAD.ActiveDocument.'+node.Name+')')
|
||||
FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+parent.Name+'.Nodes = nodes')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
found = True
|
||||
if found == False:
|
||||
addnodes.append(node)
|
||||
addholes = []
|
||||
for hole in holes:
|
||||
found = False
|
||||
for parent in hole.InList:
|
||||
if Draft.getType(parent) == "FHPlane":
|
||||
# parent found, now remove the hole
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Remove FHPlane Hole"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('holes = FreeCAD.ActiveDocument.'+parent.Name+'.Holes')
|
||||
FreeCADGui.doCommand('holes.remove(FreeCAD.ActiveDocument.'+hole.Name+')')
|
||||
FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+parent.Name+'.Holes = holes')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
found = True
|
||||
if found == False:
|
||||
addholes.append(hole)
|
||||
# the remaining nodes/holes can only be added to a plane. For this we need a plane
|
||||
# in the selection
|
||||
if plane:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Add nodes / holes to the FHPlane"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('nodeList = FreeCAD.ActiveDocument.'+plane.Name+'.Nodes')
|
||||
for node in addnodes:
|
||||
FreeCADGui.doCommand('nodeList.append(FreeCAD.ActiveDocument.'+node.Name+')')
|
||||
FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+plane.Name+'.Nodes = nodeList')
|
||||
FreeCADGui.doCommand('holeList = FreeCAD.ActiveDocument.'+plane.Name+'.Holes')
|
||||
for hole in addholes:
|
||||
FreeCADGui.doCommand('holeList.append(FreeCAD.ActiveDocument.'+hole.Name+')')
|
||||
FreeCADGui.doCommand('FreeCAD.ActiveDocument.'+plane.Name+'.Holes = holeList')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
# recompute the document (assuming something has changed; otherwise this is dummy)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHPlane',_CommandFHPlane())
|
||||
FreeCADGui.addCommand('EM_FHPlaneAddRemoveNodeHole',_CommandFHPlaneAddRemoveNodeHole())
|
||||
|
|
|
@ -55,17 +55,28 @@ __dir__ = os.path.dirname(__file__)
|
|||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHPlaneHole(baseobj=None,X=0.0,Y=0.0,Z=0.0,holetype=None,length=None,width=None,radius=None,name='FHPlaneHole'):
|
||||
'''Creates a FastHenry conductive plane hole (within a uniform plane 'G' statement in FastHenry)
|
||||
''' Creates a FastHenry conductive plane hole (within a uniform plane 'G' statement in FastHenry)
|
||||
|
||||
'baseobj' is the point object on which the node is based.
|
||||
If no 'baseobj' is given, the user must assign a base
|
||||
object later on, to be able to use this object.
|
||||
'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 hole, in absolute coordinate system
|
||||
'Y' y coordinate of the hole, in absolute coordinate system
|
||||
'Z' z coordinate of the hole, in absolute coordinate system
|
||||
'holetype' is the type of hole. Allowed values are:
|
||||
"Point", "Rect", "Circle"
|
||||
'length' is the length of the hole (along the x dimension),
|
||||
in case of rectangular "Rect" hole
|
||||
'width' the width of the hole (along the y dimension),
|
||||
in case of rectangular "Rect" hole
|
||||
'radius' is the radius of the hole, in case of circular "Circle" hole
|
||||
'name' is the name of the object
|
||||
|
||||
The FHPlaneHole has to be used only within a FHPlane object. The FHPlaneHole
|
||||
will be taken as child by the FHPlane.
|
||||
The FHPlaneHole has to be used only within a FHPlane object. The FHPlaneHole
|
||||
will be taken as child by the FHPlane.
|
||||
|
||||
Example:
|
||||
TBD
|
||||
hole = makeFHPlaneHole(X=1.0,Y=1.0,Z=0.0,holetype="Rect",length=1.0,width=2.0)
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -196,12 +207,13 @@ class _FHPlaneHole:
|
|||
fid.write("\n")
|
||||
|
||||
def getAbsCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the absolute reference point coordinates
|
||||
''' Get a FreeCAD.Vector containing the reference point coordinates
|
||||
in the absolute reference system
|
||||
'''
|
||||
return self.Object.Placement.multVec(Vector(self.Object.X, self.Object.Y, self.Object.Z))
|
||||
|
||||
def getRelCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the relative reference point coordinates w.r.t. the Placement
|
||||
''' Get a FreeCAD.Vector containing the hole coordinates relative to the FHPlaneHole Placement
|
||||
|
||||
These coordinates correspond to (self.Object.X, self.Object.Y, self.Object.Z),
|
||||
that are the same as self.Object.Placement.inverse().multVec(reference_point_pos))
|
||||
|
@ -209,10 +221,10 @@ class _FHPlaneHole:
|
|||
return Vector(self.Object.X,self.Object.Y,self.Object.Z)
|
||||
|
||||
def setRelCoord(self,rel_coord,placement=None):
|
||||
''' Sets the relative reference point position w.r.t. the placement
|
||||
''' Sets the hole position relative to the placement
|
||||
|
||||
'rel_coord': FreeCAD.Vector containing the relative reference point coordinates w.r.t. the Placement
|
||||
'placement': the new placement. If 'None', the placement is not changed
|
||||
'rel_coord': FreeCAD.Vector containing the hole coordinates relative to the FHPlaneHole Placement
|
||||
'placement': a new FHPlaneHole placement. If 'None', the placement is not changed
|
||||
|
||||
Remark: the function will not recalculate() the object (i.e. change of position is not visible
|
||||
just by calling this function)
|
||||
|
@ -228,8 +240,8 @@ class _FHPlaneHole:
|
|||
def setAbsCoord(self,abs_coord,placement=None):
|
||||
''' Sets the absolute reference point position, considering the object placement, and in case forcing a new placement
|
||||
|
||||
'abs_coord': FreeCAD.Vector containing the absolute reference point coordinates
|
||||
'placement': the new placement. If 'None', the placement is not changed
|
||||
'abs_coord': FreeCAD.Vector containing the hole 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. change of position is not visible
|
||||
just by calling this function)
|
||||
|
@ -265,9 +277,8 @@ class _ViewProviderFHPlaneHole:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -275,11 +286,11 @@ class _ViewProviderFHPlaneHole:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' 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 in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'planehole_icon.svg')
|
||||
|
@ -323,12 +334,12 @@ class _CommandFHPlaneHole:
|
|||
done = True
|
||||
# if no selection, or nothing good in the selected objects
|
||||
if not done:
|
||||
#FreeCAD.DraftWorkingPlane.setup()
|
||||
FreeCAD.DraftWorkingPlane.setup()
|
||||
# get a 3D point via Snapper, setting the callback functions
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
|
||||
|
||||
def getPoint(self,point=None,obj=None):
|
||||
"this function is called by the snapper when it has a 3D point"
|
||||
'''This function is called by the Snapper when it has a 3D point'''
|
||||
if point == None:
|
||||
return
|
||||
coord = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
||||
|
@ -341,6 +352,14 @@ class _CommandFHPlaneHole:
|
|||
#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_FHPlaneHole',_CommandFHPlaneHole())
|
||||
|
||||
|
|
25
EM_FHPort.py
25
EM_FHPort.py
|
@ -36,7 +36,6 @@ __url__ = "http://www.fastfieldsolvers.com"
|
|||
EMFHPORT_LENTOL = 1e-12
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from FreeCAD import Vector
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -58,11 +57,12 @@ iconPath = os.path.join( __dir__, 'Resources' )
|
|||
def makeFHPort(nodeStart=None,nodeEnd=None,name='FHPort'):
|
||||
''' Creates a FastHenry port ('.external' statement in FastHenry)
|
||||
|
||||
'nodeStart' is the positive node
|
||||
'nodeEnd' is the negative node
|
||||
'nodeStart' is the positive node FHNode object
|
||||
'nodeEnd' is the negative node FHNode object
|
||||
'name' is the name of the object
|
||||
|
||||
Example:
|
||||
TBD
|
||||
port = makeFHPort(App.ActiveDocument.FHNode,App.ActiveDocument.FHNode001)
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -128,8 +128,8 @@ class _FHPort:
|
|||
def makePortShape(self,n1,n2):
|
||||
''' Compute a port shape given:
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'n1': start node position (FreeCAD.Vector)
|
||||
'n2': end node position (FreeCAD.Vector)
|
||||
'''
|
||||
# do not accept coincident nodes
|
||||
if (n2-n1).Length < EMFHPORT_LENTOL:
|
||||
|
@ -185,9 +185,8 @@ class _ViewProviderFHPort:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -195,8 +194,8 @@ class _ViewProviderFHPort:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' If the 'prop' property changed for the ViewProvider 'vp' '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n") # debug
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
|
@ -209,9 +208,9 @@ class _ViewProviderFHPort:
|
|||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'port_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
|
@ -233,8 +232,6 @@ class _CommandFHPort:
|
|||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# init properties (future)
|
||||
#self.Length = None
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
|
|
144
EM_FHSegment.py
144
EM_FHSegment.py
|
@ -33,13 +33,11 @@ __url__ = "http://www.fastfieldsolvers.com"
|
|||
#
|
||||
EMFHSEGMENT_DEF_SEGWIDTH = 0.2
|
||||
EMFHSEGMENT_DEF_SEGHEIGHT = 0.2
|
||||
# tolerance in degrees when verifying if vectors are parallel
|
||||
EMFHSEGMENT_PARTOL = 0.01
|
||||
# tolerance in length
|
||||
EMFHSEGMENT_LENTOL = 1e-12
|
||||
# imported defines
|
||||
from EM_Globals import EMFHSEGMENT_PARTOL, EMFHSEGMENT_LENTOL
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from EM_Globals import makeSegShape
|
||||
from FreeCAD import Vector
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -59,14 +57,17 @@ __dir__ = os.path.dirname(__file__)
|
|||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHSegment(baseobj=None,nodeStart=None,nodeEnd=None,name='FHSegment'):
|
||||
'''Creates a FastHenry segment ('E' statement in FastHenry)
|
||||
''' Creates a FastHenry segment ('E' statement in FastHenry)
|
||||
|
||||
'baseobj' is the line object on which the node is based.
|
||||
If no 'baseobj' is given, the user must assign a base
|
||||
object later on, to be able to use this object.
|
||||
'baseobj' is the line object on which the node is based.
|
||||
If no 'baseobj' is given, the user must provide the
|
||||
'nodeStart' and 'nodeEnd' parameters.
|
||||
'nodeStart' is the segment starting node FHNode object
|
||||
'nodeEnd' is the segment ending node FHNode object
|
||||
'name' is the name of the object
|
||||
|
||||
Example:
|
||||
TBD
|
||||
segment = makeFHSegment(nodeStart=App.ActiveDocument.FHNode002,nodeEnd=App.ActiveDocument.FHNode003)
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -89,9 +90,10 @@ def makeFHSegment(baseobj=None,nodeStart=None,nodeEnd=None,name='FHSegment'):
|
|||
if baseobj and not obj.NodeStart and not obj.NodeEnd:
|
||||
if Draft.getType(baseobj) == "Wire":
|
||||
if len(baseobj.Shape.Vertexes) == 2:
|
||||
import EM_FHNode
|
||||
obj.Base = baseobj
|
||||
obj.NodeStart = EM.makeFHNode(X=obj.Base.Start.x, Y=obj.Base.Start.y, Z=obj.Base.Start.z)
|
||||
obj.NodeEnd = EM.makeFHNode(X=obj.Base.End.x, Y=obj.Base.End.y, Z=obj.Base.End.z)
|
||||
obj.NodeStart = EM_FHNode.makeFHNode(X=obj.Base.Start.x, Y=obj.Base.Start.y, Z=obj.Base.Start.z)
|
||||
obj.NodeEnd = EM_FHNode.makeFHNode(X=obj.Base.End.x, Y=obj.Base.End.y, Z=obj.Base.End.z)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHSegments can only be based on Line objects (not multi-segment wires)"))
|
||||
else:
|
||||
|
@ -126,6 +128,7 @@ class _FHSegment:
|
|||
def execute(self, obj):
|
||||
''' this method is mandatory. It is called on Document.recompute()
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("_FHSegment execute()\n") #debug
|
||||
if obj.NodeStart == None:
|
||||
return
|
||||
elif Draft.getType(obj.NodeStart) <> "FHNode":
|
||||
|
@ -136,6 +139,17 @@ class _FHSegment:
|
|||
elif Draft.getType(obj.NodeEnd) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeEnd is not a FHNode"))
|
||||
return
|
||||
# the FHSegment has no Placement in itself:
|
||||
# 1) either it has the same placement of the Base object
|
||||
# (FHSegment will track the Base object, if present)
|
||||
# However, since the Base object is a Draft::Wire, the 'Start' and 'End' points
|
||||
# are in absolute coordinates.
|
||||
# 2) or it has a position defined by the endpoint nodes
|
||||
# So let's keep the FHSegment placement at zero, and use the FHNodes to move the segment position.
|
||||
# This is also necessary as we should not change the segment cross-section orientation
|
||||
# using the Placement, otherwise it will not be relative to the global axis system
|
||||
if obj.Placement <> FreeCAD.Placement():
|
||||
obj.Placement = FreeCAD.Placement()
|
||||
# check if we have a 'Base' object; if so, if segment end nodes
|
||||
# were already defined, re-set them according to the 'Base' object;
|
||||
# this means that the user cannot move freely the end nodes, if
|
||||
|
@ -150,10 +164,6 @@ class _FHSegment:
|
|||
return
|
||||
# ok, it's valid. Let's verify if this is a Wire.
|
||||
if Draft.getType(obj.Base) == "Wire":
|
||||
# set the FHSegment Placement to the same placement of the Base object
|
||||
# (FHSegment will track the Base object, if present)
|
||||
#obj.Placement = obj.Base.Placement
|
||||
obj.Placement = FreeCAD.Placement()
|
||||
if obj.NodeStart <> None:
|
||||
abs_pos = obj.NodeStart.Proxy.getAbsCoord()
|
||||
# 'obj.Base.Start' is an absolute position
|
||||
|
@ -172,96 +182,27 @@ class _FHSegment:
|
|||
obj.Height = EMFHSEGMENT_DEF_SEGHEIGHT
|
||||
# and finally, if everything is ok, make and assing the shape
|
||||
self.assignShape(obj)
|
||||
#FreeCAD.Console.PrintWarning("_FHSegment execute() ends\n") #debug
|
||||
|
||||
def assignShape(self, obj):
|
||||
''' Compute and assign the shape to the object 'obj' '''
|
||||
n1 = obj.NodeStart.Proxy.getAbsCoord()
|
||||
n2 = obj.NodeEnd.Proxy.getAbsCoord()
|
||||
shape = self.makeSegShape(n1,n2,obj.Width,obj.Height,obj.ww)
|
||||
shape = makeSegShape(n1,n2,obj.Width,obj.Height,obj.ww)
|
||||
# shape may be None, e.g. if endpoints coincide. Do not assign in this case
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
|
||||
def makeSegShape(self,n1,n2,width,height,ww):
|
||||
''' Compute a segment shape given:
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'width': segment width
|
||||
'height': segment height
|
||||
'ww': cross-section direction (along width)
|
||||
'''
|
||||
# do not accept coincident nodes
|
||||
if (n2-n1).Length < EMFHSEGMENT_LENTOL:
|
||||
return None
|
||||
# vector along length
|
||||
wl = n2-n1;
|
||||
# calculate the vector along the height
|
||||
wh = (ww.cross(wl))
|
||||
# if cross-section is not defined, by default the width vector
|
||||
# is assumed to lie in x-y plane perpendicular to the length.
|
||||
# If the length direction is parallel to the z-axis, then
|
||||
# the width is assumed along the x-axis.
|
||||
# The same is done if 'ww' has been defined parallel to 'wl'
|
||||
if ww.Length < EMFHSEGMENT_LENTOL or wh.Length < EMFHSEGMENT_LENTOL:
|
||||
# if length parallel to the z-axis
|
||||
if wl.getAngle(Vector(0,0,1))*FreeCAD.Units.Radian < EMFHSEGMENT_PARTOL:
|
||||
ww = Vector(1,0,0)
|
||||
else:
|
||||
ww = (wl.cross(Vector(0,0,1))).normalize()
|
||||
# and re-calculate 'wh' since we changed 'ww'
|
||||
wh = (ww.cross(wl))
|
||||
# normalize the freshly calculated 'wh'
|
||||
wh.normalize()
|
||||
# copy ww as the multiply() method changes the vector on which is called
|
||||
wwHalf = Vector(ww)
|
||||
# must normalize. We don't want to touch 'ww', as this is user's defined
|
||||
wwHalf.normalize()
|
||||
wwHalf.multiply(width / 2)
|
||||
# copy wh as the multiply() method changes the vector on which is called
|
||||
whHalf = Vector(wh)
|
||||
whHalf.multiply(height / 2)
|
||||
# calculate the vertexes
|
||||
v11 = n1 - wwHalf - whHalf
|
||||
v12 = n1 + wwHalf - whHalf
|
||||
v13 = n1 + wwHalf + whHalf
|
||||
v14 = n1 - wwHalf + whHalf
|
||||
v21 = n2 - wwHalf - whHalf
|
||||
v22 = n2 + wwHalf - whHalf
|
||||
v23 = n2 + wwHalf + whHalf
|
||||
v24 = n2 - wwHalf + whHalf
|
||||
# now make faces
|
||||
# front
|
||||
poly = Part.makePolygon( [v11,v12,v13,v14,v11])
|
||||
face1 = Part.Face(poly)
|
||||
# back
|
||||
poly = Part.makePolygon( [v21,v24,v23,v22,v21])
|
||||
face2 = Part.Face(poly)
|
||||
# left
|
||||
poly = Part.makePolygon( [v11,v14,v24,v21,v11])
|
||||
face3 = Part.Face(poly)
|
||||
# right
|
||||
poly = Part.makePolygon( [v12,v22,v23,v13,v12])
|
||||
face4 = Part.Face(poly)
|
||||
# top
|
||||
poly = Part.makePolygon( [v14,v13,v23,v24,v14])
|
||||
face5 = Part.Face(poly)
|
||||
# bottom
|
||||
poly = Part.makePolygon( [v11,v21,v22,v12,v11])
|
||||
face6 = Part.Face(poly)
|
||||
# create a shell. Does not need to be solid.
|
||||
segShell = Part.makeShell([face1,face2,face3,face4,face5,face6])
|
||||
return segShell
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHSegment onChanged(" + str(prop)+")\n") #debug
|
||||
#FreeCAD.Console.PrintWarning("_FHSegment 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("_FHSegment onChanged(" + str(prop)+") ends\n") #debug
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
|
@ -304,9 +245,8 @@ class _ViewProviderFHSegment:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -314,8 +254,8 @@ class _ViewProviderFHSegment:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' If the 'prop' property changed for the ViewProvider 'vp' '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n") # debug
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
|
@ -330,7 +270,7 @@ class _ViewProviderFHSegment:
|
|||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'segment_icon.svg')
|
||||
|
@ -354,8 +294,6 @@ class _CommandFHSegment:
|
|||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# init properties (future)
|
||||
#self.Length = None
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
|
@ -396,7 +334,7 @@ class _CommandFHSegment:
|
|||
done = True
|
||||
# if no selection, or nothing good in the selected objects
|
||||
if not done:
|
||||
#FreeCAD.DraftWorkingPlane.setup()
|
||||
FreeCAD.DraftWorkingPlane.setup()
|
||||
# get two 3D point via Snapper, setting the callback functions
|
||||
self.points = []
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
|
||||
|
@ -408,10 +346,8 @@ class _CommandFHSegment:
|
|||
self.points.append(point)
|
||||
if len(self.points) == 1:
|
||||
# get the second point
|
||||
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint)
|
||||
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint,movecallback=self.move)
|
||||
elif len(self.points) >= 2:
|
||||
#coord1 = FreeCAD.DraftWorkingPlane.getLocalCoords(self.points[0])
|
||||
#coord2 = FreeCAD.DraftWorkingPlane.getLocalCoords(self.points[1])
|
||||
coord1 = self.points[0]
|
||||
coord2 = self.points[1]
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
|
||||
|
@ -427,6 +363,14 @@ class _CommandFHSegment:
|
|||
#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_FHSegment',_CommandFHSegment())
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ EMFHSOLVER_DEFNDEC = 1
|
|||
EMFHSOLVER_DEF_FILENAME = "fasthenry_input_file.inp"
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from FreeCAD import Vector
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
@ -67,11 +66,34 @@ else:
|
|||
__dir__ = os.path.dirname(__file__)
|
||||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHSolver(units=None,sigma=None,nhinc=None,nwinc=None,rh=None,rw=None,fmin=None,fmax=None,ndec=None,filename=None,name='FHSolver'):
|
||||
'''Creates a FastHenry Solver object (all statements needed for the simulation)
|
||||
def makeFHSolver(units=None,sigma=None,nhinc=None,nwinc=None,rh=None,rw=None,fmin=None,fmax=None,ndec=None,folder=None,filename=None,name='FHSolver'):
|
||||
''' Creates a FastHenry Solver object (all statements needed for the simulation)
|
||||
|
||||
'units' is the FastHenry unit of measurement. Each unit in FreeCad will be
|
||||
one unit of the corresponding unit of measurement in FastHenry.
|
||||
Allowed values are: "km", "m", "cm", "mm", "um", "in", "mils".
|
||||
Defaults to EMFHSOLVER_DEFUNITS
|
||||
'sigma' is the float default conductivity. Defaults to EMFHSOLVER_DEF_SEGSIGMA
|
||||
'nhinc' is the integer default nhinc parameter in FastHenry, for defining
|
||||
the segment height discretization into filaments. Defaults to EMFHSOLVER_DEFNHINC
|
||||
'nwinc' is the integer default nwinc parameter in FastHenry, for defining
|
||||
the segment width discretization into filaments. Defaults to EMFHSOLVER_DEFNWINC
|
||||
'rh' is the integer default rh parameter in FastHenry, for defining
|
||||
the segment height discretization ratio. Defaults to EMFHSOLVER_DEFRH
|
||||
'rw' is the integer default rw parameter in FastHenry, for defining
|
||||
the segment width discretization ratio. Defaults to EMFHSOLVER_DEFRW
|
||||
'fmin' is the float minimum simulation frequency
|
||||
'fmax' is the float maximum simulation frequency
|
||||
'ndec' is the float value defining how many frequency points per decade
|
||||
will be simulated
|
||||
'folder' is the folder into which the FastHenry file will be saved.
|
||||
Defaults to the user's home path (e.g. in Windows "C:\Documents
|
||||
and Settings\username\My Documents", in Linux "/home/username")
|
||||
'filename' is the name of the file that will be exported.
|
||||
Defaults to EMFHSOLVER_DEF_FILENAME
|
||||
'name' is the name of the object
|
||||
Example:
|
||||
TBD
|
||||
solver = makeFHSolver()
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
|
@ -126,6 +148,12 @@ def makeFHSolver(units=None,sigma=None,nhinc=None,nwinc=None,rh=None,rw=None,fmi
|
|||
obj.Filename = filename
|
||||
else:
|
||||
obj.Filename = EMFHSOLVER_DEF_FILENAME
|
||||
if folder:
|
||||
obj.Folder = folder
|
||||
else:
|
||||
# if not specified, default to the user's home path
|
||||
# (e.g. in Windows "C:\Documents and Settings\username\My Documents", in Linux "/home/username")
|
||||
obj.Folder = FreeCAD.ConfigGet("UserHomePath")
|
||||
# return the newly created Python object
|
||||
return obj
|
||||
|
||||
|
@ -142,7 +170,8 @@ class _FHSolver:
|
|||
obj.addProperty("App::PropertyFloat","fmin","EM",QT_TRANSLATE_NOOP("App::Property","Lowest simulation frequency ('fmin' parameter in '.freq')"))
|
||||
obj.addProperty("App::PropertyFloat","fmax","EM",QT_TRANSLATE_NOOP("App::Property","Highest simulation frequency ('fmzx' parameter in '.freq')"))
|
||||
obj.addProperty("App::PropertyFloat","ndec","EM",QT_TRANSLATE_NOOP("App::Property","Number of desired frequency points per decade ('ndec' parameter in '.freq')"))
|
||||
obj.addProperty("App::PropertyFile","Filename","EM",QT_TRANSLATE_NOOP("App::Property","Simulation filename when exporting to FastHenry input file format"))
|
||||
obj.addProperty("App::PropertyPath","Folder","EM",QT_TRANSLATE_NOOP("App::Property","Folder path for exporting the file in FastHenry input file format"))
|
||||
obj.addProperty("App::PropertyString","Filename","EM",QT_TRANSLATE_NOOP("App::Property","Simulation filename when exporting to FastHenry input file format"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHSolver"
|
||||
obj.Units = EMFHSOLVER_UNITS
|
||||
|
@ -202,9 +231,8 @@ class _ViewProviderFHSolver:
|
|||
return
|
||||
|
||||
def updateData(self, fp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider updateData(), property: " + str(prop) + "\n")
|
||||
''' 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):
|
||||
|
@ -212,11 +240,11 @@ class _ViewProviderFHSolver:
|
|||
return "Flat Lines"
|
||||
|
||||
def onChanged(self, vp, prop):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
''' 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 in XMP format which will appear in the tree view. This method is optional
|
||||
''' 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, 'solver_icon.svg')
|
||||
|
@ -240,8 +268,6 @@ class _CommandFHSolver:
|
|||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# init properties (future)
|
||||
#self.Length = None
|
||||
# preferences
|
||||
#p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/EM")
|
||||
#self.Width = p.GetFloat("Width",200)
|
||||
|
|
106
EM_Globals.py
106
EM_Globals.py
|
@ -29,8 +29,112 @@ __title__="FreeCAD E.M. Workbench global definitions"
|
|||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
from FreeCAD import Vector
|
||||
|
||||
# defines
|
||||
#
|
||||
#EM_DEF_XXX = 1.0
|
||||
# default node color
|
||||
EMFHNODE_DEF_NODECOLOR = (1.0,0.0,0.0)
|
||||
# tolerance in degrees when verifying if vectors are parallel
|
||||
EMFHSEGMENT_PARTOL = 0.01
|
||||
# tolerance in length
|
||||
EMFHSEGMENT_LENTOL = 1e-12
|
||||
|
||||
import FreeCAD, Part
|
||||
from FreeCAD import Vector
|
||||
|
||||
def getAbsCoordBodyPart(obj,position):
|
||||
''' Retrieve the absolute coordinates of a point belonging to an object, even if in a Body or Part
|
||||
|
||||
'obj': object to which the 'position' is relative
|
||||
'position': FreeCAD.Vector 3D position relative to objects that contain 'obj'
|
||||
(Note: 'position' is NOT relative to the 'obj.Placement', only to the containers)
|
||||
|
||||
return value: FreeCAD.Vector 3D absolute position
|
||||
'''
|
||||
if obj == None:
|
||||
return None
|
||||
for parent in obj.InList:
|
||||
if parent.TypeId == "PartDesign::Body" or parent.TypeId == "App::Part":
|
||||
# make the position absolute
|
||||
position = parent.Placement.multVec(position)
|
||||
# and recursively check upside
|
||||
position = getAbsCoordBodyPart(parent,position)
|
||||
break
|
||||
return position
|
||||
|
||||
def makeSegShape(n1,n2,width,height,ww):
|
||||
''' Compute a segment shape given:
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'width': segment width
|
||||
'height': segment height
|
||||
'ww': cross-section direction (along width)
|
||||
|
||||
Returns the created Shape
|
||||
'''
|
||||
# do not accept coincident nodes
|
||||
if (n2-n1).Length < EMFHSEGMENT_LENTOL:
|
||||
return None
|
||||
# vector along length
|
||||
wl = n2-n1;
|
||||
# calculate the vector along the height
|
||||
wh = (ww.cross(wl))
|
||||
# if cross-section is not defined, by default the width vector
|
||||
# is assumed to lie in x-y plane perpendicular to the length.
|
||||
# If the length direction is parallel to the z-axis, then
|
||||
# the width is assumed along the x-axis.
|
||||
# The same is done if 'ww' has been defined parallel to 'wl'
|
||||
if ww.Length < EMFHSEGMENT_LENTOL or wh.Length < EMFHSEGMENT_LENTOL:
|
||||
# if length parallel to the z-axis (note that getAngle() always returns a value
|
||||
# between 0 and 180)
|
||||
angle = wl.getAngle(Vector(0,0,1))*FreeCAD.Units.Radian
|
||||
if angle < EMFHSEGMENT_PARTOL or angle > 180-EMFHSEGMENT_PARTOL:
|
||||
ww = Vector(1,0,0)
|
||||
else:
|
||||
ww = (wl.cross(Vector(0,0,1))).normalize()
|
||||
# and re-calculate 'wh' since we changed 'ww'
|
||||
wh = (ww.cross(wl))
|
||||
# normalize the freshly calculated 'wh'
|
||||
wh.normalize()
|
||||
# copy ww as the multiply() method changes the vector on which is called
|
||||
wwHalf = Vector(ww)
|
||||
# must normalize. We don't want to touch 'ww', as this is user's defined
|
||||
wwHalf.normalize()
|
||||
wwHalf.multiply(width / 2)
|
||||
# copy wh as the multiply() method changes the vector on which is called
|
||||
whHalf = Vector(wh)
|
||||
whHalf.multiply(height / 2)
|
||||
# calculate the vertexes
|
||||
v11 = n1 - wwHalf - whHalf
|
||||
v12 = n1 + wwHalf - whHalf
|
||||
v13 = n1 + wwHalf + whHalf
|
||||
v14 = n1 - wwHalf + whHalf
|
||||
v21 = n2 - wwHalf - whHalf
|
||||
v22 = n2 + wwHalf - whHalf
|
||||
v23 = n2 + wwHalf + whHalf
|
||||
v24 = n2 - wwHalf + whHalf
|
||||
# now make faces
|
||||
# front
|
||||
poly = Part.makePolygon( [v11,v12,v13,v14,v11])
|
||||
face1 = Part.Face(poly)
|
||||
# back
|
||||
poly = Part.makePolygon( [v21,v24,v23,v22,v21])
|
||||
face2 = Part.Face(poly)
|
||||
# left
|
||||
poly = Part.makePolygon( [v11,v14,v24,v21,v11])
|
||||
face3 = Part.Face(poly)
|
||||
# right
|
||||
poly = Part.makePolygon( [v12,v22,v23,v13,v12])
|
||||
face4 = Part.Face(poly)
|
||||
# top
|
||||
poly = Part.makePolygon( [v14,v13,v23,v24,v14])
|
||||
face5 = Part.Face(poly)
|
||||
# bottom
|
||||
poly = Part.makePolygon( [v11,v21,v22,v12,v11])
|
||||
face6 = Part.Face(poly)
|
||||
# create a shell. Does not need to be solid.
|
||||
segShell = Part.makeShell([face1,face2,face3,face4,face5,face6])
|
||||
return segShell
|
||||
|
||||
|
|
2
Init.py
2
Init.py
|
@ -26,4 +26,4 @@
|
|||
|
||||
|
||||
# add import/export types
|
||||
FreeCAD.addExportType("FastHenry file format (*.inp)","exportFH")
|
||||
#FreeCAD.addExportType("FastHenry file format (*.inp)","exportFH")
|
||||
|
|
13
InitGui.py
13
InitGui.py
|
@ -42,9 +42,11 @@ class EMWorkbench(Workbench):
|
|||
# import the EM module (and therefore all commands makeXXX)
|
||||
import EM
|
||||
# E.M. tools
|
||||
self.emtools = ["EM_FHSolver", "EM_FHNode", "EM_FHSegment", "EM_FHPlane",
|
||||
"EM_FHPlaneHole", "EM_FHEquiv", "EM_FHPort", "EM_FHInputFile"]
|
||||
self.emfhtools = ["EM_FHSolver", "EM_FHNode", "EM_FHSegment", "EM_FHPath", "EM_FHPlane",
|
||||
"EM_FHPlaneHole", "EM_FHPlaneAddRemoveNodeHole", "EM_FHEquiv", "EM_FHPort", "EM_FHInputFile"]
|
||||
# draft tools
|
||||
# setup menus
|
||||
self.draftcmdList = ["Draft_Line","Draft_Rectangle"]
|
||||
self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset",
|
||||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
|
||||
|
@ -57,10 +59,11 @@ class EMWorkbench(Workbench):
|
|||
'Draft_Snap_Dimensions','Draft_Snap_WorkingPlane']
|
||||
|
||||
def QT_TRANSLATE_NOOP(scope, text): return text
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","E.M. tools"),self.emtools)
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","E.M. tools"),self.emfhtools)
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","Draft creation tools"),self.draftcmdList)
|
||||
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench","Draft mod tools"),self.draftmodtools)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("EM","&EM"),self.emtools)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("EM","&Draft"),self.draftmodtools+self.treecmdList)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("EM","&EM"),self.emfhtools)
|
||||
self.appendMenu(QT_TRANSLATE_NOOP("EM","&Draft"),self.draftcmdList+self.draftmodtools+self.treecmdList)
|
||||
self.appendMenu([QT_TRANSLATE_NOOP("EM","&Draft"),QT_TRANSLATE_NOOP("arch","Snapping")],self.snapList)
|
||||
#FreeCADGui.addIconPath(":/icons")
|
||||
#FreeCADGui.addLanguagePath(":/translations")
|
||||
|
|
599
Resources/path_icon.svg
Normal file
599
Resources/path_icon.svg
Normal file
|
@ -0,0 +1,599 @@
|
|||
<?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="svg2816"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="path_icon.svg">
|
||||
<defs
|
||||
id="defs2818">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-476.76952 : -33.034494 : 1"
|
||||
inkscape:vp_y="-583.44547 : 761.90599 : 0"
|
||||
inkscape:vp_z="165.08877 : 517.07075 : 1"
|
||||
inkscape:persp3d-origin="12.24239 : 32.422882 : 1"
|
||||
id="perspective2824" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3653"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3675"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3697"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3720"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3742"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3764"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3785"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3835"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781"
|
||||
id="linearGradient3787"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789"
|
||||
id="linearGradient3795"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-6"
|
||||
id="linearGradient3804-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-6">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-7" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-5"
|
||||
id="linearGradient3806-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-5">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-6" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-0"
|
||||
id="linearGradient3804-36"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-0">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-6" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-1"
|
||||
id="linearGradient3806-6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-1">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-8" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-8"
|
||||
id="linearGradient3804-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.814743"
|
||||
y2="-5.3353744" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-8">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-12"
|
||||
id="linearGradient3806-36"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-12">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-9" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-03"
|
||||
id="linearGradient3804-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.814743"
|
||||
y2="-5.3353744" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-03">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-61" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-2"
|
||||
id="linearGradient3806-63"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-2">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-0" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="18.0625"
|
||||
fy="41.625"
|
||||
fx="25.1875"
|
||||
cy="41.625"
|
||||
cx="25.1875"
|
||||
gradientTransform="matrix(1,0,0,0.32526,0,28.08607)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3169"
|
||||
xlink:href="#linearGradient2269-0"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2269-0">
|
||||
<stop
|
||||
offset="0"
|
||||
id="stop2271-4"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
id="stop2273-87"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-53.20799 : 529.50086 : 1"
|
||||
inkscape:vp_y="749.07605 : 599.82867 : 0"
|
||||
inkscape:vp_z="510.68531 : -100.27862 : 1"
|
||||
inkscape:persp3d-origin="22.83722 : 42.023228 : 1"
|
||||
id="perspective2824-9" />
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-306.9418 : -330.3005 : 1"
|
||||
inkscape:vp_y="-923.45584 : 261.03382 : 0"
|
||||
inkscape:vp_z="-122.11032 : 494.58444 : 1"
|
||||
inkscape:persp3d-origin="45.582874 : 14.871445 : 1"
|
||||
id="perspective2824-0" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.5287771"
|
||||
inkscape:cx="13.493907"
|
||||
inkscape:cy="22.064998"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3005"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2821">
|
||||
<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:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[triplus]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>ArchWorkbench</dc:title>
|
||||
<dc:date>2016-02-26</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/ArchWorkbench.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
sodipodi:type="inkscape:box3d"
|
||||
id="g4206-59"
|
||||
style="fill:#d9c6c0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:perspectiveID="#perspective2824-0"
|
||||
inkscape:corner0="0.02270184 : 0.0013788012 : 0 : 1"
|
||||
inkscape:corner7="-0.0094747102 : -0.015361581 : 0.061115991 : 1">
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4216-9"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="13"
|
||||
d="M 51.628412,60.711531 63.276325,49.875116 52.502605,21.953274 41.831365,33.007576 Z"
|
||||
points="63.276325,49.875116 52.502605,21.953274 41.831365,33.007576 51.628412,60.711531 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4208-6"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="6"
|
||||
d="M 36.512566,56.438725 51.628412,60.711531 41.831365,33.007576 27.567894,28.975712 Z"
|
||||
points="51.628412,60.711531 41.831365,33.007576 27.567894,28.975712 36.512566,56.438725 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4218-7"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="11"
|
||||
d="M 27.567894,28.975712 37.802722,17.798049 52.502605,21.953274 41.831365,33.007576 Z"
|
||||
points="37.802722,17.798049 52.502605,21.953274 41.831365,33.007576 27.567894,28.975712 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4210-2"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="5"
|
||||
d="M 36.512566,56.438725 47.669451,45.463512 37.802722,17.798049 27.567894,28.975712 Z"
|
||||
points="47.669451,45.463512 37.802722,17.798049 27.567894,28.975712 36.512566,56.438725 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4214-7"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="14"
|
||||
d="M 47.669451,45.463512 63.276325,49.875116 52.502605,21.953274 37.802722,17.798049 Z"
|
||||
points="63.276325,49.875116 52.502605,21.953274 37.802722,17.798049 47.669451,45.463512 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4212-74"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="3"
|
||||
d="M 36.512566,56.438725 47.669451,45.463512 63.276325,49.875116 51.628412,60.711531 Z"
|
||||
points="47.669451,45.463512 63.276325,49.875116 51.628412,60.711531 36.512566,56.438725 " />
|
||||
</g>
|
||||
<g
|
||||
sodipodi:type="inkscape:box3d"
|
||||
id="g4206-5"
|
||||
style="fill:#d9c6c0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:perspectiveID="#perspective2824-9"
|
||||
inkscape:corner0="0.036480552 : 0.0053349794 : 0 : 1"
|
||||
inkscape:corner7="0.0043040016 : -0.011405403 : 0.061115991 : 1">
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4216-2"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="13"
|
||||
d="m 11.917878,11.419733 2.086545,15.279895 28.491248,7.891983 -2.805597,-14.660442 z"
|
||||
points="14.004423,26.699628 42.495671,34.591611 39.690074,19.931169 11.917878,11.419733 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4208-8"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="6"
|
||||
d="m 24.016339,1.7317933 -12.098461,9.6879397 27.772196,8.511436 11.424798,-9.1485 z"
|
||||
points="11.917878,11.419733 39.690074,19.931169 51.114872,10.782669 24.016339,1.7317933 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4218-8"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="11"
|
||||
d="m 51.114872,10.782669 3.150636,14.38415 -11.769837,9.424792 -2.805597,-14.660442 z"
|
||||
points="54.265508,25.166819 42.495671,34.591611 39.690074,19.931169 51.114872,10.782669 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4210-0"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="5"
|
||||
d="M 24.016339,1.7317933 26.490503,16.7013 54.265508,25.166819 51.114872,10.782669 Z"
|
||||
points="26.490503,16.7013 54.265508,25.166819 51.114872,10.782669 24.016339,1.7317933 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4214-5"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="14"
|
||||
d="m 26.490503,16.7013 -12.48608,9.998328 28.491248,7.891983 11.769837,-9.424792 z"
|
||||
points="14.004423,26.699628 42.495671,34.591611 54.265508,25.166819 26.490503,16.7013 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4212-7"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="3"
|
||||
d="M 24.016339,1.7317933 26.490503,16.7013 14.004423,26.699628 11.917878,11.419733 Z"
|
||||
points="26.490503,16.7013 14.004423,26.699628 11.917878,11.419733 24.016339,1.7317933 " />
|
||||
</g>
|
||||
<g
|
||||
sodipodi:type="inkscape:box3d"
|
||||
id="g4206"
|
||||
style="fill:#d9c6c0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:perspectiveID="#perspective2824"
|
||||
inkscape:corner0="0.02270184 : 0.0013788012 : 0 : 1"
|
||||
inkscape:corner7="-0.0094747102 : -0.015361581 : 0.061115991 : 1">
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4216"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="13"
|
||||
d="M 10.151042,44.474409 25.96833,42.76703 34.053296,13.951484 18.88791,16.418066 Z"
|
||||
points="25.96833,42.76703 34.053296,13.951484 18.88791,16.418066 10.151042,44.474409 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4208"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="6"
|
||||
d="M 0.60075117,32.002937 10.151042,44.474409 18.88791,16.418066 9.8761552,4.6498541 Z"
|
||||
points="10.151042,44.474409 18.88791,16.418066 9.8761552,4.6498541 0.60075117,32.002937 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4218"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="11"
|
||||
d="M 9.8761552,4.6498541 24.765813,1.8232068 34.053296,13.951484 18.88791,16.418066 Z"
|
||||
points="24.765813,1.8232068 34.053296,13.951484 18.88791,16.418066 9.8761552,4.6498541 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4210"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="5"
|
||||
d="M 0.60075117,32.002937 16.107804,29.890431 24.765813,1.8232068 9.8761552,4.6498541 Z"
|
||||
points="16.107804,29.890431 24.765813,1.8232068 9.8761552,4.6498541 0.60075117,32.002937 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4214"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="14"
|
||||
d="M 16.107804,29.890431 25.96833,42.76703 34.053296,13.951484 24.765813,1.8232068 Z"
|
||||
points="25.96833,42.76703 34.053296,13.951484 24.765813,1.8232068 16.107804,29.890431 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4212"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.87024283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="3"
|
||||
d="M 0.60075117,32.002937 16.107804,29.890431 25.96833,42.76703 10.151042,44.474409 Z"
|
||||
points="16.107804,29.890431 25.96833,42.76703 10.151042,44.474409 0.60075117,32.002937 " />
|
||||
</g>
|
||||
<circle
|
||||
style="fill:#ff3d00;fill-opacity:1;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path5776"
|
||||
cx="12.806991"
|
||||
cy="37.502781"
|
||||
r="1.5456711" />
|
||||
<circle
|
||||
style="fill:#ff3d00;fill-opacity:1;stroke:#ff0000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path5776-8"
|
||||
cx="49.682285"
|
||||
cy="52.959496"
|
||||
r="1.5456711" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 24 KiB |
639
Resources/plane_addremovenodehole_icon.svg
Normal file
639
Resources/plane_addremovenodehole_icon.svg
Normal file
|
@ -0,0 +1,639 @@
|
|||
<?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="svg2816"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="plane_addremovenodehole_icon.svg">
|
||||
<defs
|
||||
id="defs2818">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-441.94317 : 279.20114 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="484.34212 : 327.67408 : 1"
|
||||
inkscape:persp3d-origin="27.804607 : 23.541435 : 1"
|
||||
id="perspective2824" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3653"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3675"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3697"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3720"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3742"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3764"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3785"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3835"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781"
|
||||
id="linearGradient3787"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789"
|
||||
id="linearGradient3795"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-6"
|
||||
id="linearGradient3804-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-6">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-7" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-5" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-5"
|
||||
id="linearGradient3806-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-5">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-6" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-0"
|
||||
id="linearGradient3804-36"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.882462"
|
||||
y2="-7.2011309" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-0">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-6" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-1"
|
||||
id="linearGradient3806-6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-1">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-8" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-8"
|
||||
id="linearGradient3804-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.814743"
|
||||
y2="-5.3353744" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-8">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-9" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-12"
|
||||
id="linearGradient3806-36"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-12">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-9" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-3" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3781-03"
|
||||
id="linearGradient3804-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="93.501396"
|
||||
y1="-0.52792466"
|
||||
x2="92.814743"
|
||||
y2="-5.3353744" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3781-03">
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3783-61" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3785-0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3789-2"
|
||||
id="linearGradient3806-63"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="140.23918"
|
||||
y1="124.16501"
|
||||
x2="137.60997"
|
||||
y2="117.06711" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3789-2">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3791-0" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3793-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="18.0625"
|
||||
fy="41.625"
|
||||
fx="25.1875"
|
||||
cy="41.625"
|
||||
cx="25.1875"
|
||||
gradientTransform="matrix(1,0,0,0.32526,0,28.08607)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3169"
|
||||
xlink:href="#linearGradient2269-0"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient2269-0">
|
||||
<stop
|
||||
offset="0"
|
||||
id="stop2271-4"
|
||||
style="stop-color:#000000;stop-opacity:1;" />
|
||||
<stop
|
||||
offset="1"
|
||||
id="stop2273-87"
|
||||
style="stop-color:#000000;stop-opacity:0;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836"
|
||||
id="linearGradient3922"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(-0.17115118,-0.14860822,0.14656966,-0.17353174,-48.236494,137.85809)"
|
||||
x1="11.390151"
|
||||
y1="453.55045"
|
||||
x2="54.509644"
|
||||
y2="485.54004" />
|
||||
<linearGradient
|
||||
id="linearGradient3836">
|
||||
<stop
|
||||
style="stop-color:#a40000;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3838" />
|
||||
<stop
|
||||
style="stop-color:#ef2929;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3840" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="-644.83794 : 167.37374 : 1"
|
||||
inkscape:vp_y="0 : 475.05947 : 0"
|
||||
inkscape:vp_z="680.20381 : 190.40128 : 1"
|
||||
inkscape:persp3d-origin="27.13151 : 45.920184 : 1"
|
||||
id="perspective2824-0" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0,-0.41606601,0.34661681,0,-2.1489268,49.482837)"
|
||||
y2="36.079998"
|
||||
x2="21.689653"
|
||||
y1="29.279999"
|
||||
x1="56.172409"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3036"
|
||||
xlink:href="#linearGradient3895"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient3895">
|
||||
<stop
|
||||
style="stop-color:#729fcf;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3897" />
|
||||
<stop
|
||||
style="stop-color:#204a87;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3899" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0,-0.41606601,0.34661681,0,28.654091,49.482835)"
|
||||
y2="36.079998"
|
||||
x2="21.689653"
|
||||
y1="29.279999"
|
||||
x1="56.172409"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3036-7"
|
||||
xlink:href="#linearGradient3895"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.00647546,0.41601562,-0.34657483,0.00539458,33.126726,15.636518)"
|
||||
y2="36.079998"
|
||||
x2="21.689653"
|
||||
y1="29.279999"
|
||||
x1="56.172409"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3036-7-4"
|
||||
xlink:href="#linearGradient3895"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0.00647546,0.41601562,-0.34657483,0.00539458,63.364767,15.656699)"
|
||||
y2="36.079998"
|
||||
x2="21.689653"
|
||||
y1="29.279999"
|
||||
x1="56.172409"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3036-7-4-6"
|
||||
xlink:href="#linearGradient3895"
|
||||
inkscape:collect="always" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.5287771"
|
||||
inkscape:cx="-19.075592"
|
||||
inkscape:cy="22.064998"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3005"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2821">
|
||||
<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:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>[triplus]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>ArchWorkbench</dc:title>
|
||||
<dc:date>2016-02-26</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Arch/Resources/icons/ArchWorkbench.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<ellipse
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.08379006;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4199"
|
||||
cx="47.142971"
|
||||
cy="52.407467"
|
||||
rx="14.94198"
|
||||
ry="8.0968657" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3465a4;fill-opacity:1;stroke:#280000;stroke-width:1.25258482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250"
|
||||
d="M 8.5887742,46.827759 A 9.4782489,9.2571 0 1 1 22.987079,58.870413 9.4782489,9.2571 0 1 1 8.5887742,46.827759 Z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3922);fill-opacity:1;stroke:#ef2929;stroke-width:1.25258482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7"
|
||||
d="M 9.5515912,47.642699 A 8.2385055,8.045913 0 1 1 22.066623,58.109703 8.2385055,8.045913 0 0 1 9.5515912,47.642699 Z" />
|
||||
<g
|
||||
sodipodi:type="inkscape:box3d"
|
||||
id="g4206"
|
||||
style="fill:#d9c6c0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:perspectiveID="#perspective2824-0"
|
||||
inkscape:corner0="0.036480552 : 0.0053349794 : 0 : 1"
|
||||
inkscape:corner7="0.0043040016 : -0.011405403 : 0.061115991 : 1">
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4216"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="13"
|
||||
d="M 3.4804956,19.03261 24.251747,22.954344 61.879312,14.386815 41.161569,10.93461 Z"
|
||||
points="24.251747,22.954344 61.879312,14.386815 41.161569,10.93461 3.4804956,19.03261 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4208"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="6"
|
||||
d="m 3.4804956,11.359839 0,7.672771 37.6810734,-8.098 0,-7.2455378 z"
|
||||
points="3.4804956,19.03261 41.161569,10.93461 41.161569,3.6890722 3.4804956,11.359839 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4218"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="11"
|
||||
d="m 41.161569,3.6890722 20.717743,3.233384 0,7.4643588 -20.717743,-3.452205 z"
|
||||
points="61.879312,6.9224562 61.879312,14.386815 41.161569,10.93461 41.161569,3.6890722 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4210"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="5"
|
||||
d="M 3.4804956,11.359839 24.251747,15.035749 61.879312,6.9224562 41.161569,3.6890722 Z"
|
||||
points="24.251747,15.035749 61.879312,6.9224562 41.161569,3.6890722 3.4804956,11.359839 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4214"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="14"
|
||||
d="m 24.251747,15.035749 0,7.918595 37.627565,-8.567529 0,-7.4643588 z"
|
||||
points="24.251747,22.954344 61.879312,14.386815 61.879312,6.9224562 24.251747,15.035749 " />
|
||||
<path
|
||||
sodipodi:type="inkscape:box3dside"
|
||||
id="path4212"
|
||||
style="fill:#d9c6c0;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.64871824;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
inkscape:box3dsidetype="3"
|
||||
d="m 3.4804956,11.359839 20.7712514,3.67591 0,7.918595 L 3.4804956,19.03261 Z"
|
||||
points="24.251747,15.035749 24.251747,22.954344 3.4804956,19.03261 3.4804956,11.359839 " />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 15.892341,9.0208412 36.659589,12.085668"
|
||||
id="path4818"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 29.140951,6.1503082 20.767248,3.064827"
|
||||
id="path4818-4"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 13.684239,12.995423 51.23306,5.2405532"
|
||||
id="path4818-4-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 13.463428,13.547449 0.01109,8.364271"
|
||||
id="path4818-4-4-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 36.648497,12.112183 0.01109,8.364271"
|
||||
id="path4818-4-4-6-9"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.68573666;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 49.455487,9.2416512 0.01109,8.3642708"
|
||||
id="path4818-4-4-6-9-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3036);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:0.52012336;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 3.0503254,31.592007 3.2997924,-0.02869 0,9.182145 5.1853882,0 0,-9.182145 3.299791,0.02869 -5.8924862,-7.489188 z"
|
||||
id="path3343"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
inkscape:export-xdpi="4.1683898"
|
||||
inkscape:export-ydpi="4.1683898" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3036-7);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:0.52012336;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 33.853343,31.592005 3.299793,-0.02869 0,9.182145 5.185388,0 0,-9.182145 3.29979,0.02869 -5.892485,-7.489188 z"
|
||||
id="path3343-5"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
inkscape:export-xdpi="4.1683898"
|
||||
inkscape:export-ydpi="4.1683898" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3036-7-4);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:0.52012336;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 28.206548,33.6061 -3.298946,0.08004 -0.142907,-9.181034 -5.18476,0.0807 0.142907,9.181034 -3.299837,0.02267 6.008329,7.396573 z"
|
||||
id="path3343-5-9"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
inkscape:export-xdpi="4.1683898"
|
||||
inkscape:export-ydpi="4.1683898" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3036-7-4-6);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:0.52012336;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 58.444589,33.626281 -3.298946,0.08004 -0.142907,-9.181034 -5.18476,0.0807 0.142907,9.181034 -3.299837,0.02267 6.008329,7.396572 z"
|
||||
id="path3343-5-9-4"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
inkscape:export-xdpi="4.1683898"
|
||||
inkscape:export-ydpi="4.1683898" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 24 KiB |
|
@ -153,9 +153,9 @@ def export_segs(filename="", disc=3, custDot="", FHbug=False, w=0, h=0, nhinc=0,
|
|||
elif type(edge.Curve) == Part.Line:
|
||||
# if w=0, the following condition is always true
|
||||
if edge.Length > abs(w)*3:
|
||||
nodes.append(edge.Curve.StartPoint)
|
||||
nodes.append(edge.valueAt(edge.FirstParameter))
|
||||
# quick & dirty trick
|
||||
lastvertex = edge.Curve.EndPoint
|
||||
lastvertex = edge.valueAt(edge.LastParameter)
|
||||
else:
|
||||
FreeCAD.Console.PrintMessage("Unknown edge: " + str(type(edge.Curve)) + " in '" + obj.Label + "',, skipping\n")
|
||||
# now add the very last vertex
|
||||
|
|
88
wbrl.py
Normal file
88
wbrl.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. 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 *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import EM
|
||||
import EM_FHNode
|
||||
import EM_FHSegment
|
||||
import EM_FHPath
|
||||
import EM_FHPlane
|
||||
import EM_FHPlaneHole
|
||||
import EM_FHEquiv
|
||||
import EM_FHPort
|
||||
import EM_FHSolver
|
||||
import EM_FHInputFile
|
||||
reload(EM)
|
||||
reload(EM_FHNode)
|
||||
reload(EM_FHSegment)
|
||||
reload(EM_FHPath)
|
||||
reload(EM_FHPlane)
|
||||
reload(EM_FHPlaneHole)
|
||||
reload(EM_FHEquiv)
|
||||
reload(EM_FHPort)
|
||||
reload(EM_FHSolver)
|
||||
reload(EM_FHInputFile)
|
||||
|
||||
def go(c='s'):
|
||||
''' Function to reload the workbench objects and commands
|
||||
'''
|
||||
import EM
|
||||
import EM_Globals
|
||||
import EM_FHNode
|
||||
import EM_FHSegment
|
||||
import EM_FHPath
|
||||
import EM_FHPlane
|
||||
import EM_FHPlaneHole
|
||||
import EM_FHEquiv
|
||||
import EM_FHPort
|
||||
import EM_FHSolver
|
||||
import EM_FHInputFile
|
||||
reload(EM)
|
||||
reload(EM_Globals)
|
||||
reload(EM_FHNode)
|
||||
reload(EM_FHSegment)
|
||||
reload(EM_FHPath)
|
||||
reload(EM_FHPlane)
|
||||
reload(EM_FHPlaneHole)
|
||||
reload(EM_FHEquiv)
|
||||
reload(EM_FHPort)
|
||||
reload(EM_FHSolver)
|
||||
reload(EM_FHInputFile)
|
||||
if c=='n' or c==1:
|
||||
EM_FHNode._CommandFHNode().Activated()
|
||||
elif c=='s' or c==2:
|
||||
EM_FHSegment._CommandFHSegment().Activated()
|
||||
elif c=='p' or c==3:
|
||||
EM_FHPlane._CommandFHPlane().Activated()
|
||||
elif c=='h' or c==4:
|
||||
EM_FHPlaneHole._CommandFHPlaneHole().Activated()
|
||||
elif c=='t' or c==5:
|
||||
EM_FHPath._CommandFHPath().Activated()
|
||||
elif c=='a' or c==6:
|
||||
EM_FHPlane._CommandFHPlaneAddRemoveNodeHole().Activated()
|
||||
|
||||
#import EM
|
||||
#import EM_FHNode
|
||||
#EM_FHNode._CommandFHNode().Activated()
|
||||
|
||||
#import EM_FHPlaneHole
|
||||
#EM_FHPlaneHole._CommandFHPlaneHole().Activated()
|
Loading…
Reference in New Issue
Block a user