* Support for conductive planes (G) and .equiv statement
- Conductive uniform plane support ('G' statement in FastHenry), including holes - Support for Node equivalence ('-equiv' statement in FastHenry) - FreeCAD save support for the models created in the workbench - Updated copyright including Efficient Power Conversion Corp. Inc.
8
EM.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -42,7 +45,10 @@ if FreeCAD.GuiUp:
|
|||
|
||||
from EM_FHNode import *
|
||||
from EM_FHSegment import *
|
||||
from EM_FHPlaneHole import *
|
||||
from EM_FHPlane import *
|
||||
from EM_FHPort import *
|
||||
from EM_FHEquiv import *
|
||||
from EM_FHSolver import *
|
||||
from EM_FHInputFile import *
|
||||
|
||||
|
|
248
EM_FHEquiv.py
Normal file
|
@ -0,0 +1,248 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* Efficient Power Conversion Corporation, Inc. http://epc-co.com *
|
||||
#* *
|
||||
#* Developed by FastFieldSolvers S.R.L. under contract by EPC *
|
||||
#* http://www.fastfieldsolvers.com *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry node Equivalence Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
# tolerance in distance between nodes to define a port
|
||||
EMFHEQUIV_LENTOL = 1e-12
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
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 makeFHEquiv(node1=None,node2=None,name='FHEquiv'):
|
||||
''' Creates a FastHenry node equivalence ('.equiv' statement in FastHenry)
|
||||
|
||||
'node1' is the first node to shortcut
|
||||
'node2' is the second node to shortcut
|
||||
|
||||
Example:
|
||||
TBD
|
||||
'''
|
||||
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 _FHEquiv
|
||||
_FHEquiv(obj)
|
||||
# manage ViewProvider object
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderFHEquiv(obj.ViewObject)
|
||||
# set base ViewObject properties to user-selected values (if any)
|
||||
# check if 'nodeStart' is a FHNode, and if so, assign it as first node
|
||||
if node1:
|
||||
if Draft.getType(node2) == "FHNode":
|
||||
obj.Node1 = node1
|
||||
# check if 'nodeEnd' is a FHNode, and if so, assign it as second node
|
||||
if node2:
|
||||
if Draft.getType(node2) == "FHNode":
|
||||
obj.Node2 = node2
|
||||
# return the newly created Python object
|
||||
return obj
|
||||
|
||||
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.Proxy = self
|
||||
self.Type = "FHEquiv"
|
||||
# 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()
|
||||
'''
|
||||
if obj.Node1 == None:
|
||||
return
|
||||
elif Draft.getType(obj.Node1) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Node1 is not a FHNode"))
|
||||
return
|
||||
if obj.Node2 == None:
|
||||
return
|
||||
elif Draft.getType(obj.Node2) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Node2 is not a FHNode"))
|
||||
return
|
||||
# and finally, if everything is ok, make and assign the shape
|
||||
self.assignShape(obj)
|
||||
|
||||
def assignShape(self, obj):
|
||||
''' Compute and assign the shape to the object 'obj' '''
|
||||
n1 = obj.Node1.Proxy.getAbsCoord()
|
||||
n2 = obj.Node1.Proxy.getAbsCoord()
|
||||
shape = self.makeEquivShape(n1,n2)
|
||||
# shape may be None, e.g. if endpoints coincide. Do not assign in this case.
|
||||
# FHEquiv is still valid, but not visible.
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
|
||||
def makeEquivShape(self,n1,n2):
|
||||
''' Compute a node equivalence shape given:
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'''
|
||||
# do not accept coincident nodes
|
||||
if (n2-n1).Length < EMFHEQUIV_LENTOL:
|
||||
return None
|
||||
line = Part.makeLine(n1, n2)
|
||||
return line
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHEquiv 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
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
'''
|
||||
fid.write(".equiv N" + self.Object.Node1.Label + " N" + self.Object.Node2.Label + "\n")
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHEquiv:
|
||||
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):
|
||||
''' 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 '''
|
||||
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):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
c = []
|
||||
if hasattr(self,"Object"):
|
||||
if hasattr(self.Object,"Node1"):
|
||||
c.append(self.Object.Node1)
|
||||
if hasattr(self.Object,"Node2"):
|
||||
c.append(self.Object.Node2)
|
||||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format 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')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHEquiv:
|
||||
''' The EM FastHenry equivalent node (FHEquiv) command definition
|
||||
'''
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : os.path.join(iconPath, 'equiv_icon.svg') ,
|
||||
'MenuText': QT_TRANSLATE_NOOP("EM_FHEquiv","FHEquiv"),
|
||||
'Accel': "E, E",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHEquiv","Creates a FastHenry equivalent node object from two FHNodes")}
|
||||
|
||||
def IsActive(self):
|
||||
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)
|
||||
# get the selected object(s)
|
||||
selection = FreeCADGui.Selection.getSelectionEx()
|
||||
nodes = []
|
||||
# if selection is not empty
|
||||
for selobj in selection:
|
||||
if Draft.getType(selobj.Object) == "FHNode":
|
||||
nodes.append(selobj.Object)
|
||||
if len(nodes) <= 1:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Less than FHNodes selected when creating a FHEquiv. Nothing created."))
|
||||
else:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHEquiv"))
|
||||
FreeCADGui.addModule("EM")
|
||||
for node_num in range(len(nodes)-1):
|
||||
FreeCADGui.doCommand('obj=EM.makeFHEquiv(node1=FreeCAD.ActiveDocument.'+nodes[node_num].Name+',node2=FreeCAD.ActiveDocument.'+nodes[node_num+1].Name+')')
|
||||
# autogrouping, for later on
|
||||
#FreeCADGui.addModule("Draft")
|
||||
#FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHEquiv',_CommandFHEquiv())
|
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,14 +24,11 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry create input file command"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
EMFHINPUTFILE_DEF_FILENAME = "fasthenry_input_file.inp"
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import EM
|
||||
from FreeCAD import Vector
|
||||
|
@ -103,22 +103,35 @@ def makeFHInputFile(doc=None,filename=None,folder=None):
|
|||
nodes = [obj for obj in doc.Objects if Draft.getType(obj) == "FHNode"]
|
||||
for node in nodes:
|
||||
node.Proxy.serialize(fid)
|
||||
# then the segments
|
||||
fid.write("\n")
|
||||
fid.write("* Segments\n")
|
||||
# then the segments
|
||||
segments = [obj for obj in doc.Objects if Draft.getType(obj) == "FHSegment"]
|
||||
if segments:
|
||||
fid.write("* Segments\n")
|
||||
for segment in segments:
|
||||
segment.Proxy.serialize(fid)
|
||||
# then the .equiv
|
||||
# TBC
|
||||
# then the ports
|
||||
fid.write("\n")
|
||||
# then the planes
|
||||
planes = [obj for obj in doc.Objects if Draft.getType(obj) == "FHPlane"]
|
||||
if planes:
|
||||
fid.write("* Planes\n")
|
||||
for plane in planes:
|
||||
plane.Proxy.serialize(fid)
|
||||
fid.write("\n")
|
||||
# then the .equiv
|
||||
equivs = [obj for obj in doc.Objects if Draft.getType(obj) == "FHEquiv"]
|
||||
if equivs:
|
||||
fid.write("* Node shorts\n")
|
||||
for equiv in equivs:
|
||||
equiv.Proxy.serialize(fid)
|
||||
fid.write("\n")
|
||||
# then the ports
|
||||
fid.write("* Ports\n")
|
||||
ports = [obj for obj in doc.Objects if Draft.getType(obj) == "FHPort"]
|
||||
for port in ports:
|
||||
port.Proxy.serialize(fid)
|
||||
# and finally the tail
|
||||
fid.write("\n")
|
||||
# and finally the tail
|
||||
solver.Proxy.serialize(fid,"tail")
|
||||
FreeCAD.Console.PrintMessage(QT_TRANSLATE_NOOP("EM","Finished exporting")+"\n")
|
||||
|
||||
|
|
170
EM_FHNode.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,6 +24,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry Node Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
@ -50,12 +54,11 @@ else:
|
|||
__dir__ = os.path.dirname(__file__)
|
||||
iconPath = os.path.join( __dir__, 'Resources' )
|
||||
|
||||
def makeFHNode(baseobj=None,color=None,size=None,name='FHNode'):
|
||||
def makeFHNode(baseobj=None,X=0.0,Y=0.0,Z=0.0,color=None,size=None,name='FHNode'):
|
||||
'''Creates a FastHenry node ('N' statement in FastHenry)
|
||||
|
||||
'baseobj' is the point object 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
|
||||
If no 'baseobj' is given, X,Y,Z are used as coordinates
|
||||
|
||||
Example:
|
||||
TBD
|
||||
|
@ -80,12 +83,14 @@ def makeFHNode(baseobj=None,color=None,size=None,name='FHNode'):
|
|||
# check if 'baseobj' is a point (only base object allowed)
|
||||
if baseobj:
|
||||
if Draft.getType(baseobj) == "Point":
|
||||
obj.Base = baseobj
|
||||
# get the absolute coordinates of the Point
|
||||
X = baseobj.Shape.Point.x
|
||||
Y = baseobj.Shape.Point.y
|
||||
Z = baseobj.Shape.Point.z
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHNodes can only be based on Point objects"))
|
||||
# hide the base object
|
||||
if obj.Base and FreeCAD.GuiUp:
|
||||
obj.Base.ViewObject.hide()
|
||||
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
|
||||
|
@ -95,7 +100,9 @@ class _FHNode:
|
|||
'''The EM FastHenry Node 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::PropertyDistance","X","EM",QT_TRANSLATE_NOOP("App::Property","X Location"))
|
||||
obj.addProperty("App::PropertyDistance","Y","EM",QT_TRANSLATE_NOOP("App::Property","Y Location"))
|
||||
obj.addProperty("App::PropertyDistance","Z","EM",QT_TRANSLATE_NOOP("App::Property","Z Location"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHNode"
|
||||
# save the object in the class, to store or retrieve specific data from it
|
||||
|
@ -105,54 +112,96 @@ class _FHNode:
|
|||
def execute(self, obj):
|
||||
''' this method is mandatory. It is called on Document.recompute()
|
||||
'''
|
||||
# check if we have a 'Base' object
|
||||
if obj.Base:
|
||||
# computing a shape from a base object
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
# check validity
|
||||
if obj.Base.Shape.isNull():
|
||||
return
|
||||
if not obj.Base.Shape.isValid():
|
||||
return
|
||||
# ok, it's valid. Let's verify if this is a Point.
|
||||
if Draft.getType(obj.Base) == "Point":
|
||||
shape = Part.Vertex(obj.Base.Shape)
|
||||
obj.Shape = shape
|
||||
#FreeCAD.Console.PrintWarning("\n_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())
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHNode onChanged(" + str(prop)+")\n") #debug
|
||||
if not hasattr(self,"Object"):
|
||||
# on restore, self.Object is not there anymore
|
||||
# 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 the user changed the base object
|
||||
if prop == "Base":
|
||||
# check if we have a 'Base' object
|
||||
if obj.Base:
|
||||
# computing a shape from a base object
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
# check validity
|
||||
if obj.Base.Shape.isNull():
|
||||
return
|
||||
if not obj.Base.Shape.isValid():
|
||||
return
|
||||
# ok, it's valid. Let's verify if this is a Point.
|
||||
if Draft.getType(obj.Base) == "Point":
|
||||
shape = Part.Vertex(obj.Base.Shape)
|
||||
obj.Shape = shape
|
||||
|
||||
def serialize(self,fid):
|
||||
def serialize(self,fid,extension=""):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
|
||||
'fid': the file descriptor
|
||||
'extension': any extension to add to the node name, in case of a node
|
||||
belonging to a conductive plane. If not empty, it also changes
|
||||
the way the node is serialized, according to the plane node definition.
|
||||
Defaults to empty string.
|
||||
'''
|
||||
# check if we have a 'Base' object
|
||||
if self.Object.Base:
|
||||
# ok, it's valid. Let's verify if this is a Point.
|
||||
if Draft.getType(self.Object.Base) == "Point":
|
||||
pos = self.getAbsCoord()
|
||||
if extension == "":
|
||||
fid.write("N" + self.Object.Label)
|
||||
fid.write(" x=" + str(self.Object.Base.X.Value) + " y=" + str(self.Object.Base.Y.Value) + " z=" + str(self.Object.Base.Z.Value))
|
||||
fid.write(" x=" + str(pos.x) + " y=" + str(pos.y) + " z=" + str(pos.z))
|
||||
else:
|
||||
fid.write("+ N" + self.Object.Label + extension)
|
||||
fid.write(" (" + str(pos.x) + "," + str(pos.y) + "," + str(pos.z) + ")")
|
||||
fid.write("\n")
|
||||
|
||||
def getAbsCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the absolute node coordinates
|
||||
'''
|
||||
# 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
|
||||
|
||||
def getRelCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the relative node coordinates w.r.t. the Placement
|
||||
|
||||
These coordinates correspond to (self.Object.X, self.Object.Y, self.Object.Z),
|
||||
that are the same as self.Object.Placement.inverse().multVec(self.Object.Shape.Point))
|
||||
'''
|
||||
return Vector(self.Object.X,self.Object.Y,self.Object.Z)
|
||||
|
||||
def setRelCoord(self,rel_coord,placement=None):
|
||||
''' Sets the relative node position w.r.t. 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
|
||||
|
||||
Remark: the function will not recalculate() the object (i.e. change of position is not visible
|
||||
just by calling this function)
|
||||
'''
|
||||
if placement:
|
||||
# validation of the parameter
|
||||
if isinstance(placement, FreeCAD.Placement):
|
||||
self.Object.Placement = placement
|
||||
self.Object.X = rel_coord.x
|
||||
self.Object.Y = rel_coord.y
|
||||
self.Object.Z = rel_coord.z
|
||||
|
||||
def setAbsCoord(self,abs_coord,placement=None):
|
||||
''' Sets the absolute node position, considering the object placement, and in case forcing a new placement
|
||||
|
||||
'abs_coord': FreeCAD.Vector containing the absolute node coordinates
|
||||
'placement': the 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)
|
||||
'''
|
||||
if placement:
|
||||
# validation of the parameter
|
||||
if isinstance(placement, FreeCAD.Placement):
|
||||
self.Object.Placement = placement
|
||||
rel_coord = self.Object.Placement.inverse().multVec(abs_coord)
|
||||
self.Object.X = rel_coord.x
|
||||
self.Object.Y = rel_coord.y
|
||||
self.Object.Z = rel_coord.z
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHNode:
|
||||
def __init__(self, obj):
|
||||
''' Set this object to the proxy object of the actual view provider '''
|
||||
|
@ -161,6 +210,10 @@ class _ViewProviderFHNode:
|
|||
|
||||
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):
|
||||
|
@ -177,20 +230,18 @@ class _ViewProviderFHNode:
|
|||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens in the tree'''
|
||||
c = []
|
||||
if hasattr(self,"Object"):
|
||||
if hasattr(self.Object,"Base"):
|
||||
c = [self.Object.Base]
|
||||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format which will appear in the tree view. This method is optional
|
||||
and if not defined a default icon is shown.
|
||||
'''
|
||||
return os.path.join(iconPath, 'node_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHNode:
|
||||
''' The EM FastHenry Node (FHNode) command definition
|
||||
'''
|
||||
|
@ -211,7 +262,7 @@ class _CommandFHNode:
|
|||
if sel:
|
||||
# automatic mode
|
||||
import Draft
|
||||
if Draft.getType(sel[0].Object) != "FHNode":
|
||||
if Draft.getType(sel[0].Object) == "Point":
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
|
||||
FreeCADGui.addModule("EM")
|
||||
for selobj in sel:
|
||||
|
@ -224,7 +275,7 @@ 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)
|
||||
|
||||
|
@ -232,12 +283,11 @@ class _CommandFHNode:
|
|||
"this function is called by the snapper when it has a 3D point"
|
||||
if point == None:
|
||||
return
|
||||
coord = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
||||
#coord = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
||||
coord = point
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHNode"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('import Draft')
|
||||
FreeCADGui.doCommand('base=Draft.makePoint('+str(coord.x)+','+str(coord.y)+','+str(coord.z)+')')
|
||||
FreeCADGui.doCommand('obj=EM.makeFHNode(base)')
|
||||
FreeCADGui.doCommand('obj=EM.makeFHNode(X='+str(coord.x)+',Y='+str(coord.y)+',Z='+str(coord.z)+')')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# might improve in the future with continue command
|
||||
|
|
606
EM_FHPlane.py
Normal file
|
@ -0,0 +1,606 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* 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 uniform Plane Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
# default plane thickness
|
||||
EMFHPLANE_DEF_THICKNESS = 0.1
|
||||
# default number of segments along length
|
||||
EMFHPLANE_DEF_SEG1 = 10
|
||||
# default number of segments along width
|
||||
EMFHPLANE_DEF_SEG2 = 10
|
||||
# default plane node color
|
||||
EMFHNODE_DEF_PLANENODECOLOR = (0.0,0.0,1.0)
|
||||
# default plane node name extension
|
||||
EMFHNODE_DEF_NODENAMEEXT = "p"
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
import numpy as np
|
||||
from math import sqrt
|
||||
import EM
|
||||
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 makeFHPlane(baseobj=None,thickness=None,seg1=None,seg2=None,nodes=[],holes=[],name='FHPlane'):
|
||||
'''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.
|
||||
|
||||
Example:
|
||||
TBD
|
||||
'''
|
||||
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 _FHSegment
|
||||
_FHPlane(obj)
|
||||
# manage ViewProvider object
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderFHPlane(obj.ViewObject)
|
||||
# set base ViewObject properties to user-selected values (if any)
|
||||
# check if 'nodeStart' is a FHNode, and if so, assign it as segment start node
|
||||
# check if 'baseobj' is a wire (only base object allowed)
|
||||
if baseobj:
|
||||
if baseobj.TypeId == "Part::Box" or Draft.getType(baseobj) == "Rectangle":
|
||||
obj.Base = baseobj
|
||||
# if we have a Base object, we can also adopt children.
|
||||
# Not possible without, otherwise we don't have a valid placement
|
||||
#
|
||||
# verify the list of nodes, and link the actual nodes to the plane
|
||||
real_nodes = []
|
||||
if len(nodes) > 0:
|
||||
for node in nodes:
|
||||
# get only the nodes out of the list (if this is a selection,
|
||||
# it might contain other objects; we don't complain, but extract only the FHNodes)
|
||||
if Draft.getType(node) == "FHNode":
|
||||
real_nodes.append(node)
|
||||
obj.Nodes = real_nodes
|
||||
# verify the list of holes, and link the actual holes to the plane
|
||||
real_holes = []
|
||||
if len(holes) > 0:
|
||||
for hole in holes:
|
||||
# get only the holes out of the list (if this is a selection,
|
||||
# it might contain other objects; we don't complain, but extract only the FHHoles)
|
||||
if Draft.getType(hole) == "FHPlaneHole":
|
||||
real_holes.append(hole)
|
||||
obj.Holes = real_holes
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlane can only be based on Part::Box or Plane::Rectangle objects"))
|
||||
if thickness:
|
||||
if thickness > 0.0:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.Thickness = float(thickness)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlane thickness parameter must be strictly positive"))
|
||||
if seg1:
|
||||
if seg1 > 0.0:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.seg1 = int(seg1)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlane seg1 parameter must be strictly positive"))
|
||||
if seg2:
|
||||
if seg2 > 0.0:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.seg2 = int(seg2)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlane seg2 parameter must be strictly positive"))
|
||||
# hide the base object
|
||||
if obj.Base and FreeCAD.GuiUp:
|
||||
obj.Base.ViewObject.hide()
|
||||
# return the newly created Python object
|
||||
return obj
|
||||
|
||||
class _FHPlane:
|
||||
'''The EM FastHenry uniform Plane 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::PropertyLength","Thickness","EM",QT_TRANSLATE_NOOP("App::Property","Plane thickness ('thick' plane parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","seg1","EM",QT_TRANSLATE_NOOP("App::Property","Number of segments along the length direction ('seg1' plane parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","seg2","EM",QT_TRANSLATE_NOOP("App::Property","Number of segments along the width direction ('seg2' plane parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","nhinc","EM",QT_TRANSLATE_NOOP("App::Property","Number of filaments along the plane thickness ('nhinc' plane parameter)"))
|
||||
obj.addProperty("App::PropertyInteger","rh","EM",QT_TRANSLATE_NOOP("App::Property","Ratio of adjacent filaments along the thickness ('rh' plane parameter)"))
|
||||
obj.addProperty("App::PropertyFloat","Sigma","EM",QT_TRANSLATE_NOOP("App::Property","Plane conductivity ('sigma' plane parameter)"))
|
||||
obj.addProperty("App::PropertyLength","segwid1","EM",QT_TRANSLATE_NOOP("App::Property","Width of segments along the plane length direction ('segwid1' plane parameter)"))
|
||||
obj.addProperty("App::PropertyLength","segwid2","EM",QT_TRANSLATE_NOOP("App::Property","Width of segments along the plane width direction ('segwid2' plane parameter)"))
|
||||
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.Proxy = self
|
||||
self.Type = "FHPlane"
|
||||
self.FineMesh = False
|
||||
# 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("\n_FHPlane execute\n") #debug
|
||||
if obj.Thickness == None or obj.Thickness <= 0:
|
||||
obj.Thickness = EMFHPLANE_DEF_THICKNESS
|
||||
if obj.seg1 == None or obj.seg1 <= 0:
|
||||
obj.seg1 = EMFHPLANE_DEF_SEG1
|
||||
if obj.seg2 == None or obj.seg2 <= 0:
|
||||
obj.seg2 = EMFHPLANE_DEF_SEG2
|
||||
# check if we have a 'Base' object; if not, cannot assign a shape
|
||||
shape = None
|
||||
if obj.Base:
|
||||
# if right type of base
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
# check validity
|
||||
if obj.Base.Shape.isNull():
|
||||
return
|
||||
if not obj.Base.Shape.isValid():
|
||||
return
|
||||
# ok, 'Base' is valid. Go on.
|
||||
#
|
||||
# Part::Box and Draft::Rectangle define the Plane width through
|
||||
# a different property. For the Box it is the Width, but for the
|
||||
# Rectangle this is the Height
|
||||
if obj.Base.TypeId == "Part::Box":
|
||||
width = obj.Base.Width.Value
|
||||
# Also, if Part::Box, set the Plane thickness
|
||||
# to the Box thickness, no matter what the user entered as 'Thickness'
|
||||
obj.Thickness = obj.Base.Height
|
||||
elif Draft.getType(obj.Base) == "Rectangle":
|
||||
width = obj.Base.Height.Value
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Plane Base object is not a Part::Box nor a Draft::Rectangle"))
|
||||
return
|
||||
# The length property is instead the same for the Box and the Rectangle alike
|
||||
length = obj.Base.Length.Value
|
||||
# And thickness has been set, can get it
|
||||
thickness = obj.Thickness.Value
|
||||
# Let's calculate the segments width
|
||||
segwid1 = width / obj.seg2
|
||||
segwid2 = length / obj.seg1
|
||||
# if the user specified a different segment width, assign it to the segments, unless
|
||||
# the specified width is larger than the segment width needed to completely
|
||||
# fill up the plane. In this case reset 'segwid1' to this width.
|
||||
if obj.segwid1 > 0:
|
||||
if obj.segwid1 < segwid1:
|
||||
segwid1 = obj.segwid1.Value
|
||||
else:
|
||||
obj.segwid1 = segwid1
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Plane segwid1 would cause segments overlap, re-setting segwid1 to the maximum possible"))
|
||||
if obj.segwid2 > 0:
|
||||
if obj.segwid2 < segwid2:
|
||||
segwid2 = obj.segwid2.Value
|
||||
else:
|
||||
obj.segwid2 = segwid2
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Plane segwid2 would cause segments overlap, re-setting segwid2 to the maximum possible"))
|
||||
# if needed, apply the same Placement of the Base object to the FHPlane object
|
||||
if obj.Placement <> obj.Base.Placement:
|
||||
obj.Placement = obj.Base.Placement
|
||||
# Start node and hole repositioning in relative coordinate system of the conductive plane
|
||||
#
|
||||
# These nodes have already been adopted by the plane, if they are in the obj.Nodes list;
|
||||
# therefore, must just make sure they track the plane placement. Also, this assures
|
||||
# that once a node is child of a plane, it cannot be moved independently by changing
|
||||
# its placement
|
||||
for node in obj.Nodes:
|
||||
if node.Placement <> obj.Placement:
|
||||
node.Placement = obj.Placement
|
||||
# These holes have already been adopted by the plane, if they are in the obj.Holes list;
|
||||
# therefore, must just make sure they track the plane placement. Also, this assures
|
||||
# that once a hole is child of a plane, it cannot be moved independently by changing
|
||||
# its placement
|
||||
for hole in obj.Holes:
|
||||
if hole.Placement <> obj.Placement:
|
||||
hole.Placement = obj.Placement
|
||||
# Check if the user selected a coarse or a fine mesh.
|
||||
if obj.FineMesh == False:
|
||||
# Now we can define the coarse shape. A uniform plane will extend half-a-segment
|
||||
# beyond the boundaries of the plane definition.
|
||||
# Therefore, we'll make two box shapes, crossing over each other.
|
||||
#
|
||||
# Extend half segwid1 on both y sides
|
||||
# makeBox(length, width, height, point, direction)
|
||||
boxshape1 = Part.makeBox(length,width+segwid1,thickness,Vector(0,-segwid1/2,0))
|
||||
# Extend half segwid2 on both x sides
|
||||
# makeBox(length, width, height, point, direction)
|
||||
boxshape2 = Part.makeBox(length+segwid2,width,thickness,Vector(-segwid2/2,0,0))
|
||||
# create the compound object
|
||||
shape = Part.makeCompound([boxshape1,boxshape2])
|
||||
else:
|
||||
shape = self.makeFinePlane(obj,length,width,thickness,segwid1,segwid2)
|
||||
# and finally assign the shape
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
|
||||
def adoptNode(self,node):
|
||||
''' Adopt a node in the FHPlane
|
||||
|
||||
'node': FHNode object
|
||||
|
||||
The placement is changed to be relative to the plane coordinate system.
|
||||
The color of the node will be changed, to show that this is a FHPlane node
|
||||
'''
|
||||
# must now keep the node in the same absolute position, but making the position
|
||||
# relative to the plane coordinate system
|
||||
abs_pos = node.Proxy.getAbsCoord()
|
||||
node.Proxy.setAbsCoord(abs_pos,self.Object.Base.Placement)
|
||||
node.ViewObject.PointColor = EMFHNODE_DEF_PLANENODECOLOR
|
||||
|
||||
def adoptHole(self,hole):
|
||||
''' Adopt a hole in the FHPlane
|
||||
|
||||
'hole': FHPlaneHole object
|
||||
|
||||
The placement is changed to be relative to the plane coordinate system.
|
||||
'''
|
||||
# must now keep the node in the same absolute position, but making the position
|
||||
# relative to the plane coordinate system
|
||||
abs_pos = hole.Proxy.getAbsCoord()
|
||||
hole.Proxy.setAbsCoord(abs_pos,self.Object.Base.Placement)
|
||||
|
||||
def makeFinePlane(self,obj,length,width,thickness,segwid1,segwid2):
|
||||
''' Compute a fine mesh plane shape given:
|
||||
|
||||
TBD
|
||||
TBD
|
||||
|
||||
'n1': start node position (Vector)
|
||||
'n2': end node position (Vector)
|
||||
'width': segment width
|
||||
'height': segment height
|
||||
'ww': cross-section direction (along width)
|
||||
|
||||
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=[]
|
||||
# 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;
|
||||
# (note that 'obj.seg1' refers to the # of segment parallel to the length, 'obj.seg2' parallel to the width)
|
||||
nodes=np.full((obj.seg1+1,obj.seg2+1), True, np.bool)
|
||||
# find segment lengths
|
||||
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
|
||||
#
|
||||
# now process the holes
|
||||
for hole in obj.Holes:
|
||||
if hole.Type == 'Point':
|
||||
seg1seg2pos = self.findNearestNode(hole.X, hole.Y, obj, seg1len, seg2len)
|
||||
# mark the node as deleted
|
||||
nodes[seg1seg2pos] = False
|
||||
elif hole.Type == 'Rect':
|
||||
seg1seg2corner1 = self.findNearestNode(hole.X, hole.Y, obj, seg1len, seg2len)
|
||||
seg1seg2corner2 = self.findNearestNode(hole.X + hole.Length, hole.Y + hole.Width, obj, seg1len, seg2len)
|
||||
# hole.Length and hole.Width may be negative. Must check which comes first
|
||||
if seg1seg2corner1[0] <= seg1seg2corner2[0]:
|
||||
xstart = seg1seg2corner1[0]
|
||||
xend = seg1seg2corner2[0]
|
||||
else:
|
||||
xstart = seg1seg2corner2[0]
|
||||
xend = seg1seg2corner1[0]
|
||||
if seg1seg2corner1[1] <= seg1seg2corner2[1]:
|
||||
ystart = seg1seg2corner1[1]
|
||||
yend = seg1seg2corner2[1]
|
||||
else:
|
||||
ystart = seg1seg2corner2[1]
|
||||
yend = seg1seg2corner1[1]
|
||||
# mark the nodes as deleted
|
||||
nodes[xstart:xend+1,ystart:yend+1] = False
|
||||
elif hole.Type == 'Circle':
|
||||
seg1seg2pos = self.findNearestNode(hole.X, hole.Y, obj, seg1len, seg2len)
|
||||
# find the offset between the center of the circle and the actual nearest plane node node
|
||||
offsetX = seg1seg2pos[0]*seg1len - hole.X.Value
|
||||
offsetY = seg1seg2pos[1]*seg2len - hole.Y.Value
|
||||
# check if the offset is larger than the radius
|
||||
if abs(offsetX) > hole.Radius.Value or abs(offsetY) > hole.Radius.Value:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Circular hole offset w.r.t. the nearest node plane is greater than hole radius. Hole not performed."))
|
||||
# 'nodes_up' and 'nodes_down' are the (relative) number of hole nodes along plane width
|
||||
# we will then step from 'nodes_down' to 'nodes_up' and for each of these values
|
||||
# we will calculate the (relative) extent from 'nodes_left' to 'node_right' along the length
|
||||
# corresponding to the hole radius and we will remove all the contained nodes
|
||||
nodes_up = int( (hole.Radius.Value - offsetY)/seg2len)
|
||||
nodes_down = int( (-hole.Radius.Value - offsetY)/seg2len)
|
||||
for nodeY in range(nodes_down,nodes_up+1):
|
||||
side = sqrt(hole.Radius.Value**2 - (offsetY+nodeY*seg2len)**2)
|
||||
nodes_left = int( (-side - offsetX)/seg1len)
|
||||
nodes_right = int( (side - offsetX)/seg1len)
|
||||
for nodeX in range(nodes_left,nodes_right+1):
|
||||
# delete node, but only if internal to the plane!
|
||||
if nodeY+seg1seg2pos[1] >= 0 and nodeY+seg1seg2pos[1] <= obj.seg2 and nodeX+seg1seg2pos[0] >= 0 and nodeX+seg1seg2pos[1] <= obj.seg1:
|
||||
nodes[nodeX+seg1seg2pos[0], nodeY+seg1seg2pos[1]] = False
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Unknown hole type in the FHPlane!"))
|
||||
# layout segments along plane length
|
||||
for seg2 in range(obj.seg2+1):
|
||||
for seg1 in range(obj.seg1):
|
||||
# if both starting and ending nodes exist
|
||||
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)
|
||||
# layout segments along plane width
|
||||
for seg1 in range(obj.seg1+1):
|
||||
for seg2 in range(obj.seg2):
|
||||
# if both starting and ending nodes exist
|
||||
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)
|
||||
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)
|
||||
|
||||
'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
|
||||
'''
|
||||
# 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
|
||||
nodeX = int(x_coord.Value/seg1len + 0.5)
|
||||
nodeY = int(y_coord.Value/seg2len + 0.5)
|
||||
# assure that the node is on the plane
|
||||
if nodeX < 0:
|
||||
nodeX = 0
|
||||
elif nodeX > obj.seg1:
|
||||
nodeX = obj.seg1
|
||||
if nodeY < 0:
|
||||
nodeY = 0
|
||||
elif nodeY > obj.seg2:
|
||||
nodeY = obj.seg2
|
||||
return (nodeX,nodeY)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHPlane 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 prop == "Nodes":
|
||||
for node in obj.Nodes:
|
||||
self.adoptNode(node)
|
||||
if prop == "Holes":
|
||||
for hole in obj.Holes:
|
||||
self.adoptHole(hole)
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
'''
|
||||
if not self.Object.Base:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","No Plane Base object set. Cannot serialize the object.\n"))
|
||||
return
|
||||
# must retrieve the three corners in clockwise order from the self.Object.Base
|
||||
# parameters (Position and dimensions)
|
||||
if self.Object.Base.TypeId == "Part::Box":
|
||||
width = self.Object.Base.Width.Value
|
||||
elif Draft.getType(self.Object.Base) == "Rectangle":
|
||||
width = self.Object.Base.Height.Value
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Plane Base object is not a Part::Box nor a Draft::Rectangle. Cannot serialize the object.\n"))
|
||||
return
|
||||
# These two properties are instead the same for the Box and the Rectangle alike
|
||||
length = self.Object.Base.Length.Value
|
||||
placement = self.Object.Base.Placement
|
||||
# plus height (thickness)
|
||||
height = self.Object.Thickness.Value
|
||||
planeOrigin = placement.Base
|
||||
# plane versors. 'vx' is along length, 'vy' is along width, 'vz' is along height (thickness)
|
||||
vx = (placement.multVec(Vector(1,0,0))-planeOrigin).normalize()
|
||||
vy = (placement.multVec(Vector(0,1,0))-planeOrigin).normalize()
|
||||
vz = (placement.multVec(Vector(0,0,1))-planeOrigin).normalize()
|
||||
# compute the corners
|
||||
corner = []
|
||||
corner.append(planeOrigin + vz*(height/2))
|
||||
corner.append(corner[0] + vx*length)
|
||||
corner.append(corner[1] + vy*width)
|
||||
# now output plane parameters
|
||||
fid.write("G" + self.Object.Label + " x1=" + str(corner[0].x) + " y1=" + str(corner[0].y) + " z1=" + str(corner[0].z))
|
||||
fid.write(" x2=" + str(corner[1].x) + " y2=" + str(corner[1].y) + " z2=" + str(corner[1].z) + "\n")
|
||||
fid.write("+ x3=" + str(corner[2].x) + " y3=" + str(corner[2].y) + " z3=" + str(corner[2].z) + "\n")
|
||||
fid.write("+ thick=" + str(height) + " seg1=" + str(self.Object.seg1) + " seg2=" + str(self.Object.seg2) + "\n")
|
||||
if self.Object.segwid1 > 0:
|
||||
fid.write("+ segwid1=" + str(self.Object.segwid1) + "\n")
|
||||
if self.Object.segwid2 > 0:
|
||||
fid.write("+ segwid2=" + str(self.Object.segwid2) + "\n")
|
||||
if self.Object.Sigma > 0:
|
||||
fid.write("+ sigma=" + str(self.Object.Sigma) + "\n")
|
||||
if self.Object.nhinc > 0:
|
||||
fid.write("+ nhinc=" + str(self.Object.nhinc) + "\n")
|
||||
if self.Object.rh > 0:
|
||||
fid.write("+ rh=" + str(self.Object.rh) + "\n")
|
||||
# Output the plane exposed nodes
|
||||
# Nstr (x_val,y_val,z_val)
|
||||
if len(self.Object.Nodes) > 0:
|
||||
for node in self.Object.Nodes:
|
||||
# plane nodes are special nodes. We assume that a node 'N' definition already exist
|
||||
# with the 'node.Label'; so we define an internal plane node with the same name
|
||||
# but with an additional extension, and then we'll '.equiv' the two
|
||||
node.Proxy.serialize(fid, EMFHNODE_DEF_NODENAMEEXT)
|
||||
# hole <hole-type> (val1,val2,....)
|
||||
# hole point (x,y,z)
|
||||
# hole rect (x1,y1,z1,x2,y2,z2)
|
||||
# hole circle (x,y,z,r)
|
||||
if len(self.Object.Holes) > 0:
|
||||
for hole in self.Object.Holes:
|
||||
hole.Proxy.serialize(fid)
|
||||
fid.write("\n")
|
||||
if len(self.Object.Nodes) > 0:
|
||||
fid.write("* Connecting internal plane nodes to actual nodes\n")
|
||||
for node in self.Object.Nodes:
|
||||
fid.write(".equiv N" + node.Label + " N" + node.Label + EMFHNODE_DEF_NODENAMEEXT + "\n")
|
||||
fid.write("\n")
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHPlane:
|
||||
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):
|
||||
''' 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 '''
|
||||
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):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
|
||||
def claimChildren(self):
|
||||
''' Used to place other objects as childrens 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)
|
||||
if hasattr(self.Object,"Holes"):
|
||||
c.extend(self.Object.Holes)
|
||||
return c
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format 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')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHPlane:
|
||||
''' The EM FastHenry uniform Plane (FHPlane) command definition
|
||||
'''
|
||||
def GetResources(self):
|
||||
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)")}
|
||||
|
||||
def IsActive(self):
|
||||
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)
|
||||
# get the selected object(s)
|
||||
selection = FreeCADGui.Selection.getSelectionEx()
|
||||
base = None
|
||||
nodes = []
|
||||
holes = []
|
||||
# if selection is not empty
|
||||
for selobj in selection:
|
||||
# automatic mode
|
||||
if selobj.Object.TypeId == "Part::Box" or Draft.getType(selobj.Object) == "Rectangle":
|
||||
if not base:
|
||||
base = selobj.Object
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","More than one Part::Box or Draft::Rectangle selected. Using the first one as FHPlane Base object.\n"))
|
||||
elif Draft.getType(selobj.Object) == "FHNode":
|
||||
nodes.append(selobj.Object)
|
||||
elif Draft.getType(selobj.Object) == "FHPlaneHole":
|
||||
holes.append(selobj.Object)
|
||||
if base:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHPlane"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('nodeList=[]')
|
||||
for node in nodes:
|
||||
FreeCADGui.doCommand('nodeList.append(FreeCAD.ActiveDocument.'+node.Name+')')
|
||||
FreeCADGui.doCommand('holeList=[]')
|
||||
for hole in holes:
|
||||
FreeCADGui.doCommand('holeList.append(FreeCAD.ActiveDocument.'+hole.Name+')')
|
||||
FreeCADGui.doCommand('obj=EM.makeFHPlane(FreeCAD.ActiveDocument.'+base.Name+',nodes=nodeList,holes=holeList)')
|
||||
# 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 positions
|
||||
# have been updated by the first execute(), called upon the first recompute()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# if no selection, or nothing good in the selected objects
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","No base Part::Box or Draft::Rectangle selected. Cannot create a FHPlane.\n"))
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHPlane',_CommandFHPlane())
|
346
EM_FHPlaneHole.py
Normal file
|
@ -0,0 +1,346 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* 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 Plane Hole Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
# default node color
|
||||
EMFHPLANEHOLE_TYPES = ["Point", "Rect", "Circle"]
|
||||
EMFHPLANEHOLE_DEFTYPE = "Point"
|
||||
|
||||
import FreeCAD, FreeCADGui, Mesh, Part, MeshPart, Draft, DraftGeomUtils, os
|
||||
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 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)
|
||||
|
||||
'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.
|
||||
|
||||
The FHPlaneHole has to be used only within a FHPlane object. The FHPlaneHole
|
||||
will be taken as child by the FHPlane.
|
||||
|
||||
Example:
|
||||
TBD
|
||||
'''
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", name)
|
||||
obj.Label = translate("EM", name)
|
||||
# this adds the relevant properties to the object
|
||||
#'obj' (e.g. 'Base' property) making it a _FHNode
|
||||
_FHPlaneHole(obj)
|
||||
# manage ViewProvider object
|
||||
if FreeCAD.GuiUp:
|
||||
_ViewProviderFHPlaneHole(obj.ViewObject)
|
||||
# set base ViewObject properties to user-selected values (if any)
|
||||
# check if 'baseobj' is a point (only base object allowed)
|
||||
if baseobj:
|
||||
if Draft.getType(baseobj) == "Point":
|
||||
# get the absolute coordinates of the Point
|
||||
X = baseobj.Shape.Point.x
|
||||
Y = baseobj.Shape.Point.y
|
||||
Z = baseobj.Shape.Point.z
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlaneHole can only take the position from Point objects"))
|
||||
if holetype:
|
||||
if holetype in EMFHPLANEHOLE_TYPES:
|
||||
obj.Type = holetype
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHPlaneHole unknown hole type"))
|
||||
else:
|
||||
obj.Type = EMFHPLANEHOLE_DEFTYPE
|
||||
if length:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.Length = float(length)
|
||||
if width:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.Width = float(width)
|
||||
if radius:
|
||||
# using a conversion and not catching errors, for input validation
|
||||
obj.Radius = float(radius)
|
||||
# set the hole reference point 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
|
||||
|
||||
class _FHPlaneHole:
|
||||
'''The EM FastHenry plane hole object'''
|
||||
def __init__(self, obj):
|
||||
''' Add properties '''
|
||||
obj.addProperty("App::PropertyDistance","X","EM",QT_TRANSLATE_NOOP("App::Property","X Location"))
|
||||
obj.addProperty("App::PropertyDistance","Y","EM",QT_TRANSLATE_NOOP("App::Property","Y Location"))
|
||||
obj.addProperty("App::PropertyDistance","Z","EM",QT_TRANSLATE_NOOP("App::Property","Z Location"))
|
||||
obj.addProperty("App::PropertyLength","Length","EM",QT_TRANSLATE_NOOP("App::Property","Rectangular hole length (along x from node base point)"))
|
||||
obj.addProperty("App::PropertyLength","Width","EM",QT_TRANSLATE_NOOP("App::Property","Rectangular hole width (along y from node base point)"))
|
||||
obj.addProperty("App::PropertyLength","Radius","EM",QT_TRANSLATE_NOOP("App::Property","Circular hole radius"))
|
||||
obj.addProperty("App::PropertyEnumeration","Type","EM",QT_TRANSLATE_NOOP("App::Property","The type of FastHenry plane hole"))
|
||||
obj.Proxy = self
|
||||
self.Type = "FHPlaneHole"
|
||||
# save the object in the class, to store or retrieve specific data from it
|
||||
# from within the class
|
||||
self.Object = obj
|
||||
obj.Type = EMFHPLANEHOLE_TYPES
|
||||
|
||||
def execute(self, obj):
|
||||
''' this method is mandatory. It is called on Document.recompute()
|
||||
'''
|
||||
# create a shape corresponding to the type of hole
|
||||
shape = None
|
||||
if obj.Type == "Point":
|
||||
# set the shape as a Vertex at relative position obj.X, obj.Y, obj.Z
|
||||
# The shape will then be adjusted according to the object Placement
|
||||
shape = Part.Vertex(self.getRelCoord())
|
||||
elif obj.Type == "Rect":
|
||||
if obj.Length <= 0 or obj.Width <= 0:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Cannot create a FHPlaneHole rectangular hole with zero length or width"))
|
||||
else:
|
||||
v0 = self.getRelCoord()
|
||||
v1 = v0 + Vector(obj.Length,0,0)
|
||||
v2 = v0 + Vector(obj.Length,obj.Width,0)
|
||||
v3 = v0 + Vector(0,obj.Width,0)
|
||||
# and create the rectangle
|
||||
poly = Part.makePolygon( [v0,v1,v2,v3,v0])
|
||||
shape = Part.Face(poly)
|
||||
elif obj.Type == "Circle":
|
||||
if obj.Radius <= 0:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","Cannot create a FHPlaneHole circular hole with zero radius"))
|
||||
else:
|
||||
# create a circle in the x,y plane (axis is along z)
|
||||
circle = Part.Circle(self.getRelCoord(),Vector(0,0,1),obj.Radius)
|
||||
edge = circle.toShape()
|
||||
wire = Part.Wire(edge)
|
||||
shape = Part.Face(wire)
|
||||
if shape:
|
||||
obj.Shape = shape
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
''' take action if an object property 'prop' changed
|
||||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHPlaneHole 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
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
'''
|
||||
pos = self.getAbsCoord()
|
||||
if self.Object.Type == "Point":
|
||||
# hole point (x,y,z)
|
||||
fid.write("+ hole point (" + str(pos.x) + "," + str(pos.y) + "," + str(pos.z) + ")")
|
||||
elif self.Object.Type == "Rect":
|
||||
# calculate the position of the second point defining the rectangle
|
||||
#
|
||||
# if the hole is on a plane, its placement corresponds to the FHPlane placement
|
||||
# (it is assigned by the FHPlane, when it takes the FHPlaneHole as child)
|
||||
placement = self.Object.Placement
|
||||
planeOrigin = placement.Base
|
||||
# plane versors. 'vx' is along length, 'vy' is along width
|
||||
vx = (placement.multVec(Vector(1,0,0))-planeOrigin).normalize()
|
||||
vy = (placement.multVec(Vector(0,1,0))-planeOrigin).normalize()
|
||||
# compute the opposite corner position
|
||||
point2 = pos + vx*self.Object.Length.Value + vy*self.Object.Width.Value
|
||||
# hole rect (x1,y1,z1,x2,y2,z2)
|
||||
fid.write("+ hole rect (" + str(pos.x) + "," + str(pos.y) + "," + str(pos.z) + ",")
|
||||
fid.write(str(point2.x) + "," + str(point2.y) + "," + str(point2.z) + ")\n")
|
||||
elif self.Object.Type == "Circle":
|
||||
# hole circle (x,y,z,r)
|
||||
fid.write("+ hole circle (" + str(pos.x) + "," + str(pos.y) + "," + str(pos.z) + "," + str(self.Object.Radius.Value) + ")")
|
||||
fid.write("\n")
|
||||
|
||||
def getAbsCoord(self):
|
||||
''' Get a FreeCAD.Vector containing the absolute reference point coordinates
|
||||
'''
|
||||
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
|
||||
|
||||
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))
|
||||
'''
|
||||
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
|
||||
|
||||
'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
|
||||
|
||||
Remark: the function will not recalculate() the object (i.e. change of position is not visible
|
||||
just by calling this function)
|
||||
'''
|
||||
if placement:
|
||||
# validation of the parameter
|
||||
if isinstance(placement, FreeCAD.Placement):
|
||||
self.Object.Placement = placement
|
||||
self.Object.X = rel_coord.x
|
||||
self.Object.Y = rel_coord.y
|
||||
self.Object.Z = rel_coord.z
|
||||
|
||||
def setAbsCoord(self,abs_coord,placement=None):
|
||||
''' Sets the absolute 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
|
||||
|
||||
Remark: the function will not recalculate() the object (i.e. change of position is not visible
|
||||
just by 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)
|
||||
self.Object.X = rel_coord.x
|
||||
self.Object.Y = rel_coord.y
|
||||
self.Object.Z = rel_coord.z
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHPlaneHole:
|
||||
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):
|
||||
''' 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 '''
|
||||
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):
|
||||
''' Print the name of the property that has changed '''
|
||||
#FreeCAD.Console.PrintMessage("ViewProvider onChanged(), property: " + str(prop) + "\n")
|
||||
|
||||
def getIcon(self):
|
||||
''' Return the icon in XMP format 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')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHPlaneHole:
|
||||
''' The EM FastHenry conductive plane hole (FHPlaneHole) command definition
|
||||
'''
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : os.path.join(iconPath, 'planehole_icon.svg') ,
|
||||
'MenuText': QT_TRANSLATE_NOOP("EM_FHPlaneHole","FHPlaneHole"),
|
||||
'Accel': "E, H",
|
||||
'ToolTip': QT_TRANSLATE_NOOP("EM_FHPlaneHole","Creates a FastHenry conductive plane Hole object from scratch or from a selected object (point)")}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
# get the selected object(s)
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
done = False
|
||||
# if selection is not empty
|
||||
if sel:
|
||||
# automatic mode
|
||||
import Draft
|
||||
if Draft.getType(sel[0].Object) == "Point":
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHPlaneHole"))
|
||||
FreeCADGui.addModule("EM")
|
||||
for selobj in sel:
|
||||
FreeCADGui.doCommand('obj=EM.makeFHPlaneHole(FreeCAD.ActiveDocument.'+selobj.Object.Name+')')
|
||||
# autogrouping, for later on
|
||||
#FreeCADGui.addModule("Draft")
|
||||
#FreeCADGui.doCommand("Draft.autogroup(obj)")
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
done = True
|
||||
# if no selection, or nothing good in the selected objects
|
||||
if not done:
|
||||
#FreeCAD.DraftWorkingPlane.setup()
|
||||
# get a 3D point via Snapper, setting the callback functions
|
||||
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
|
||||
|
||||
def getPoint(self,point=None,obj=None):
|
||||
"this function is called by the snapper when it has a 3D point"
|
||||
if point == None:
|
||||
return
|
||||
coord = FreeCAD.DraftWorkingPlane.getLocalCoords(point)
|
||||
FreeCAD.ActiveDocument.openTransaction(translate("EM","Create FHPlaneHole"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('obj=EM.makeFHPlaneHole(X='+str(coord.x)+',Y='+str(coord.y)+',Z='+str(coord.z)+')')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# might improve in the future with continue command
|
||||
#if self.continueCmd:
|
||||
# self.Activated()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('EM_FHPlaneHole',_CommandFHPlaneHole())
|
||||
|
36
EM_FHPort.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -22,6 +25,7 @@
|
|||
#***************************************************************************
|
||||
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry Port Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
@ -108,13 +112,13 @@ class _FHPort:
|
|||
if obj.NodeStart == obj.NodeEnd:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeStart and NodeEnd coincide. Cannot create a port."))
|
||||
return
|
||||
# and finally, if everything is ok, make and assing the shape
|
||||
# and finally, if everything is ok, make and assign the shape
|
||||
self.assignShape(obj)
|
||||
|
||||
def assignShape(self, obj):
|
||||
''' Compute and assign the shape to the object 'obj' '''
|
||||
n1 = obj.NodeStart.Shape.Point
|
||||
n2 = obj.NodeEnd.Shape.Point
|
||||
n1 = obj.NodeStart.Proxy.getAbsCoord()
|
||||
n2 = obj.NodeEnd.Proxy.getAbsCoord()
|
||||
shape = self.makePortShape(n1,n2)
|
||||
# shape may be None, e.g. if endpoints coincide. Do not assign in this case.
|
||||
# Port is still valid, but not visible.
|
||||
|
@ -147,17 +151,25 @@ class _FHPort:
|
|||
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("\n_FHPort onChanged(" + str(prop)+")\n") #debug
|
||||
if not hasattr(self,"Object"):
|
||||
# on restore, self.Object is not there anymore
|
||||
# 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
|
||||
return
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
'''
|
||||
fid.write(".external N" + self.Object.NodeStart.Label + " N" + self.Object.NodeEnd.Label + "\n")
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHPort:
|
||||
def __init__(self, obj):
|
||||
''' Set this object to the proxy object of the actual view provider '''
|
||||
|
@ -166,6 +178,10 @@ class _ViewProviderFHPort:
|
|||
|
||||
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):
|
||||
|
@ -198,6 +214,12 @@ class _ViewProviderFHPort:
|
|||
'''
|
||||
return os.path.join(iconPath, 'port_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHPort:
|
||||
''' The EM FastHenry Port (FHPort) command definition
|
||||
'''
|
||||
|
|
119
EM_FHSegment.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,14 +24,15 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry Segment Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
# defines
|
||||
#
|
||||
EMFHSEGMENT_DEF_SEGWIDTH = 1.0
|
||||
EMFHSEGMENT_DEF_SEGHEIGHT = 1.0
|
||||
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
|
||||
|
@ -81,11 +85,13 @@ def makeFHSegment(baseobj=None,nodeStart=None,nodeEnd=None,name='FHSegment'):
|
|||
if nodeEnd:
|
||||
if Draft.getType(nodeEnd) == "FHNode":
|
||||
obj.NodeEnd = nodeEnd
|
||||
# check if 'baseobj' is a wire (only base object allowed)
|
||||
if baseobj:
|
||||
# check if 'baseobj' is a wire (only base object allowed), and only if not passed any node
|
||||
if baseobj and not obj.NodeStart and not obj.NodeEnd:
|
||||
if Draft.getType(baseobj) == "Wire":
|
||||
if len(baseobj.Shape.Vertexes) == 2:
|
||||
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)
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(translate("EM","FHSegments can only be based on Line objects (not multi-segment wires)"))
|
||||
else:
|
||||
|
@ -120,6 +126,16 @@ class _FHSegment:
|
|||
def execute(self, obj):
|
||||
''' this method is mandatory. It is called on Document.recompute()
|
||||
'''
|
||||
if obj.NodeStart == None:
|
||||
return
|
||||
elif Draft.getType(obj.NodeStart) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeStart is not a FHNode"))
|
||||
return
|
||||
if obj.NodeEnd == None:
|
||||
return
|
||||
elif Draft.getType(obj.NodeEnd) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeEnd is not a FHNode"))
|
||||
return
|
||||
# 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
|
||||
|
@ -134,28 +150,22 @@ 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:
|
||||
# TBC warning. Should use a method of _FHNode class. Shortcut.
|
||||
obj.NodeStart.Base.X = obj.Base.Start.x
|
||||
obj.NodeStart.Base.Y = obj.Base.Start.y
|
||||
obj.NodeStart.Base.Z = obj.Base.Start.z
|
||||
#obj.NodeStart.Base.Shape = Part.Vertex(obj.Base.Start)
|
||||
abs_pos = obj.NodeStart.Proxy.getAbsCoord()
|
||||
# 'obj.Base.Start' is an absolute position
|
||||
# if 'NodeStart' is not in that position, move it
|
||||
if (abs_pos-obj.Base.Start).Length > EMFHSEGMENT_LENTOL:
|
||||
obj.NodeStart.Proxy.setAbsCoord(obj.Base.Start)
|
||||
if obj.NodeEnd <> None:
|
||||
# TBC warning. Should use a method of _FHNode class. Shortcut.
|
||||
obj.NodeEnd.Base.X = obj.Base.End.x
|
||||
obj.NodeEnd.Base.Y = obj.Base.End.y
|
||||
obj.NodeEnd.Base.Z = obj.Base.End.z
|
||||
#obj.NodeEnd.Base.Shape = Part.Vertex(obj.Base.End)
|
||||
if obj.NodeStart == None:
|
||||
return
|
||||
elif Draft.getType(obj.NodeStart) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeStart is not a FHNode"))
|
||||
return
|
||||
if obj.NodeEnd == None:
|
||||
return
|
||||
elif Draft.getType(obj.NodeEnd) <> "FHNode":
|
||||
FreeCAD.Console.PrintWarning(translate("EM","NodeEnd is not a FHNode"))
|
||||
return
|
||||
abs_pos = obj.NodeEnd.Proxy.getAbsCoord()
|
||||
# 'obj.Base.Start' is an absolute position
|
||||
# if 'NodeStart' is not in that position, move it
|
||||
if (abs_pos-obj.Base.End).Length > EMFHSEGMENT_LENTOL:
|
||||
obj.NodeEnd.Proxy.setAbsCoord(obj.Base.End)
|
||||
if obj.Width == None or obj.Width <= 0:
|
||||
obj.Width = EMFHSEGMENT_DEF_SEGWIDTH
|
||||
if obj.Height == None or obj.Height <= 0:
|
||||
|
@ -165,8 +175,8 @@ class _FHSegment:
|
|||
|
||||
def assignShape(self, obj):
|
||||
''' Compute and assign the shape to the object 'obj' '''
|
||||
n1 = obj.NodeStart.Shape.Point
|
||||
n2 = obj.NodeEnd.Shape.Point
|
||||
n1 = obj.NodeStart.Proxy.getAbsCoord()
|
||||
n2 = obj.NodeEnd.Proxy.getAbsCoord()
|
||||
shape = self.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:
|
||||
|
@ -248,27 +258,10 @@ class _FHSegment:
|
|||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHSegment onChanged(" + str(prop)+")\n") #debug
|
||||
if not hasattr(self,"Object"):
|
||||
# on restore, self.Object is not there anymore
|
||||
# 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 the user changed the base object. Remark: as makePoint() calls Document.recompute(),
|
||||
# we cannot have the node creation in the class execute() method, since execute() is called
|
||||
# upon recompute(), so we'd have recursive recompute() calls
|
||||
if prop == "Base":
|
||||
# check if we have a 'Base' object
|
||||
if obj.Base:
|
||||
# computing a shape from a base object
|
||||
if obj.Base.isDerivedFrom("Part::Feature"):
|
||||
# check validity
|
||||
if obj.Base.Shape.isNull():
|
||||
return
|
||||
if not obj.Base.Shape.isValid():
|
||||
return
|
||||
# ok, it's valid. Let's verify if this is a Wire.
|
||||
if Draft.getType(obj.Base) == "Wire":
|
||||
p1 = Draft.makePoint(obj.Base.Start)
|
||||
obj.NodeStart = EM.makeFHNode(p1)
|
||||
p2 = Draft.makePoint(obj.Base.End)
|
||||
obj.NodeEnd = EM.makeFHNode(p2)
|
||||
|
||||
def serialize(self,fid):
|
||||
''' Serialize the object to the 'fid' file descriptor
|
||||
|
@ -289,6 +282,13 @@ class _FHSegment:
|
|||
fid.write(" rw=" + str(self.Object.rw))
|
||||
fid.write("\n")
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHSegment:
|
||||
def __init__(self, obj):
|
||||
''' Set this object to the proxy object of the actual view provider '''
|
||||
|
@ -297,6 +297,10 @@ class _ViewProviderFHSegment:
|
|||
|
||||
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):
|
||||
|
@ -331,6 +335,12 @@ class _ViewProviderFHSegment:
|
|||
'''
|
||||
return os.path.join(iconPath, 'segment_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHSegment:
|
||||
''' The EM FastHenry Segment (FHSegment) command definition
|
||||
'''
|
||||
|
@ -386,7 +396,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)
|
||||
|
@ -400,16 +410,17 @@ class _CommandFHSegment:
|
|||
# get the second point
|
||||
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint)
|
||||
elif len(self.points) >= 2:
|
||||
coord1 = FreeCAD.DraftWorkingPlane.getLocalCoords(self.points[0])
|
||||
coord2 = FreeCAD.DraftWorkingPlane.getLocalCoords(self.points[1])
|
||||
#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"))
|
||||
FreeCADGui.addModule("EM")
|
||||
FreeCADGui.doCommand('import Draft')
|
||||
FreeCADGui.doCommand('from FreeCAD import Vector')
|
||||
FreeCADGui.doCommand('v1 = Vector('+str(coord1.x)+','+str(coord1.y)+','+str(coord1.z)+')')
|
||||
FreeCADGui.doCommand('v2 = Vector('+str(coord2.x)+','+str(coord2.y)+','+str(coord2.z)+')')
|
||||
FreeCADGui.doCommand('base=Draft.makeLine(v1,v2)')
|
||||
FreeCADGui.doCommand('obj=EM.makeFHSegment(base)')
|
||||
FreeCADGui.doCommand('node1 = EM.makeFHNode(X='+str(coord1.x)+',Y='+str(coord1.y)+',Z='+str(coord1.z)+')')
|
||||
FreeCADGui.doCommand('node2 = EM.makeFHNode(X='+str(coord2.x)+',Y='+str(coord2.y)+',Z='+str(coord2.z)+')')
|
||||
FreeCADGui.doCommand('obj=EM.makeFHSegment(nodeStart=node1,nodeEnd=node2)')
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
# might improve in the future with continue command
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,6 +24,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench FastHenry Solver Class"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
@ -154,7 +158,9 @@ class _FHSolver:
|
|||
'''
|
||||
#FreeCAD.Console.PrintWarning("\n_FHSolver onChanged(" + str(prop)+")\n") #debug
|
||||
if not hasattr(self,"Object"):
|
||||
# on restore, self.Object is not there anymore
|
||||
# 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
|
||||
|
||||
def serialize(self,fid,headOrTail):
|
||||
|
@ -162,7 +168,7 @@ class _FHSolver:
|
|||
'''
|
||||
if headOrTail == "head":
|
||||
fid.write("* FastHenry input file created using FreeCAD's ElectroMagnetic Workbench\n")
|
||||
fid.write("* See http://www.freecad.org and http://www.fastfieldsolvers.com\n")
|
||||
fid.write("* See http://www.freecad.org, http://www.fastfieldsolvers.com and http://epc-co.com\n")
|
||||
fid.write("\n")
|
||||
fid.write(".units " + self.Object.Units + "\n")
|
||||
fid.write("\n")
|
||||
|
@ -174,6 +180,13 @@ class _FHSolver:
|
|||
fid.write("\n")
|
||||
fid.write(".end\n")
|
||||
|
||||
def __getstate__(self):
|
||||
return self.Type
|
||||
|
||||
def __setstate__(self,state):
|
||||
if state:
|
||||
self.Type = state
|
||||
|
||||
class _ViewProviderFHSolver:
|
||||
def __init__(self, obj):
|
||||
''' Set this object to the proxy object of the actual view provider '''
|
||||
|
@ -182,6 +195,10 @@ class _ViewProviderFHSolver:
|
|||
|
||||
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):
|
||||
|
@ -204,6 +221,12 @@ class _ViewProviderFHSolver:
|
|||
'''
|
||||
return os.path.join(iconPath, 'solver_icon.svg')
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self,state):
|
||||
return None
|
||||
|
||||
class _CommandFHSolver:
|
||||
''' The EM FastHenry Solver command definition
|
||||
'''
|
||||
|
|
36
EM_Globals.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* 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 global definitions"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
||||
|
||||
# defines
|
||||
#
|
||||
#EM_DEF_XXX = 1.0
|
||||
|
6
Init.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,5 +24,6 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
# add import/export types
|
||||
FreeCAD.addExportType("FastHenry file format (*.inp)","exportFH")
|
||||
|
|
26
InitGui.py
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,6 +24,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
__title__="FreeCAD E.M. Workbench GUI"
|
||||
__author__ = "FastFieldSolvers S.R.L."
|
||||
__url__ = "http://www.fastfieldsolvers.com"
|
||||
|
@ -35,21 +39,33 @@ class EMWorkbench(Workbench):
|
|||
def Initialize(self):
|
||||
import DraftTools,DraftGui
|
||||
from DraftTools import translate
|
||||
|
||||
# import the EM module (and therefore all commands makeXXX)
|
||||
import EM
|
||||
|
||||
# E.M. tools
|
||||
self.emtools = ["EM_FHSolver", "EM_FHNode", "EM_FHSegment", "EM_FHPort", "EM_FHInputFile"]
|
||||
self.emtools = ["EM_FHSolver", "EM_FHNode", "EM_FHSegment", "EM_FHPlane",
|
||||
"EM_FHPlaneHole", "EM_FHEquiv", "EM_FHPort", "EM_FHInputFile"]
|
||||
# draft tools
|
||||
self.draftmodtools = ["Draft_Move","Draft_Rotate","Draft_Offset",
|
||||
"Draft_Trimex", "Draft_Upgrade", "Draft_Downgrade", "Draft_Scale",
|
||||
"Draft_Shape2DView","Draft_Draft2Sketch","Draft_Array",
|
||||
"Draft_Clone"]
|
||||
self.treecmdList = ["Draft_SelectPlane", "Draft_ShowSnapBar","Draft_ToggleGrid"]
|
||||
self.snapList = ['Draft_Snap_Lock','Draft_Snap_Midpoint','Draft_Snap_Perpendicular',
|
||||
'Draft_Snap_Grid','Draft_Snap_Intersection','Draft_Snap_Parallel',
|
||||
'Draft_Snap_Endpoint','Draft_Snap_Angle','Draft_Snap_Center',
|
||||
'Draft_Snap_Extension','Draft_Snap_Near','Draft_Snap_Ortho','Draft_Snap_Special',
|
||||
'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","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","&Draft"),QT_TRANSLATE_NOOP("arch","Snapping")],self.snapList)
|
||||
#FreeCADGui.addIconPath(":/icons")
|
||||
#FreeCADGui.addLanguagePath(":/translations")
|
||||
#FreeCADGui.addPreferencePage(":/ui/preferences-EM.ui","EM")
|
||||
#FreeCADGui.addPreferencePage(":/ui/preferences-aEMdefaults.ui","EM")
|
||||
|
||||
Log ('Loading EM module... done\n')
|
||||
|
||||
def Activated(self):
|
||||
|
|
15
README.md
|
@ -1,11 +1,24 @@
|
|||
# ElectroMagnetic workbench for FreeCAD
|
||||
|
||||
## Copyright
|
||||
|
||||
### FastHenry support
|
||||
|
||||
Copyright (c) 2018
|
||||
Efficient Power Conversion Corporation, Inc. http://epc-co.com
|
||||
|
||||
Developed by FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com under contract by EPC
|
||||
|
||||
|
||||
### FasterCap and FastCap support
|
||||
|
||||
Copyright (c) 2018
|
||||
FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com
|
||||
|
||||
## Description
|
||||
|
||||
This project is dedicated to building an ElectroMagnetic workbench for [FreeCAD](https://www.freecadweb.org). FreeCAD is a free 3D parametric CAD.
|
||||
FreeCAD is used as pre-processor interfacing to the electromagnetic field solvers.
|
||||
FreeCAD is used as pre-processor interfacing to the open source electromagnetic field solvers [FastHenry](https://www.fastfieldsolvers.com/fasthenry2.htm) and [FasterCap](https://www.fastfieldsolvers.com/fastercap.htm).
|
||||
|
||||
At present, the workbench supports:
|
||||
|
||||
|
|
248
Resources/equiv_icon.svg
Normal file
|
@ -0,0 +1,248 @@
|
|||
<?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="svg2726"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="equiv_icon.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1"
|
||||
inkscape:export-filename="/home/yorik/Sources/FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.svg.png"
|
||||
inkscape:export-xdpi="45"
|
||||
inkscape:export-ydpi="45">
|
||||
<defs
|
||||
id="defs2728">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3144">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3146" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3148" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2734" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3144"
|
||||
id="radialGradient3850"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
cx="225.26402"
|
||||
cy="672.79736"
|
||||
fx="225.26402"
|
||||
fy="672.79736"
|
||||
r="34.345188" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3144-3"
|
||||
id="radialGradient3850-4"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
cx="225.26402"
|
||||
cy="672.79736"
|
||||
fx="225.26402"
|
||||
fy="672.79736"
|
||||
r="34.345188" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3144-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3146-7" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3148-1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="34.345188"
|
||||
fy="672.79736"
|
||||
fx="225.26402"
|
||||
cy="672.79736"
|
||||
cx="225.26402"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3888"
|
||||
xlink:href="#linearGradient3144-3"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3144"
|
||||
id="radialGradient3767"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
|
||||
cx="225.26402"
|
||||
cy="672.79736"
|
||||
fx="225.26402"
|
||||
fy="672.79736"
|
||||
r="34.345188" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836"
|
||||
id="linearGradient3922"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.71744529,0.0088629,0.0088629,0.72020483,153.90929,201.57648)"
|
||||
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>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3836"
|
||||
id="linearGradient3922-1"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.71744528,0.0088629,0.0088629,0.72020481,-4.830661,192.44034)"
|
||||
x1="11.390151"
|
||||
y1="453.55045"
|
||||
x2="54.509644"
|
||||
y2="485.54004" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.0968312"
|
||||
inkscape:cx="27.312133"
|
||||
inkscape:cy="34.524165"
|
||||
inkscape:current-layer="g3906"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-nodes="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2997"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2731">
|
||||
<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>[wmayer]</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:title>Sketcher_CreatePoint</dc:title>
|
||||
<dc:date>2011-10-10</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/Sketcher/Gui/Resources/icons/Sketcher_CreatePoint.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
|
||||
id="g4289"
|
||||
transform="matrix(0.1621282,0,0,0.1621282,6.3605986,-66.108806)">
|
||||
<g
|
||||
style="stroke:#3465a4;stroke-width:0.97375208"
|
||||
id="g4312"
|
||||
transform="matrix(12.667234,0.1943747,-0.1943834,12.666657,-1553.0403,-2583.642)">
|
||||
<g
|
||||
style="stroke:#3465a4;stroke-width:6.49510956"
|
||||
transform="matrix(-0.1153544,-0.09575809,0.09575809,-0.1153544,98.667777,319.83687)"
|
||||
id="g4248">
|
||||
<g
|
||||
transform="translate(-3.7314339,-3.2817175)"
|
||||
id="g3906"
|
||||
style="stroke-width:6.49510956">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3465a4;fill-opacity:1;stroke:#280000;stroke-width:8.11888699;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250"
|
||||
d="m 206.64054,538.39516 a 29.139445,29.776512 49.424072 1 1 -59.01394,0.62949 29.139445,29.776512 49.424072 1 1 59.01394,-0.62949 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3922);fill-opacity:1;stroke:#ef2929;stroke-width:3.96201685;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7"
|
||||
d="m 202.56811,538.27104 a 25.630594,25.953246 49.423579 1 1 -51.63346,0.31887 25.630594,25.953246 49.423579 0 1 51.63346,-0.31887 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#3465a4;fill-opacity:1;stroke:#280000;stroke-width:8.11888695;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-70"
|
||||
d="m 47.900589,529.25903 a 29.139445,29.776512 49.424056 1 1 -59.01394,0.62949 29.139445,29.776512 49.424056 1 1 59.01394,-0.62949 z" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3922-1);fill-opacity:1;stroke:#ef2929;stroke-width:3.96201706;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4250-7-8"
|
||||
d="m 43.828159,529.13491 a 25.630593,25.953246 49.424044 1 1 -51.63346,0.31887 25.630593,25.953246 49.424044 0 1 51.63346,-0.31887 z" />
|
||||
<path
|
||||
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:8.11888699;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="M 131.72678,539.17372 62.204239,532.43759"
|
||||
id="path4182"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.8 KiB |
507
Resources/plane_icon.svg
Normal file
|
@ -0,0 +1,507 @@
|
|||
<?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_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="-645.17214 : 147.82379 : 1"
|
||||
inkscape:vp_y="0 : 475.05947 : 0"
|
||||
inkscape:vp_z="679.86961 : 170.85132 : 1"
|
||||
inkscape:persp3d-origin="26.79731 : 26.370233 : 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>
|
||||
</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: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"
|
||||
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.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.1462956,38.58256 23.917547,42.504295 61.545112,33.936767 40.827369,30.484561 Z"
|
||||
points="23.917547,42.504295 61.545112,33.936767 40.827369,30.484561 3.1462956,38.58256 " />
|
||||
<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.1462956,30.90979 0,7.67277 37.6810734,-8.097999 0,-7.245537 z"
|
||||
points="3.1462956,38.58256 40.827369,30.484561 40.827369,23.239024 3.1462956,30.90979 " />
|
||||
<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 40.827369,23.239024 20.717743,3.233384 0,7.464359 -20.717743,-3.452206 z"
|
||||
points="61.545112,26.472408 61.545112,33.936767 40.827369,30.484561 40.827369,23.239024 " />
|
||||
<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.1462956,30.90979 23.917547,34.5857 61.545112,26.472408 40.827369,23.239024 Z"
|
||||
points="23.917547,34.5857 61.545112,26.472408 40.827369,23.239024 3.1462956,30.90979 " />
|
||||
<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 23.917547,34.5857 0,7.918595 37.627565,-8.567528 0,-7.464359 z"
|
||||
points="23.917547,42.504295 61.545112,33.936767 61.545112,26.472408 23.917547,34.5857 " />
|
||||
<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.1462956,30.90979 20.7712514,3.67591 0,7.918595 L 3.1462956,38.58256 Z"
|
||||
points="23.917547,34.5857 23.917547,42.504295 3.1462956,38.58256 3.1462956,30.90979 " />
|
||||
</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.671975,28.462822 20.767248,3.064827"
|
||||
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 28.920585,25.592289 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.463873,32.437404 37.548821,-7.75487"
|
||||
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.243062,32.98943 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.428131,31.554164 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.235121,28.683632 0.01109,8.364271"
|
||||
id="path4818-4-4-6-9-7"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 18 KiB |
430
Resources/planehole_icon.svg
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?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="planehole_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>
|
||||
</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">
|
||||
<ellipse
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.60000002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4199"
|
||||
cx="33.121525"
|
||||
cy="30.878477"
|
||||
rx="22.964256"
|
||||
ry="11.482128" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 13 KiB |
|
@ -14,8 +14,8 @@
|
|||
height="64px"
|
||||
id="svg3074"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="Draft_Upgrade.svg"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="port_icon.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
<defs
|
||||
|
@ -68,7 +68,7 @@
|
|||
id="stop3899" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="matrix(0,-1.4500001,1.4705882,0,-15.05882,91.45)"
|
||||
gradientTransform="matrix(0,-1.4559844,1.1349264,0,-4.4318767,91.352655)"
|
||||
y2="36.079998"
|
||||
x2="21.689653"
|
||||
y1="29.279999"
|
||||
|
@ -86,16 +86,16 @@
|
|||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8.753976"
|
||||
inkscape:cx="12.184978"
|
||||
inkscape:cy="30.864199"
|
||||
inkscape:cx="-4.6645085"
|
||||
inkscape:cy="28.675453"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="837"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:window-maximized="1">
|
||||
|
@ -128,8 +128,8 @@
|
|||
inkscape:groupmode="layer">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:url(#linearGradient3036);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:1.99999988000000006;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="M 7.0000006,29.1 21.000001,29 l 0,32 22,0 0,-32 L 57,29.1 32,3 z"
|
||||
style="fill:url(#linearGradient3036);fill-opacity:1;fill-rule:evenodd;stroke:#0b1521;stroke-width:1.76060784;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 12.592017,28.74533 10.8045,-0.100412 0,32.132067 16.978499,0 0,-32.132067 L 51.179515,28.74533 31.885766,2.5376122 Z"
|
||||
id="path3343"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
|
@ -137,8 +137,8 @@
|
|||
inkscape:export-ydpi="4.1683898" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#729fcf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 12.000001,27 11,0 0,32 18,0 0,-32 11,0 L 32.172062,6.000006 z"
|
||||
style="fill:none;stroke:#729fcf;stroke-width:1.71596217;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 17.293932,26.608649 8.023449,0 0,32.295127 13.12928,0 0,-32.295127 8.023448,0 L 32.007523,5.4149779 Z"
|
||||
id="path3343-2"
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
inkscape:export-filename="/home/yorik/Documents/Lab/Draft/icons/changeprop.png"
|
||||
|
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.1 KiB |
|
@ -14,8 +14,8 @@
|
|||
height="64px"
|
||||
id="svg2860"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.48.4 r9939"
|
||||
sodipodi:docname="fem-analysis.svg"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="solver_icon.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
<defs
|
||||
|
@ -79,16 +79,16 @@
|
|||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.5"
|
||||
inkscape:cx="1.1818182"
|
||||
inkscape:cx="-25.636364"
|
||||
inkscape:cy="29.272727"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-width="1476"
|
||||
inkscape:window-height="750"
|
||||
inkscape:window-x="1349"
|
||||
inkscape:window-y="189"
|
||||
inkscape:window-x="164"
|
||||
inkscape:window-y="71"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata2865">
|
||||
|
@ -108,14 +108,14 @@
|
|||
inkscape:groupmode="layer">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffff00;fill-opacity:1;stroke:#241c1c;font-family:DejaVu Serif;-inkscape-font-specification:DejaVu Serif;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4.09999999999999960"
|
||||
x="10.909091"
|
||||
y="54.909092"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:64px;line-height:125%;font-family:'DejaVu Serif';-inkscape-font-specification:'DejaVu Serif';letter-spacing:0px;word-spacing:0px;fill:#ffff00;fill-opacity:1;stroke:#241c1c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:4.0999999"
|
||||
x="9.272727"
|
||||
y="54"
|
||||
id="text3014"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3016"
|
||||
x="10.909091"
|
||||
y="54.909092">A</tspan></text>
|
||||
x="9.272727"
|
||||
y="54">S</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
@ -21,6 +24,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
import FreeCAD, Mesh, Part, MeshPart, DraftGeomUtils, os
|
||||
from FreeCAD import Vector
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
|
||||
import subprocess
|
||||
from time import sleep
|
||||
import FreeCAD, FreeCADGui
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2018 *
|
||||
#* FastFieldSolvers S.R.L. http://www.fastfieldsolvers.com *
|
||||
#* 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) *
|
||||
|
|