diff --git a/.gitmodules b/.gitmodules index d62922e3d..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "Utils"] - path = Utils - url = https://github.com/KeithSloan/GDML_Command_Line_Utils diff --git a/CommandLine/combineGDML.py b/CommandLine/combineGDML.py new file mode 100644 index 000000000..cea783ca0 --- /dev/null +++ b/CommandLine/combineGDML.py @@ -0,0 +1,16 @@ +import sys + +print('Utility to combined a GDML files included XML files') +if len(sys.argv)<3: + print ("Usage: sys.argv[0] ") + sys.exit(1) + +iname=sys.argv[1] +oname=sys.argv[2] + +from lxml import etree +parser = etree.XMLParser(resolve_entities=True) +root= etree.parse(iname, parser=parser) +root.docinfo.clear() +root.write(oname) + diff --git a/CommandLine/convertObj.py b/CommandLine/convertObj.py new file mode 100644 index 000000000..67494a63c --- /dev/null +++ b/CommandLine/convertObj.py @@ -0,0 +1,303 @@ +# ************************************************************************** +# * * +# * Copyright (c) 2022 Keith Sloan * +# * * +# * 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__ = "convertOBJ - Convert Obj to GDML Tessellated" +__author__ = "Keith Sloan " +__url__ = ["https://github.com/KeithSloan/GDML/Utils"] + +import os, sys + +class switch(object): + value = None + def __new__(class_, value): + class_.value = value + return True + +def case(*args): + return any((arg == switch.value for arg in args)) + +class tessellated: + def __init__(self, name, material): + self.elem = ET.Element('tessellated',{'name':name}) + self.name = name + self.material = material + self.content = False + + def addTriFace(self, vrt1, vrt2, vrt3): + ET.SubElement(self.elem,'triangular',{ + 'vertex1' : vrt1, 'vertex2' : vrt2, 'vertex3' : vrt3, + 'type':'ABSOLUTE'}) + self.content = True + + def addQuadFace(self, vrt1, vrt2, vrt3, vrt4): + ET.SubElement(self.elem,'quadrangular',{ + 'vertex1' : vrt1, 'vertex2' : vrt2, 'vertex3' : vrt3, 'vertex4': vrt4, + 'type':'ABSOLUTE'}) + self.content = True + + def flush(self, xmlStr): + if self.content == True: + xmlStr.solids.insert(1,self.elem) + lvName = 'LV_'+self.name + xmlStr.addVol(lvName, self.material, self.name) + xmlStr.addPhysVol(self.name) + self.content = False + + +class xmlStructure: + def __init__(self): + self.vertex = ['dummy'] + + def indent(self, elem, level=0): + ######################################################### + # Pretty format GDML # + ######################################################### + i = "\n" + level*" " + j = "\n" + (level-1)*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for subelem in elem: + self.indent(subelem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = j + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = j + + def initGDML(self): + NS = 'http://www.w3.org/2001/XMLSchema-instance' + location_attribute = '{%s}noNamespaceSchemaLocation' % NS + self.element = ET.Element('gdml',attrib={location_attribute: \ + 'http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd'}) + #print(self.element.tag) + #'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance", + #'xsi:noNamespaceSchemaLocation': "http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd" +#} + + def init(self): + self.define = ET.SubElement(self.element, 'define') + ET.SubElement(self.define, 'position', {'name':'center', 'x':'0', 'y':'0', 'z':'0'}) + ET.SubElement(self.define, 'rotation', {'name':'identity', 'x':'0', 'y':'0', 'z':'0'}) + self.defineCount = 0 + self.vertexCount = 0 + self.materials = ET.SubElement(self.element, 'materials') + self.solids = ET.SubElement(self.element, 'solids') + self.structure = ET.SubElement(self.element, 'structure') + self.setup = ET.SubElement(self.element, 'setup', {'name': 'Default', 'version': '1.0'}) + + + def addWorldBox(self, x, y, z): + name = 'worldBox' + ET.SubElement(self.solids, 'box', {'name': name, \ + 'x': str(x), 'y': str(y), 'z': str(z)}) + return name + + def addWorldVol(self,name): + ET.SubElement(self.setup, 'world', {'ref': name}) + self.world = self.addVol(name, 'G4_AIR',self.addWorldBox(1000,1000,1000)) + + def addVol(self, name, material, solid): + self.elem = ET.Element('volume',{'name': name}) + ET.SubElement(self.elem, 'materialref',{'ref': material}) + ET.SubElement(self.elem, 'solidref',{'ref': solid}) + self.structure.insert(0,self.elem) + return(self.elem) + + def addPhysVol(self, name): + self.pvol = ET.SubElement(self.world,'physvol', {'name': 'PV'+name}) + ET.SubElement(self.pvol, 'volumeref', {'ref': 'LV_'+name}) + ET.SubElement(self.pvol, 'positionref', {'ref': 'center'}) + ET.SubElement(self.pvol, 'rotationref', {'ref': 'identity'}) + + def addVertex(self, x, y, z): + # Add to Define position name + vnum = 'v'+str(self.defineCount) + self.defineCount += 1 + self.vertexCount += 1 + ET.SubElement(self.define, 'position', {'name': vnum, \ + 'x': str(x), 'y': str(y), 'z': str(z)}) + self.vertex.append(vnum) + + def addTriFace(self, tess, items): + tess.addTriFace(self.vertex[int(items[1])], \ + self.vertex[int(items[2])], \ + self.vertex[int(items[3])]) + + def addQuadFace(self, tess, items): + tess.addQuadFace(self.vertex[int(items[1])], \ + self.vertex[int(items[2])], \ + self.vertex[int(items[3])], \ + self.vertex[int(items[4])]) + + def writeElement(self, path): + print('Write Element to : '+path) + self.indent(self.element) + ET.ElementTree(self.element).write(path, xml_declaration=True) + +def processObjFile(xmlStr, objFp, name, material): + xmlStr.addWorldVol('worldVol') + tessName = name + tess = tessellated(tessName, material) + for line in objFp: + #print(line) + items = line.split() + if items != []: + #print(items) + l = len(items) - 1 + while switch(items[0]) : + if case('v'): + #print('Vertex - len : '+str(l)) + if l >= 3: + xmlStr.addVertex(items[1], items[2],items[3]) + else: + print('Invalid Vertex') + print(items) + break + + if case('f'): + #print('Face') + #print(xmlStr.vertexCount) + if l == 3: + #print('Triangle') + xmlStr.addTriFace(tess, items) + elif l == 4: + #print('Quad : '+str(items)) + xmlStr.addQuadFace(tess, items) + else : + print('Warning Polygon : Number of Face Vertex = '+str(l)) + print('Converting to Triangle Faces') + #verts = [] + #for i in range(1,l+1) : + # v = vertex[getVert(items[i])] + # #print(v) + # verts.append(v) + ##print(verts) + #verts.append(verts[0]) + break + + if case('g'): + print('Group Name') + tessName = items[1] + print(items) + break + + if case('o'): + print('Object Name') + print(items) + tessName = items[1] + break + + if case('s'): + print('Smoothing Group') + tess.flush(xmlStr) + tess = tessellated(tessName, material) + break + + if case('usemtlo'): + print('Material') + print(items) + break + + if case('mtllib'): + print('Material Library') + print(items) + break + + if case('#'): # Comment ignore + break + + if case('vt'): + break + + if case('vn'): + break + + print('Tag : '+str(items)) + break + + tess.flush(xmlStr) + +def convert2GDML(objFp, outPath, tessName, material): + print('Creating GDML from Obj') + gdmlStr = xmlStructure() + gdmlStr.initGDML() + gdmlStr.init() + processObjFile(gdmlStr, objFp, tessName, material) + gdmlStr.writeElement(outPath) + +def convert2XML(objFp, outPath, tessName, material): + print('Creating XML from Obj') + xmlStr = xmlStructure() + xmlStr.initXML() + xmlStr.init() + processObjFile(xmlStr, objFp, tessName, material) + gdmlStr.writeElement(outPath) + +argLen = len(sys.argv) +if argLen<3: + print ("Usage: sys.argv[0] .obj .gdml ") + sys.exit(1) + +iPath = sys.argv[1] +oPath = sys.argv[2] +if argLen == 3: + material = 'G4_A-150_TISSUE' +else: + material = sys.argv[3] +print('\nConverting Obj file : '+iPath+' to : '+oPath) +print('Material '+material) +objFp = open(iPath) + +try: + import lxml.etree as ET + print("running with lxml.etree\n") + XML_IO_VERSION = 'lxml' +except ImportError: + try: + import xml.etree.ElementTree as ET + print("running with xml.etree.ElementTree\n") + XML_IO_VERSION = 'xml' + except ImportError: + print('xml lib not found\n') + sys.exit() +path, fileExt = os.path.splitext(iPath) +print(path) +tessName = path +print(tessName) +if fileExt.lower() != '.obj': + print('Invalid Obj file extension') + sys.exit() +objFp = open(iPath,"r") +if objFp is None: + print('Failed to open :'+iPath) + sys.exit() +path, fileExt = os.path.splitext(oPath) +print('target file extension : '+fileExt) +if fileExt.lower() == '.gdml': + convert2GDML(objFp, oPath, tessName, material) +#elif fileExt.lower() == '.xml': +# convert2XML(objFp, oPath, tessName, material) +else: + print('Target path should have File extension of gdml or xml') diff --git a/CommandLine/extractVol.py b/CommandLine/extractVol.py new file mode 100644 index 000000000..2fc1679e6 --- /dev/null +++ b/CommandLine/extractVol.py @@ -0,0 +1,174 @@ +import sys, os +from lxml import etree + +volList = [] +solidList = [] +positionList = [] +rotationList = [] + +def processSolid(sname) : + # Passed olid name volume, booleans + global solidList, solids + solid = oldSolids.find(f"*[@name='{sname}']") + print('Adding : '+sname) + print(solid.attrib) + if sname not in solidList : solidList.append(sname) + if solid.tag == 'subtraction' or solid.tag == 'union' or \ + solid.tag == 'intersection' : + print('Found Boolean') + first = solid.find('first') + print('first : '+str(first.attrib)) + fname = first.attrib.get('ref') + processSolid(fname) + second = solid.find('second') + print('second : '+str(second.attrib)) + sname = second.attrib.get('ref') + processSolid(sname) + +def processPhysVol(volasm): + for pv in volasm.findall('physvol') : + volref = pv.find('volumeref') + pname = volref.attrib.get('ref') + print('physvol : '+pname) + processVolAsm(pname) + posref = pv.find('positionref') + if posref is not None : + posname = posref.attrib.get('ref') + print('Position ref : '+posname) + if posname not in positionList : + positionList.append(posname) + rotref = pv.find('rotationref') + if rotref is not None : + rotname = rotref.attrib.get('ref') + print('Rotation ref : '+rotname) + if rotname not in rotationList : rotationList.append(rotname) + +def processVol(vol) : + global volList, solidList, oldSolids + print(vol) + print(vol.attrib) + # Need to process physvols first + processPhysVol(vol) + vname = vol.attrib.get('name') + print('volume : ' + vname) + if vname not in volList : volList.append(vname) + solid = vol.find('solidref') + sname = solid.attrib.get('ref') + processSolid(sname) + material = vol.find('materialref') + if material is not None : + #print('material : '+str(material.attrib)) + print('material : ' + material.attrib.get('ref')) + +def processAssembly(assem) : + aname = assem.attrib.get('name') + print('Process Assembly ; '+aname) + processPhysVol(assem) + if aname not in volList : volList.append(aname) + +def processVolAsm(vaname) : + volasm = structure.find(f"*[@name='{vaname}']") + if volasm.tag == 'volume' : + processVol(volasm) + elif volasm.tag == 'assembly' : + processAssembly(volasm) + else : + print('Not Volume or Assembly : '+volasm.tag) + +def exportElement(dirPath, elemName, elem) : + import os + global gdml, docString + + etree.ElementTree(elem).write(os.path.join(dirPath,elemName)) + docString += '\n' + gdml.append(etree.Entity(elemName)) + +def checkDirectory(path) : + if not os.path.exists(path): + print('Creating Directory : '+path) + os.mkdir(path) + +if len(sys.argv)<5: + print ("Usage: sys.argv[0] ") + print("/n For parms the following are or'ed together") + print(" For future") + sys.exit(1) + +parms = int(sys.argv[1]) +gdml = etree.Element('gdml') + +volume = sys.argv[2] +iName = sys.argv[3] +oName = sys.argv[4] + +print('\nExtracting Volume : '+volume+' from : '+iName+' to '+oName) +tree = etree.parse(iName) +root = tree.getroot() +structure = tree.find('structure') +oldSolids = tree.find('solids') +#print(etree.fromstring(structure)) +# Following works +#vol = structure.find('volume[@name="World"]') +# Test if Volume +vol = structure.find(f"volume[@name='{volume}']") +if vol is not None : + processVol(vol) +else : + # Test if Assembly + vol = structure.find(f"assembly[@name='{volume}']") + if vol is not None : + processAssembly(vol) + else : + print(volume+' : Not found as Volume or Assembly') + exit(0) +newDefine = etree.Element('define') +materials = tree.find('materials') +newSolids = etree.Element('solids') +newStructure = etree.Element('structure') +oldDefine = tree.find('define') +oldSolids = tree.find('solids') +oldVols = tree.find('structure') +for posName in positionList : + p = oldDefine.find(f"position[@name='{posName}']") + newDefine.append(p) +for rotName in rotationList : + p = oldDefine.find(f"rotation[@name='{rotName}']") + newDefine.append(p) +for solidName in solidList : + print('Solid : '+solidName) + s = oldSolids.find(f"*[@name='{solidName}']") + #print(s.attrib) + newSolids.append(s) +for vaName in volList : + v = oldVols.find(f"*[@name='{vaName}']") + newStructure.append(v) + +print('Vol List') +print(volList) +print('Solid List') +print(solidList) +print('Position List') +print(positionList) +print('Rotation List') +print(rotationList) +setup = etree.Element('setup', {'name':'Default', 'version':'1.0'}) +etree.SubElement(setup,'world', { 'ref' : volList[-1]}) + +print("Write GDML structure to Directory") +NS = 'http://www.w3.org/2001/XMLSchema-instance' +location_attribute = '{%s}noNameSpaceSchemaLocation' % NS +gdml = etree.Element('gdml',attrib={location_attribute: \ + 'http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd'}) + +docString = '\n\n' +#indent(gdml) +etree.ElementTree(gdml).write(os.path.join(oName,volume+'.gdml'), \ + doctype=docString.encode('UTF-8')) diff --git a/CommandLine/gdml2FC.py b/CommandLine/gdml2FC.py new file mode 100644 index 000000000..8fd129306 --- /dev/null +++ b/CommandLine/gdml2FC.py @@ -0,0 +1,31 @@ +import sys +# add folder containing FreeCAD.pyd, FreeCADGui.pyd to sys.path +#sys.path.append("C:/Program Files/FreeCAD 0.18/bin") # example for Windows +sys.path.append("/usr/lib/freecad-daily/lib") # example for Linux +sys.path.append("/usr/lib/freecad-daily/Mod") # example for Linux +#sys.path.append("/usr/lib/freecad/lib") # example for Linux +import FreeCAD +import FreeCADGui +import Part +import Draft +import Import + + +if len(sys.argv)<3: + print ("Usage: sys.argv[0] ") + sys.exit(1) + +iname=sys.argv[1] +oname=sys.argv[2] + +print('Importing : '+iname) +FreeCAD.loadFile(iname) + +#print(dir(App.ActiveDocument)) +App.ActiveDocument.saveAs(oname) + +sys.exit(0) + +print ("Error: can't find any object") +sys.exit(1) + diff --git a/CommandLine/gdml2step.py b/CommandLine/gdml2step.py new file mode 100644 index 000000000..e82c168eb --- /dev/null +++ b/CommandLine/gdml2step.py @@ -0,0 +1,51 @@ +import sys +# add folder containing FreeCAD.pyd, FreeCADGui.pyd to sys.path +#sys.path.append("C:/Program Files/FreeCAD 0.18/bin") # example for Windows +#sys.path.append("/usr/lib/freecad-daily/lib") # example for Linux +#sys.path.append("/usr/lib/freecad-daily/Mod") # example for Linux +#sys.path.append("/usr/lib/freecad/lib") # example for Linux +sys.path.append("/Applications/FreeCAD_0.19-B.app/Contents/Resources/lib") +sys.path.append("/Applications/FreeCAD_0.19-B.app/Contents/Resources/lib/python3.8/site-packages") # example for Mac OS + +print(sys.path) +try : + import FreeCAD +except : + print("FreeCAD module not found - Update the sys.path.append in this script") + exit(0) +import FreeCADGui +import Part +#import PySide2 +import Draft +import Import + + +if len(sys.argv)<3: + print ("Usage: sys.argv[0] ") + sys.exit(1) + +iname=sys.argv[1] +oname=sys.argv[2] + +print('Importing : '+iname) +FreeCAD.loadFile(iname) + +# iterate through all objects +for o in App.ActiveDocument.Objects: + # find root object and export the shape + #print(dir(o)) + #print(o.Name) + if o.TypeId == 'App::Part' : + #print(o.TypeId) + print('Exporting STEP file : '+oname) + print('This can be a very slow process') + print('for large files Please be patient') + #Import.export([o],"/tmp/test4.step") + Import.export([o],oname) + sys.exit(0) + +sys.exit(0) + +print ("Error: can't find any object") +sys.exit(1) + diff --git a/CommandLine/listSolids.py b/CommandLine/listSolids.py new file mode 100644 index 000000000..301d1f3c2 --- /dev/null +++ b/CommandLine/listSolids.py @@ -0,0 +1,22 @@ +import sys +from lxml import etree + +if len(sys.argv)<2: + print ("Usage: sys.argv[0] ") + sys.exit(1) + +iname = sys.argv[1] + +print('\nParsing : '+iname+'\n') +tree = etree.parse(iname) +root = tree.getroot() +solidList = [] +for s in tree.xpath('//solids/*') : + #print(s.tag) + if s.tag not in solidList : + solidList.append(s.tag) +l = 'List of Solids in : '+iname +print(l) +print('=' * len(l)) +for i in solidList : + print(i) diff --git a/CommandLine/listVols.py b/CommandLine/listVols.py new file mode 100644 index 000000000..3f7fc06c1 --- /dev/null +++ b/CommandLine/listVols.py @@ -0,0 +1,18 @@ +import sys +from lxml import etree + +if len(sys.argv)<2: + print ("Usage: sys.argv[0] ") + sys.exit(1) + +iname = sys.argv[1] + +print('\nListing Volumes in : '+iname+'\n') +tree = etree.parse(iname) +root = tree.getroot() +s = root.find('structure') +#print(etree.tostring(s)) +for v in s.findall('volume') : + #print(etree.tostring(v)) + n = v.get('name') + print(n) diff --git a/README.md b/README.md index c151e86e9..ec5a165eb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ ## FreeCAD GDML Workbench +### IMPORTANT + +FreeCAD 0.20 has been released, the default GDML workbench is now **Main** + +* Install FreeCAD 0.20 +* Delete workbench GDML +* install workbench GDML via the Addon_manager + ( use refresh Cache option to make sure latest version ) + ### Introduction **[FreeCAD](https://freecad.org)** is a Free Libre Open Source multi-platform CAD/CAM/FEM suite. @@ -22,6 +31,10 @@ Viewing CERN's LHCBVelo.gdml using the experimental FreeCAD LinkStage3 Daily bra ## Important Notes +#### New Branch Main + +This branch will be tested with the up and coming FreeCAD 0.20 release with the view to become the Default installed branch. + #### ATTENTION WINDOWS users using FreeCAD v0.19.1 The 3rd party python dependency `lxml` should have been installed in prebuilt versions of FreeCAD. @@ -32,38 +45,51 @@ the required libraries section of this README. #### Regression with STEP export -With OpenCasCade v7.5.0 and v7.5.1 as used in FreeCAD 0.19.2 +There is a regression with STEP export in OpenCasCade v7.5.0 and v7.5.1 as used in FreeCAD 0.19.2 +Recomended to use at least FreeCAD 0.19.3 with OpenCasCade v7.5.3 ( i.e OCC 7.5.3 ) -#### Problem with export of Rotations +#### NEW BRANCH beta2 -This issues applies to all operating systems. To fix a problem with export and rotations please use +A new branch beta2 has a lot of exciting enhancements. The plan is that in time this will merged with the master branch -* the FreeCAD_Assembly3 release STABLE or DAILY see https://github.com/realthunder/FreeCAD_assembly3/releases +* The branch is selectable using the new Addon manager in FreeCAD 0.20 - scroll down to Assets. + For FreeCAD 0.20 builds see weekly builds at https://github.com/FreeCAD/FreeCAD-Bundle/releases/tag/weekly-builds +* Supports GDML exports without needing toEuler facility in FreeCAD +* Creation of GDML solids from [FreeCAD Sketches](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches) + * [Extrude](https://github.com/KeithSloan/GDML/wiki/Extrude--:-Examples-of-Extruded-sketches) + * [Revolve](https://github.com/KeithSloan/GDML/wiki/Revolved-:-Examples-of-Revolved-sketches) +* Creation of [Arrays](https://github.com/KeithSloan/GDML/wiki/Array-:-Example-of-use-of-Array) +* Creation of [Mirrors](https://github.com/KeithSloan/GDML/wiki/Mirror-:-Examples-of-use-of-Mirror) +* Export of GDML object takes into account the Placements of the GDML objects and the App::Part ( GDML Volume ) + +#### Enhancements with Realthunder LinkDaily branch + +For installation see the FreeCAD_Assembly3 release STABLE or DAILY see https://github.com/realthunder/FreeCAD_assembly3/releases +scroll down to Assets. -* the GDML git branch called **link3**. Install it via: - 1. Access your FreeCAD config directory (for example on Linux: `cd ~/.FreeCAD/Mod/GDML`) - 2. `git fetch origin link3` - 3. `git checkout link3` - 4. Restart FreeCAD +Realthunders LinkDaily branch has the following enhancements -You should also see a dramatic improvement especially with LinkDaily in import times with these builds. +* Faster import of GDML objects +* Add extra toEulerAngles function ( Note: as of branch beta2 this is no longer needed ) +* Enhanced Rendering which helps with complex models. -#### Enhanced Rendering (on Realthunder's LinkDaily branch) -There is also the option to use enhanced rendering which helps with complex models. To enable enhanced rendering in LinkDaily: +To enable enhanced rendering in LinkDaily: `FreeCAD > Preferences > Display > Render Cache > Experimental` If you like what you see you might like to thank Lei Zhang by contributing to his [FreeCAD Patreon](https://www.patreon.com/thundereal/posts) -You can use FreeCAD 0.19.1 but this does not have the toEulerAngles function that facilities the -fixing of exports with rotations and import speed will still be slow. The toEulerAngles facility should be in the -process of being added to FreeCAD 0.20 -For latest versions of FreeCAD 0.19 see the Assets section of https://github.com/FreeCAD/FreeCAD/releases +#### Problem with export of Rotations ( Fixed in beta2 ) -**Changes to Placement (GDML Position & Rotation)** +For correct export of GDML rotations please use one the the following + +* beta2 branch ( All operating systems even FreeCAD 0.19) +* A Realthunder version of FreeCAD +* A recent version fo FreeCAD 0.20 + +#### Changes to Placement (GDML Position & Rotation) [Fixed in beta2] In order to support copies of GDML Volumes the following changes have been made @@ -77,29 +103,6 @@ In order to support copies of GDML Volumes the following changes have been made ## Installation -### Prerequisites - -* FreeCAD (https://freecad.org/) -* `lxml` (bundled in to FreeCAD v0.19) -* `gmsh` python library - -### gmsh python library - -The workbench uses the gmsh python library and must be installed in a location that FreeCAD sees - -To check path FreeCAD uses from a command line/window. - - freecad -c - import sys - print(sys.path) - -In a command window / line - - pip install --upgrade --target gmsh - -Windows: if no --target option upgrade pip - - ### Install via the Addon Manager The GDML workbench can be installed via the [Addon Manager](https://wiki.freecad.org/Std_AddonMgr) @@ -108,73 +111,61 @@ The GDML workbench can be installed via the [Addon Manager](https://wiki.freecad 2. Click the `Tools` → `Addon manager` dropdown menu 3. Browse through the list of workbench and look for GDML -### Prerequisite 3rd party Python libraries - -
-Click to expand! - -Currently there are two 3rd party libraries that are necessary for the GDML workbench to function: `lxml` and `gmsh`. - -#### `lxml` - -[lxml](https://lxml.de/) is a Python library that processes XML and HTML. Starting with the below versions of FreeCAD, `lxml` should be auto-bundled as part of the installation: +### Prerequisites - * `FreeCAD_0.19.19424` and above - * `FreeCAD_0.19.19409_x64_Conda_Py3QT5-WinVS2015.7z` and above. - +* FreeCAD (https://freecad.org/) +* `lxml` (bundled in to FreeCAD v0.19) +* `gmsh` python library -##### Checking if `lxml` is installed +The Addon Manager should install the preequisite python libraries lxml and gmsh, you can check if it is successful +by from the FreeCAD python console -To discover if **`lxml`** is installed and accessible by FreeCAD: +* import lxml +* import gmsh -1. Open the CLI -2. Invoke the following: - ```python - freecad -c - import sys - print(sys.path) - ``` - Result: +### gmsh shared library +Gmsh shared library is also required otherwise you will get the following error message in report View -**Note:** To check if **`lxml`** is installed correctly: + AttributeError: dlsym(RTLD_DEFAULT, gmshInitialize): symbol not found - ```python - freecad -c - import lxml - from lxml import etree - print(etree.LXML_VERSION) - ``` +To download the gmsh shared library you need to obtain a copy of the Gmsh SDK see https://gmsh.info -##### Manual install of lxml +It should then copied to FreeCAD as follows -In case there is a need to manually install `lxml`: +#### MacOS - ```bash - pip3 install lxml -t < directory > - ``` +Copy from lib + * gmsh.py + * libgmsh.4.9.4.dylib -##### FreeCAD v0.18 +To : -There are known limitations with FreeCAD 0.18 and **lxml**. Therefore, it is recommended that you use FreeCAD v0.19 as above. -(Note: You can install both versions v0.18 & v0.19 and still use v0.18 for non GDML related work) + /Applications/FreeCAD_0.20.app/Contents/Resources/lib + +#### Linux -### `Gmsh` +Copy from lib -[Gmsh](https://gmsh.info/) is an open source 3D finite element mesh generator. FreeCAD & Gmsh should both be using the same version of OCC (OpenCasCade), the underlying CAD kernel that FreeCAD uses. + * gmsh.py + * libgmsh.so +To : -| OCC | FreeCAD | gmsh | -|---------|-------------|---------------| -| `7.4` | `0.19.1` |`4.7.0 - 4.7.1`| -| `7.6` | |`4.8.0 - 4.8.4`| + ?????? -At the time of writing a prebuilt version of FreeCAD with OCC 7.6 is not available +#### Windows -Note: The version of OCC with FreeCAD 0.19.2 has a regression with STEP functionality +Copy from lib -You can check the version FreeCAD is using with About FreeCAD, copy to clipboard, paste. + * gmsh.py + * gmsh.lib + * gmsh-4.10.dll + +To : + ?????? + #### Checking what version of Gmsh is installed To ascertain the Gmsh version, paste the following in to the python console @@ -186,43 +177,12 @@ To ascertain the Gmsh version, paste the following in to the python console gmsh.finalize() ``` -#### Check the Gmsh version using `gmshVer.py` -It is also possible to run the `gmshVer.py` script (available in this workbench's `Utils` directory). To see what versions of Gmsh are available to install, open the CLI and type: - - ```bash - pip install gmsh== - ``` - **Note:** Gmsh must be installed in a location that FreeCAD can access. To check the path FreeCAD uses, open the CLI and type: - - ```bash - freecad -c - import sys - print(sys.path) - ``` -In a command window / line - - pip install --upgrade --target gmsh=='version' - -Windows: if no --target option upgrade pip - - python -m pip install -U pip - -For example on Windows system where FreeCAD is installed on the D drive - - pip install --target="D:\FreeCAD 0.19\FreeCAD_0.19\bin\Lib\site-packages" gmsh - -will create - - D:\FreeCAD 0.19\FreeCAD_0.19\bin\Lib\site-packages\gmsh-4.6.0-py3.8.egg-info - D:\FreeCAD 0.19\FreeCAD_0.19\bin\Lib\site-packages\Lib\site-packages\gmsh-4.6.0-Windows64-sdk - -
- ## Usage * To read more about the general usage of the GDML workbench checkout the [GDML Workbench wiki](https://github.com/KeithSloan/GDML/wiki) * Converting STEP files to GDML [Convert Step to GDML](https://github.com/KeithSloan/GDML/wiki/Step2Tessellate) * Creating Tessellated Objects from FreeCAD Part Design Objects [Tessellate Part Design](https://github.com/KeithSloan/GDML/wiki/Tessellating-Part-Design-Objects) +* Creating a GDML object from an [Extruded sketch](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches)
@@ -513,7 +473,7 @@ to facilitate preparation for FEM analysis ## Standalone Utilities -The standalone utilities and documentation are now in a submodule repository https://github.com/KeithSloan/GDML_Command_Line_Utils +The standalone utilities and documentation are now in a directory CommandLine In the [Utils](Utils/) directory, you'll find a python script named **`gdml2step.py`** for creating a STEP file from a GDML file. diff --git a/Utils b/Utils index e41bc6839..10d3925eb 160000 --- a/Utils +++ b/Utils @@ -1 +1 @@ -Subproject commit e41bc6839b7231ad1007fee3976d27df1fb3b565 +Subproject commit 10d3925eb7c7b7cafebdb43beca1e0a335587300 diff --git a/freecad/gdml/.gitignore b/freecad/gdml/.gitignore index 6797c8f63..8ba2f1f76 100644 --- a/freecad/gdml/.gitignore +++ b/freecad/gdml/.gitignore @@ -1,4 +1,5 @@ *.pyc +\#*\# Resources/ColorDict.csv Resources/MapMaterials.xml diff --git a/freecad/gdml/GDMLCommands.py b/freecad/gdml/GDMLCommands.py index 37c0b6583..26c61ec4a 100644 --- a/freecad/gdml/GDMLCommands.py +++ b/freecad/gdml/GDMLCommands.py @@ -181,7 +181,6 @@ def __init__(self, selList): def initUI(self): from .GDMLMaterials import GDMLMaterial, newGetGroupedMaterials - print('initUI') self.setGeometry(150, 150, 250, 250) self.setWindowTitle("Set GDML Material") @@ -197,7 +196,9 @@ def initUI(self): self.materialComboBox.addItems(self.groupedMaterials[groups[0]]) self.matList = [] for group in self.groupedMaterials: + print(group) self.matList += self.groupedMaterials[group] + print(len(self.matList)) self.completer = QtGui.QCompleter(self.matList, self) self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) self.materialComboBox.setCompleter(self.completer) @@ -218,7 +219,11 @@ def initUI(self): obj = self.SelList[0].Object if hasattr(obj, 'material'): mat = obj.material - self.lineedit.setText(mat) + if mat in self.matList : # Check valid material in list + self.lineedit.setText(mat) + else : + print(f'Default to {self.materialComboBox.currentText()}') + self.lineedit.setText(self.materialComboBox.currentText()) self.setMaterial(mat) self.show() @@ -239,10 +244,12 @@ def completionActivated(self, text): def groupChanged(self, index): from .GDMLObjects import GroupedMaterials + print('Group Change') self.materialComboBox.blockSignals(True) self.materialComboBox.clear() group = self.groupsCombo.currentText() self.materialComboBox.addItems(GroupedMaterials[group]) + self.lineedit.setText(self.materialComboBox.currentText()) self.materialComboBox.blockSignals(False) def materialChanged(self, text): @@ -258,12 +265,15 @@ def onSet(self): print(f'Set Material {mat}') for sel in self.SelList: obj = sel.Object - if hasattr(obj, 'material'): - obj.material = mat - else: + if not hasattr(obj, 'material'): # Make sure obj has material prop obj.addProperty("App::PropertyEnumeration", "material", "GDML", "Material") + if obj.material.find(mat) == True: + obj.material = mat + else: + print('Reset Materials list') obj.material = self.matList + print(f'Set Material to {mat}') obj.material = self.matList.index(mat) @@ -1235,12 +1245,15 @@ def Activated(self): # GDMLTessellated(myTess,mesh.Topology[0],mesh.Topology[1], \ GDMLTessellated(myTess, mesh.Topology[0], mesh.Facets, True, "mm", mat) - # Update Part Placment with source Placement - vol.Placement = obj.Placement - base = obj.Placement.Base - print(type(base)) - myTess.Placement.Base = base.multiply(-1.0) - FreeCAD.ActiveDocument.recompute() + # After meshing points have values as per identity Placement + myTess.Placement = FreeCAD.Placement() + # Update Part Placement with source Placement + #vol.Placement = obj.Placement + #mat = obj.Placement.toMatrix().inverse() + #myTess.Placement = FreeCAD.Placement() + #myTess.recompute() + #myTess.Shape.transformGeometry(mat) + myTess.recompute() if FreeCAD.GuiUp: ViewProvider(myTess.ViewObject) obj.ViewObject.Visibility = False diff --git a/freecad/gdml/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index 81ae9d084..77243c061 100644 --- a/freecad/gdml/GDMLMaterials.py +++ b/freecad/gdml/GDMLMaterials.py @@ -50,7 +50,6 @@ def getMaterialsList(): doc = FreeCAD.activeDocument() try: materials = doc.Materials - geant4 = doc.Geant4 g4Mats = doc.getObject('G4Materials') except: @@ -60,7 +59,6 @@ def getMaterialsList(): print('Load Geant4 Materials XML') processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) materials = doc.Materials - geant4 = doc.Geant4 g4Mats = doc.getObject('G4Materials') try: @@ -82,6 +80,7 @@ def getMaterialsList(): return matList + def refreshG4Materials(doc): from .importGDML import joinDir, setupEtree, processMaterialsG4, newGroupPython, processNewG4 print('Get latest G4 Materials') @@ -97,32 +96,40 @@ def refreshG4Materials(doc): processNewG4(G4matGrp, mats_xml) doc.recompute() + def newGetGroupedMaterials(): + from .importGDML import joinDir, processGEANT4 print('New getGroupedMaterials') from .GDMLObjects import GroupedMaterials - doc = FreeCAD.activeDocument() - docG4Materials = doc.G4Materials - if not hasattr(docG4Materials, 'version'): - refreshG4Materials(doc) - docG4Materials = doc.G4Materials - for g in docG4Materials.Group: - # print(f'g : {g.Name}') - for s in g.Group: - # print(f's : {s.Name}') - if g.Name in GroupedMaterials: - GroupedMaterials[g.Name].append(s.Name) - else: - GroupedMaterials[g.Name] = [s.Name] - matList = [] - docMaterials = doc.Materials - if docMaterials is not None: - for m in docMaterials.OutList: - if m.Label != "Geant4": - if m.Label not in matList: - matList.append(m.Label) + if len(GroupedMaterials) == 0: + doc = FreeCAD.activeDocument() + if not hasattr(doc, 'Materials') or not hasattr(doc, 'G4Materials'): + print('Reload Geant4 Materials') + processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) + docG4Materials = doc.G4Materials + if not hasattr(docG4Materials, 'version'): + refreshG4Materials(doc) + docG4Materials = doc.G4Materials + print('Build Materials SubGroups') + for g in docG4Materials.Group: + # print(f'g : {g.Label}') + for s in g.Group: + # print(f's : {s.Label}') + if g.Name in GroupedMaterials: + GroupedMaterials[g.Label].append(s.Label) + else: + GroupedMaterials[g.Label] = [s.Label] + print('Build Non Geant4 Materials') + matList = [] + docMaterials = doc.Materials + if docMaterials is not None: + for m in docMaterials.OutList: + if m.Label != "Geant4": + if m.Label not in matList: + matList.append(m.Label) - if len(matList) > 0: - GroupedMaterials['Normal'] = matList + if len(matList) > 0: + GroupedMaterials['Normal'] = matList return GroupedMaterials @@ -155,7 +162,8 @@ def getGroupedMaterials(): doc = FreeCAD.activeDocument() docMaterials = doc.Materials matList = [] - if docMaterials is not None: + + if doc.Materials is not None: for m in docMaterials.OutList: if m.Label != "Geant4": if m.Label not in matList: diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index e1d10ee60..2ca31a974 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -64,7 +64,8 @@ def addMaterialsFromGroup(doc, MatList, grpName): if mmats is not None: if hasattr(mmats, 'Group'): for i in mmats.Group: - MatList.append(i.Name) + if i.Label != 'Geant4': + MatList.append(i.Label) def rebuildMaterialsList(): @@ -72,7 +73,12 @@ def rebuildMaterialsList(): print('Restore MaterialsList from Materials Lists') doc = FreeCAD.ActiveDocument addMaterialsFromGroup(doc, MaterialsList, "Materials") - addMaterialsFromGroup(doc, MaterialsList, "G4Materials") + #print(MaterialsList) + G4Materials = doc.getObject('G4Materials') + if G4Materials is not None : + for g in G4Materials.Group: + #print(g.Label) + addMaterialsFromGroup(doc, MaterialsList, g.Label) # print('MaterialsList') # print(MaterialsList) @@ -87,9 +93,11 @@ def checkMaterial(material): def setMaterial(obj, m): - # print('setMaterial') + print('setMaterial') if MaterialsList is not None: if len(MaterialsList) > 0: + print('MaterialsList Ok') + #print(MaterialsList) obj.material = MaterialsList obj.material = 0 if not (m == 0 or m is None): @@ -358,10 +366,16 @@ def __init__(self, obj): if (ln - r) >= 2: # print('Tool : '+obj.Label) return # Let Placement default to 0 - obj.setEditorMode('Placement', 2) + #obj.setEditorMode('Placement', 2) def getMaterial(self): - return obj.material + return self.obj.material + + def scale(self,fp): + print(f'Rescale : {fp.scale}') + mat = FreeCAD.Matrix() + mat.scale(fp.scale) + fp.Shape = fp.Shape.transformGeometry(mat) def execute(self, fp): self.createGeometry(fp) @@ -476,6 +490,10 @@ def onChanged(self, fp, prop): 'dz', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid # http://geant4-userdoc.web.cern.ch/geant4-userdoc/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html @@ -525,6 +543,7 @@ def createGeometry(self, fp): faceYminA, faceYminB, faceYmaxA, faceYmaxB, faceZmin, faceZmax])) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -551,7 +570,7 @@ def __init__(self, obj, x, y, z, lunit, material, colour=None): def onChanged(self, fp, prop): '''Do something when a property has changed''' - # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # Changing Shape in createGeometry will redrive onChanged if ('Restore' in fp.State): return @@ -565,11 +584,14 @@ def onChanged(self, fp, prop): if prop in ['x', 'y', 'z', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # execute(self, fp): in GDMLsolid def createGeometry(self, fp): - # print('createGeometry') - # print(fp) + print('createGeometry') + print(fp) if all((fp.x, fp.y, fp.z)): currPlacement = fp.Placement @@ -584,6 +606,7 @@ def createGeometry(self, fp): base = FreeCAD.Vector(-x/2, -y/2, -z/2) fp.Shape = translate(box, base) fp.Placement = currPlacement + if hasattr(fp,'scale'): super().scale(fp) def OnDocumentRestored(self, obj): print('Doc Restored') @@ -644,6 +667,9 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -693,6 +719,7 @@ def createGeometry(self, fp): fp.Shape = translate(cone, base) else: fp.Shape = translate(cone3, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -736,6 +763,9 @@ def onChanged(self, fp, prop): if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -784,6 +814,7 @@ def createGeometry(self, fp): fp.Shape = cone2.cut(box) else: fp.Shape = cone2 + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -830,47 +861,59 @@ def onChanged(self, fp, prop): if prop in ['ax', 'by', 'cz', 'zcut1', 'zcut2', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): currPlacement = fp.Placement mul = GDMLShared.getMult(fp) - sphere = Part.makeSphere(100) + sphere = Part.makeSphere(100) # 100= sphere radius = 1/2 diameter ax = fp.ax * mul by = fp.by * mul cz = fp.cz * mul mat = FreeCAD.Matrix() mat.unity() - # Semi axis values so need to double - mat.A11 = ax / 50 - mat.A22 = by / 50 - mat.A33 = cz / 50 + mat.A11 = ax / 100 + mat.A22 = by / 100 + mat.A33 = cz / 100 mat.A44 = 1 - zcut1 = abs(fp.zcut1*mul) - zcut2 = abs(fp.zcut2*mul) + + if fp.zcut1 is not None: + zcut1 = fp.zcut1*mul + else: + zcut1 = -2*cz + + if fp.zcut2 is not None: + zcut2 = fp.zcut2*mul + else: + zcut2 = 2*cz + GDMLShared.trace("zcut2 : " + str(zcut2)) t1ellipsoid = sphere.transformGeometry(mat) - if zcut2 is not None and zcut2 > 0 and zcut2 < cz: # Remove from upper z - box1 = Part.makeBox(2*ax, 2*by, zcut2) + if zcut2 > -cz and zcut2 < cz: # Remove from upper z + box1 = Part.makeBox(2*ax, 2*by, 2*cz) pl = FreeCAD.Placement() # Only need to move to semi axis - pl.move(FreeCAD.Vector(-ax, -by, cz-zcut2)) + pl.move(FreeCAD.Vector(-ax, -by, zcut2)) box1.Placement = pl t2ellipsoid = t1ellipsoid.cut(box1) else: t2ellipsoid = t1ellipsoid - if zcut1 is not None and zcut1 > 0 and zcut1 < cz: - # Remove from lower z, seems to be a negative number - box2 = Part.makeBox(2*ax, 2*by, zcut1) + if zcut1 < zcut2 and zcut1 > -cz and zcut1 < cz: + box2 = Part.makeBox(2*ax, 2*by, 2*cz) pl = FreeCAD.Placement() - pl.move(FreeCAD.Vector(-ax, -by, -cz)) + # cut with the upper edge of the box + pl.move(FreeCAD.Vector(-ax, -by, -2*cz+zcut1)) box2.Placement = pl shape = t2ellipsoid.cut(box2) else: shape = t2ellipsoid - base = FreeCAD.Vector(0, 0, cz/4) + base = FreeCAD.Vector(0, 0, 0) fp.Shape = translate(shape, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -913,6 +956,9 @@ def onChanged(self, fp, prop): if prop in ['dx', 'dy', 'dz', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -929,6 +975,7 @@ def createGeometry(self, fp): newtube = tube.transformGeometry(mat) base = FreeCAD.Vector(0, 0, -(fp.dz*mul)) # dz is half height fp.Shape = translate(newtube, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -967,6 +1014,9 @@ def onChanged(self, fp, prop): # print(dir(fp)) self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -976,6 +1026,7 @@ def createGeometry(self, fp): mul = GDMLShared.getMult(fp.lunit) r = mul * fp.r fp.Shape = Part.makeSphere(r) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1027,6 +1078,8 @@ def onChanged(self, fp, prop): if prop in ['x', 'y', 'z', 'alpha', 'theta', 'phi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1095,6 +1148,7 @@ def createGeometry(self, fp): # center = (v7 - v1)/2 fp.Shape = translate(solid, -center) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1147,6 +1201,9 @@ def onChanged(self, fp, prop): if prop in ['rmin', 'rmax', 'z', 'inst', 'outst', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1214,7 +1271,7 @@ def createGeometry(self, fp): rr = [math.sqrt(sqrtan1*zi*zi + rmin*rmin) for zi in zz] innersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) fp.Shape = outersolid.cut(innersolid) - + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1259,6 +1316,9 @@ def onChanged(self, fp, prop): if prop in ['rlo', 'rhi', 'z', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1294,7 +1354,7 @@ def createGeometry(self, fp): rr = [math.sqrt(k1*zi+k2) for zi in zz] outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) fp.Shape = outersolid - + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1343,6 +1403,9 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1410,6 +1473,7 @@ def createGeometry(self, fp): fp.Shape = newShape else: fp.Shape = shape + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1458,6 +1522,9 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1519,6 +1586,7 @@ def createGeometry(self, fp): shell = Part.makeShell(faces) solid = Part.makeSolid(shell) fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1569,6 +1637,9 @@ def onChanged(self, fp, prop): # print(f'Change Prop : {prop}') self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1593,6 +1664,7 @@ def createGeometry(self, fp): if fp.startphi != 0: torus.rotate(spnt, sdir, getAngleDeg(fp.aunit, fp.startphi)) fp.Shape = torus + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1656,6 +1728,9 @@ def onChanged(self, fp, prop): if prop in ['x', 'y', 'z', 'PhiTwist', 'lunit', 'aunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1690,6 +1765,7 @@ def createGeometry(self, fp): loft = Part.makeLoft(slices, True, False) fp.Shape = loft + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): @@ -1756,6 +1832,9 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1811,6 +1890,7 @@ def createGeometry(self, fp): loft = Part.makeLoft(slices, True, False) fp.Shape = loft + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1875,6 +1955,9 @@ def onChanged(self, fp, prop): if prop in ['x1', 'y1', 'x2', 'y2', 'z', 'PhiTwist', 'lunit', 'aunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1912,6 +1995,7 @@ def createGeometry(self, fp): loft = Part.makeLoft(slices, True, False) fp.Shape = loft + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): @@ -1979,6 +2063,9 @@ def onChanged(self, fp, prop): 'phi', 'lunit', 'aunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2032,6 +2119,7 @@ def createGeometry(self, fp): loft = Part.makeLoft(slices, True, False) fp.Shape = loft + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): @@ -2070,6 +2158,9 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def layerPoints(self, polyList, sf, xOffset, yOffset, zPosition): @@ -2168,6 +2259,7 @@ def createGeometry(self, fp): # print(dir(fp)) # solid.exportBrep("/tmp/"+fp.Label+".brep") fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2320,6 +2412,9 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2374,6 +2469,7 @@ def createGeometry(self, fp): angleDeltaPhiDeg) # compound of all faces fp.Shape = Part.makeCompound(listShape) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2422,6 +2518,9 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2447,6 +2546,7 @@ def createGeometry(self, fp): solid = Part.makeSolid(surf) fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2502,6 +2602,9 @@ def onChanged(self, fp, prop): 'deltatheta', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2584,6 +2687,7 @@ def createGeometry(self, fp): fp.Shape = sphere2 else: fp.Shape = sphere2.cut(Part.makeSphere(rmin)) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2643,6 +2747,9 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self,fp): @@ -2715,6 +2822,7 @@ def createGeometry(self,fp): center = (topCenter+botCenter)/2 fp.Shape = translate(solid, -center) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2760,6 +2868,9 @@ def onChanged(self, fp, prop): if prop in ['z', 'x1', 'x2', 'y1', 'y2', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2794,6 +2905,7 @@ def createGeometry(self, fp): # solid = Part.makePolygon([v1,v2,v3,v4,v5,v6,v7,v1]) fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2845,6 +2957,9 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2870,6 +2985,7 @@ def createGeometry(self, fp): base = FreeCAD.Vector(0, 0, -z/2) fp.Shape = translate(tube, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2938,6 +3054,9 @@ def onChanged(self, fp, prop): 'highX', 'highY', 'highZ', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def cutShapeWithPlane(self, shape, plane, depth): @@ -2980,6 +3099,7 @@ def createGeometry(self, fp): cutTube2 = self.cutShapeWithPlane(cutTube1, botPlane, depth) base = FreeCAD.Vector(0, 0, -z/2) fp.Shape = translate(cutTube2, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement def createGeometry_hardcoded(self, fp): @@ -3118,7 +3238,8 @@ def __init__(self, obj, sourceObj, meshLen, vertex, facets, lunit, self.colour = colour obj.Proxy = self - def updateParams(self, vertex, facets): + def updateParams(self, vertex, facets, flag): + self.Vertex = vertex self.Facets = facets self.facets = len(facets) @@ -3145,6 +3266,9 @@ def onChanged(self, fp, prop): self.reMesh(fp) self.execute(fp) + if prop in ['scale']: + self.createGeometry(fp) + def execute(self, fp): # Here for remesh? self.createGeometry(fp) @@ -3204,6 +3328,7 @@ def createGeometry(self, fp): # base = FreeCAD.Vector(0,0,0) # fp.Shape = translate(solid,base) fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -3259,6 +3384,9 @@ def onChanged(self, fp, prop): if fp.editable is True: self.addProperties() + if prop in ['scale']: + self.createGeometry(fp) + def addProperties(self): print('Add Properties') @@ -3272,6 +3400,7 @@ def createGeometry(self, fp): fp.pshape = self.pshape fp.vertex = self.vertex fp.facets = self.facets + if hasattr(fp,'scale'): super().scale(fp) def createShape(self, vertex, facets, flag): # Viewing outside of face vertex must be counter clockwise @@ -3285,7 +3414,24 @@ def createShape(self, vertex, facets, flag): # print('Facet') # print(f) if flag is True: - FCfaces.append(GDMLShared.facet(f)) + #FCfaces.append(GDMLShared.facet(f)) + if len(f.Points) == 3: + face = GDMLShared.triangle(f.Points[0], \ + f.Points[1], f.Points[2]) + FCfaces.append(face) + else : # Four points might be close to but not coplanar OBJ file + try: + face = GDMLShared.quad(f.Points[0], f.Points[1], \ + f.Points[2], f.Points[3]) + FCfaces.append(face) + except: + print('Four points not coplanar use 2 triangular faces') + face = GDMLShared.triangle(f.Points[0], f.Points[1], \ + f.Points[2]) + FCfaces.append(face) + face = GDMLShared.triangle(f.Points[1], f.Points[2], \ + f.Points[3]) + FCfaces.append(face) else: if len(f) == 3: FCfaces.append(GDMLShared.triangle( @@ -3293,11 +3439,27 @@ def createShape(self, vertex, facets, flag): mul*vertex[f[1]], mul*vertex[f[2]])) else: # len should then be 4 - FCfaces.append(GDMLShared.quad( - mul*vertex[f[0]], - mul*vertex[f[1]], - mul*vertex[f[2]], - mul*vertex[f[3]])) + try: + face = GDMLShared.quad( + mul*vertex[f[0]], + mul*vertex[f[1]], + mul*vertex[f[2]], + mul*vertex[f[3]]) + FCfaces.append(face) + + except: # quad may not be coplanar OBJ file + print('Four points not coplanar use 2 triangular faces') + face = GDMLShared.triangle( + mul*vertex[f[0]], + mul*vertex[f[1]], + mul*vertex[f[2]]) + FCfaces.append(face) + face = GDMLShared.triangle( + mul*vertex[f[1]], + mul*vertex[f[2]], + mul*vertex[f[3]]) + FCfaces.append(face) + shell = Part.makeShell(FCfaces) shell = Part.makeShell(FCfaces) if shell.isValid is False: FreeCAD.Console.PrintWarning('Not a valid Shell/n') @@ -3356,6 +3518,9 @@ def onChanged(self, fp, prop): if prop in ['v1', 'v2', 'v3', 'v4', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -3370,6 +3535,7 @@ def createGeometry(self, fp): face3 = Part.Face(Part.makePolygon([pt4, pt2, pt3, pt4])) face4 = Part.Face(Part.makePolygon([pt1, pt3, pt4, pt1])) fp.Shape = Part.makeSolid(Part.makeShell([face1, face2, face3, face4])) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -3415,6 +3581,9 @@ def onChanged(self, fp, prop): if prop in ['lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + # def execute(self, fp): in GDMLsolid def makeTetra(self, pt1, pt2, pt3, pt4): @@ -3437,6 +3606,7 @@ def createGeometry(self, fp): pt4 = mul * t[3] tetraShells.append(self.makeTetra(pt1, pt2, pt3, pt4)) fp.Shape = Part.makeCompound(tetraShells) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement diff --git a/freecad/gdml/GDMLShared.py b/freecad/gdml/GDMLShared.py index 0fd5ba8ac..4f5db8ad2 100644 --- a/freecad/gdml/GDMLShared.py +++ b/freecad/gdml/GDMLShared.py @@ -2,37 +2,37 @@ # single access to globals # anything requiring access to globals needs to call a function in this file # anything needing to call eval needs to be in this file -#************************************************************************** -#* * -#* Copyright (c) 2017 Keith Sloan * -#* (c) Dam Lambert 2020 * -#* * -#* 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 * -#* * -#* Acknowledgements : * -#* * -#* * -#************************************************************************** +# ************************************************************************** +# * * +# * Copyright (c) 2017 Keith Sloan * +# * (c) Dam Lambert 2020 * +# * * +# * 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 * +# * * +# * Acknowledgements : * +# * * +# * * +# ************************************************************************** from math import * import FreeCAD, Part from PySide import QtCore, QtGui -#from lxml import etree as ET +# from lxml import etree as ET global define global tracefp @@ -40,41 +40,46 @@ global printverbose printverbose = False -def setTrace(flag) : + +def setTrace(flag): global tracefp - print('Trace set to : '+str(flag)) + print('Trace set to : ' + str(flag)) global printverbose printverbose = flag - if flag == True : - tracePath = FreeCAD.getUserAppDataDir() - tracefp = open(tracePath+'FC-trace','w') - print('Trace path : '+tracePath) + if flag is True: + tracePath = FreeCAD.getUserAppDataDir() + tracefp = open(tracePath + 'FC-trace', 'w') + print('Trace path : ' + tracePath) + -def getTrace() : +def getTrace(): global printverbose - #print('Get Trace : '+str(printverbose)) + # print('Get Trace : '+str(printverbose)) return(printverbose) + def trace(s): global tracefp - if printverbose == True : - print(s) - print(s,file = tracefp) - tracefp.flush() + if printverbose is True: + print(s) + print(s, file=tracefp) + tracefp.flush() return + def errorDialog(msg, title='Warning', type=2): # Create a simple dialog QMessageBox # type indicates the icon used: one of QtGui.QMessageBox.{NoIcon, Information, Warning, Critical, Question} - typeDict = {0:QtGui.QMessageBox.NoIcon, \ - 1:QtGui.QMessageBox.Information, \ - 2:QtGui.QMessageBox.Warning, \ - 3:QtGui.QMessageBox.Critical, \ - 4:QtGui.QMessageBox.Question} - diag = QtGui.QMessageBox(Dict(type),title,msg) + typeDict = {0: QtGui.QMessageBox.NoIcon, + 1: QtGui.QMessageBox.Information, + 2: QtGui.QMessageBox.Warning, + 3: QtGui.QMessageBox.Critical, + 4: QtGui.QMessageBox.Question} + diag = QtGui.QMessageBox(Dict(type), title, msg) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() + def getFloatVal(expr): try: ret = float(eval(expr)) @@ -87,112 +92,115 @@ def getFloatVal(expr): ret = 0.0 print("Illegal float value: {}".format(expr)) return ret - -def setDefine(val) : - #print("Set Define") + +def setDefine(val): + # print("Set Define") global define define = val + def processConstants(doc): # all of math must be imported at global level - #setTrace(True) + # setTrace(True) trace("Process Constants") constantGrp = doc.getObject('Constants') - if constantGrp is None : - constantGrp = doc.addObject("App::DocumentObjectGroupPython","Constants") + if constantGrp is None: + constantGrp = doc.addObject("App::DocumentObjectGroupPython", + "Constants") from .GDMLObjects import GDMLconstant - for cdefine in define.findall('constant') : - #print cdefine.attrib - name = str(cdefine.attrib.get('name')) - trace('name : '+name) + for cdefine in define.findall('constant'): + # print cdefine.attrib + name = str(cdefine.attrib.get('name')) + trace('name : ' + name) value = cdefine.attrib.get('value') - trace('value : '+ value) - #constDict[name] = value - #trace(name) - #print(dir(name)) - try : - globals()[name] = eval(value) - except : # eg 5*cm - globals()[name] = value - constObj = constantGrp.newObject("App::DocumentObjectGroupPython", \ - name) - GDMLconstant(constObj,name,value) - - #print("Globals") - #print(str(globals())) + trace('value : ' + value) + # constDict[name] = value + # trace(name) + # print(dir(name)) + try: + globals()[name] = eval(value) + except: # eg 5*cm + globals()[name] = value + constObj = constantGrp.newObject("App::DocumentObjectGroupPython", + name) + GDMLconstant(constObj, name, value) + + # print("Globals") + # print(str(globals())) + def processVariables(doc): # all of math must be imported at global level trace("Process Variables") variablesGrp = doc.getObject('Variables') - if variablesGrp is None : - variablesGrp = doc.addObject("App::DocumentObjectGroupPython","Variables") + if variablesGrp is None: + variablesGrp = doc.addObject("App::DocumentObjectGroupPython", + "Variables") from .GDMLObjects import GDMLconstant from .GDMLObjects import GDMLvariable - #import math - - #globals()['sin'] = math.sin - #globals()['cos'] = math.cos globals()['false'] = False globals()['true'] = True - for cdefine in define.findall('variable') : - #print cdefine.attrib - name = str(cdefine.attrib.get('name')) + for cdefine in define.findall('variable'): + # print cdefine.attrib + name = str(cdefine.attrib.get('name')) trace('name : '+name) value = cdefine.attrib.get('value') - trace('value : '+ value) - #constDict[name] = value + trace('value : ' + value) + # constDict[name] = value trace(name) - #print(dir(name)) - #print('Name : '+name) - try : - globals()[name] = eval(value) - #print('Value : '+value) - except : - globals()[name] = value - #print('Value String : '+value) - variableObj = variablesGrp.newObject("App::DocumentObjectGroupPython", \ - name) - GDMLvariable(variableObj,name,value) - #print("Globals") - #print(str(globals())) + # print(dir(name)) + # print('Name : '+name) + try: + globals()[name] = eval(value) + # print('Value : '+value) + except: + globals()[name] = value + # print('Value String : '+value) + variableObj = variablesGrp.newObject("App::DocumentObjectGroupPython", + name) + GDMLvariable(variableObj, name, value) + # print("Globals") + # print(str(globals())) + def processQuantities(doc): # all of math must be imported at global level trace("Process Quantitities") quantityGrp = doc.getObject('Quantities') - if quantityGrp is None : - quantityGrp = doc.addObject("App::DocumentObjectGroupPython","Quantities") + if quantityGrp is None: + quantityGrp = doc.addObject("App::DocumentObjectGroupPython", + "Quantities") from .GDMLObjects import GDMLquantity - for cdefine in define.findall('quantity') : - #print cdefine.attrib - name = str(cdefine.attrib.get('name')) + for cdefine in define.findall('quantity'): + # print cdefine.attrib + name = str(cdefine.attrib.get('name')) trace('name : '+name) type = cdefine.attrib.get('type') - trace('type : '+ type) + trace('type : ' + type) unit = cdefine.attrib.get('unit') - trace('unit : '+ unit) + trace('unit : ' + unit) value = cdefine.attrib.get('value') - trace('value : '+ value) - #constDict[name] = value + trace('value : ' + value) + # constDict[name] = value trace(name) - #print(dir(name)) - #print('Name : '+name) - try : - globals()[name] = eval(value) - #print('Value : '+value) - except : - globals()[name] = value - #print('Value String : '+value) - quantityObj = quantityGrp.newObject("App::DocumentObjectGroupPython", \ - name) - GDMLquantity(quantityObj,name,type,unit,value) - #print("Globals") - #print(str(globals())) + # print(dir(name)) + # print('Name : '+name) + try: + globals()[name] = eval(value) + # print('Value : '+value) + except: + globals()[name] = value + # print('Value String : '+value) + quantityObj = quantityGrp.newObject("App::DocumentObjectGroupPython", + name) + GDMLquantity(quantityObj, name, type, unit, value) + # print("Globals") + # print(str(globals())) + def processPositions(doc): print('Process Positions') @@ -218,191 +226,199 @@ def processPositions(doc): else: z = 0 - positions[atts['name']] = {'unit': unit, \ + positions[atts['name']] = {'unit': unit, 'x': x, 'y': y, 'z': z} trace("Positions processed") + def processExpression(doc): # need to be done ? trace("Expressions Not processed & Displayed") + def processRotation(doc): # need to be done ? trace("Rotations Not processed & Displayed") -def getVal(ptr,var,default=0) : + +def getVal(ptr, var, default=0): # all of math must be imported at global level - #print ptr.attrib + # print ptr.attrib # is the variable defined in passed attribute - if var in ptr.attrib : - # if yes get its value - vval = ptr.attrib.get(var) - trace(var+" : "+str(vval)) - if vval[0] == '&' : # Is this referring to an HTML entity constant - chkval = vval[1:] - else : - chkval = vval - trace("chkval : "+str(chkval)) - try: - ret = float(eval(chkval)) - except: - try: - ret = float(chkval) - except: - print("Illegal float: {}" % chkval) - ret = 0.0 - - trace('return value : '+str(ret)) - return(ret) + if var in ptr.attrib: + # if yes get its value + vval = ptr.attrib.get(var) + trace(var + " : " + str(vval)) + if vval[0] == '&': # Is this referring to an HTML entity constant + chkval = vval[1:] + else: + chkval = vval + trace("chkval : " + str(chkval)) + try: + ret = float(eval(chkval)) + except: + try: + ret = float(chkval) + except: + print("Illegal float: {}" % chkval) + ret = 0.0 + + trace('return value : ' + str(ret)) + return(ret) return default # get ref e.g name world, solidref, materialref -def getRef(ptr, name) : + + +def getRef(ptr, name): wrk = ptr.find(name) - if wrk is not None : - ref = wrk.get('ref') - trace(name + ' : ' + ref) - return ref + if wrk is not None: + ref = wrk.get('ref') + trace(name + ' : ' + ref) + return ref return wrk -def getMult(fp) : - unit = 'mm' # set default + +def getMult(fp): + unit = 'mm' # set default # Watch for unit and lunit - #print('getMult : '+str(fp)) - if hasattr(fp,'lunit') : + # print('getMult : '+str(fp)) + if hasattr(fp, 'lunit'): trace('lunit : '+fp.lunit) unit = fp.lunit - elif hasattr(fp,'unit') : - trace('unit : '+fp.unit) + elif hasattr(fp, 'unit'): + trace('unit : ' + fp.unit) unit = fp.unit - elif hasattr(fp,'attrib') : - if 'unit' in fp.attrib : - unit = fp.attrib['unit'] - elif 'lunit' in fp.attrib : - unit = fp.attrib['lunit'] - else : + elif hasattr(fp, 'attrib'): + if 'unit' in fp.attrib: + unit = fp.attrib['unit'] + elif 'lunit' in fp.attrib: + unit = fp.attrib['lunit'] + else: return 1 - if unit == 'mm' : return(1) - elif unit == 'cm' : return(10) - elif unit == 'm' : return(1000) - elif unit == 'um' : return(0.001) - elif unit == 'nm' : return(0.000001) - elif unit == 'dm' : return(100) - elif unit == 'm' : return(1000) - elif unit == 'km' : return(1000000) - print('unit not handled : '+unit) - -def getDegrees(flag, r) : + + unitsDict = {'mm': 1, 'cm': 10, 'm': 1000, 'um': 0.001, 'nm': 0.000001, + 'dm': 100, 'm': 1000, 'km': 1000000} + if unit in unitsDict: + return unitsDict[unit] + + print('unit not handled : ' + unit) + + +def getDegrees(flag, r): import math - if flag == True : - return r * 180/math.pi - else : - return r + if flag is True: + return r * 180/math.pi + else: + return r + -def getRadians(flag,r) : +def getRadians(flag, r): import math - if flag == True : + if flag is True: return r - else : + else: return r * math.pi / 180 -def processPlacement(base,rot) : - #setTrace(True) - #trace('processPlacement') + +def processPlacement(base, rot): + # setTrace(True) + # trace('processPlacement') # Different Objects will have adjusted base GDML-FreeCAD - # rot is rotation or None if default + # rot is rotation or None if default # set rotation matrix - #print('process Placement : '+str(base)) - if rot is None : - return FreeCAD.Placement(base,FreeCAD.Rotation(0,0,0,1)) - - else : + # print('process Placement : '+str(base)) + if rot is None: + return FreeCAD.Placement(base, FreeCAD.Rotation(0, 0, 0, 1)) + + else: trace("Rotation : ") trace(rot.attrib) x = y = z = 0 - if 'name' in rot.attrib : - if rot.attrib['name'] == 'identity' : + if 'name' in rot.attrib: + if rot.attrib['name'] == 'identity': trace('identity') - return FreeCAD.Placement(base,FreeCAD.Rotation(0,0,0,1)) + return FreeCAD.Placement(base, FreeCAD.Rotation(0, 0, 0, 1)) radianFlg = True - if 'unit' in rot.attrib : - #print(rot.attrib['unit'][:3]) - if rot.attrib['unit'][:3] == 'deg' : + if 'unit' in rot.attrib: + # print(rot.attrib['unit'][:3]) + if rot.attrib['unit'][:3] == 'deg': radianFlg = False - if 'x' in rot.attrib : - trace('x : '+rot.attrib['x']) - #print(eval('HALFPI')) + if 'x' in rot.attrib: + trace('x : ' + rot.attrib['x']) + # print(eval('HALFPI')) trace(eval(rot.attrib['x'])) - x = getDegrees(radianFlg,float(eval(rot.attrib['x']))) - trace('x deg : '+str(x)) - - if 'y' in rot.attrib : - trace('y : '+rot.attrib['y']) - y = getDegrees(radianFlg,float(eval(rot.attrib['y']))) - trace('y deg : '+str(y)) - - if 'z' in rot.attrib : + x = getDegrees(radianFlg, float(eval(rot.attrib['x']))) + trace('x deg : ' + str(x)) + + if 'y' in rot.attrib: + trace('y : ' + rot.attrib['y']) + y = getDegrees(radianFlg, float(eval(rot.attrib['y']))) + trace('y deg : ' + str(y)) + + if 'z' in rot.attrib: trace('z : '+rot.attrib['z']) - z = getDegrees(radianFlg,float(eval(rot.attrib['z']))) - trace('z deg : '+str(z)) - - rotX = FreeCAD.Rotation(FreeCAD.Vector(1,0,0), -x) - rotY = FreeCAD.Rotation(FreeCAD.Vector(0,1,0), -y) - rotZ = FreeCAD.Rotation(FreeCAD.Vector(0,0,1), -z) - - rot = rotX.multiply(rotY).multiply(rotZ) - #rot = rotX - #c_rot = FreeCAD.Vector(0,0,0) # Center of rotation - #print('base : '+str(base)) - #print('rot : '+str(rot)) - #return FreeCAD.Placement(base, rot, c_rot) - - #placement = FreeCAD.Placement(base, FreeCAD.Rotation(-x,-y,-z)) - #placement = FreeCAD.Placement(base, FreeCAD.Rotation(-z,-y,-x), \ + z = getDegrees(radianFlg, float(eval(rot.attrib['z']))) + trace('z deg : ' + str(z)) + + rotX = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), -x) + rotY = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), -y) + rotZ = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -z) + + rot = rotX*rotY*rotZ + #rot = rotZ*rotY*rotX + # rot = rotX.multiply(rotY).multiply(rotZ) + # rot = rotX + # c_rot = FreeCAD.Vector(0,0,0) # Center of rotation + # print('base : '+str(base)) + # print('rot : '+str(rot)) + # return FreeCAD.Placement(base, rot, c_rot) + + # placement = FreeCAD.Placement(base, FreeCAD.Rotation(-x,-y,-z)) + # placement = FreeCAD.Placement(base, FreeCAD.Rotation(-z,-y,-x), \ # base) placement = FreeCAD.Placement(base, FreeCAD.Rotation(rot)) - #print('placement : '+str(placement)) + # print('placement : '+str(placement)) return placement -def getPositionFromAttrib(pos) : - #print('getPositionFromAttrib') - #print('pos : '+str(ET.tostring(pos))) - #print(pos.attrib) - #if hasattr(pos.attrib, 'unit') : # Note unit NOT lunit - #if hasattr(pos.attrib,'name') : + +def getPositionFromAttrib(pos): + # print('getPositionFromAttrib') + # print('pos : '+str(ET.tostring(pos))) + # print(pos.attrib) + # if hasattr(pos.attrib, 'unit') : # Note unit NOT lunit + # if hasattr(pos.attrib,'name') : # name = pos.get('name') # if name == 'center' : # return(0,0,0) mul = getMult(pos) - px = mul * getVal(pos,'x') - py = mul * getVal(pos,'y') - pz = mul * getVal(pos,'z') - return px, py, pz - -def getPositionFromDict(pos) : - #print('getPositionFromAttrib') - #print('pos : '+str(ET.tostring(pos))) - #print(pos.attrib) - #if hasattr(pos.attrib, 'unit') : # Note unit NOT lunit - #if hasattr(pos.attrib,'name') : + px = mul * getVal(pos, 'x') + py = mul * getVal(pos, 'y') + pz = mul * getVal(pos, 'z') + return px, py, pz + + +def getPositionFromDict(pos): + # print('getPositionFromAttrib') + # print('pos : '+str(ET.tostring(pos))) + # print(pos.attrib) + # if hasattr(pos.attrib, 'unit') : # Note unit NOT lunit + # if hasattr(pos.attrib,'name') : # name = pos.get('name') # if name == 'center' : # return(0,0,0) unit = pos['unit'] mul = 1 - #mul = getMult(pos) - if unit == 'mm' : mul = 1 - elif unit == 'cm' : mul = 10 - elif unit == 'm' : mul = 1000 - elif unit == 'um' : mul = 0.001 - elif unit == 'nm' : mul = 0.000001 - elif unit == 'dm' : mul = 100 - elif unit == 'm' : mul = 1000 - elif unit == 'km' : mul = 1000000 + + unitsDict = {'mm': 1, 'cm': 10, 'm': 1000, 'um': 0.001, 'nm': 0.000001, + 'dm': 100, 'm': 1000, 'km': 1000000} + + if unit in unitsDict: + mul = unitsDict[unit] try: px = mul * float(pos['x']) @@ -410,150 +426,145 @@ def getPositionFromDict(pos) : pz = mul * float(pos['z']) except: px = py = pz = 0 - - return px, py, pz -# Return x,y,z from position definition -def getElementPosition(xmlElem) : - # get Position from local element - #setTrace(True) + return px, py, pz + + +# Return x,y,z from position definition +def getElementPosition(xmlElem): + # get Position from local element + # setTrace(True) trace("Get Element Position : ") pos = xmlElem.find("position") - if pos is not None : + if pos is not None: trace(pos.attrib) return(getPositionFromAttrib(pos)) - else : - return 0,0,0 + else: + return 0, 0, 0 -def getDefinedPosition(name) : - # get Position from define section - #pos = define.find("position[@name='%s']" % name ) + +def getDefinedPosition(name): + # get Position from define section + # pos = define.find("position[@name='%s']" % name ) pos = positions[name] - if pos is not None : - #print('Position : '+str(pos)) + if pos is not None: + # print('Position : '+str(pos)) trace(pos) - #return(getPositionFromAttrib(pos)) + # return(getPositionFromAttrib(pos)) return(getPositionFromDict(pos)) - else : - return 0,0,0 + else: + return 0, 0, 0 + -def getPosition(xmlEntity) : +def getPosition(xmlEntity): # Get position via reference - #setTrace(True) + # setTrace(True) trace('GetPosition via Reference if any') - posName = getRef(xmlEntity,"positionref") - if posName is not None : - trace("positionref : "+posName) - return(getDefinedPosition(posName)) - else : - return(getElementPosition(xmlEntity)) - -def testPosition(xmlEntity,px,py,pz) : - posName = getRef(xmlEntity,"positionref") - if posName is not None : - trace("positionref : "+posName) - return(getDefinedPosition(posName)) + posName = getRef(xmlEntity, "positionref") + if posName is not None: + trace("positionref : " + posName) + return(getDefinedPosition(posName)) + else: + return(getElementPosition(xmlEntity)) + + +def testPosition(xmlEntity, px, py, pz): + posName = getRef(xmlEntity, "positionref") + if posName is not None: + trace("positionref : " + posName) + return(getDefinedPosition(posName)) pos = xmlEntity.find("position") - if pos is not None : + if pos is not None: trace(pos.attrib) return(getPositionFromAttrib(pos)) - else : - return px,py,pz + else: + return px, py, pz -def getDefinedRotation(name) : + +def getDefinedRotation(name): # Just get definition - used by parseMultiUnion passed to create solids - return(define.find("rotation[@name='%s']" % name )) + return(define.find("rotation[@name='%s']" % name)) + -def getRotation(xmlEntity) : +def getRotation(xmlEntity): trace('GetRotation') - rotref = getRef(xmlEntity,"rotationref") - trace('rotref : '+str(rotref)) - if rotref is not None : - rot = define.find("rotation[@name='%s']" % rotref ) - else : - rot = xmlEntity.find("rotation") - if rot is not None : - trace(rot.attrib) + rotref = getRef(xmlEntity, "rotationref") + trace('rotref : ' + str(rotref)) + if rotref is not None: + rot = define.find("rotation[@name='%s']" % rotref) + else: + rot = xmlEntity.find("rotation") + if rot is not None: + trace(rot.attrib) return rot -def getRotFromRefs(ptr) : + +def getRotFromRefs(ptr): printverbose = True trace("getRotFromRef") - rot = define.find("rotation[@name='%s']" % getRef(ptr,'rotationref')) - if rot is not None : + rot = define.find("rotation[@name='%s']" % getRef(ptr, 'rotationref')) + if rot is not None: trace(rot.attrib) return rot -def getDefinedVector(solid, v) : + +def getDefinedVector(solid, v): global define - #print('get Defined Vector : '+v) + # print('get Defined Vector : '+v) name = solid.get(v) pos = define.find("position[@name='%s']" % name) - #print(pos.attrib) - x = getVal(pos,'x') - y = getVal(pos,'y') - z = getVal(pos,'z') - return(FreeCAD.Vector(x,y,z)) + # print(pos.attrib) + x = getVal(pos, 'x') + y = getVal(pos, 'y') + z = getVal(pos, 'z') + return(FreeCAD.Vector(x, y, z)) + -def getPlacement(pvXML) : +def getPlacement(pvXML): base = FreeCAD.Vector(getPosition(pvXML)) - #print('base: '+str(base)) - rot = getRotation(pvXML) - return(processPlacement(base,rot)) + # print('base: '+str(base)) + rot = getRotation(pvXML) + return(processPlacement(base, rot)) -def getScale(pvXML) : - #print(ET.tostring(pvXML)) + +def getScale(pvXML): + # print(ET.tostring(pvXML)) scale = pvXML.find('scale') x = y = z = 1. - if scale is not None : - #print(ET.tostring(scale)) - x = getVal(scale,'x') - y = getVal(scale,'y') - z = getVal(scale,'z') - return(FreeCAD.Vector(x,y,z)) + if scale is not None: + # print(ET.tostring(scale)) + x = getVal(scale, 'x') + y = getVal(scale, 'y') + z = getVal(scale, 'z') + return(FreeCAD.Vector(x, y, z)) + def getVertex(v): global define trace("Vertex") - #print(dir(v)) + # print(dir(v)) pos = define.find("position[@name='%s']" % v) - #print("Position") - #print(dir(pos)) - x = getVal(pos,'x') - trace('x : '+str(x)) - y = getVal(pos,'y') - trace('y : '+str(y)) - z = getVal(pos,'z') - trace('z : '+str(z)) - return(FreeCAD.Vector(x,y,z)) - -def facet(f) : - #vec = FreeCAD.Vector(1.0,1.0,1.0) - #print(f"Facet {f}") - #print(f.Points) - if len(f.Points) == 3 : - return(triangle(f.Points[0],f.Points[1],f.Points[2])) - #if f.Normal.dot(vec) > 0 : - # return(triangle(f.Points[0],f.Points[1],f.Points[2])) - #else : - # return(triangle(f.Points[2],f.Points[1],f.Points[0])) - else : - return(quad(f.Points[0],f.Points[1],f.Points[2],f.Points[3])) - #if f.Normal.dot(vec) > 0 : - # return(quad(f.Points[0],f.Points[1],f.Points[2],f.Points[3])) - #else : - # return(quad(f.Points[3],f.Points[2],f.Points[1],f.Points[0])) - - -def triangle(v1,v2,v3) : + # print("Position") + # print(dir(pos)) + x = getVal(pos, 'x') + trace('x : ' + str(x)) + y = getVal(pos, 'y') + trace('y : ' + str(y)) + z = getVal(pos, 'z') + trace('z : ' + str(z)) + return(FreeCAD.Vector(x, y, z)) + + +def triangle(v1, v2, v3): # passed vertex return face - #print('v1 : '+str(v1)+' v2 : '+str(v2)+' v3 : '+str(v3)) - w1 = Part.makePolygon([v1,v2,v3,v1]) + # print('v1 : '+str(v1)+' v2 : '+str(v2)+' v3 : '+str(v3)) + w1 = Part.makePolygon([v1, v2, v3, v1]) f1 = Part.Face(w1) return(f1) -def quad(v1,v2,v3,v4) : + +def quad(v1, v2, v3, v4): # passed vertex return face - w1 = Part.makePolygon([v1,v2,v3,v4,v1]) + w1 = Part.makePolygon([v1, v2, v3, v4, v1]) f1 = Part.Face(w1) return(f1) diff --git a/freecad/gdml/exportExtrusion.py b/freecad/gdml/exportExtrusion.py new file mode 100644 index 000000000..05a4f17a2 --- /dev/null +++ b/freecad/gdml/exportExtrusion.py @@ -0,0 +1,845 @@ +# -*- mode: Python; python-indent-offset: 4; python-guess-indent: nil -*- +# Mon Dec 6 08:49:56 AM PST 2021 +# **************************************************************************pp +# * * +# * Copyright (c) 2019 Keith Sloan * +# * (c) 2020 Dam Lambert * +# * (c) 2021 Munther Hindi +# * +# * 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 * +# * * +# * Acknowledgements : Ideas & code copied from * +# * https://github.com/ignamv/geanTipi * +# * * +# *************************************************************************** +__title__ = "FreeCAD - GDML Extrude exporter Version" +__author__ = "Keith Sloan " +__url__ = ["https://github.com/KeithSloan/FreeCAD_Geant4"] + +import FreeCAD, Part, math +from FreeCAD import Vector +from .GDMLObjects import GDMLcommon, GDMLBox, GDMLTube + +# modif add +# from .GDMLObjects import getMult, convertionlisteCharToLunit + +import sys +try: + import lxml.etree as ET + FreeCAD.Console.PrintMessage("running with lxml.etree\n") + XML_IO_VERSION = 'lxml' +except ImportError: + try: + import xml.etree.ElementTree as ET + FreeCAD.Console.PrintMessage("running with xml.etree.ElementTree\n") + XML_IO_VERSION = 'xml' + except ImportError: + FreeCAD.Console.PrintMessage('pb xml lib not found\n') + sys.exit() +# xml handling +# import argparse +# from xml.etree.ElementTree import XML +################################# + +# *************************************************************************** +# Tailor following to your requirements ( Should all be strings ) * +# no doubt there will be a problem when they do implement Value +if open.__module__ in ['__builtin__', 'io']: + pythonopen = open # to distinguish python built-in open function from the one declared here + +# ## modifs lambda + + +def verifNameUnique(name): + # need to be done!! + return True + +# ## end modifs lambda + +################################# +# Switch functions +################################ + + +class switch(object): + value = None + + def __new__(class_, value): + class_.value = value + return True + + +def case(*args): + return any((arg == switch.value for arg in args)) + +############################################# +# Helper functions for extrude construction + +# One of the closed curves (list of edges) representing a part +# of the sketch + + +class ClosedCurve: + def __init__(self, name, edgeList): + self.name = name + self.face = Part.Face(Part.Wire(edgeList)) + self.edgeList = edgeList + + def isInside(self, otherCurve): + # ClosedCurves are closed: so if ANY vertex of the otherCurve + # is inside, then the whole curve is inside + return self.face.isInside(otherCurve.edgeList[0].Vertexes[0].Point, 0.001, True) + + +class ExtrudedClosedCurve(ClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist) + self.height = height + self.position = Vector(0, 0, 0) + self.rotation = [0, 0, 0] # TBD + + def export(self): + print('export not implemented') + + +class ExtrudedCircle(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + self.position = edgelist[0].Curve.Center + Vector(0, 0, height/2) + + def export(self): + edge = self.edgeList[0] + exportTube(self.name, edge.Curve.Radius, self.height) + + +class ExtrudedArcSection(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + # Note extrusion polyogn will be in absolute coordinates + # since arc section is relative to that, position is actually (0,0,0) + # same goes for rotation + + # return midpoint of arc, relative to center + def midPoint(self): + edge = self.edgeList[0] + radius = edge.Curve.Radius + thetmid = (edge.FirstParameter+edge.LastParameter)/2 + arcAngle = edge.LastParameter - edge.FirstParameter + v0 = edge.Vertexes[0].Point + v1 = edge.Vertexes[1].Point + vc = (v0+v1)/2 # chord center + vc_vcenter = vc - edge.Curve.Center + if vc_vcenter.Length < 0.001: # arc chord = diameter + # Although it seems that this should always work, I've seen cases in which + # each of the first, last parameters were shifter by pi and thetmid + # was off by pi + vmid = edge.Curve.Center + radius*Vector(math.cos(thetmid), math.sin(thetmid), 0) + else: + u_vc_vcenter = vc_vcenter.normalize() # unit vector fom center of circle to center of chord + if arcAngle < math.pi: # shorter of two arc segments, mid point and center are on opposite side + vmid = edge.Curve.Center + edge.Curve.Radius*u_vc_vcenter + else: #longer of two arc segments: midpoint is on opposite side of chord + vmid = edge.Curve.Center - edge.Curve.Radius*u_vc_vcenter + + return vmid + + def export(self): + global solids + from .exportGDML import exportPosition + + edge = self.edgeList[0] + radius = edge.Curve.Radius + # First form a bounding rectangle (polygon) for the arc. + # Arc edges + v1 = edge.Vertexes[0].Point + v2 = edge.Vertexes[1].Point + vmid = self.midPoint() + + # midpoint of chord + vc = (v1+v2)/2 + v = v2-v1 + u = v.normalize() + # extend the ends of the chord so extrusion can cut all of circle, if needed + v1 = vc + radius*u + v2 = vc - radius*u + # component of vmid perpendicular to u + vc_vmid = vmid - vc + n = vc_vmid - u.dot(vc_vmid)*u + n.normalize() + # complete edges of box paerpendicular to chord, toward mid arc point + v3 = v2 + 2*radius*n + v4 = v1 + 2*radius*n + + xtruName = self.name+'_xtru' + exportXtru(xtruName, [v1, v2, v3, v4], self.height) + + # tube to be cut1 + tubeName = self.name+'_tube' + exportTube(tubeName, edge.Curve.Radius, self.height) + + # note, it is mandatory that name be that of ClosedCurve + intersect = ET.SubElement(solids, 'intersection', {'name': self.name}) + ET.SubElement(intersect, 'first', {'ref': xtruName}) + ET.SubElement(intersect, 'second', {'ref': tubeName}) + pos = edge.Curve.Center + Vector(0, 0, self.height/2) + exportPosition(tubeName, intersect, pos) + + +class ExtrudedEllipse(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + curve = edgelist[0].Curve + self.position = curve.Center + Vector(0, 0, height/2) + angle = math.degrees(curve.AngleXU) + self.rotation = [0, 0, angle] + + def export(self): + edge = self.edgeList[0] + exportEllipticalTube(self.name, edge.Curve.MajorRadius, + edge.Curve.MinorRadius, + self.height) + + +class ExtrudedEllipticalSection(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + # Note extrusion polyogn will be in absolute coordinates + # since arc section is relative to that, position is actually (0,0,0) + # same goes for rotation + + def midPoint(self): + edge = self.edgeList[0] + a = edge.Curve.MajorRadius + b = edge.Curve.MinorRadius + angleXU = edge.Curve.AngleXU + thet1 = edge.FirstParameter # in radians, in unorated ellipse + thet2 = edge.LastParameter # in radians, in onrated ellipse + thetmid = (thet1+thet2)/2 + angleXU + + # Major axis angle seems to be off by pi for some ellipse. Restrict it to be + # be between 0 an pi + if angleXU < 0: + angleXU += 180 + v0 = edge.Vertexes[0].Point + v1 = edge.Vertexes[1].Point + + # TODO must deal with case where cutting chord is along major axis + # u_vc_vcenter = vc_vcenter.normalize() # unit vector fom center of circle to center of chord + + # vertexes of triangle formed by chord ends and ellise mid point + # In polar coordinates equation of ellipse is r(thet) = a*(1-eps*eps)/(1+eps*cos(thet)) + # if the ellipse is rotatated by an angle AngleXU, then + # x = r*cos(thet+angleXU), y = r*sin(thet+angleXU), for thet in frame of unrotated ellipse + # now edge.FirstParameter is begining angle of unrotaeted ellipse + + def sqr(x): + return x*x + + def r(thet): + return math.sqrt(1.0/(sqr(math.cos(thet)/a) + sqr(math.sin(thet)/b))) + + rmid = r(thetmid) + vmid = Vector(rmid*math.cos(thetmid), rmid*math.sin(thetmid), 0) + + vmid += edge.Curve.Center + + ''' + uxaxis = Vector(math.cos(angleXU), math.sin(angleXU), 0) + costhet = uxaxis.dot(u_vc_vcenter) # angle between major axis and center of chor + sinthet = math.sqrt(1-costhet*costhet) + # polar equation of ellipse, with r measured from FOCUS. Focus at a*eps + # r = lambda thet: a*(1-eps*eps)/(1+eps*math.cos(thet)) + # polar equation of ellipse, with r measured from center a*eps + sqr = lambda x: x*x + rmid = math.sqrt(1.0/(sqr(costhet/a) + sqr(sinthet/b))) + if arcAngle < math.pi: # shorter of two arc segments, mid point and center are on opposite side + vmid = edge.Curve.Center + rmid*u_vc_vcenter + else: #longer of two arc segments: midpoint is on opposite side of chord + vmid = edge.Curve.Center - rmid*u_vc_vcenter + ''' + + return vmid + + def export(self): + global solids + from .exportGDML import exportPosition + + edge = self.edgeList[0] + a = dx = edge.Curve.MajorRadius + b = dy = edge.Curve.MinorRadius + + # vertexes of triangle formed by chord ends and ellise mid point + # In polar coordinates equation of ellipse is r(thet) = a*(1-eps*eps)/(1+eps*cos(thet)) + # if the ellipse is rotatated by an angle AngleXU, then + # x = r*cos(thet+angleXU), y = r*sin(thet+angleXU), for thet in frame of unrotated ellipse + # now edge.FirstParameter is begining angle of unrotaeted ellipse + # polar equation of ellipse, with r measured from FOCUS. Focus at a*eps + # r = lambda thet: a*(1-eps*eps)/(1+eps*math.cos(thet)) + # polar equation of ellipse, with r measured from center a*eps + + def sqr(x): + return x*x + + def r(thet): + return math.sqrt(1.0/(sqr(math.cos(thet)/a) + sqr(math.sin(thet)/b))) + + v1 = edge.Vertexes[0].Point + v2 = edge.Vertexes[1].Point + vmid = self.midPoint() + + # midpoint of chord + vc = (v1+v2)/2 + v = v2-v1 + u = v.normalize() # unit vector from v1 to v2 + # extend the ends of the chord so extrusion can cut all of ellipse, if needed + v1 = vc + 2*a*u + v2 = vc - 2*a*u + + # component of vmid perpendicular to u + vc_vmid = vmid - vc + n = vc_vmid - u.dot(vc_vmid)*u + n.normalize() + v3 = v2 + 2*a*n + v4 = v1 + 2*a*n + + xtruName = self.name+'_xtru' + exportXtru(xtruName, [v1, v2, v3, v4], self.height) + + # tube to be cut1 + tubeName = self.name+'_tube' + exportEllipticalTube(tubeName, dx, dy, self.height) + + # note, it is mandatory that name be that of ClosedCurve + intersect = ET.SubElement(solids, 'intersection', {'name': self.name}) + ET.SubElement(intersect, 'first', {'ref': xtruName}) + ET.SubElement(intersect, 'second', {'ref': tubeName}) + pos = edge.Curve.Center + Vector(0, 0, self.height/2) + exportPosition(tubeName, intersect, pos) + rotName = tubeName+'_rot' + # zAngle = math.degrees(edge.Curve.AngleXU) + # Focus1 is on the positive x side, Focus2 on the negative side + dy = edge.Curve.Focus1[1] - edge.Curve.Focus2[1] + dx = edge.Curve.Focus1[0] - edge.Curve.Focus2[0] + zAngle = math.degrees(math.atan2(dy, dx)) + print(f'{self.name} zAngle = {zAngle}') + # if zAngle < 0: + # zAngle += 180 + ET.SubElement(define, 'rotation', {'name': rotName, 'unit': 'deg', + 'x': '0', 'y': '0', 'z': str(zAngle)}) + + ET.SubElement(intersect, 'rotationref', {'ref': rotName}) + + +class Extruded2Edges(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + + def export(self): + global solids + + # form normals to the edges. For case of two edges, sidedness is irrelevant + v0 = self.edgeList[0].Vertexes[0].Point + v1 = self.edgeList[0].Vertexes[1].Point + e = v1 - v0 + if e.x == 0: + ny = 0 + nx = 1 + elif e.y == 0: + nx = 0 + ny = 1 + else: + nx = 1 + ny = -e.x/e.y + normal = Vector(nx, ny, 0).normalize() + + edgeCurves = [] # list of ExtrudedClosedCurve's + + for i, e in enumerate(self.edgeList): # just TWO edges + while switch(e.Curve.TypeId): + if case('Part::GeomLineSegment'): + break + + if case('Part::GeomLine'): + break + + if case('Part::GeomCircle'): + print('Arc of Circle') + arcXtruName = self.name + '_c'+str(i) + arcSection = ExtrudedArcSection(arcXtruName, [e], self.height) + arcSection.export() + + midpnt = arcSection.midPoint() + inside = pointInsideEdge(midpnt, v0, normal) + edgeCurves.append([arcXtruName, inside]) + break + + if case('Part::GeomEllipse'): + print('Arc of Ellipse') + arcXtruName = self.name+'_e'+str(i) + arcSection = ExtrudedEllipticalSection(arcXtruName, [e], self.height) + arcSection.export() + midpnt = arcSection.midPoint() + inside = pointInsideEdge(midpnt, v0, normal) + edgeCurves.append([arcXtruName, inside]) + break + + if case('Part::GeomBSplineCurve'): + print('BSpline not implemented yet') + break + + if len(edgeCurves) == 1: + # change our name to be that of the constructed curve + # not a violation of the contract of a unique name, since the curve name is based on ours + self.position = arcSection.position + self.rotation = arcSection.rotation + self.name = edgeCurves[0][0] + + else: + inside0 = edgeCurves[0][1] + inside1 = edgeCurves[1][1] + sameSide = (inside0 == inside1) + if sameSide is False: + booleanSolid = ET.SubElement(solids, 'union', {'name': self.name}) + else: + booleanSolid = ET.SubElement(solids, 'subtraction', {'name': self.name}) + + area0 = edgelistBBoxArea([self.edgeList[0]]) + area1 = edgelistBBoxArea([self.edgeList[1]]) + if area0 > area1: + firstSolid = edgeCurves[0][0] + secondSolid = edgeCurves[1][0] + else: + firstSolid = edgeCurves[1][0] + secondSolid = edgeCurves[0][0] + + ET.SubElement(booleanSolid, 'first', {'ref': firstSolid}) + ET.SubElement(booleanSolid, 'second', {'ref': secondSolid}) + + +class ExtrudedNEdges(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + + def export(self): + global solids + from .exportGDML import exportPosition + + verts = [] + for e in self.edgeList: + if len(e.Vertexes) > 1: + verts.append(e.Vertexes[0].Point) + verts.append(verts[0]) + + face = Part.Face(Part.makePolygon(verts)) + + edgeCurves = [] # list of ExtrudedClosedCurve's + for i, e in enumerate(self.edgeList): + + while switch(e.Curve.TypeId): + if case('Part::GeomLineSegment'): + break + + if case('Part::GeomLine'): + break + + if case('Part::GeomCircle'): + print('Arc of Circle') + arcXtruName = self.name + '_c'+str(i) + arcSection = ExtrudedArcSection(arcXtruName, [e], self.height) + midpnt = arcSection.midPoint() + inside = face.isInside(midpnt, 0.001, True) + if inside is True: + arcSection.height = 1.02*self.height # for a cutting solid, increase its height + arcSection.export() + # this is not general. Needs to be changed + # to a test against sidedness of edge of section + edgeCurves.append([arcXtruName, inside]) + break + + if case('Part::GeomEllipse'): + print('Arc of Ellipse') + arcXtruName = self.name+'_e'+str(i) + arcSection = ExtrudedEllipticalSection(arcXtruName, [e], self.height) + midpnt = arcSection.midPoint() + inside = face.isInside(midpnt, 0.001, True) + if inside is True: + arcSection.height = 1.02*self.height # for a cutting solid, increase its height + arcSection.export() + edgeCurves.append([arcXtruName, inside]) + break + + if case('Part::GeomBSplineCurve'): + print('BSpline not implemented yet') + break + + xtruName = self.name + if len(edgeCurves) > 0: + xtruName += '_xtru' + exportXtru(xtruName, verts, self.height) + + currentSolid = xtruName + if len(edgeCurves) > 0: + for i, c in enumerate(edgeCurves): + if i == len(edgeCurves) - 1: + name = self.name # last boolean must have this classes name + else: + name = 'bool' + c[0] + if c[1] is False: + booleanSolid = ET.SubElement(solids, 'union', {'name': name}) + ET.SubElement(booleanSolid, 'first', {'ref': currentSolid}) + ET.SubElement(booleanSolid, 'second', {'ref': c[0]}) + else: + booleanSolid = ET.SubElement(solids, 'subtraction', {'name': name}) + pos = Vector(0,0, -0.01*self.height) # move subtracted solid down a bit + ET.SubElement(booleanSolid, 'first', {'ref': currentSolid}) + ET.SubElement(booleanSolid, 'second', {'ref': c[0]}) + exportPosition(c[0], booleanSolid, pos) + currentSolid = name + +# Node of a tree that represents the topology of the sketch being exported +# a left_child is a ClosedCurve that is inside of its parent +# a right_sibling is a closedCurve that is outside of its parent + + +class Node: + def __init__(self, closedCurve, parent, parity): + # the nomenclature is redundant, but a reminder that left is a child and right + # a sibling + self.parent = parent + if parent is None: + self.parity = 0 # if parity is 0, print as union with current solid + # if parity is 1, print as subtraction from other solid + else: + self.parity = parity + + self.left_child = None + self.right_sibling = None + self.closedCurve = closedCurve + + def insert(self, closedCurve): + if self.closedCurve: # not sure why this test is necessary + if self.closedCurve.isInside(closedCurve): + # given curve is inside this curve: + # if this node does not have a child, insert it as the left_child + # otherwise check if it is a child of the child + if self.left_child is None: + self.left_child = Node(closedCurve, self, 1-self.parity) + else: + self.left_child.insert(closedCurve) + else: # Since we have no intersecting curves (for well constructed sketch + # if the given curve is not inside this node, it must be outside + if self.right_sibling is None: + self.right_sibling = Node(closedCurve, self, self.parity) + else: + self.right_sibling.insert(closedCurve) + else: + self.closedCurve = closedCurve + + def preOrderTraversal(self, root): + res = [] + if root: + res.append([root, root.parity]) + res = res + self.preOrderTraversal(root.left_child) + res = res + self.preOrderTraversal(root.right_sibling) + + return res + +# arrange a list of edges in the x-y plane in Counter Clock Wise direction +# This can be easily generalized for points in ANY plane: if the normal +# defining the desired direction of the plane is given, then the z component +# below should be changed a dot prduct with the normal + + +def arrangeCCW(verts, normal=Vector(0, 0, 1)): + reverse = False + v0 = verts[0] + rays = [(v - v0) for v in verts[1:]] + area = 0 + for i, ray in enumerate(rays[:-1]): + area += (rays[i].cross(rays[i+1])).dot(normal) + if area < 0: + verts.reverse() + reverse = True + + return reverse + +# Utility to determine if vector from point v0 to point v1 (v1-v0) +# is on sime side of normal or opposite. Return true if v ploints along normal + + +def pointInsideEdge(v0, v1, normal): + v = v1 - v0 + if v.dot(normal) < 0: + return False + else: + return True + + +def edgelistBB(edgelist): + # get edge list bounding box + bb = FreeCAD.BoundBox(0, 0, 0, 0, 0, 0) + for e in edgelist: + bb.add(e.BoundBox) + return bb + + +def edgelistBBoxArea(edgelist): + bb = edgelistBB(edgelist) + return bb.XLength * bb.YLength + + +def sortEdgelistsByBoundingBoxArea(listoflists): + listoflists.sort(reverse=True, key=edgelistBBoxArea) + + +def exportEllipticalTube(name, dx, dy, height): + global solids + + ET.SubElement(solids, 'eltube', {'name': name, + 'dx': str(dx), + 'dy': str(dy), + 'dz': str(height/2), + 'lunit': 'mm'}) + + +def exportTube(name, radius, height): + global solids + + ET.SubElement(solids, 'tube', {'name': name, + 'rmax': str(radius), + 'z': str(height), + 'startphi': '0', + 'deltaphi': '360', + 'aunit': 'deg', 'lunit': 'mm'}) + + +def exportXtru(name, vlist, height, zoffset=0): + global solids + + xtru = ET.SubElement(solids, 'xtru', {'name': name, 'lunit': 'mm'}) + for v in vlist: + ET.SubElement(xtru, 'twoDimVertex', {'x': str(v.x), + 'y': str(v.y)}) + ET.SubElement(xtru, 'section', {'zOrder': '0', + 'zPosition': str(zoffset), + 'xOffset': '0', 'yOffset': '0', + 'scalingFactor': '1'}) + ET.SubElement(xtru, 'section', {'zOrder': '1', + 'zPosition': str(height+zoffset), + 'xOffset': '0', 'yOffset': '0', + 'scalingFactor': '1'}) + + +def getExtrudedCurve(name, edges, height): + # Return an ExtrudedClosedCurve object of the list of edges + + if len(edges) == 1: # single edge ==> a closed curve, or curve section + e = edges[0] + if len(e.Vertexes) == 1: # a closed curve + closed = True + else: + closed = False # a section of a curve + + while switch(e.Curve.TypeId): + if case('Part::GeomLineSegment'): + print(' Sketch not closed') + return ExtrudedClosedCurve(edges, name, height) + + if case('Part::GeomLine'): + print(' Sketch not closed') + return ExtrudedClosedCurve(name, edges, height) + + if case('Part::GeomCircle'): + if closed is True: + print('Circle') + return ExtrudedCircle(name, edges, height) + else: + print('Arc of Circle') + return ExtrudedArcSection(name, edges, height) + + if case('Part::GeomEllipse'): + if closed is True: + print('Ellipse') + return ExtrudedEllipse(name, edges, height) + else: + print('Arc of Ellipse') + return ExtrudedEllipticalSection(name, edges, height) + + if case('Part::GeomBSplineCurve'): + print(' B spline extrusion not implemented yet') + return ExtrudedClosedCurve(name, edges, height) + + elif len(edges) == 2: # exactly two edges + return Extruded2Edges(name, edges, height) + else: # three or more edges + return ExtrudedNEdges(name, edges, height) + + +def setGlobals(defineV, materialsV, solidsV): + global define, materials, solids + define = defineV + materials = materialsV + solids = solidsV + + +# duplicate of exportDefine in exportGDML +def exportDefine(name, v): + global define + # print('define : '+name) + # print(v) + # print(v[0]) + ET.SubElement(define, 'position', {'name': name, 'unit': 'mm', + 'x': str(v[0]), + 'y': str(v[1]), + 'z': str(v[2])}) + + +# scale up a solid that will be subtracted so it ounched thru parent +def scaleUp(scaledName, originalName, zFactor): + ss = ET.SubElement(solids, 'scaledSolid', {'name': scaledName}) + ET.SubElement(ss, 'solidref', {'ref': originalName}) + ET.SubElement(ss, 'scale', {'name': originalName+'_ss', + 'x': '1', 'y': '1', 'z': str(zFactor)}) + + +def rotatedPos(closedCurve, rot): + # Circles and ellipses (tubes and elliptical tubes) are referenced to origin + # in GDML and have to be translated to their position via a position reference + # when placed as a physical volume. This is done by adding the translation + # to the Part::Extrusion Placement. However, if the placement includes + # a rotation, Geant4 GDML would rotate the Origin-based curve THEN translate. + # This would not work. We have to translate first THEN rotate. This method + # just does the needed rotation of the poisition vector + # + pos = closedCurve.position + if isinstance(closedCurve, ExtrudedCircle) or \ + isinstance(closedCurve, ExtrudedEllipse): + pos = rot*closedCurve.position + + return pos + + +def processExtrudedSketch(extrudeObj, sketchObj, xmlVol): + from .exportGDML import insertXMLvolume, exportPosition, addVolRef, \ + quaternion2XYZ + + sortededges = Part.sortEdges(sketchObj.Shape.Edges) + # sort by largest area to smallest area + sortEdgelistsByBoundingBoxArea(sortededges) + # getCurve returns one of the sub classes of ClosedCurve that + # knows how to export the specifc closed edges + # Make names based on Extrude name + # curves = [getCurve(edges, extrudeObj.Label + str(i)) for i, edges in enumerate(sortededges)] + if extrudeObj.Symmetric is True: + height = extrudeObj.LengthFwd.Value + else: + height = extrudeObj.LengthFwd.Value + extrudeObj.LengthRev.Value + eName = extrudeObj.Label + # get a list of curves (instances of class ClosedCurve) for each set of closed edges + curves = [getExtrudedCurve(eName+str(i), edges, height) + for i, edges in enumerate(sortededges)] + # build a generalized binary tree of closed curves. + root = Node(curves[0], None, 0) + for c in curves[1:]: + root.insert(c) + + # Traverse the tree. The list returned is a list of [Node, parity], where parity = 0, says add to parent, 1 mean subtract + lst = root.preOrderTraversal(root) + rootnode = lst[0][0] + rootCurve = rootnode.closedCurve + rootCurve.export() # a curve is created with a unique name + firstName = rootCurve.name + booleanName = firstName + + rootPos = rootCurve.position + rootRot = rootCurve.rotation # for now consider only angle of rotation about z-axis + + for c in lst[1:]: + node = c[0] + parity = c[1] + curve = node.closedCurve + curve.export() + if parity == 0: + boolType = 'union' + secondName = curve.name + secondPos = curve.position + else: + boolType = 'subtraction' + secondName = curve.name+'_s' # scale solids along z, so it punches thru + scaleUp(secondName, curve.name, 1.10) + secondPos = curve.position - Vector(0, 0, 0.01*height) + + booleanName = curve.name + '_bool' + boolSolid = ET.SubElement(solids, boolType, {'name': booleanName}) + ET.SubElement(boolSolid, 'first', {'ref': firstName}) + ET.SubElement(boolSolid, 'second', {'ref': secondName}) + relativePosition = secondPos - rootPos + zAngle = curve.rotation[2] - rootRot[2] + posName = curve.name+'_pos' + rotName = curve.name+'_rot' + exportDefine(posName, relativePosition) # position of second relative to first + ET.SubElement(define, 'rotation', {'name': rotName, 'unit': 'deg', + 'x': '0', 'y': '0', + 'z': str(zAngle)}) + + ET.SubElement(boolSolid, 'positionref', {'ref': posName}) + ET.SubElement(boolSolid, 'rotationref', {'ref': rotName}) + firstName = booleanName + + # now create logical and physical volumes for the last boolean. + # Because the position of each closed curve might not be at the + # origin, whereas primitives (tubes, cones, etc, are created centered at + # the origin, we need to shift the position of the very first node by its + # position, in addition to the shift by the Extrusion placement + volName = booleanName+'Vol' # booleanName is name of last boolean + newvol = insertXMLvolume(volName) + + addVolRef(newvol, volName, extrudeObj, booleanName) + # ET.SubElement(newvol,'materialref',{'ref': 'G4_Si'}) + # ET.SubElement(newvol,'solidref',{'ref': booleanName}) + + pvol = ET.SubElement(xmlVol, 'physvol', {'name': 'PV'+volName}) + ET.SubElement(pvol, 'volumeref', {'ref': volName}) + extrudePosition = extrudeObj.Placement.Base + if extrudeObj.Symmetric is False: + if extrudeObj.Reversed is False: + zoffset = Vector(0, 0, extrudeObj.LengthRev.Value) + else: + zoffset = Vector(0, 0, extrudeObj.LengthFwd.Value) + else: + zoffset = Vector(0, 0, extrudeObj.LengthFwd.Value/2) + + angles = quaternion2XYZ(extrudeObj.Placement.Rotation) + # need to add rotations of elliptical tubes. Assume extrusion is on z-axis + # Probably wil not work in general + zAngle = angles[2] + rootRot[2] + print(rootPos) + print(rootCurve.name) + print(rootCurve.position) + rootPos = rotatedPos(rootCurve, extrudeObj.Placement.Rotation) + print(rootPos) + volPos = extrudePosition + rootPos - zoffset + + print(volPos) + exportPosition(volName, pvol, volPos) + + rotName = booleanName + '_rot' + ET.SubElement(define, 'rotation', {'name': rotName, 'unit': 'deg', + 'x': str(-angles[0]), + 'y': str(-angles[1]), + 'z': str(-zAngle)}) + ET.SubElement(pvol, 'rotationref', {'ref': rotName}) diff --git a/freecad/gdml/exportGDML.py b/freecad/gdml/exportGDML.py index 6116c27b4..437f59bf8 100644 --- a/freecad/gdml/exportGDML.py +++ b/freecad/gdml/exportGDML.py @@ -1,30 +1,30 @@ # Mon Dec 6 08:49:56 AM PST 2021 -#**************************************************************************pp -#* * -#* Copyright (c) 2019 Keith Sloan * -#* (c) 2020 Dam Lambert * -#* * -#* 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 * -#* * -#* Acknowledgements : Ideas & code copied from * -#* https://github.com/ignamv/geanTipi * -#* * -#*************************************************************************** -__title__="FreeCAD - GDML exporter Version" +# ************************************************************************** +# * * +# * Copyright (c) 2019 Keith Sloan * +# * (c) 2020 Dam Lambert * +# * * +# * 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 * +# * * +# * Acknowledgements : Ideas & code copied from * +# * https://github.com/ignamv/geanTipi * +# * * +# *************************************************************************** +__title__ = "FreeCAD - GDML exporter Version" __author__ = "Keith Sloan " __url__ = ["https://github.com/KeithSloan/FreeCAD_Geant4"] @@ -32,70 +32,75 @@ from FreeCAD import Vector from .GDMLObjects import GDMLcommon, GDMLBox, GDMLTube -# modif add -#from .GDMLObjects import getMult, convertionlisteCharToLunit +# modif add +# from .GDMLObjects import getMult, convertionlisteCharToLunit import sys try: - import lxml.etree as ET - FreeCAD.Console.PrintMessage("running with lxml.etree\n") - XML_IO_VERSION='lxml' + import lxml.etree as ET + FreeCAD.Console.PrintMessage("running with lxml.etree\n") + XML_IO_VERSION = 'lxml' except ImportError: - try: - import xml.etree.ElementTree as ET - FreeCAD.Console.PrintMessage("running with xml.etree.ElementTree\n") - XML_IO_VERSION = 'xml' - except ImportError: - FreeCAD.Console.PrintMessage('pb xml lib not found\n') - sys.exit() + try: + import xml.etree.ElementTree as ET + FreeCAD.Console.PrintMessage("running with xml.etree.ElementTree\n") + XML_IO_VERSION = 'xml' + except ImportError: + FreeCAD.Console.PrintMessage('pb xml lib not found\n') + sys.exit() # xml handling -#import argparse -#from xml.etree.ElementTree import XML +# import argparse +# from xml.etree.ElementTree import XML ################################# -try: import FreeCADGui -except ValueError: gui = False -else: gui = True - global zOrder from .GDMLObjects import GDMLQuadrangular, GDMLTriangular, \ GDML2dVertex, GDMLSection, \ GDMLmaterial, GDMLfraction, \ GDMLcomposite, GDMLisotope, \ - GDMLelement, GDMLconstant, GDMLvariable, GDMLquantity + GDMLelement, GDMLconstant, GDMLvariable, \ + GDMLquantity from . import GDMLShared +from . import exportExtrusion -#*************************************************************************** +# *************************************************************************** # Tailor following to your requirements ( Should all be strings ) * # no doubt there will be a problem when they do implement Value if open.__module__ in ['__builtin__', 'io']: - pythonopen = open # to distinguish python built-in open function from the one declared here + pythonopen = open # to distinguish python built-in open function from the one declared here + +# ## modifs lambda -### modifs lambda def verifNameUnique(name): - # need to be done!! - return True + # need to be done!! + return True -### end modifs lambda +# ## end modifs lambda ################################# # Switch functions ################################ + + class switch(object): value = None + def __new__(class_, value): class_.value = value return True + def case(*args): return any((arg == switch.value for arg in args)) ######################################################### # Pretty format GDML # ######################################################### + + def indent(elem, level=0): i = "\n" + level*" " j = "\n" + (level-1)*" " @@ -113,29 +118,31 @@ def indent(elem, level=0): elem.tail = j return elem -def nameFromLabel(label) : - if ' ' not in label : - return label - else : - return(label.split(' ')[0]) +######################################### + +def nameFromLabel(label): + if ' ' not in label: + return label + else: + return(label.split(' ')[0]) -def initGDML() : + +def initGDML(): NS = 'http://www.w3.org/2001/XMLSchema-instance' location_attribute = '{%s}noNamespaceSchemaLocation' % NS - gdml = ET.Element('gdml',attrib={location_attribute: \ - 'http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd'}) - #print(gdml.tag) + gdml = ET.Element('gdml', attrib={location_attribute: + 'http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd'}) + # print(gdml.tag) - #'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance", - #'xsi:noNamespaceSchemaLocation': "http://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd" -#}) return gdml ################################# # Setup GDML environment ################################# -def GDMLstructure() : - #print("Setup GDML structure") + + +def GDMLstructure(): + # print("Setup GDML structure") ################################# # globals ################################ @@ -145,7 +152,7 @@ def GDMLstructure() : global defineCnt, LVcount, PVcount, POScount, ROTcount global gxml - defineCnt = LVcount = PVcount = POScount = ROTcount = 1 + defineCnt = LVcount = PVcount = POScount = ROTcount = 1 gdml = initGDML() define = ET.SubElement(gdml, 'define') @@ -154,51 +161,113 @@ def GDMLstructure() : structure = ET.SubElement(gdml, 'structure') setup = ET.SubElement(gdml, 'setup', {'name': 'Default', 'version': '1.0'}) gxml = ET.Element('gxml') + + exportExtrusion.setGlobals(define, materials, solids) + return structure + def defineMaterials(): # Replaced by loading Default - #print("Define Materials") + # print("Define Materials") global materials -def exportDefine(name, v) : + +def exportDefine(name, v): global define - #print('define : '+name) - #print(v) - #print(v[0]) - ET.SubElement(define,'position',{'name' : name, 'unit': 'mm', \ - 'x': str(v[0]), 'y': str(v[1]), 'z': str(v[2]) }) + ET.SubElement(define, 'position', {'name': name, 'unit': 'mm', + 'x': str(v[0]), 'y': str(v[1]), 'z': str(v[2])}) + -def exportDefineVertex(name, v, index) : +def exportDefineVertex(name, v, index): global define - #print('define Vertex : '+name) - #print(v) - ET.SubElement(define,'position',{'name' : name + str(index), \ - 'unit': 'mm', 'x': str(v.X), 'y': str(v.Y), 'z': str(v.Z) }) + ET.SubElement(define, 'position', {'name': name + str(index), + 'unit': 'mm', 'x': str(v.X), 'y': str(v.Y), 'z': str(v.Z)}) + def defineWorldBox(bbox): global solids - for obj in FreeCAD.ActiveDocument.Objects : + for obj in FreeCAD.ActiveDocument.Objects: # print("{} + {} = ".format(bbox, obj.Shape.BoundBox)) - if hasattr(obj,"Shape"): - bbox.add(obj.Shape.BoundBox) - if hasattr(obj,"Mesh"): - bbox.add(obj.Mesh.BoundBox) - if hasattr(obj,"Points"): - bbox.add(obj.Points.BoundBox) + if hasattr(obj, "Shape"): + bbox.add(obj.Shape.BoundBox) + if hasattr(obj, "Mesh"): + bbox.add(obj.Mesh.BoundBox) + if hasattr(obj, "Points"): + bbox.add(obj.Points.BoundBox) # print(bbox) # Solids get added to solids section of gdml ( solids is a global ) name = 'WorldBox' ET.SubElement(solids, 'box', {'name': name, - 'x': str(1000), \ - 'y': str(1000), \ - 'z': str(1000), \ - #'x': str(2*max(abs(bbox.XMin), abs(bbox.XMax))), \ - #'y': str(2*max(abs(bbox.YMin), abs(bbox.YMax))), \ - #'z': str(2*max(abs(bbox.ZMin), abs(bbox.ZMax))), \ - 'lunit': 'mm'}) + 'x': str(1000), + 'y': str(1000), + 'z': str(1000), + # 'x': str(2*max(abs(bbox.XMin), abs(bbox.XMax))), \ + # 'y': str(2*max(abs(bbox.YMin), abs(bbox.YMax))), \ + # 'z': str(2*max(abs(bbox.ZMin), abs(bbox.ZMax))), \ + 'lunit': 'mm'}) return(name) + +def quaternion2XYZ(rot): + ''' + convert a quaternion rotation to a sequence of rotations around X, Y, Z + Here is my (Munther Hindi) derivation: + First, the rotation matrices for rotations around X, Y, Z axes. + + Rx = [ 1 0 0] + [ 0 cos(a) -sin(a)] + [ 0 sin(a) cos(a)] + + Ry= [ cos(b) 0 sin(b)] + [ 0 1 0] + [-sin(b) 0 cos(b)] + + Rz = [ cos(g) -sin(g) 0] + [ sin(g) cos(g) 0] + [ 0 0 1] + + R = Rz Ry Rx = [ + [cos(b)*cos(g), cos(g)*sin(a)*sin(b) - cos(a)*sin(g), cos(a)*cos(g)*sin(b) + sin(a)*sin(g)] + [cos(b)*sin(g), sin(a)*sin(b)*sin(g) + cos(a)*cos(g), cos(a)*sin(b)*sin(g) - cos(g)*sin(a)] + [-sin(b), cos(b)*sin(a), , cos(a)*cos(b)]] + + To get the angles b(eta), g(amma) for rotations around y, z axes, transform the unit vector (1,0,0) + [x,y,z] = Q*(1,0,0) = R*(1,0,0) ==> + x = cos(b)*cos(g) + y = cos(b)*sin(g) + z = -sin(b) + + ==> g = atan2(y, x) = atan2(sin(b)*sin(g), cos(g)*sin(b)) = atan2(sin(g), cos(g)) + then b = atan2(-z*cos(g), x) = atan2(sin(b)*cos(g), cos(b)*cos(g)] = atan2(sin(b), cos(b)) + + Once b, g are found, a(lpha) can be found by transforming (0, 0, 1), or (0,1,0) + Since now g, b are known, one can form the inverses of Ry, Rz: + Rz^-1 = Rz(-g) + Ry^-1 = Ry(-b) + + Now R*(0,0,1) = Rz*Ry*Rz(0,1,0) = (x, y, z) + multiply both sides by Ry^-1 Rz^-1: + Ry^-1 Rz^-1 Rz Ry Rx (0,1,0) = Rx (0,1,0) = Ry(-b) Rz(-g) (x, y, z) = (xp, yp, zp) + ==> + xp = 0 + yp = cos(a) + zp = sin(a) + + and a = atan2(zp, yp) + ''' + v = rot*Vector(1, 0, 0) + g = math.atan2(v.y, v.x) + b = math.atan2(-v.z*math.cos(g), v.x) + Ryinv = FreeCAD.Rotation(Vector(0, 1, 0), math.degrees(-b)) + Rzinv = FreeCAD.Rotation(Vector(0, 0, 1), math.degrees(-g)) + vp = Ryinv*Rzinv*rot*Vector(0, 1, 0) + a = math.atan2(vp.z, vp.y) + + print([math.degrees(a), math.degrees(b), math.degrees(g)]) + return [math.degrees(a), math.degrees(b), math.degrees(g)] + + def createLVandPV(obj, name, solidName): # # Logical & Physical Volumes get added to structure section of gdml @@ -206,32 +275,25 @@ def createLVandPV(obj, name, solidName): # Need to update so that use export of Rotation & position # rather than this as well i.e one Place # - #print('createLVandPV') - #ET.ElementTree(gdml).write("test9d", 'utf-8', True) - #print("Object Base") - #dir(obj.Base) - #print dir(obj) - #print dir(obj.Placement) global PVcount, POScount, ROTcount - #return pvName = 'PV'+name+str(PVcount) PVcount += 1 - pos = obj.Placement.Base - lvol = ET.SubElement(structure,'volume', {'name':pvName}) + pos = obj.Placement.Base + lvol = ET.SubElement(structure, 'volume', {'name': pvName}) ET.SubElement(lvol, 'materialref', {'ref': 'SSteel0x56070ee87d10'}) ET.SubElement(lvol, 'solidref', {'ref': solidName}) # Place child physical volume in World Volume - phys = ET.SubElement(lvol, 'physvol',{'name':'PV-'+name}) + phys = ET.SubElement(lvol, 'physvol', {'name': 'PV-'+name}) ET.SubElement(phys, 'volumeref', {'ref': pvName}) x = pos[0] y = pos[1] z = pos[2] - if x!=0 and y!=0 and z!=0 : - posName = 'Pos'+name+str(POScount) - POScount += 1 - ET.SubElement(phys, 'positionref', {'name': posName}) - ET.SubElement(define, 'position', {'name': posName, 'unit': 'mm', \ - 'x': str(x), 'y': str(y), 'z': str(z) }) + if x != 0 and y != 0 and z != 0: + posName = 'Pos'+name+str(POScount) + POScount += 1 + ET.SubElement(phys, 'positionref', {'name': posName}) + ET.SubElement(define, 'position', {'name': posName, 'unit': 'mm', + 'x': str(x), 'y': str(y), 'z': str(z)}) # Realthunders enhancement to toEuler ixyz is intrinsic rot = obj.Placement.Rotation if hasattr(rot, 'toEulerAngles'): @@ -243,196 +305,204 @@ def createLVandPV(obj, name, solidName): angles = rot.toEuler() GDMLShared.trace("Angles") GDMLShared.trace(angles) + angles = quaternion2XYZ(rot) a0 = angles[0] - #print(a0) + # print(a0) a1 = angles[1] - #print(a1) + # print(a1) a2 = angles[2] - #print(a2) - if a0!=0 and a1!=0 and a2!=0 : - rotName = 'Rot'+name+str(ROTcount) - ROTcount += 1 - ET.SubElement(phys, 'rotationref', {'name': rotName}) - ET.SubElement(define, 'rotation', {'name': rotName, 'unit': 'deg', \ - 'x': str(-a2), 'y': str(-a1), 'z': str(-a0)}) + # print(a2) + if a0 != 0 and a1 != 0 and a2 != 0: + rotName = 'Rot'+name+str(ROTcount) + ROTcount += 1 + ET.SubElement(phys, 'rotationref', {'name': rotName}) + ET.SubElement(define, 'rotation', {'name': rotName, 'unit': 'deg', + 'x': str(-a0), 'y': str(-a1), 'z': str(-a2)}) + def createAdjustedLVandPV(obj, name, solidName, delta): # Allow for difference in placement between FreeCAD and GDML adjObj = obj rot = FreeCAD.Rotation(obj.Placement.Rotation) - adjObj.Placement.move(rot.multVec(delta))#.negative() + adjObj.Placement.move(rot.multVec(delta)) # .negative() createLVandPV(adjObj, name, solidName) -def reportObject(obj) : - + +def reportObject(obj): + GDMLShared.trace("Report Object") GDMLShared.trace(obj) GDMLShared.trace("Name : "+obj.Name) - GDMLShared.trace("Type : "+obj.TypeId) - if hasattr(obj,'Placement') : - print("Placement") - print("Pos : "+str(obj.Placement.Base)) - print("axis : "+str(obj.Placement.Rotation.Axis)) - print("angle : "+str(obj.Placement.Rotation.Angle)) - - while switch(obj.TypeId) : - - ########################################### - # FreeCAD GDML Parts # - ########################################### - if case("Part::FeaturePython") : - GDMLShared.trace("Part::FeaturePython") - # - #if hasattr(obj.Proxy,'Type'): - # print (obj.Proxy.Type) - # print (obj.Name) - #else : - # print("Not a GDML Feature") - - #print dir(obj) - #print dir(obj.Proxy) - #print("cylinder : Height "+str(obj.Height)+ " Radius "+str(obj.Radius)) - break - ########################################### - # FreeCAD Parts # - ########################################### - if case("Part::Sphere") : - print("Sphere Radius : "+str(obj.Radius)) - break - - if case("Part::Box") : - print("cube : ("+ str(obj.Length)+","+str(obj.Width)+","+str(obj.Height)+")") - break - - if case("Part::Cylinder") : - print("cylinder : Height "+str(obj.Height)+ " Radius "+str(obj.Radius)) - break - - if case("Part::Cone") : - print("cone : Height "+str(obj.Height)+ " Radius1 "+str(obj.Radius1)+" Radius2 "+str(obj.Radius2)) - break - - if case("Part::Torus") : - print("Torus") - print(obj.Radius1) - print(obj.Radius2) - break - - if case("Part::Prism") : - print("Prism") - break - - if case("Part::RegularPolygon") : - print("RegularPolygon") - break - - if case("Part::Extrusion") : - print("Extrusion") - break - - if case("Circle") : - print("Circle") - break - - if case("Extrusion") : - print("Wire extrusion") - break - - if case("Mesh::Feature") : - print("Mesh") - #print dir(obj.Mesh) - break - - print("Other") - print(obj.TypeId) - break - -def processPlanar(obj, shape, name ) : - print ('Polyhedron ????') + GDMLShared.trace("Type : "+obj.TypeId) + if hasattr(obj, 'Placement'): + print("Placement") + print("Pos : "+str(obj.Placement.Base)) + print("axis : "+str(obj.Placement.Rotation.Axis)) + print("angle : "+str(obj.Placement.Rotation.Angle)) + + while switch(obj.TypeId): + + ########################################### + # FreeCAD GDML Parts # + ########################################### + if case("Part::FeaturePython"): + GDMLShared.trace("Part::FeaturePython") + # + # if hasattr(obj.Proxy,'Type'): + # print (obj.Proxy.Type) + # print (obj.Name) + # else : + # print("Not a GDML Feature") + + # print dir(obj) + # print dir(obj.Proxy) + # print("cylinder : Height "+str(obj.Height)+ " Radius "+str(obj.Radius)) + break + ########################################### + # FreeCAD Parts # + ########################################### + if case("Part::Sphere"): + print("Sphere Radius : "+str(obj.Radius)) + break + + if case("Part::Box"): + print("cube : (" + str(obj.Length)+"," + str(obj.Width)+"," + + str(obj.Height) + ")") + break + + if case("Part::Cylinder"): + print("cylinder : Height " + str(obj.Height) + + " Radius "+str(obj.Radius)) + break + + if case("Part::Cone"): + print("cone : Height "+str(obj.Height) + + " Radius1 "+str(obj.Radius1)+" Radius2 "+str(obj.Radius2)) + break + + if case("Part::Torus"): + print("Torus") + print(obj.Radius1) + print(obj.Radius2) + break + + if case("Part::Prism"): + print("Prism") + break + + if case("Part::RegularPolygon"): + print("RegularPolygon") + break + + if case("Part::Extrusion"): + print("Extrusion") + break + + if case("Circle"): + print("Circle") + break + + if case("Extrusion"): + print("Wire extrusion") + break + + if case("Mesh::Feature"): + print("Mesh") + # print dir(obj.Mesh) + break + + print("Other") + print(obj.TypeId) + break + + +def processPlanar(obj, shape, name): + print('Polyhedron ????') global defineCnt # - #print("Add tessellated Solid") - tess = ET.SubElement(solids,'tessellated',{'name': name}) - #print("Add Vertex positions") - for f in shape.Faces : - baseVrt = defineCnt - for vrt in f.Vertexes : - vnum = 'v'+str(defineCnt) - ET.SubElement(define, 'position', {'name': vnum, \ - 'x': str(vrt.Point.x), \ - 'y': str(vrt.Point.y), \ - 'z': str(vrt.Point.z), \ - 'unit': 'mm'}) - defineCnt += 1 - #print("Add vertex to tessellated Solid") - vrt1 = 'v'+str(baseVrt) - vrt2 = 'v'+str(baseVrt+1) - vrt3 = 'v'+str(baseVrt+2) - vrt4 = 'v'+str(baseVrt+3) - NumVrt = len(f.Vertexes) - if NumVrt == 3 : - ET.SubElement(tess,'triangular',{ \ - 'vertex1': vrt1, \ - 'vertex2': vrt2, \ - 'vertex3': vrt3, \ - 'type': 'ABSOLUTE'}) - elif NumVrt == 4 : - ET.SubElement(tess,'quadrangular',{ \ - 'vertex1': vrt1, \ - 'vertex2': vrt2, \ - 'vertex3': vrt3, \ - 'vertex4': vrt4, \ - 'type': 'ABSOLUTE'}) - -def checkShapeAllPlanar(Shape) : - for f in Shape.Faces : - if f.Surface.isPlanar() == False : - return False - break + # print("Add tessellated Solid") + tess = ET.SubElement(solids, 'tessellated', {'name': name}) + # print("Add Vertex positions") + for f in shape.Faces: + baseVrt = defineCnt + for vrt in f.Vertexes: + vnum = 'v'+str(defineCnt) + ET.SubElement(define, 'position', {'name': vnum, + 'x': str(vrt.Point.x), + 'y': str(vrt.Point.y), + 'z': str(vrt.Point.z), + 'unit': 'mm'}) + defineCnt += 1 + # print("Add vertex to tessellated Solid") + vrt1 = 'v'+str(baseVrt) + vrt2 = 'v'+str(baseVrt+1) + vrt3 = 'v'+str(baseVrt+2) + vrt4 = 'v'+str(baseVrt+3) + NumVrt = len(f.Vertexes) + if NumVrt == 3: + ET.SubElement(tess, 'triangular', { + 'vertex1': vrt1, + 'vertex2': vrt2, + 'vertex3': vrt3, + 'type': 'ABSOLUTE'}) + elif NumVrt == 4: + ET.SubElement(tess, 'quadrangular', { + 'vertex1': vrt1, + 'vertex2': vrt2, + 'vertex3': vrt3, + 'vertex4': vrt4, + 'type': 'ABSOLUTE'}) + + +def checkShapeAllPlanar(Shape): + for f in Shape.Faces: + if f.Surface.isPlanar() is False: + return False return True # Add XML for TessellateSolid -def mesh2Tessellate(mesh, name) : - global defineCnt - - baseVrt = defineCnt - #print ("mesh") - #print (mesh) - #print ("Facets") - #print (mesh.Facets) - #print ("mesh topology") - #print (dir(mesh.Topology)) - #print (mesh.Topology) -# -# mesh.Topology[0] = points -# mesh.Topology[1] = faces -# -# First setup vertex in define section vetexs (points) - #print("Add Vertex positions") - for fc_points in mesh.Topology[0] : - #print(fc_points) - v = 'v'+str(defineCnt) - ET.SubElement(define, 'position', {'name': v, \ - 'x': str(fc_points[0]), \ - 'y': str(fc_points[1]), \ - 'z': str(fc_points[2]), \ - 'unit': 'mm'}) - defineCnt += 1 -# -# Add faces -# - #print("Add Triangular vertex") - tess = ET.SubElement(solids,'tessellated',{'name': name}) - for fc_facet in mesh.Topology[1] : - #print(fc_facet) - vrt1 = 'v'+str(baseVrt+fc_facet[0]) - vrt2 = 'v'+str(baseVrt+fc_facet[1]) - vrt3 = 'v'+str(baseVrt+fc_facet[2]) - ET.SubElement(tess,'triangular',{ \ - 'vertex1': vrt1, 'vertex2': vrt2 ,'vertex3': vrt3, 'type': 'ABSOLUTE'}) - - -def processMesh(obj, Mesh, Name) : +def mesh2Tessellate(mesh, name): + global defineCnt + + baseVrt = defineCnt + # print ("mesh") + # print (mesh) + # print ("Facets") + # print (mesh.Facets) + # print ("mesh topology") + # print (dir(mesh.Topology)) + # print (mesh.Topology) + # + # mesh.Topology[0] = points + # mesh.Topology[1] = faces + # + # First setup vertex in define section vetexs (points) + # print("Add Vertex positions") + for fc_points in mesh.Topology[0]: + # print(fc_points) + v = 'v'+str(defineCnt) + ET.SubElement(define, 'position', {'name': v, + 'x': str(fc_points[0]), + 'y': str(fc_points[1]), + 'z': str(fc_points[2]), + 'unit': 'mm'}) + defineCnt += 1 + # + # Add faces + # + # print("Add Triangular vertex") + tess = ET.SubElement(solids, 'tessellated', {'name': name}) + for fc_facet in mesh.Topology[1]: + # print(fc_facet) + vrt1 = 'v'+str(baseVrt+fc_facet[0]) + vrt2 = 'v'+str(baseVrt+fc_facet[1]) + vrt3 = 'v'+str(baseVrt+fc_facet[2]) + ET.SubElement(tess, 'triangular', { + 'vertex1': vrt1, 'vertex2': vrt2, + 'vertex3': vrt3, 'type': 'ABSOLUTE'}) + + +def processMesh(obj, Mesh, Name): # obj needed for Volune names # object maynot have Mesh as part of Obj # Name - allows control over name @@ -441,192 +511,208 @@ def processMesh(obj, Mesh, Name) : mesh2Tessellate(Mesh, Name) return(Name) -def shape2Mesh(shape) : - import MeshPart - return (MeshPart.meshFromShape(Shape=shape, Deflection = 0.0)) -# Deflection= params.GetFloat('meshdeflection',0.0)) -def processObjectShape(obj) : +def shape2Mesh(shape): + import MeshPart + return (MeshPart.meshFromShape(Shape=shape, Deflection=0.0)) +# Deflection= params.GetFloat('meshdeflection',0.0)) + + +def processObjectShape(obj): # Check if Planar # If plannar create Tessellated Solid with 3 & 4 vertex as appropriate # If not planar create a mesh and the a Tessellated Solid with 3 vertex - #print("Process Object Shape") - #print(obj) - #print(obj.PropertiesList) - if not hasattr(obj,'Shape') : - return + # print("Process Object Shape") + # print(obj) + # print(obj.PropertiesList) + if not hasattr(obj, 'Shape'): + return shape = obj.Shape - #print (shape) - #print(shape.ShapeType) - while switch(shape.ShapeType) : - if case("Mesh::Feature") : - print("Mesh - Should not occur should have been handled") - #print("Mesh") - #tessellate = mesh2Tessellate(mesh) - #return(tessellate) - #break - - print("ShapeType Not handled") - print(shape.ShapeType) - break + # print (shape) + # print(shape.ShapeType) + while switch(shape.ShapeType): + if case("Mesh::Feature"): + print("Mesh - Should not occur should have been handled") + # print("Mesh") + # tessellate = mesh2Tessellate(mesh) + # return(tessellate) + # break + + print("ShapeType Not handled") + print(shape.ShapeType) + break # Dropped through to here # Need to check has Shape - #print('Check if All planar') + # print('Check if All planar') planar = checkShapeAllPlanar(shape) - #print(planar) + # print(planar) - if planar : - return(processPlanar(obj,shape,obj.Name)) + if planar: + return(processPlanar(obj, shape, obj.Name)) + + else: + # Create Mesh from shape & then Process Mesh + # to create Tessellated Solid in Geant4 + return(processMesh(obj, shape2Mesh(shape), obj.Name)) - else : - # Create Mesh from shape & then Process Mesh - #to create Tessellated Solid in Geant4 - return(processMesh(obj,shape2Mesh(shape),obj.Name)) -def processBoxObject(obj, addVolsFlag) : +def processBoxObject(obj, addVolsFlag): # Needs unique Name # This for non GDML Box - boxName = obj.Name - - ET.SubElement(solids, 'box',{'name': boxName, \ - 'x': str(obj.Length.Value), \ - 'y': str(obj.Width.Value), \ - 'z': str(obj.Height.Value), \ - 'lunit' : 'mm'}) - if addVolsFlag : - # Adjustment for position in GDML - delta = FreeCAD.Vector(obj.Length.Value / 2, \ - obj.Width.Value / 2, \ - obj.Height.Value / 2) - - createAdjustedLVandPV(obj, obj.Name, boxName, delta) + + boxName = obj.Name + + ET.SubElement(solids, 'box', {'name': boxName, + 'x': str(obj.Length.Value), + 'y': str(obj.Width.Value), + 'z': str(obj.Height.Value), + 'lunit': 'mm'}) + if addVolsFlag: + # Adjustment for position in GDML + delta = FreeCAD.Vector(obj.Length.Value / 2, + obj.Width.Value / 2, + obj.Height.Value / 2) + + createAdjustedLVandPV(obj, obj.Name, boxName, delta) return(boxName) -def processCylinderObject(obj, addVolsFlag) : + +def processCylinderObject(obj, addVolsFlag): # Needs unique Name # This is for non GDML cylinder/tube cylName = obj.Name - ET.SubElement(solids, 'tube',{'name': cylName, \ - 'rmax': str(obj.Radius.Value), \ - 'deltaphi': str(float(obj.Angle)), \ - 'aunit': obj.aunit, - 'z': str(obj.Height.Value), \ - 'lunit' : 'mm'}) - if addVolsFlag : - # Adjustment for position in GDML - delta = FreeCAD.Vector(0, 0, obj.Height.Value / 2) - createAdjustedLVandPV(obj, obj.Name, cylName, delta) + ET.SubElement(solids, 'tube', {'name': cylName, + 'rmax': str(obj.Radius.Value), + 'deltaphi': str(float(obj.Angle)), + 'aunit': obj.aunit, + 'z': str(obj.Height.Value), + 'lunit': 'mm'}) + if addVolsFlag: + # Adjustment for position in GDML + delta = FreeCAD.Vector(0, 0, obj.Height.Value / 2) + createAdjustedLVandPV(obj, obj.Name, cylName, delta) return(cylName) -def processConeObject(obj, addVolsFlag) : + +def processConeObject(obj, addVolsFlag): # Needs unique Name coneName = obj.Name - ET.SubElement(solids, 'cone',{'name': coneName, \ - 'rmax1': str(obj.Radius1.Value), \ - 'rmax2': str(obj.Radius2.Value), \ - 'deltaphi': str(float(obj.Angle)), \ - 'aunit': obj.aunit, - 'z': str(obj.Height.Value), \ - 'lunit' : 'mm'}) - if addVolsFlag : - # Adjustment for position in GDML - delta = FreeCAD.Vector(0, 0, obj.Height.Value / 2) - createAdjustedLVandPV(obj, obj.Name, coneName, delta) + ET.SubElement(solids, 'cone', { + 'name': coneName, + 'rmax1': str(obj.Radius1.Value), + 'rmax2': str(obj.Radius2.Value), + 'deltaphi': str(float(obj.Angle)), + 'aunit': obj.aunit, + 'z': str(obj.Height.Value), + 'lunit': 'mm'}) + if addVolsFlag: + # Adjustment for position in GDML + delta = FreeCAD.Vector(0, 0, obj.Height.Value / 2) + createAdjustedLVandPV(obj, obj.Name, coneName, delta) return(coneName) -def processSection(obj, addVolsflag) : - #print("Process Section") - ET.SubElement(solids, 'section',{'vertex1': obj.v1, \ - 'vertex2': obj.v2, 'vertex3': obj.v3, 'vertex4': obj.v4, \ - 'type': obj.vtype}) +def processSection(obj, addVolsflag): + # print("Process Section") + ET.SubElement(solids, 'section', { + 'vertex1': obj.v1, + 'vertex2': obj.v2, + 'vertex3': obj.v3, 'vertex4': obj.v4, + 'type': obj.vtype}) -def processSphereObject(obj, addVolsFlag) : + +def processSphereObject(obj, addVolsFlag): # Needs unique Name - #modif lambda (if we change the name here, each time we import and export the file, the name will be change - #sphereName = 'Sphere' + obj.Name + # modif lambda (if we change the name here, each time we import and export the file, the name will be change + # sphereName = 'Sphere' + obj.Name sphereName = obj.Name - ET.SubElement(solids, 'sphere',{'name': sphereName, \ - 'rmax': str(obj.Radius.Value), \ - 'starttheta': str(90.-float(obj.Angle2)), \ - 'deltatheta': str(float(obj.Angle2-obj.Angle1)), \ - 'deltaphi': str(float(obj.Angle3)), \ - 'aunit': obj.aunit, - 'lunit' : 'mm'}) - if addVolsFlag : - createLVandPV(obj,obj.Name,sphereName) + ET.SubElement(solids, 'sphere', { + 'name': sphereName, + 'rmax': str(obj.Radius.Value), + 'starttheta': str(90.-float(obj.Angle2)), + 'deltatheta': str(float(obj.Angle2-obj.Angle1)), + 'deltaphi': str(float(obj.Angle3)), + 'aunit': obj.aunit, + 'lunit': 'mm'}) + if addVolsFlag: + createLVandPV(obj, obj.Name, sphereName) return(sphereName) -def addPhysVol(xmlVol, volName) : - GDMLShared.trace("Add PhysVol to Vol : "+volName) - #print(ET.tostring(xmlVol)) - pvol = ET.SubElement(xmlVol,'physvol',{'name':'PV-'+volName}) - ET.SubElement(pvol,'volumeref',{'ref':volName}) + +def addPhysVol(xmlVol, volName): + GDMLShared.trace("Add PhysVol to Vol : " + volName) + # print(ET.tostring(xmlVol)) + pvol = ET.SubElement(xmlVol, 'physvol', {'name': 'PV-'+volName}) + ET.SubElement(pvol, 'volumeref', {'ref': volName}) return pvol -def cleanVolName(obj, volName) : + +def cleanVolName(obj, volName): # Get proper Volume Name - #print('clean name : '+volName) - if hasattr(obj,'Copynumber') : - #print('Has copynumber') - i = len(volName) - if '_' in volName and i > 2 : - volName = volName[:-2] - #print('returning name : '+volName) + # print('clean name : '+volName) + if hasattr(obj, 'Copynumber'): + # print('Has copynumber') + i = len(volName) + if '_' in volName and i > 2: + volName = volName[:-2] + # print('returning name : '+volName) return volName -def addPhysVolPlacement(obj, xmlVol, volName) : + +def addPhysVolPlacement(obj, xmlVol, volName): # ??? Is volName not obj.Label after correction # Get proper Volume Name refName = cleanVolName(obj, volName) - #GDMLShared.setTrace(True) - GDMLShared.trace("Add PhysVol to Vol : "+refName) - #print(ET.tostring(xmlVol)) - if xmlVol is not None : - if not hasattr(obj,'CopyNumber') : - pvol = ET.SubElement(xmlVol,'physvol',{'name':'PV-'+volName}) - else : - cpyNum = str(obj.CopyNumber) - GDMLShared.trace('CopyNumber : '+cpyNum) - pvol = ET.SubElement(xmlVol,'physvol',{'copynumber':cpyNum}) - ET.SubElement(pvol,'volumeref',{'ref':refName}) - processPosition(obj,pvol) - processRotation(obj,pvol) - if hasattr(obj,'GDMLscale') : - scaleName = refName+'scl' - ET.SubElement(pvol,'scale',{'name':scaleName,\ - 'x':str(obj.GDMLscale[0]), \ - 'y':str(obj.GDMLscale[1]), \ - 'z':str(obj.GDMLscale[2])}) - - return pvol - -def exportPosition(name, xml, pos) : + # GDMLShared.setTrace(True) + GDMLShared.trace("Add PhysVol to Vol : "+refName) + # print(ET.tostring(xmlVol)) + if xmlVol is not None: + if not hasattr(obj, 'CopyNumber'): + pvol = ET.SubElement(xmlVol, 'physvol', {'name': 'PV-' + volName}) + else: + cpyNum = str(obj.CopyNumber) + GDMLShared.trace('CopyNumber : '+cpyNum) + pvol = ET.SubElement(xmlVol, 'physvol', {'copynumber': cpyNum}) + ET.SubElement(pvol, 'volumeref', {'ref': refName}) + processPosition(obj, pvol) + processRotation(obj, pvol) + if hasattr(obj, 'GDMLscale'): + scaleName = refName+'scl' + ET.SubElement(pvol, 'scale', {'name': scaleName, + 'x': str(obj.GDMLscale[0]), + 'y': str(obj.GDMLscale[1]), + 'z': str(obj.GDMLscale[2])}) + + return pvol + + +def exportPosition(name, xml, pos): global POScount GDMLShared.trace('export Position') GDMLShared.trace(pos) x = pos[0] y = pos[1] z = pos[2] - posName = 'P-'+name+str(POScount) + posName = 'P-' + name + str(POScount) POScount += 1 - posxml = ET.SubElement(define,'position',{'name' : posName, \ - 'unit': 'mm'}) - if x != 0 : - posxml.attrib['x'] = str(x) - if y != 0 : - posxml.attrib['y'] = str(y) - if z != 0 : - posxml.attrib['z'] = str(z) - ET.SubElement(xml,'positionref',{'ref' : posName}) - -def exportRotation(name, xml, Rotation) : + posxml = ET.SubElement(define, 'position', {'name': posName, + 'unit': 'mm'}) + if x != 0: + posxml.attrib['x'] = str(x) + if y != 0: + posxml.attrib['y'] = str(y) + if z != 0: + posxml.attrib['z'] = str(z) + ET.SubElement(xml, 'positionref', {'ref': posName}) + + +def exportRotation(name, xml, Rotation): print('Export Rotation') global ROTcount - if Rotation.Angle != 0 : + if Rotation.Angle != 0: # Realthunders enhancement to toEuler ixyz is intrinsic if hasattr(Rotation, 'toEulerAngles'): angles = Rotation.toEulerAngles('ixyz') @@ -643,943 +729,1060 @@ def exportRotation(name, xml, Rotation) : print(a1) a2 = angles[2] print(a2) - if a0!=0 or a1!=0 or a2!=0 : + if a0 != 0 or a1 != 0 or a2 != 0: rotName = 'R-'+name+str(ROTcount) ROTcount += 1 - rotxml = ET.SubElement(define, 'rotation', {'name': rotName, \ - 'unit': 'deg'}) - if abs(a2) != 0 : - rotxml.attrib['x']=str(-a2) - if abs(a1) != 0 : - rotxml.attrib['y']=str(-a1) - if abs(a0) != 0 : - rotxml.attrib['z']=str(-a0) + rotxml = ET.SubElement(define, 'rotation', {'name': rotName, + 'unit': 'deg'}) + if abs(a2) != 0: + rotxml.attrib['x'] = str(-a2) + if abs(a1) != 0: + rotxml.attrib['y'] = str(-a1) + if abs(a0) != 0: + rotxml.attrib['z'] = str(-a0) ET.SubElement(xml, 'rotationref', {'ref': rotName}) -def processPosition(obj, solid) : - if obj.Placement.Base == FreeCAD.Vector(0,0,0) : + +def processPosition(obj, solid): + if obj.Placement.Base == FreeCAD.Vector(0, 0, 0): return GDMLShared.trace("Define position & references to Solid") exportPosition(obj.Name, solid, obj.Placement.Base) -def processRotation(obj, solid) : - if obj.Placement.Rotation.Angle == 0 : - return + +def processRotation(obj, solid): + if obj.Placement.Rotation.Angle == 0: + return GDMLShared.trace('Deal with Rotation') - exportRotation(obj.Name,solid,obj.Placement.Rotation) - -def testDefaultPlacement(obj) : - #print(dir(obj.Placement.Rotation)) - #print('Test Default Placement : '+obj.Name) - #print(obj.Placement.Base) - #print(obj.Placement.Rotation.Angle) - if obj.Placement.Base == FreeCAD.Vector(0,0,0) and \ - obj.Placement.Rotation.Angle == 0 : - return True - else : - return False + exportRotation(obj.Name, solid, obj.Placement.Rotation) + + +def testDefaultPlacement(obj): + # print(dir(obj.Placement.Rotation)) + # print('Test Default Placement : '+obj.Name) + # print(obj.Placement.Base) + # print(obj.Placement.Rotation.Angle) + if obj.Placement.Base == FreeCAD.Vector(0, 0, 0) and \ + obj.Placement.Rotation.Angle == 0: + return True + else: + return False + def testAddPhysVol(obj, xmlParent, volName): - if testDefaultPlacement(obj) == False : - if xmlParent is not None : - pvol = addPhysVol(xmlParent,volName) - processPosition(obj,pvol) - processRotation(obj,pvol) - else : - print('Root/World Volume') - -def addVolRef(volxml, volName, obj) : - # obj is GDML object + if testDefaultPlacement(obj) is False: + if xmlParent is not None: + pvol = addPhysVol(xmlParent, volName) + processPosition(obj, pvol) + processRotation(obj, pvol) + else: + print('Root/World Volume') + + +def addVolRef(volxml, volName, obj, solidName=None): # Pass material as Boolean material = getMaterial(obj) - ET.SubElement(volxml,'materialref',{'ref': material}) - ET.SubElement(volxml,'solidref',{'ref': nameOfGDMLobject(obj)}) - ET.SubElement(gxml,'volume',{'name': volName, 'material':material}) - if hasattr(obj.ViewObject,'ShapeColor') and volName != WorldVOL : - colour = obj.ViewObject.ShapeColor - colStr = '#'+''.join('{:02x}'.format(round(v*255)) for v in colour) - ET.SubElement(volxml,'auxiliary',{'auxtype': 'Color', 'auxvalue':colStr}) - #print(ET.tostring(volxml)) - -def nameOfGDMLobject(obj) : + if solidName is None: + solidName = nameOfGDMLobject(obj) + ET.SubElement(volxml, 'materialref', {'ref': material}) + ET.SubElement(volxml, 'solidref', {'ref': solidName}) + ET.SubElement(gxml, 'volume', {'name': volName, 'material': material}) + if hasattr(obj.ViewObject, 'ShapeColor') and volName != WorldVOL: + colour = obj.ViewObject.ShapeColor + colStr = '#'+''.join('{:02x}'.format(round(v*255)) for v in colour) + ET.SubElement(volxml, 'auxiliary', {'auxtype': 'Color', + 'auxvalue': colStr}) + # print(ET.tostring(volxml)) + + +def nameOfGDMLobject(obj): name = obj.Label - if len(name) > 4 : - if name[0:4] == 'GDML' : - if '_' in name : - return(name.split('_',1)[1]) + if len(name) > 4: + if name[0:4] == 'GDML': + if '_' in name: + return(name.split('_', 1)[1]) return name -def processGDMLArb8Object(obj) : + +def processGDMLArb8Object(obj): # Needs unique Name - # Remove leading GDMLArb8 from name on export + # Remove leading GDMLArb8 from name on export arb8Name = nameOfGDMLobject(obj) - solid = ET.Element('arb8',{'name': arb8Name, \ - 'v1x': str(obj.v1x), \ - 'v1y': str(obj.v1y), \ - 'v2x': str(obj.v2x), \ - 'v2y': str(obj.v2y), \ - 'v3x': str(obj.v3x), \ - 'v3y': str(obj.v3y), \ - 'v4x': str(obj.v4x), \ - 'v4y': str(obj.v4y), \ - 'v5x': str(obj.v5x), \ - 'v5y': str(obj.v5y), \ - 'v6x': str(obj.v6x), \ - 'v6y': str(obj.v6y), \ - 'v7x': str(obj.v7x), \ - 'v7y': str(obj.v7y), \ - 'v8x': str(obj.v8x), \ - 'v8y': str(obj.v8y), \ - 'dz': str(obj.dz), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'arb8', {'name': arb8Name, + 'v1x': str(obj.v1x), + 'v1y': str(obj.v1y), + 'v2x': str(obj.v2x), + 'v2y': str(obj.v2y), + 'v3x': str(obj.v3x), + 'v3y': str(obj.v3y), + 'v4x': str(obj.v4x), + 'v4y': str(obj.v4y), + 'v5x': str(obj.v5x), + 'v5y': str(obj.v5y), + 'v6x': str(obj.v6x), + 'v6y': str(obj.v6y), + 'v7x': str(obj.v7x), + 'v7y': str(obj.v7y), + 'v8x': str(obj.v8x), + 'v8y': str(obj.v8y), + 'dz': str(obj.dz), + 'lunit': obj.lunit}) return solid, arb8Name -def processGDMLBoxObject(obj) : + +def processGDMLBoxObject(obj): # Needs unique Name - # Remove leading GDMLBox_ from name on export - boxName = nameOfGDMLobject(obj) - - solid = ET.Element('box',{'name': boxName, \ - 'x': str(obj.x), \ - 'y': str(obj.y), \ - 'z': str(obj.z), \ - 'lunit' : obj.lunit}) + # Remove leading GDMLBox_ from name on export + boxName = nameOfGDMLobject(obj) + + solid = ET.SubElement(solids, 'box', {'name': boxName, + 'x': str(obj.x), + 'y': str(obj.y), + 'z': str(obj.z), + 'lunit': obj.lunit}) return solid, boxName -def processGDMLConeObject(obj) : + +def processGDMLConeObject(obj): # Needs unique Name - # Remove leading GDMLTube_ from name on export + # Remove leading GDMLTube_ from name on export coneName = nameOfGDMLobject(obj) - solid = ET.Element('cone',{'name': coneName, \ - 'rmin1': str(obj.rmin1), \ - 'rmin2': str(obj.rmin2), \ - 'rmax1': str(obj.rmax1), \ - 'rmax2': str(obj.rmax2), \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, \ - 'z': str(obj.z), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'cone', {'name': coneName, + 'rmin1': str(obj.rmin1), + 'rmin2': str(obj.rmin2), + 'rmax1': str(obj.rmax1), + 'rmax2': str(obj.rmax2), + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'z': str(obj.z), + 'lunit': obj.lunit}) # modif 'mm' -> obj.lunit return solid, coneName -def processGDMLCutTubeObject(obj) : + +def processGDMLCutTubeObject(obj): # Needs unique Name # Remove leading GDML text from name cTubeName = nameOfGDMLobject(obj) - solid = ET.Element('cutTube',{'name': cTubeName, \ - 'rmin': str(obj.rmin), \ - 'rmax': str(obj.rmax), \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, \ - 'z': str(obj.z), \ - 'highX':str(obj.highX), \ - 'highY':str(obj.highY), \ - 'highZ':str(obj.highZ), \ - 'lowX':str(obj.lowX), \ - 'lowY':str(obj.lowY), \ - 'lowZ':str(obj.lowZ), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'cutTube', {'name': cTubeName, + 'rmin': str(obj.rmin), + 'rmax': str(obj.rmax), + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'z': str(obj.z), + 'highX': str(obj.highX), + 'highY': str(obj.highY), + 'highZ': str(obj.highZ), + 'lowX': str(obj.lowX), + 'lowY': str(obj.lowY), + 'lowZ': str(obj.lowZ), + 'lunit': obj.lunit}) return solid, cTubeName -def processGDMLElConeObject(obj) : + +def processGDMLElConeObject(obj): GDMLShared.trace('Elliptical Cone') elconeName = nameOfGDMLobject(obj) - solid = ET.Element('elcone',{'name': elconeName, \ - 'dx': str(obj.dx), \ - 'dy': str(obj.dy), \ - 'zcut' : str(obj.zcut), \ - 'zmax' : str(obj.zmax), \ - 'lunit' : str(obj.lunit)}) + solid = ET.SubElement(solids, 'elcone', {'name': elconeName, + 'dx': str(obj.dx), + 'dy': str(obj.dy), + 'zcut': str(obj.zcut), + 'zmax': str(obj.zmax), + 'lunit': str(obj.lunit)}) return solid, elconeName -def processGDMLEllipsoidObject(obj) : + +def processGDMLEllipsoidObject(obj): # Needs unique Name ellipsoidName = nameOfGDMLobject(obj) - solid = ET.Element('ellipsoid',{'name': ellipsoidName, \ - 'ax': str(obj.ax), \ - 'by': str(obj.by), \ - 'cz': str(obj.cz), \ - 'zcut1': str(obj.zcut1), \ - 'zcut2': str(obj.zcut2), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'ellipsoid', {'name': ellipsoidName, + 'ax': str(obj.ax), + 'by': str(obj.by), + 'cz': str(obj.cz), + 'zcut1': str(obj.zcut1), + 'zcut2': str(obj.zcut2), + 'lunit': obj.lunit}) return solid, ellipsoidName -def processGDMLElTubeObject(obj) : + +def processGDMLElTubeObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice eltubeName = nameOfGDMLobject(obj) - solid = ET.Element('eltube',{'name': eltubeName, \ - 'dx': str(obj.dx), \ - 'dy': str(obj.dy), \ - 'dz': str(obj.dz), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'eltube', {'name': eltubeName, + 'dx': str(obj.dx), + 'dy': str(obj.dy), + 'dz': str(obj.dz), + 'lunit': obj.lunit}) return solid, eltubeName -def processGDMLHypeObject(obj) : +def processGDMLHypeObject(obj): # Needs unique Name - # Remove leading GDMLTube_ from name on export - hypeName = nameOfGDMLobject(obj) - solid = ET.Element('hype',{'name': hypeName, \ - 'rmin': str(obj.rmin), \ - 'rmax': str(obj.rmax), \ - 'z': str(obj.z), \ - 'inst': str(obj.inst), \ - 'outst': str(obj.outst), \ - 'aunit': obj.aunit, \ - 'lunit' : obj.lunit}) - # modif 'mm' -> obj.lunit - return solid, hypeName - -def processGDMLParaboloidObject(obj) : + # Remove leading GDMLTube_ from name on export + hypeName = nameOfGDMLobject(obj) + solid = ET.SubElement(solids, 'hype', {'name': hypeName, + 'rmin': str(obj.rmin), + 'rmax': str(obj.rmax), + 'z': str(obj.z), + 'inst': str(obj.inst), + 'outst': str(obj.outst), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) + # modif 'mm' -> obj.lunit + return solid, hypeName + + +def processGDMLParaboloidObject(obj): # Needs unique Name - # Remove leading GDMLTube_ from name on export - solidName = nameOfGDMLobject(obj) - solid = ET.Element('paraboloid',{'name': solidName, \ - 'rlo': str(obj.rlo), \ - 'rhi': str(obj.rhi), \ - 'dz': str(obj.dz), \ - 'lunit' : obj.lunit}) - # modif 'mm' -> obj.lunit - return solid, solidName - -def processGDMLOrbObject(obj) : + # Remove leading GDMLTube_ from name on export + solidName = nameOfGDMLobject(obj) + solid = ET.SubElement(solids, 'paraboloid', {'name': solidName, + 'rlo': str(obj.rlo), + 'rhi': str(obj.rhi), + 'dz': str(obj.dz), + 'lunit': obj.lunit}) + # modif 'mm' -> obj.lunit + return solid, solidName + + +def processGDMLOrbObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice orbName = nameOfGDMLobject(obj) - solid = ET.Element('orb',{'name': orbName, \ - 'r': str(obj.r), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'orb', {'name': orbName, + 'r': str(obj.r), + 'lunit': obj.lunit}) return solid, orbName -def processGDMLParaObject(obj) : + +def processGDMLParaObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice paraName = nameOfGDMLobject(obj) - solid = ET.Element('para',{'name': paraName, \ - 'x': str(obj.x), \ - 'y': str(obj.y), \ - 'z': str(obj.z), \ - 'alpha':str(obj.alpha), \ - 'theta':str(obj.theta), \ - 'phi':str(obj.phi), \ - 'aunit':str(obj.aunit), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'para', {'name': paraName, + 'x': str(obj.x), + 'y': str(obj.y), + 'z': str(obj.z), + 'alpha': str(obj.alpha), + 'theta': str(obj.theta), + 'phi': str(obj.phi), + 'aunit': str(obj.aunit), + 'lunit': obj.lunit}) return solid, paraName -def processGDMLPolyconeObject(obj) : + +def processGDMLPolyconeObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice - #polyconeName = 'Cone' + obj.Name + # polyconeName = 'Cone' + obj.Name polyconeName = nameOfGDMLobject(obj) - cone = ET.Element('polycone',{'name': polyconeName, \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, \ - 'lunit' : obj.lunit }) - for zplane in obj.OutList : - ET.SubElement(cone, 'zplane',{'rmin': str(zplane.rmin), \ - 'rmax' : str(zplane.rmax), \ - 'z' : str(zplane.z)}) + cone = ET.SubElement(solids, 'polycone', {'name': polyconeName, + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) + for zplane in obj.OutList: + ET.SubElement(cone, 'zplane', {'rmin': str(zplane.rmin), + 'rmax': str(zplane.rmax), + 'z': str(zplane.z)}) return cone, polyconeName -def processGDMLGenericPolyconeObject(obj) : + +def processGDMLGenericPolyconeObject(obj): polyconeName = nameOfGDMLobject(obj) - cone = ET.Element('genericPolycone',{'name': polyconeName, \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, \ - 'lunit' : obj.lunit }) - for rzpoint in obj.OutList : - ET.SubElement(cone, 'rzpoint',{'r': str(rzpoint.r), \ - 'z' : str(rzpoint.z)}) + cone = ET.SubElement(solids, 'genericPolycone', { + 'name': polyconeName, + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) + for rzpoint in obj.OutList: + ET.SubElement(cone, 'rzpoint', {'r': str(rzpoint.r), + 'z': str(rzpoint.z)}) return cone, polyconeName -def processGDMLGenericPolyhedraObject(obj) : + +def processGDMLGenericPolyhedraObject(obj): polyhedraName = nameOfGDMLobject(obj) - polyhedra = ET.Element('genericPolyhedra',{'name': polyhedraName, \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'numsides': str(obj.numsides), \ - 'aunit': obj.aunit, \ - 'lunit' : obj.lunit }) - for rzpoint in obj.OutList : - ET.SubElement(polyhedra, 'rzpoint',{'r': str(rzpoint.r), \ - 'z' : str(rzpoint.z)}) + polyhedra = ET.SubElement(solids, 'genericPolyhedra', { + 'name': polyhedraName, + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'numsides': str(obj.numsides), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) + for rzpoint in obj.OutList: + ET.SubElement(polyhedra, 'rzpoint', {'r': str(rzpoint.r), + 'z': str(rzpoint.z)}) return polyhedra, polyhedraName -def processGDMLPolyhedraObject(obj) : + +def processGDMLPolyhedraObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice - #polyconeName = 'Cone' + obj.Name + # polyconeName = 'Cone' + obj.Name GDMLShared.trace('export Polyhedra') polyhedraName = nameOfGDMLobject(obj) - poly = ET.Element('polyhedra',{'name': polyhedraName, \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'numsides': str(obj.numsides), \ - 'aunit': obj.aunit, \ - 'lunit' : obj.lunit }) - for zplane in obj.OutList : - ET.SubElement(poly, 'zplane',{'rmin': str(zplane.rmin), \ - 'rmax' : str(zplane.rmax), \ - 'z' : str(zplane.z)}) + poly = ET.SubElement(solids, 'polyhedra', {'name': polyhedraName, + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'numsides': str(obj.numsides), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) + for zplane in obj.OutList: + ET.SubElement(poly, 'zplane', {'rmin': str(zplane.rmin), + 'rmax': str(zplane.rmax), + 'z': str(zplane.z)}) return poly, polyhedraName -def processGDMLQuadObject(obj) : + +def processGDMLQuadObject(obj): GDMLShared.trace("GDMLQuadrangular") - ET.SubElement(solids,'quadrangular',{'vertex1': obj.v1, \ - 'vertex2': obj.v2, 'vertex3': obj.v3, 'vertex4': obj.v4, \ - 'type': obj.vtype}) - -def processGDMLSphereObject(obj) : + ET.SubElement(solids, 'quadrangular', {'vertex1': obj.v1, + 'vertex2': obj.v2, + 'vertex3': obj.v3, + 'vertex4': obj.v4, + 'type': obj.vtype}) + + +def processGDMLSphereObject(obj): # Needs unique Name sphereName = nameOfGDMLobject(obj) - - solid = ET.Element('sphere',{'name': sphereName, - 'rmin': str(obj.rmin), - 'rmax': str(obj.rmax), - 'startphi': str(obj.startphi), - 'deltaphi': str(obj.deltaphi), - 'starttheta': str(obj.starttheta), - 'deltatheta': str(obj.deltatheta), - 'aunit': obj.aunit, - 'lunit' : obj.lunit}) + + solid = ET.SubElement(solids, 'sphere', {'name': sphereName, + 'rmin': str(obj.rmin), + 'rmax': str(obj.rmax), + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'starttheta': str(obj.starttheta), + 'deltatheta': str(obj.deltatheta), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) return solid, sphereName -def processGDMLTessellatedObject(obj) : + +def processGDMLTessellatedObject(obj): # Needs unique Name # Need to output unique define positions # Need to create set of positions - tessName = nameOfGDMLobject(obj) + tessName = nameOfGDMLobject(obj) # Use more readable version tessVname = tessName + '_' - #print(dir(obj)) + # print(dir(obj)) vertexHashcodeDict = {} - - tess = ET.Element('tessellated',{'name': tessName}) - for i, v in enumerate(obj.Shape.Vertexes) : - vertexHashcodeDict[v.hashCode()] = i - exportDefineVertex(tessVname,v,i) - - for f in obj.Shape.Faces : - #print(f'Normal at : {n} dot {dot} {clockWise}') + + tess = ET.SubElement(solids, 'tessellated', {'name': tessName}) + for i, v in enumerate(obj.Shape.Vertexes): + vertexHashcodeDict[v.hashCode()] = i + exportDefineVertex(tessVname, v, i) + + for f in obj.Shape.Faces: + # print(f'Normal at : {n} dot {dot} {clockWise}') vertexes = f.OuterWire.OrderedVertexes - if len(f.Edges) == 3 : - i0 = vertexHashcodeDict[vertexes[0].hashCode()] - i1 = vertexHashcodeDict[vertexes[1].hashCode()] - i2 = vertexHashcodeDict[vertexes[2].hashCode()] - ET.SubElement(tess,'triangular',{ \ - 'vertex1': tessVname+str(i0), \ - 'vertex2': tessVname+str(i1), \ - 'vertex3': tessVname+str(i2), \ - 'type':'ABSOLUTE'}) - elif len(f.Edges) == 4 : - i3 = vertexHashcodeDict[vertexes[3].hashCode()] - ET.SubElement(tess,'quadrangular',{ \ - 'vertex1': tessVname+str(i0), \ - 'vertex2': tessVname+str(i1), \ - 'vertex3': tessVname+str(i2), \ - 'vertex4': tessVname+str(i3), \ - 'type':'ABSOLUTE'}) + i0 = vertexHashcodeDict[vertexes[0].hashCode()] + i1 = vertexHashcodeDict[vertexes[1].hashCode()] + i2 = vertexHashcodeDict[vertexes[2].hashCode()] + if len(f.Edges) == 3: + ET.SubElement(tess, 'triangular', { + 'vertex1': tessVname+str(i0), + 'vertex2': tessVname+str(i1), + 'vertex3': tessVname+str(i2), + 'type': 'ABSOLUTE'}) + elif len(f.Edges) == 4: + i3 = vertexHashcodeDict[vertexes[3].hashCode()] + ET.SubElement(tess, 'quadrangular', { + 'vertex1': tessVname+str(i0), + 'vertex2': tessVname+str(i1), + 'vertex3': tessVname+str(i2), + 'vertex4': tessVname+str(i3), + 'type': 'ABSOLUTE'}) return tess, tessName -def processGDMLTetraObject(obj) : + +def processGDMLTetraObject(obj): tetraName = nameOfGDMLobject(obj) v1Name = tetraName + 'v1' v2Name = tetraName + 'v2' v3Name = tetraName + 'v3' v4Name = tetraName + 'v4' - exportDefine(v1Name,obj.v1) - exportDefine(v2Name,obj.v2) - exportDefine(v3Name,obj.v3) - exportDefine(v4Name,obj.v4) - - tetra = ET.Element('tet',{'name': tetraName, \ - 'vertex1': v1Name, \ - 'vertex2': v2Name, \ - 'vertex3': v3Name, \ - 'vertex4': v4Name}) - return tetra, tetraName - -def processGDMLTetrahedronObject(obj) : + exportDefine(v1Name, obj.v1) + exportDefine(v2Name, obj.v2) + exportDefine(v3Name, obj.v3) + exportDefine(v4Name, obj.v4) + + tetra = ET.SubElement(solids, 'tet', {'name': tetraName, + 'vertex1': v1Name, + 'vertex2': v2Name, + 'vertex3': v3Name, + 'vertex4': v4Name}) + return tetra, tetraName + + +def processGDMLTetrahedronObject(obj): global structure global solids tetrahedronName = nameOfGDMLobject(obj) - print('Len Tet'+str(len(obj.Proxy.Tetra))) + print('Len Tet' + str(len(obj.Proxy.Tetra))) count = 0 - for t in obj.Proxy.Tetra : - tetraName = 'Tetra_'+str(count) + for t in obj.Proxy.Tetra: + tetraName = 'Tetra_' + str(count) v1Name = tetraName + 'v1' v2Name = tetraName + 'v2' v3Name = tetraName + 'v3' v4Name = tetraName + 'v4' - exportDefine(v1Name,t[0]) - exportDefine(v2Name,t[1]) - exportDefine(v3Name,t[2]) - exportDefine(v4Name,t[3]) - tetsolid = ET.SubElement(solids,'tet',{'name': tetraName, \ - 'vertex1': v1Name, \ - 'vertex2': v2Name, \ - 'vertex3': v3Name, \ - 'vertex4': v4Name}) - lvName = 'LVtetra'+str(count) - lvol = ET.SubElement(structure,'volume', {'name':lvName}) + exportDefine(v1Name, t[0]) + exportDefine(v2Name, t[1]) + exportDefine(v3Name, t[2]) + exportDefine(v4Name, t[3]) + ET.SubElement(solids, 'tet', {'name': tetraName, + 'vertex1': v1Name, + 'vertex2': v2Name, + 'vertex3': v3Name, + 'vertex4': v4Name}) + lvName = 'LVtetra' + str(count) + lvol = ET.SubElement(structure, 'volume', {'name': lvName}) ET.SubElement(lvol, 'materialref', {'ref': obj.material}) ET.SubElement(lvol, 'solidref', {'ref': tetraName}) count += 1 # Now put out Assembly - assembly = ET.SubElement(structure, 'assembly',{'name':tetrahedronName}) + assembly = ET.SubElement(structure, 'assembly', {'name': tetrahedronName}) count = 0 - for t in obj.Proxy.Tetra : - lvName = 'LVtetra'+str(count) + for t in obj.Proxy.Tetra: + lvName = 'LVtetra' + str(count) physvol = ET.SubElement(assembly, 'physvol') - ET.SubElement(physvol, 'volumeref', {'ref':lvName}) - #ET.SubElement(physvol, 'position') - #ET.SubElement(physvol, 'rotation') + ET.SubElement(physvol, 'volumeref', {'ref': lvName}) + # ET.SubElement(physvol, 'position') + # ET.SubElement(physvol, 'rotation') count += 1 - return assembly, tetrahedronName + return assembly, tetrahedronName + -def processGDMLTorusObject(obj) : +def processGDMLTorusObject(obj): torusName = nameOfGDMLobject(obj) - torus = ET.Element('torus',{'name': torusName, - 'rmin': str(obj.rmin), \ - 'rmax': str(obj.rmax), \ - 'rtor': str(obj.rtor), \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, \ - 'lunit': obj.lunit}) + print(f'Torus: {torusName}') + torus = ET.SubElement(solids, 'torus', {'name': torusName, + 'rmin': str(obj.rmin), + 'rmax': str(obj.rmax), + 'rtor': str(obj.rtor), + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) return torus, torusName -def processGDMLTrapObject(obj) : + +def processGDMLTrapObject(obj): # Needs unique Name trapName = nameOfGDMLobject(obj) - trap = ET.Element('trap',{'name': trapName, \ - 'z': str(obj.z), \ - 'theta': str(obj.theta), \ - 'phi': str(obj.phi), \ - 'x1': str(obj.x1), \ - 'x2': str(obj.x2), \ - 'x3': str(obj.x3), \ - 'x4': str(obj.x4), \ - 'y1': str(obj.y1), \ - 'y2': str(obj.y2), \ - 'alpha1': str(obj.alpha), \ - 'alpha2': str(obj.alpha), \ - 'aunit': obj.aunit, \ - 'lunit': obj.lunit}) + trap = ET.SubElement(solids, 'trap', {'name': trapName, + 'z': str(obj.z), + 'theta': str(obj.theta), + 'phi': str(obj.phi), + 'x1': str(obj.x1), + 'x2': str(obj.x2), + 'x3': str(obj.x3), + 'x4': str(obj.x4), + 'y1': str(obj.y1), + 'y2': str(obj.y2), + 'alpha1': str(obj.alpha), + 'alpha2': str(obj.alpha), + 'aunit': obj.aunit, + 'lunit': obj.lunit}) return trap, trapName -def processGDMLTrdObject(obj) : + +def processGDMLTrdObject(obj): # Needs unique Name trdName = nameOfGDMLobject(obj) - trd = ET.Element('trd',{'name': trdName, \ - 'z': str(obj.z), \ - 'x1': str(obj.x1), \ - 'x2': str(obj.x2), \ - 'y1': str(obj.y1), \ - 'y2': str(obj.y2), \ - 'lunit': obj.lunit}) + trd = ET.SubElement(solids, 'trd', {'name': trdName, + 'z': str(obj.z), + 'x1': str(obj.x1), + 'x2': str(obj.x2), + 'y1': str(obj.y1), + 'y2': str(obj.y2), + 'lunit': obj.lunit}) return trd, trdName -def processGDMLTriangle(obj) : - #print("Process GDML Triangle") - ET.SubElement(solids,'triangular',{'vertex1': obj.v1, \ - 'vertex2': obj.v2, 'vertex3': obj.v3, \ - 'type': obj.vtype}) +def processGDMLTriangle(obj): + # print("Process GDML Triangle") + ET.SubElement(solids, 'triangular', {'vertex1': obj.v1, + 'vertex2': obj.v2, 'vertex3': obj.v3, + 'type': obj.vtype}) -def processGDMLTubeObject(obj) : + +def processGDMLTubeObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice tubeName = nameOfGDMLobject(obj) - tube = ET.Element('tube',{'name': tubeName, \ - 'rmin': str(obj.rmin), \ - 'rmax': str(obj.rmax), \ - 'startphi': str(obj.startphi), \ - 'deltaphi': str(obj.deltaphi), \ - 'aunit': obj.aunit, - 'z': str(obj.z), \ - 'lunit' : obj.lunit}) + print(f'Tube: {tubeName}') + tube = ET.SubElement(solids, 'tube', {'name': tubeName, + 'rmin': str(obj.rmin), + 'rmax': str(obj.rmax), + 'startphi': str(obj.startphi), + 'deltaphi': str(obj.deltaphi), + 'aunit': obj.aunit, + 'z': str(obj.z), + 'lunit': obj.lunit}) return tube, tubeName -def processGDMLTwistedboxObject(obj) : - - solidName = nameOfGDMLobject(obj) - - solid = ET.Element('twistedbox',{'name': solidName, \ - 'PhiTwist': str(obj.PhiTwist), \ - 'x': str(obj.x), \ - 'y': str(obj.y), \ - 'z': str(obj.z), \ - 'aunit': str(obj.aunit), \ - 'lunit' : obj.lunit}) + +def processGDMLTwistedboxObject(obj): + + solidName = nameOfGDMLobject(obj) + + solid = ET.SubElement(solids, 'twistedbox', {'name': solidName, + 'PhiTwist': str(obj.PhiTwist), + 'x': str(obj.x), + 'y': str(obj.y), + 'z': str(obj.z), + 'aunit': str(obj.aunit), + 'lunit': obj.lunit}) return solid, solidName -def processGDMLTwistedtrdObject(obj) : + +def processGDMLTwistedtrdObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice solidName = nameOfGDMLobject(obj) - solid = ET.Element('twistedtrd',{'name': solidName, \ - 'PhiTwist':str(obj.PhiTwist), \ - 'x1': str(obj.x1), \ - 'x2': str(obj.x2), \ - 'y1': str(obj.y1), \ - 'y2': str(obj.y2), \ - 'z': str(obj.z), \ - 'aunit': str(obj.aunit), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'twistedtrd', {'name': solidName, + 'PhiTwist': str(obj.PhiTwist), + 'x1': str(obj.x1), + 'x2': str(obj.x2), + 'y1': str(obj.y1), + 'y2': str(obj.y2), + 'z': str(obj.z), + 'aunit': str(obj.aunit), + 'lunit': obj.lunit}) return solid, solidName -def processGDMLTwistedtrapObject(obj) : + +def processGDMLTwistedtrapObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice solidName = nameOfGDMLobject(obj) - solid = ET.Element('twistedtrap',{'name': solidName, \ - 'PhiTwist':str(obj.PhiTwist), \ - 'x1': str(obj.x1), \ - 'x2': str(obj.x2), \ - 'y1': str(obj.y1), \ - 'y2': str(obj.y2), \ - 'x3': str(obj.x3), \ - 'x4': str(obj.x4), \ - 'z': str(obj.z), \ - 'Theta': str(obj.Theta), \ - 'Phi': str(obj.Phi), \ - 'Alph': str(obj.Alph), \ - 'aunit': str(obj.aunit), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'twistedtrap', {'name': solidName, + 'PhiTwist': str(obj.PhiTwist), + 'x1': str(obj.x1), + 'x2': str(obj.x2), + 'y1': str(obj.y1), + 'y2': str(obj.y2), + 'x3': str(obj.x3), + 'x4': str(obj.x4), + 'z': str(obj.z), + 'Theta': str(obj.Theta), + 'Phi': str(obj.Phi), + 'Alph': str(obj.Alph), + 'aunit': str(obj.aunit), + 'lunit': obj.lunit}) return solid, solidName -def processGDMLTwistedtubsObject(obj) : + +def processGDMLTwistedtubsObject(obj): # Needs unique Name # flag needed for boolean otherwise parse twice solidName = nameOfGDMLobject(obj) - solid = ET.Element('twistedtubs',{'name': solidName, \ - 'twistedangle':str(obj.twistedangle), \ - 'endinnerrad': str(obj.endinnerrad), \ - 'endouterrad': str(obj.endouterrad), \ - 'zlen': str(obj.zlen), \ - 'phi': str(obj.phi), \ - 'aunit': str(obj.aunit), \ - 'lunit' : obj.lunit}) + solid = ET.SubElement(solids, 'twistedtubs', { + 'name': solidName, + 'twistedangle': str(obj.twistedangle), + 'endinnerrad': str(obj.endinnerrad), + 'endouterrad': str(obj.endouterrad), + 'zlen': str(obj.zlen), + 'phi': str(obj.phi), + 'aunit': str(obj.aunit), + 'lunit': obj.lunit}) return solid, solidName -def processGDMLXtruObject(obj) : + +def processGDMLXtruObject(obj): # Needs unique Name xtruName = nameOfGDMLobject(obj) - xtru = ET.Element('xtru',{'name': xtruName, \ - 'lunit' : obj.lunit}) - for items in obj.OutList : - if items.Type == 'twoDimVertex' : - ET.SubElement(xtru, 'twoDimVertex',{'x': str(items.x), \ - 'y': str(items.y)}) - if items.Type == 'section' : - ET.SubElement(xtru, 'section',{'zOrder': str(items.zOrder), \ - 'zPosition': str(items.zPosition), \ - 'xOffset' : str(items.xOffset), \ - 'yOffset' : str(items.yOffset), \ - 'scalingFactor' : str(items.scalingFactor)}) + xtru = ET.SubElement(solids, 'xtru', {'name': xtruName, + 'lunit': obj.lunit}) + for items in obj.OutList: + if items.Type == 'twoDimVertex': + ET.SubElement(xtru, 'twoDimVertex', {'x': str(items.x), + 'y': str(items.y)}) + if items.Type == 'section': + ET.SubElement(xtru, 'section', { + 'zOrder': str(items.zOrder), + 'zPosition': str(items.zPosition), + 'xOffset': str(items.xOffset), + 'yOffset': str(items.yOffset), + 'scalingFactor': str(items.scalingFactor)}) return xtru, xtruName -def processGDML2dVertex(obj) : - #print("Process 2d Vertex") - ET.SubElement(solids, 'twoDimVertex',{'x': obj.x, 'y': obj.y}) -def processIsotope(obj, item): # maybe part of material or element (common code) - if hasattr(obj,'Z') : - #print(dir(obj)) - item.set('Z',str(obj.Z)) +def processGDML2dVertex(obj): + # print("Process 2d Vertex") + ET.SubElement(solids, 'twoDimVertex', {'x': obj.x, 'y': obj.y}) - if hasattr(obj,'N') : - #print(dir(obj)) - item.set('N',str(obj.N)) - if hasattr(obj,'formula') : - #print(dir(obj)) - item.set('formula',str(obj.formula)) +def processIsotope(obj, item): # maybe part of material or element (common code) + if hasattr(obj, 'Z'): + # print(dir(obj)) + item.set('Z', str(obj.Z)) - if hasattr(obj,'unit') or hasattr(obj,'atom_value') or hasattr(obj,'value') : - atom = ET.SubElement(item,'atom') - - if hasattr(obj,'unit') : - atom.set('unit',str(obj.unit)) - - if hasattr(obj,'type') : - atom.set('unit',str(obj.type)) - - if hasattr(obj,'atom_value') : - atom.set('value',str(obj.atom_value)) + if hasattr(obj, 'N'): + # print(dir(obj)) + item.set('N', str(obj.N)) + + if hasattr(obj, 'formula'): + # print(dir(obj)) + item.set('formula', str(obj.formula)) - if hasattr(obj,'value') : - atom.set('value',str(obj.value)) + if hasattr(obj, 'unit') or hasattr(obj, 'atom_value') or \ + hasattr(obj, 'value'): + atom = ET.SubElement(item, 'atom') -def processMaterials() : + if hasattr(obj, 'unit'): + atom.set('unit', str(obj.unit)) + + if hasattr(obj, 'type'): + atom.set('unit', str(obj.type)) + + if hasattr(obj, 'atom_value'): + atom.set('value', str(obj.atom_value)) + + if hasattr(obj, 'value'): + atom.set('value', str(obj.value)) + + +def processMaterials(): print("\nProcess Materials") global materials - - for GName in ['Constants','Variables','Isotopes','Elements','Materials'] : + + for GName in ['Constants', 'Variables', + 'Isotopes', 'Elements', 'Materials']: Grp = FreeCAD.ActiveDocument.getObject(GName) - if Grp is not None : - #print(Grp.TypeId+" : "+Grp.Name) - print(Grp.Label) - if processGroup(Grp) == False : - break + if Grp is not None: + # print(Grp.TypeId+" : "+Grp.Name) + print(Grp.Label) + if processGroup(Grp) is False: + break -def processFractionsComposites(obj, item) : + +def processFractionsComposites(obj, item): # Fractions are used in Material and Elements - if isinstance(obj.Proxy,GDMLfraction) : - #print("GDML fraction :" + obj.Label) + if isinstance(obj.Proxy, GDMLfraction): + # print("GDML fraction :" + obj.Label) # need to strip number making it unique - ET.SubElement(item,'fraction',{'n': str(obj.n), \ - 'ref': nameFromLabel(obj.Label)}) + ET.SubElement(item, 'fraction', {'n': str(obj.n), + 'ref': nameFromLabel(obj.Label)}) + + if isinstance(obj.Proxy, GDMLcomposite): + # print("GDML Composite") + ET.SubElement(item, 'composite', {'n': str(obj.n), + 'ref': nameFromLabel(obj.Label)}) - if isinstance(obj.Proxy,GDMLcomposite) : - #print("GDML Composite") - ET.SubElement(item,'composite',{'n': str(obj.n), \ - 'ref': nameFromLabel(obj.Label)}) def createMaterials(group): global materials - for obj in group : + for obj in group: if obj.Label != 'Geant4': - item = ET.SubElement(materials,'material',{'name': \ - nameFromLabel(obj.Label)}) - - # Dunit & Dvalue must be first for Geant4 - if hasattr(obj,'Dunit') or hasattr(obj,'Dvalue') : - #print("Dunit or DValue") - D = ET.SubElement(item,'D') - if hasattr(obj,'Dunit') : - D.set('unit',str(obj.Dunit)) - - if hasattr(obj,'Dvalue') : - D.set('value',str(obj.Dvalue)) - - if hasattr(obj,'Tunit') and hasattr(obj,'Tvalue') : - ET.SubElement(item,'T',{'unit': obj.Tunit, \ - 'value': str(obj.Tvalue)}) - - if hasattr(obj,'MEEunit') : - ET.SubElement(item,'MEE',{'unit': obj.MEEunit, \ - 'value': str(obj.MEEvalue)}) - # process common options material / element - processIsotope(obj, item) - if len(obj.Group) > 0 : - for o in obj.Group : - processFractionsComposites(o,item) - -def createElements(group) : + item = ET.SubElement(materials, 'material', { + 'name': + nameFromLabel(obj.Label)}) + + # Dunit & Dvalue must be first for Geant4 + if hasattr(obj, 'Dunit') or hasattr(obj, 'Dvalue'): + # print("Dunit or DValue") + D = ET.SubElement(item, 'D') + if hasattr(obj, 'Dunit'): + D.set('unit', str(obj.Dunit)) + + if hasattr(obj, 'Dvalue'): + D.set('value', str(obj.Dvalue)) + + if hasattr(obj, 'Tunit') and hasattr(obj, 'Tvalue'): + ET.SubElement(item, 'T', {'unit': obj.Tunit, + 'value': str(obj.Tvalue)}) + + if hasattr(obj, 'MEEunit'): + ET.SubElement(item, 'MEE', {'unit': obj.MEEunit, + 'value': str(obj.MEEvalue)}) + # process common options material / element + processIsotope(obj, item) + if len(obj.Group) > 0: + for o in obj.Group: + processFractionsComposites(o, item) + + +def createElements(group): global materials - for obj in group : - #print(f'Element : {obj.Label}') - item = ET.SubElement(materials,'element',{'name': \ - nameFromLabel(obj.Label)}) + for obj in group: + # print(f'Element : {obj.Label}') + item = ET.SubElement(materials, 'element', { + 'name': nameFromLabel(obj.Label)}) # Common code IsoTope and Elements1 processIsotope(obj, item) - if len(obj.Group) > 0 : - for o in obj.Group : - processFractionsComposites(o,item) + if len(obj.Group) > 0: + for o in obj.Group: + processFractionsComposites(o, item) -def createConstants(group) : + +def createConstants(group): global define - for obj in group : - if isinstance(obj.Proxy,GDMLconstant) : - #print("GDML constant") - #print(dir(obj)) + for obj in group: + if isinstance(obj.Proxy, GDMLconstant): + # print("GDML constant") + # print(dir(obj)) + + ET.SubElement(define, 'constant', {'name': obj.Label, + 'value': obj.value}) - item = ET.SubElement(define,'constant',{'name': obj.Label, \ - 'value': obj.value }) -def createVariables(group) : +def createVariables(group): global define - for obj in group : - if isinstance(obj.Proxy,GDMLvariable) : - #print("GDML variable") - #print(dir(obj)) - - item = ET.SubElement(define,'variable',{'name': obj.Label, \ - 'value': obj.value }) -def createQuantities(group) : + for obj in group: + if isinstance(obj.Proxy, GDMLvariable): + # print("GDML variable") + # print(dir(obj)) + + ET.SubElement(define, 'variable', {'name': obj.Label, + 'value': obj.value}) + + +def createQuantities(group): global define - for obj in group : - if isinstance(obj.Proxy,GDMLquantity) : - #print("GDML quantity") - #print(dir(obj)) + for obj in group: + if isinstance(obj.Proxy, GDMLquantity): + # print("GDML quantity") + # print(dir(obj)) - item = ET.SubElement(define,'quantity',{'name': obj.Label, \ - 'type': obj.type, \ - 'unit': obj.unit, \ - 'value': obj.value }) + ET.SubElement(define, 'quantity', {'name': obj.Label, + 'type': obj.type, + 'unit': obj.unit, + 'value': obj.value}) -def createIsotopes(group) : + +def createIsotopes(group): global materials - for obj in group : - if isinstance(obj.Proxy,GDMLisotope) : - #print("GDML isotope") - #item = ET.SubElement(materials,'isotope',{'N': str(obj.N), \ - # 'Z': str(obj.Z), \ - # 'name' : obj.Label}) - #ET.SubElement(item,'atom',{'unit': obj.unit, \ - # 'value': str(obj.value)}) - item = ET.SubElement(materials,'isotope',{'name' : obj.Label}) - processIsotope(obj,item) - -def processGroup(obj) : + for obj in group: + if isinstance(obj.Proxy, GDMLisotope): + # print("GDML isotope") + # item = ET.SubElement(materials,'isotope',{'N': str(obj.N), \ + # 'Z': str(obj.Z), \ + # 'name' : obj.Label}) + # ET.SubElement(item,'atom',{'unit': obj.unit, \ + # 'value': str(obj.value)}) + item = ET.SubElement(materials, 'isotope', {'name': obj.Label}) + processIsotope(obj, item) + + +def processGroup(obj): print('Process Group '+obj.Label) - #print(obj.TypeId) - #print(obj.Group) + # print(obj.TypeId) + # print(obj.Group) # if hasattr(obj,'Group') : - #return - if hasattr(obj,'Group') : - #print(" Object List : "+obj.Label) - #print(obj) - while switch(obj.Name) : - if case("Constants") : - #print("Constants") + # return + if hasattr(obj, 'Group'): + # print(" Object List : "+obj.Label) + # print(obj) + while switch(obj.Name): + if case("Constants"): + # print("Constants") createConstants(obj.Group) break - if case("Variables") : - #print("Variables") + if case("Variables"): + # print("Variables") createVariables(obj.Group) break - if case("Quantities") : - #print("Quantities") + if case("Quantities"): + # print("Quantities") createQuantities(obj.Group) break - if case("Isotopes") : - #print("Isotopes") + if case("Isotopes"): + # print("Isotopes") createIsotopes(obj.Group) break - - if case("Elements") : - #print("Elements") + + if case("Elements"): + # print("Elements") createElements(obj.Group) break - - if case("Materials") : + + if case("Materials"): print("Materials") createMaterials(obj.Group) break - if case("Geant4") : + if case("Geant4"): # Do not export predefine in Geant4 print("Geant4") break -def processGDMLSolid(obj) : + +def processGDMLSolid(obj): # Deal with GDML Solids first # Deal with FC Objects that convert - #print(dir(obj)) - #print(dir(obj.Proxy)) + # print(dir(obj)) + # print(dir(obj.Proxy)) print(obj.Proxy.Type) - while switch(obj.Proxy.Type) : - if case("GDMLArb8") : - #print(" GDMLArb8") - solid, name = processGDMLArb8Object(obj) - break - - if case("GDMLBox") : - #print(" GDMLBox") - solid, name = processGDMLBoxObject(obj) - break - - if case("GDMLCone") : - #print(" GDMLCone") - solid, name = processGDMLConeObject(obj) - break - - if case("GDMLcutTube") : - #print(" GDMLcutTube") - solid, name = processGDMLCutTubeObject(obj) - break - - if case("GDMLElCone") : - #print(" GDMLElCone") - solid, name = processGDMLElConeObject(obj) - break - - if case("GDMLEllipsoid") : - #print(" GDMLEllipsoid") - solid, name = processGDMLEllipsoidObject(obj) - break - - if case("GDMLElTube") : - #print(" GDMLElTube") - solid, name = processGDMLElTubeObject(obj) - break - - if case("GDMLHype") : - #print(" GDMLHype") - solid, name = processGDMLHypeObject(obj) - break - - if case("GDMLOrb") : - #print(" GDMLOrb") - solid, name = processGDMLOrbObject(obj) - break - - if case("GDMLPara") : - #print(" GDMLPara") - solid, name = processGDMLParaObject(obj) - break - - if case("GDMLParaboloid") : - #print(" GDMLParaboloid") - solid, name = processGDMLParaboloidObject(obj) - break - - if case("GDMLPolycone") : - #print(" GDMLPolycone") - solid, name = processGDMLPolyconeObject(obj) - break - - if case("GDMLGenericPolycone") : - #print(" GDMLGenericPolycone") - solid, name = processGDMLGenericPolyconeObject(obj) - break - - if case("GDMLPolyhedra") : - #print(" GDMLPolyhedra") - solid, name = processGDMLPolyhedraObject(obj) - break - - if case("GDMLGenericPolyhedra") : - #print(" GDMLPolyhedra") - solid, name = processGDMLGenericPolyhedraObject(obj) - break - - if case("GDMLSphere") : - #print(" GDMLSphere") - solid, name = processGDMLSphereObject(obj) - break - - if case("GDMLTessellated") : - #print(" GDMLTessellated") - solid, name = processGDMLTessellatedObject(obj) - break - - if case("GDMLGmshTessellated") : - #print(" GDMLGmshTessellated") - # export GDMLTessellated & GDMLGmshTesssellated should be the same - solid, name = processGDMLTessellatedObject(obj) - break - - if case("GDMLTetra") : - #print(" GDMLTetra") - solid, name = processGDMLTetraObject(obj) - break - - if case("GDMLTetrahedron") : - print(" GDMLTetrahedron") - solid, name = processGDMLTetrahedronObject(obj) - break - - if case("GDMLTorus") : - print(" GDMLTorus") - solid, name = processGDMLTorusObject(obj) - break - - if case("GDMLTrap") : - #print(" GDMLTrap") - solid, name = processGDMLTrapObject(obj) - break - - if case("GDMLTrd") : - #print(" GDMLTrd") - solid, name = processGDMLTrdObject(obj) - break - - if case("GDMLTube") : - #print(" GDMLTube") - solid, name = processGDMLTubeObject(obj) - break - - if case("GDMLTwistedbox") : - #print(" GDMLTwistedbox") - solid, name = processGDMLTwistedboxObject(obj) - break - - if case("GDMLTwistedtrap") : - #print(" GDMLTwistedtrap") - solid, name = processGDMLTwistedtrapObject(obj) - break - - if case("GDMLTwistedtrd") : - #print(" GDMLTwistedbox") - solid, name = processGDMLTwistedtrdObject(obj) - break - - if case("GDMLTwistedtubs") : - #print(" GDMLTwistedbox") - solid, name = processGDMLTwistedtubsObject(obj) - break - - if case("GDMLXtru") : - #print(" GDMLXtru") - solid, name = processGDMLXtruObject(obj) - break - - print("Not yet Handled") - break - solids.insert(0,solid) - -def processMuNod(xmlelem, name) : - node = ET.SubElement(xmlelem,'multiUnionNode',{'name' : name}) + while switch(obj.Proxy.Type): + if case("GDMLArb8"): + # print(" GDMLArb8") + return(processGDMLArb8Object(obj)) + + if case("GDMLBox"): + # print(" GDMLBox") + return(processGDMLBoxObject(obj)) + + if case("GDMLCone"): + # print(" GDMLCone") + return(processGDMLConeObject(obj)) + + if case("GDMLcutTube"): + # print(" GDMLcutTube") + return(processGDMLCutTubeObject(obj)) + + if case("GDMLElCone"): + # print(" GDMLElCone") + return(processGDMLElConeObject(obj)) + + if case("GDMLEllipsoid"): + # print(" GDMLEllipsoid") + return(processGDMLEllipsoidObject(obj)) + + if case("GDMLElTube"): + # print(" GDMLElTube") + return(processGDMLElTubeObject(obj)) + + if case("GDMLHype"): + # print(" GDMLHype") + return(processGDMLHypeObject(obj)) + + if case("GDMLOrb"): + # print(" GDMLOrb") + return(processGDMLOrbObject(obj)) + + if case("GDMLPara"): + # print(" GDMLPara") + return(processGDMLParaObject(obj)) + + if case("GDMLParaboloid"): + # print(" GDMLParaboloid") + return(processGDMLParaboloidObject(obj)) + + if case("GDMLPolycone"): + # print(" GDMLPolycone") + return(processGDMLPolyconeObject(obj)) + + if case("GDMLGenericPolycone"): + # print(" GDMLGenericPolycone") + return(processGDMLGenericPolyconeObject(obj)) + + if case("GDMLPolyhedra"): + # print(" GDMLPolyhedra") + return(processGDMLPolyhedraObject(obj)) + + if case("GDMLGenericPolyhedra"): + # print(" GDMLPolyhedra") + return(processGDMLGenericPolyhedraObject(obj)) + + if case("GDMLSphere"): + # print(" GDMLSphere") + return(processGDMLSphereObject(obj)) + + if case("GDMLTessellated"): + # print(" GDMLTessellated") + ret = processGDMLTessellatedObject(obj) + return ret + + if case("GDMLGmshTessellated"): + # print(" GDMLGmshTessellated") + # export GDMLTessellated & GDMLGmshTesssellated should be the same + return(processGDMLTessellatedObject(obj)) + + if case("GDMLTetra"): + # print(" GDMLTetra") + return(processGDMLTetraObject(obj)) + + if case("GDMLTetrahedron"): + print(" GDMLTetrahedron") + return(processGDMLTetrahedronObject(obj)) + + if case("GDMLTorus"): + print(" GDMLTorus") + return(processGDMLTorusObject(obj)) + + if case("GDMLTrap"): + # print(" GDMLTrap") + return(processGDMLTrapObject(obj)) + + if case("GDMLTrd"): + # print(" GDMLTrd") + return(processGDMLTrdObject(obj)) + + if case("GDMLTube"): + # print(" GDMLTube") + return(processGDMLTubeObject(obj)) + + if case("GDMLTwistedbox"): + # print(" GDMLTwistedbox") + return(processGDMLTwistedboxObject(obj)) + + if case("GDMLTwistedtrap"): + # print(" GDMLTwistedtrap") + return(processGDMLTwistedtrapObject(obj)) + + if case("GDMLTwistedtrd"): + # print(" GDMLTwistedbox") + return(processGDMLTwistedtrdObject(obj)) + + if case("GDMLTwistedtubs"): + # print(" GDMLTwistedbox") + return(processGDMLTwistedtubsObject(obj)) + + if case("GDMLXtru"): + # print(" GDMLXtru") + return(processGDMLXtruObject(obj)) + + print("Not yet Handled") + break + + +def processSolid(obj, addVolsFlag): + # export solid & return Name + # Needs to deal with Boolean solids + # separate from Boolean Objects + # return count, solidxml, solidName + # print('Process Solid') + while switch(obj.TypeId): + + if case("Part::FeaturePython"): + # print(" Python Feature") + # if hasattr(obj.Proxy, 'Type') : + # #print(obj.Proxy.Type) + # return(processGDMLSolid(obj)) + solidxml, solidName = processGDMLSolid(obj) + return solidxml, solidName + # + # Now deal with Boolean solids + # Note handle different from Bookean Objects + # that need volume, physvol etc + # i.e. just details needed to be added to Solids + # + if case("Part::MultiFuse"): + GDMLShared.trace("Multifuse - multiunion") + # test and fix + solidName = 'MultiFuse' + obj.Name + # First add solids in list before reference + print('Output Solids') + for sub in obj.OutList: + processSolid(sub, False) + GDMLShared.trace('Output Solids Complete') + multUnion = ET.SubElement(solids, 'multiUnion', { + 'name': solidName}) + num = 1 + + for sub in obj.OutList: + GDMLShared.trace(sub.Name) + node = processMuNod(multUnion, 'node-'+str(num)) + ET.SubElement(node, 'solid', {'ref': sub.Name}) + processPosition(sub, node) + processRotation(sub, node) + num += 1 + + GDMLShared.trace('Return MultiUnion') + # return idx + num + return solidName + + if case("Part::MultiCommon"): + print(" Multi Common / intersection") + print(" Not available in GDML") + exit(-3) + break + + # Now deal with objects that map to GDML solids + # + if case("Part::Box"): + print(" Box") + return(processBoxObject(obj, addVolsFlag)) + break + + if case("Part::Cylinder"): + print(" Cylinder") + return(processCylinderObject(obj, addVolsFlag)) + break + + if case("Part::Cone"): + print(" Cone") + return(processConeObject(obj, addVolsFlag)) + break + + if case("Part::Sphere"): + print(" Sphere") + return(processSphereObject(obj, addVolsFlag)) + break + + print(f'Part : {obj.Label}') + print(f'TypeId : {obj.TypeId}') + + +def processMuNod(xmlelem, name): + node = ET.SubElement(xmlelem, 'multiUnionNode', {'name': name}) return node + import collections from itertools import islice -def consume(iterator) : - next(islice(iterator,2,2), None) -def getXmlVolume(volObj) : +def consume(iterator): + next(islice(iterator, 2, 2), None) + + +def getXmlVolume(volObj): global structure - if volObj is None : - return None + if volObj is None: + return None xmlvol = structure.find("volume[@name='%s']" % volObj.Label) - if xmlvol is None : - print(volObj.Label+' Not Found') + if xmlvol is None: + print(volObj.Label+' Not Found') return xmlvol -def getCount(obj) : - GDMLShared.trace('get Count : '+obj.Name) - if hasattr(obj,'Tool') : - GDMLShared.trace('Has tool - check Base') - baseCnt = getCount(obj.Base) - toolCnt = getCount(obj.Tool) - GDMLShared.trace('Count is : '+str(baseCnt + toolCnt)) - return (baseCnt + toolCnt) - else : - return 1 -def getMaterial(obj) : +def getBooleanCount(obj): + GDMLShared.trace('get Count : ' + obj.Name) + if hasattr(obj, 'Tool'): + GDMLShared.trace('Has tool - check Base') + baseCnt = getBooleanCount(obj.Base) + toolCnt = getBooleanCount(obj.Tool) + GDMLShared.trace('Count is : ' + str(baseCnt + toolCnt)) + return (baseCnt + toolCnt) + else: + return 0 + + +def getMaterial(obj): GDMLShared.trace('get Material : '+obj.Label) - if hasattr(obj,'material') : - return obj.material - if hasattr(obj,'Tool') : - GDMLShared.trace('Has tool - check Base') - material = getMaterial(obj.Base) - return material - else : - return None + if hasattr(obj, 'material'): + return obj.material + if hasattr(obj, 'Tool'): + GDMLShared.trace('Has tool - check Base') + material = getMaterial(obj.Base) + return material + else: + return None -def printObjectInfo(xmlVol, volName, xmlParent, parentName) : + +''' +def printObjectInfo(xmlVol, volName, xmlParent, parentName): print("Process Object : "+obj.Label+' Type '+obj.TypeId) if xmlVol is not None : xmlstr = ET.tostring(xmlVol) @@ -1591,266 +1794,359 @@ def printObjectInfo(xmlVol, volName, xmlParent, parentName) : else : xmlstr = 'None' print('Parent : '+str(parentName)+' : '+str(xmlstr)) +''' + -def processObject(obj, xmlVol, volName, xmlParent, parentName) : +def isBoolean(obj): + id = obj.TypeId + return (id == "Part::Cut" or id == "Part::Fuse" or + id == "Part::Common") + + +def boolOperation(obj): + opsDict = {"Part::Cut": 'subtraction', + "Part::Fuse": 'union', + "Part::Common": 'intersection'} + if obj.TypeId in opsDict: + return opsDict[obj.TypeId] + else: + print(f'Boolean type {obj.TypId} not handled yet') + return None + + +def processBooleanObject(obj, xmlVol, volName, xmlParent, parentName): + ''' + In FreeCAD doc booleans that are themselves composed of other booleans + are listed in sequence, eg: + topBool: + Base: Nonbool_0 + Tool: bool1: + Base: bool2: + Base: Nonbool_1 + Tool: Nonbool_2 + Tool: bool3: + Base: Nonbool_3 + Tool: Nonbool_4 + In the gdml file, boolean solids must always refer to PREVIOUSLY defined + solids. So the last booleans must be written first: + + + + + + + + + + + The code below first builds the list of booleans in order: + [topBool, bool1, bool2, bool3] + + Then outputs them to gdml in reverse order. + In the process of scanning for booleans, the Nonbooleans are exported + + ''' + GDMLShared.trace('Process Boolean Object') + + boolsList = [obj] # list of booleans that are part of obj + # dynamic list the is used to figure out when we've iterated over all subobjects + # that are booleans + tmpList = [obj] + ref1 = {} # first solid reference of boolean + ref2 = {} # second solid reference of boolean + count = 1 # number of solids under this boolean + while(len(tmpList) > 0): + obj1 = tmpList.pop() + if isBoolean(obj1.Base): + tmpList.append(obj1.Base) + boolsList.append(obj1.Base) + ref1[obj1] = obj1.Base.Label + else: + solidxml, solidName = processSolid(obj1.Base, False) + ref1[obj1] = solidName + + if isBoolean(obj1.Tool): + tmpList.append(obj1.Tool) + boolsList.append(obj1.Tool) + ref2[obj1] = obj1.Tool.Label + else: + solidxml, solidName = processSolid(obj1.Tool, False) + ref2[obj1] = solidName + + count += len(obj1.Base.OutList) + len(obj1.Tool.OutList) + + # Now tmpList is empty and boolsList has list of all booleans + for obj1 in reversed(boolsList): + operation = boolOperation(obj1) + if operation is None: + continue + solidName = obj1.Label + boolXML = ET.SubElement(solids, str(operation), { + 'name': solidName}) + ET.SubElement(boolXML, 'first', {'ref': ref1[obj1]}) + ET.SubElement(boolXML, 'second', {'ref': ref2[obj1]}) + # process position & rotationt + processPosition(obj1.Tool, boolXML) + # For booleans, gdml want actual rotation, not reverse + # processRotation export negative of rotation angle(s) + # This is ugly way of NOT reversing angle: + angle = obj1.Tool.Placement.Rotation.Angle + obj1.Tool.Placement.Rotation.Angle = -angle + processRotation(obj1.Tool, boolXML) + obj1.Tool.Placement.Rotation.Angle = angle + + # The material and colour are those of the Base of the boolean + # the solidName is that of the LAST solid in the above loop. Since + # the boolList is traversed in reverse order, this is the topmost boolean + addVolRef(xmlVol, volName, obj) + + return 2 + count + + +def exportCone(name, radius, height): + cylEl = ET.SubElement(solids, 'cone', {'name': name, + 'rmin1': '0', + 'rmax1': str(radius), + 'rmin2': '0', + 'rmax2': str(radius), + 'z': str(height), + 'startphi': '0', + 'deltaphi': '360', + 'aunit': 'deg', 'lunit': 'mm'}) + return cylEl + + +def processObject(cnt, idx, obj, xmlVol, volName, + xmlParent, parentName): + # cnt - number of GDML objects in Part/Volume + # If cnt == 1 - No need to create Volume use Part.Label & No PhysVol + # idx - index into OutList + # obj - This object # xmlVol - xmlVol # xmlParent - xmlParent Volume # parentName - Parent Name - #ET.ElementTree(gdml).write("test9a", 'utf-8', True) - #if obj.Label[:12] != 'NOT_Expanded' : - # printObjectInfo(xmlVol, volName, xmlParent, parentName) - #print('structure : '+str(xmlstr)) - GDMLShared.trace('Process Object : '+obj.Label) - while switch(obj.TypeId) : - - if case("App::Part") : - if obj.Label[:12] != 'NOT_Expanded' : - if hasattr(obj,'InList') : - parentName = obj.InList[0].Label - else : - parentName = None + GDMLShared.trace('Process Object : ' + obj.Label) + while switch(obj.TypeId): + + if case("App::Part"): + if obj.Label[:12] != 'NOT_Expanded': + if hasattr(obj, 'InList'): + parentName = obj.InList[0].Label + else: + parentName = None print(obj.Label) - #print(dir(obj)) + # print(dir(obj)) processVolAssem(obj, xmlVol, volName, True) - return - - if case("PartDesign::Body") : - print("Part Design Body - ignoring") - return - - if case("App::Origin") : - #print("App Origin") - return - - #if case("App::GeoFeature") : - # #print("App GeoFeature") - # return - - #if case("App::Line") : - # #print("App Line") - # return - - #f case("App::Plane") : - # #print("App Plane") - # return - - # Okay this is duplicate Volume cpynum > 1 - parent is a Volume - if case("App::Link") : - print('App::Link :'+obj.Label) - #print(dir(obj)) - print(obj.LinkedObject.Label) - addPhysVolPlacement(obj,xmlVol,obj.LinkedObject.Label) - return - - if case("Part::Cut") : - GDMLShared.trace("Cut - subtraction") - solidName = obj.Label - subtract = ET.SubElement(solids,'subtraction',{'name': solidName }) - ET.SubElement(subtract,'first', {'ref': nameOfGDMLobject(obj.Base)}) - ET.SubElement(subtract,'second',{'ref': nameOfGDMLobject(obj.Tool)}) - #solids.insert(0,subtract) - # process position & rotation ? - processPosition(obj.Tool,subtract) - processRotation(obj.Tool,subtract) - return obj - - if case("Part::Fuse") : - GDMLShared.trace("Fuse - union") - print("Fuse - union") - #print(dir(obj)) - #print(len(obj.InList)) - #print('InList') - #for o in obj.InList : - # print(o.Label) - #print(len(obj.OutList)) - #print('OutList') - #for o in obj.OutList : - # print(o.Label) - solidName = obj.Label - union = ET.SubElement(solids,'union',{'name': solidName }) - ET.SubElement(union,'first', {'ref': nameOfGDMLobject(obj.Base)}) - ET.SubElement(union,'second',{'ref': nameOfGDMLobject(obj.Tool)}) - print(f'Insert union : {solidName}') - #solids.insert(0,union) - # process position & rotation ? - processPosition(obj.Tool,union) - processRotation(obj.Tool,union) - return obj - - if case("Part::Common") : - GDMLShared.trace("Common - Intersection") - solidName = obj.Label - intersect = ET.SubElement(solids,'subtraction',{'name': solidName }) - ET.SubElement(intersect,'first', {'ref': nameOfGDMLobject(obj.Base)}) - ET.SubElement(intersect,'second',{'ref': nameOfGDMLobject(obj.Tool)}) - #solids.insert(0,intersect) - # process position & rotation ? - processPosition(obj.Tool,intersect) - processRotation(obj.Tool,intersect) - return obj - - if case("Part::MultiFuse") : - GDMLShared.trace(" Multifuse") - print(" Multifuse") - # test and fix - solidName = obj.Label - #boolCount = getCount(obj.Base) - #GDMLShared.trace('Count : '+str(boolCount)) - #addVolRef(xmlVol, volName, solidName, obj.Base) - #testAddPhysVol(obj, xmlParent, parentName) - # First add solids in list before reference - print('Output Solids') - for sub in obj.OutList: - #processSolid(sub,False) - processGDMLSolid(sub) - print('Output Solids Complete') - multUnion = ET.SubElement(solids,'multiUnion',{'name': solidName }) - num = 1 - - for sub in obj.OutList: - print(sub.Name) - node = processMuNod(multUnion, 'node-'+str(num)) - ET.SubElement(node,'solid',{'ref':sub.Name}) - processPosition(sub,node) - processRotation(sub,node) - num += 1 - - return obj - - if case("Part::MultiCommon") : - print(" Multi Common / intersection") - print(" Not available in GDML") - exit(-3) - break - - if case("Mesh::Feature") : - print(" Mesh Feature") - # test and Fix - #processMesh(obj, obj.Mesh, obj.Label) - #addVolRef(xmlVol, volName, solidName, obj) - #print('Need to add code for Mesh Material and colour') - #testAddPhysVol(obj, xmlParent, parentName): - # return solid ??? - return - - if case("Part::FeaturePython"): - GDMLShared.trace(" Python Feature") - if GDMLShared.getTrace == True : - if hasattr(obj.Proxy, 'Type') : - print(obj.Proxy.Type) - #if cnt > 1 : - # volName = 'LV-'+solidName - # xmlVol = insertXMLvolume(volName) - #addVolRef(xmlVol, volName, solidName, obj) - #if asmFlg == True : # Don't add physvol if GDML object in an assembly - # testAddPhysVol(obj, xmlParent, parentName) - processGDMLSolid(obj) - return obj - - # Same as Part::Feature but no position - if case("App::FeaturePython") : - print("App::FeaturePython") - # Following not needed as handled bu Outlist on Tessellated - #if isinstance(obj.Proxy, GDMLQuadrangular) : - # return(processGDMLQuadObject(obj, addVolsFlag)) - # break + return idx + 1 + + if case("PartDesign::Body"): + print("Part Design Body - ignoring") + return idx + 1 + + if case("Sketcher::SketchObject"): + print(f'Sketch {obj.Label}') + if hasattr(obj, 'InList'): + print(f'Has InList {obj.InList}') + for subObj in obj.InList: + print(f'subobj typeid {subObj.TypeId}') + if subObj.TypeId == "Part::Extrusion": + exportExtrusion.processExtrudedSketch(subObj, obj, xmlVol) + return idx + 1 + + if case("Part::Extrusion"): + print("Part Extrusion - Handle in Sketch") + return idx + 1 + + if case("App::Origin"): + # print("App Origin") + return idx + 1 + + # Okay this is duplicate Volume cpynum > 1 - parent is a Volume + if case("App::Link"): + print('App::Link :' + obj.Label) + # print(dir(obj)) + print(obj.LinkedObject.Label) + addPhysVolPlacement(obj, xmlVol, obj.LinkedObject.Label) + return idx + 1 + + if case("Part::Cut"): + GDMLShared.trace("Cut - subtraction") + retval = idx + processBooleanObject(obj, xmlVol, volName, + xmlParent, parentName) + return retval + + if case("Part::Fuse"): + GDMLShared.trace("Fuse - union") + retval = idx + processBooleanObject(obj, xmlVol, volName, + xmlParent, parentName) + print(f'retval {retval}') + return retval + + if case("Part::Common"): + GDMLShared.trace("Common - Intersection") + retval = idx + processBooleanObject(obj, xmlVol, volName, + xmlParent, parentName) + return retval + + if case("Part::MultiFuse"): + GDMLShared.trace(" Multifuse") + print(" Multifuse") + # test and fix + solidName = obj.Label + print('Output Solids') + for sub in obj.OutList: + processGDMLSolid(sub) + print('Output Solids Complete') + multUnion = ET.SubElement(solids, 'multiUnion', { + 'name': solidName}) + num = 1 + + for sub in obj.OutList: + print(sub.Name) + node = processMuNod(multUnion, 'node-' + str(num)) + ET.SubElement(node, 'solid', {'ref': sub.Name}) + processPosition(sub, node) + processRotation(sub, node) + num += 1 + + return idx + num + + if case("Part::MultiCommon"): + print(" Multi Common / intersection") + print(" Not available in GDML") + exit(-3) + + if case("Mesh::Feature"): + print(" Mesh Feature") + # test and Fix + # processMesh(obj, obj.Mesh, obj.Label) + # addVolRef(xmlVol, volName, solidName, obj) + # print('Need to add code for Mesh Material and colour') + # testAddPhysVol(obj, xmlParent, parentName): + # return solid ??? + return idx + 1 + + if case("Part::FeaturePython"): + GDMLShared.trace(" Python Feature") + print(f'FeaturePython: {obj.Label}') + if GDMLShared.getTrace is True: + if hasattr(obj.Proxy, 'Type'): + print(obj.Proxy.Type) + solidxml, solidName = processSolid(obj, True) + if cnt > 1: + volName = 'LV-'+solidName + xmlVol = insertXMLvolume(volName) + addVolRef(xmlVol, volName, obj, solidName) + return idx + 1 + + # Same as Part::Feature but no position + if case("App::FeaturePython"): + print("App::FeaturePython") + # Following not needed as handled bu Outlist on Tessellated + # if isinstance(obj.Proxy, GDMLQuadrangular) : + # return(processGDMLQuadObject(obj, addVolsFlag)) + # break - #if isinstance(obj.Proxy, GDMLTriangular) : - # return(processGDMLTriangleObject(obj, addVolsFlag)) - # break + # if isinstance(obj.Proxy, GDMLTriangular) : + # return(processGDMLTriangleObject(obj, addVolsFlag)) + # break - # Following not needed as handled bu Outlist on Xtru + # Following not needed as handled bu Outlist on Xtru - #if isinstance(obj.Proxy, GDML2dVertex) : - # return(processGDML2dVertObject(obj, addVolsFlag)) - # break + # if isinstance(obj.Proxy, GDML2dVertex) : + # return(processGDML2dVertObject(obj, addVolsFlag)) + # break - #if isinstance(obj.Proxy, GDMLSection) : - # return(processGDMLSection(obj, addVolsFlag)) - # break - return - - # - # Now deal with objects that map to GDML solids - # - if case("Part::Box") : - print(" Box") - #return(processBoxObject(obj, addVolsFlag)) - processBoxObject(obj, addVolsFlag) - #testAddPhysVol(obj, xmlParent, parentName) - return - - if case("Part::Cylinder") : - print(" Cylinder") - #return(processCylinderObject(obj, addVolsFlag)) - processCylinderObject(obj, addVolsFlag) - #testAddPhysVol(obj, xmlParent, parentName) - return - - if case("Part::Cone") : - print(" Cone") - #return(processConeObject(obj, addVolsFlag)) - processConeObject(obj, addVolsFlag) - #testAddPhysVol(obj, xmlParent, parentName) - return - - if case("Part::Sphere") : - print(" Sphere") - #return(processSphereObject(obj, addVolsFlag)) - processSphereObject(obj, addVolsFlag) - #testAddPhysVol(obj, xmlParent, parentName) - return - - # Not a Solid that translated to GDML solid - # Dropped through so treat object as a shape - # Need to check obj has attribute Shape - # Create tessellated solid - # - #return(processObjectShape(obj, addVolsFlag)) - print("Convert FreeCAD shape to GDML Tessellated") - print(obj.TypeId) - return - - if hasattr(obj,'Shape') : - if obj.Shape.isValid() : - #return(processObjectShape(obj)) - processObjectShape(obj) - #testAddPhysVol(obj, xmlParent, parentName) - return + # if isinstance(obj.Proxy, GDMLSection) : + # return(processGDMLSection(obj, addVolsFlag)) + # break + return idx + 1 + + # + # Now deal with objects that map to GDML solids + # + if case("Part::Box"): + print(" Box") + # return(processBoxObject(obj, addVolsFlag)) + processBoxObject(obj, True) + # testAddPhysVol(obj, xmlParent, parentName) + return idx + 1 + + if case("Part::Cylinder"): + print(" Cylinder") + # return(processCylinderObject(obj, addVolsFlag)) + processCylinderObject(obj, True) + # testAddPhysVol(obj, xmlParent, parentName) + return idx + 1 + + if case("Part::Cone"): + print(" Cone") + # return(processConeObject(obj, addVolsFlag)) + processConeObject(obj, True) + # testAddPhysVol(obj, xmlParent, parentName) + return idx + 1 + + if case("Part::Sphere"): + print(" Sphere") + # return(processSphereObject(obj, addVolsFlag)) + processSphereObject(obj, True) + # testAddPhysVol(obj, xmlParent, parentName) + return idx + 1 + + # Not a Solid that translated to GDML solid + # Dropped through so treat object as a shape + # Need to check obj has attribute Shape + # Create tessellated solid + # + # return(processObjectShape(obj, addVolsFlag)) + # print("Convert FreeCAD shape to GDML Tessellated") + print(f"Object {obj.Label} Type : {obj.TypeId} Not yet handled") + print(obj.TypeId) + return idx + 1 + + if hasattr(obj, 'Shape'): + if obj.Shape.isValid(): + # return(processObjectShape(obj)) + processObjectShape(obj) + # testAddPhysVol(obj, xmlParent, parentName) + return idx + 1 + def insertXMLvolume(name): # Insert at beginning for sub volumes - GDMLShared.trace('insert xml volume : '+name) - elem = ET.Element('volume',{'name': name}) + GDMLShared.trace('insert xml volume : ' + name) + elem = ET.Element('volume', {'name': name}) global structure - structure.insert(0,elem) + structure.insert(0, elem) return elem -def insertXMLvolObj(obj) : - #name = cleanVolName(obj, obj.Label) + +def insertXMLvolObj(obj): + # name = cleanVolName(obj, obj.Label) name = obj.Label return insertXMLvolume(name) + def insertXMLassembly(name): # Insert at beginning for sub volumes - GDMLShared.trace('insert xml assembly : '+name) - elem = ET.Element('assembly',{'name': name}) + GDMLShared.trace('insert xml assembly : ' + name) + elem = ET.Element('assembly', {'name': name}) global structure - structure.insert(0,elem) + structure.insert(0, elem) return elem -def insertXMLassemObj(obj) : - #name = cleanVolName(obj, obj.Label) + +def insertXMLassemObj(obj): + # name = cleanVolName(obj, obj.Label) name = obj.Label return insertXMLassembly(name) -def createXMLvol(name): - return ET.SubElement(structure,'volume',{'name': name}) +def createXMLvol(name): + return ET.SubElement(structure, 'volume', {'name': name}) - volName = cleanVolName(vol, vol.Label) -def processAssembly(vol, xmlVol, xmlParent, parentName, addVolsFlag) : +def processAssembly(vol, xmlVol, xmlParent, parentName, addVolsFlag): # vol - Volume Object # xmlVol - xml of this volume # xmlParent - xml of this volumes Paretnt @@ -1858,39 +2154,51 @@ def processAssembly(vol, xmlVol, xmlParent, parentName, addVolsFlag) : # So for s in list is not so good # type 1 straight GDML type = 2 for GEMC # xmlVol could be created dummy volume - #GDMLShared.setTrace(True) + GDMLShared.setTrace(True) volName = vol.Label - #volName = cleanVolName(vol, vol.Label) + # volName = cleanVolName(vol, vol.Label) GDMLShared.trace('Process Assembly : '+volName) - if GDMLShared.getTrace() == True : - printVolumeInfo(vol, xmlVol, xmlParent, parentName) - if hasattr(vol,'OutList') : - for obj in vol.OutList : - if obj.TypeId == 'App::Part' : - processVolAssem(obj, xmlVol, volName, addVolsFlag) - - elif obj.TypeId == 'App::Link' : + # if GDMLShared.getTrace() == True : + # printVolumeInfo(vol, xmlVol, xmlParent, parentName) + if hasattr(vol, 'OutList'): + print('Has OutList') + for obj in vol.OutList: + if obj.TypeId == 'App::Part': + processVolAssem(obj, xmlVol, volName, addVolsFlag) + + elif obj.TypeId == 'App::Link': print('Process Link') - #objName = cleanVolName(obj, obj.Label) - addPhysVolPlacement(obj,xmlVol,obj.LinkedObject.Label) + # objName = cleanVolName(obj, obj.Label) + addPhysVolPlacement(obj, xmlVol, obj.LinkedObject.Label) - addPhysVolPlacement(vol,xmlParent,volName) + elif obj.TypeId == "Sketcher::SketchObject": + print(f'Sketch {obj.Label}') + if hasattr(obj, 'InList'): + print(f'Has InList {obj.InList}') + for subObj in obj.InList: + print(f'subobj typeid {subObj.TypeId}') + if subObj.TypeId == "Part::Extrusion": + exportExtrusion.processExtrudedSketch(subObj, + obj, xmlVol) + addPhysVolPlacement(vol, xmlParent, volName) -def printVolumeInfo(vol, xmlVol, xmlParent, parentName) : - if xmlVol is not None : - xmlstr = ET.tostring(xmlVol) - else : - xmlstr ='None' + +def printVolumeInfo(vol, xmlVol, xmlParent, parentName): + if xmlVol is not None: + xmlstr = ET.tostring(xmlVol) + else: + xmlstr = 'None' print(xmlstr) - GDMLShared.trace(' '+vol.Label+ ' - '+str(xmlstr)) - if xmlParent is not None : - xmlstr = ET.tostring(xmlParent) - else : - xmlstr ='None' - GDMLShared.trace(' Parent : '+str(parentName)+' : '+ str(xmlstr)) + GDMLShared.trace(' '+vol.Label + ' - ' + str(xmlstr)) + if xmlParent is not None: + xmlstr = ET.tostring(xmlParent) + else: + xmlstr = 'None' + GDMLShared.trace(' Parent : ' + str(parentName) + ' : ' + str(xmlstr)) -def processVolume(vol, xmlVol, xmlParent, parentName, addVolsFlag) : + +def processVolume(vol, xmlVol, xmlParent, parentName, addVolsFlag): # vol - Volume Object # xmlVol - xml of this volume # xmlParent - xml of this volumes Paretnt @@ -1900,179 +2208,208 @@ def processVolume(vol, xmlVol, xmlParent, parentName, addVolsFlag) : # xmlVol could be created dummy volume volName = vol.Label print(f'Process Volume : {volName}') - #volName = cleanVolName(vol, vol.Label) - if GDMLShared.getTrace() == True : - GDMLShared.trace('Process Volume : '+volName) - printVolumeInfo(vol, xmlVol, xmlParent, parentName) - - if hasattr(vol,'SensDet') : - if vol.SensDet is not None : - print('Volume : '+volName) - print('SensDet : '+vol.SensDet) - ET.SubElement(xmlVol,'auxiliary',{'auxtype':'SensDet', \ - 'auxvalue' : vol.SensDet}) - if hasattr(vol,'OutList') : - GDMLShared.trace('OutList length : '+str(len(vol.OutList))) - gdmlObj = None - for obj in vol.OutList : - robj = processObject(obj,xmlVol, volName, xmlParent, parentName) - if robj is not None : - gdmlObj = robj - addVolRef(xmlVol, volName, gdmlObj) - addPhysVolPlacement(vol,xmlParent,volName) - -def processVolAssem(vol, xmlParent, parentName, addVolsFlag) : + # volName = cleanVolName(vol, vol.Label) + if GDMLShared.getTrace() is True: + GDMLShared.trace('Process Volume : ' + volName) + printVolumeInfo(vol, xmlVol, xmlParent, parentName) + + if hasattr(vol, 'SensDet'): + if vol.SensDet is not None: + print('Volume : ' + volName) + print('SensDet : ' + vol.SensDet) + ET.SubElement(xmlVol, 'auxiliary', {'auxtype': 'SensDet', + 'auxvalue': vol.SensDet}) + idx = 0 + cnt = 0 + if hasattr(vol, 'OutList'): + num = len(vol.OutList) + cnt = countGDMLObj(vol.OutList) + # Depending on how the Parts were constructed, the + # the order of items in the OutList may not reflect + # the tree hierarchy in view. If we have bolleans of + # booleans, we must start with the top most boolean + # code below gets the boolean that has the largest + # number of sub booleans + maxCount = 0 + rootBool = None + for obj in vol.OutList: + boolCount = getBooleanCount(obj) + if boolCount > maxCount: + maxCount = boolCount + rootBool = obj + + if rootBool is not None: + processObject(cnt, idx, rootBool, + xmlVol, volName, xmlParent, parentName) + else: + GDMLShared.trace('OutList length : ' + str(num)) + while idx < num: + print(f'idx {idx} {vol.OutList[idx].TypeId}') + idx = processObject(cnt, idx, vol.OutList[idx], + xmlVol, volName, xmlParent, parentName) + addPhysVolPlacement(vol, xmlParent, volName) + + +def processVolAssem(vol, xmlParent, parentName, addVolsFlag): # vol - Volume Object # xmlVol - xml of this volume # xmlParent - xml of this volumes Paretnt # xmlVol could be created dummy volume print('process volasm '+vol.Label) volName = vol.Label - #volName = cleanVolName(vol, vol.Label) - if hasattr(vol,'OutList') : # Do we have Objects ? - cnt = countGDMLObj(vol.OutList) - print('VolAsm - count '+str(cnt)) - if cnt > 0 : - newXmlVol = insertXMLvolume(volName) - processVolume(vol, newXmlVol, xmlParent, parentName, addVolsFlag) - else : - newXmlVol = insertXMLassembly(volName) - processAssembly(vol, newXmlVol, xmlParent, parentName, addVolsFlag) - - #addPhysVolPlacement(vol,xmlParent,volName) - #elif obj.TypeId == 'App::Link' : - # addPhysVolPlacement(obj,xmlVol,objName) - -def createWorldVol(volName) : + # volName = cleanVolName(vol, vol.Label) + if hasattr(vol, 'OutList'): # Do we have Objects ? + cnt = countGDMLObj(vol.OutList) + print('VolAsm - count ' + str(cnt)) + if cnt > 0: + newXmlVol = insertXMLvolume(volName) + processVolume(vol, newXmlVol, xmlParent, parentName, addVolsFlag) + else: + newXmlVol = insertXMLassembly(volName) + processAssembly(vol, newXmlVol, xmlParent, parentName, addVolsFlag) + + # addPhysVolPlacement(vol,xmlParent,volName) + # elif obj.TypeId == 'App::Link' : + # addPhysVolPlacement(obj,xmlVol,objName) + + +def createWorldVol(volName): print("Need to create Dummy Volume and World Box ") bbox = FreeCAD.BoundBox() boxName = defineWorldBox(bbox) - worldVol = ET.SubElement(structure,'volume',{'name': volName}) + worldVol = ET.SubElement(structure, 'volume', {'name': volName}) print("Need to FIX !!!! To use defined gas") - ET.SubElement(worldVol, 'materialref',{'ref': 'G4_Galactic'}) - ET.SubElement(worldVol, 'solidref',{'ref': boxName}) - ET.SubElement(gxml,'volume',{'name': volName, 'material':'G4_AIR'}) + ET.SubElement(worldVol, 'materialref', {'ref': 'G4_Galactic'}) + ET.SubElement(worldVol, 'solidref', {'ref': boxName}) + ET.SubElement(gxml, 'volume', {'name': volName, 'material': 'G4_AIR'}) return worldVol + def countGDMLObj(objList): - # Return position of first GDML object and count - #print('countGDMLObj') + # Return counts GDML objects exportables + # #rint('countGDMLObj') GDMLShared.trace('countGDMLObj') - count = 0 - #print(range(len(objList))) - for idx in range(len(objList)) : - #print('idx : '+str(idx)) + gcount = 0 + # print(range(len(objList))) + for idx in range(len(objList)): + # print('idx : '+str(idx)) obj = objList[idx] - if obj.TypeId == 'Part::FeaturePython' : - count += 1 + if obj.TypeId == 'Part::FeaturePython': + gcount += 1 if obj.TypeId == 'Part::Cut' \ or obj.TypeId == 'Part::Fuse' \ - or obj.TypeId == 'Part::Common' : - count -= 1 - #print('countGDMLObj - Count : '+str(count)) - GDMLShared.trace('countGDMLObj - Count : '+str(count)) - return count - -def checkGDMLstructure(objList) : - # Should be + or obj.TypeId == 'Part::Common': + gcount -= 1 + # print('countGDMLObj - Count : '+str(gcount)) + GDMLShared.trace('countGDMLObj - gdml : ' + str(gcount)) + return gcount + + +def checkGDMLstructure(objList): + # Should be # World Vol - App::Part # App::Origin # GDML Object GDMLShared.trace('check GDML structure') GDMLShared.trace(objList) - #print(objList) + # print(objList) cnt = countGDMLObj(objList) - if cnt > 1 : # More than one GDML Object need to insert Dummy - return False - if cnt == 1 and len(objList) == 2 : # Just a single GDML obj insert Dummy - return False + if cnt > 1: # More than one GDML Object need to insert Dummy + return False + if cnt == 1 and len(objList) == 2: # Just a single GDML obj insert Dummy + return False return True - #if len(objList) < 3 : - # return False - #if objList[0].TypeId != 'App::Origin' \ - # or objList[2].TypeId != 'App::Part' : + # if len(objList) < 3 : + # return False + # if objList[0].TypeId != 'App::Origin' \ + # or objList[2].TypeId != 'App::Part' : # return False - #return True + # return True -def locateXMLvol(vol) : + +def locateXMLvol(vol): global structure xmlVol = structure.find("volume[@name='%s']" % vol.Label) return xmlVol -def exportWorldVol(vol, fileExt) : + +def exportWorldVol(vol, fileExt): global WorldVOL WorldVOL = vol.Label - if fileExt != '.xml' : - print('Export World Process Volume : '+vol.Label) - GDMLShared.trace('Export Word Process Volume'+vol.Label) - ET.SubElement(setup,'world',{'ref':vol.Label}) - - if checkGDMLstructure(vol.OutList) == False : - GDMLShared.trace('Insert Dummy Volume') - xmlVol = createXMLvol('dummy') - xmlParent = createWorldVol(vol.Label) - parentName = vol.Label - addPhysVol(xmlParent,'dummy') - else : - GDMLShared.trace('Valid Structure') - xmlParent = None - parentName = None - else : - xmlParent = None - parentName = None - if hasattr(vol,'OutList') : - #print(vol.OutList) - cnt = countGDMLObj(vol.OutList) - #print('Root GDML Count '+str(cnt)) - if cnt > 0 : - xmlVol = insertXMLvolume(vol.Label) - processVolume(vol, xmlVol, xmlParent, parentName, False) - else : - xmlVol = insertXMLassembly(vol.Label) - processAssembly(vol, xmlVol, xmlParent, parentName, False) + if fileExt != '.xml': + print('Export World Process Volume : ' + vol.Label) + GDMLShared.trace('Export Word Process Volume' + vol.Label) + ET.SubElement(setup, 'world', {'ref': vol.Label}) + + if checkGDMLstructure(vol.OutList) is False: + GDMLShared.trace('Insert Dummy Volume') + xmlVol = createXMLvol('dummy') + xmlParent = createWorldVol(vol.Label) + parentName = vol.Label + addPhysVol(xmlParent, 'dummy') + else: + GDMLShared.trace('Valid Structure') + xmlParent = None + parentName = None + else: + xmlParent = None + parentName = None + if hasattr(vol, 'OutList'): + # print(vol.OutList) + cnt = countGDMLObj(vol.OutList) + print('Root GDML Count ' + str(cnt)) + if cnt > 0: + xmlVol = insertXMLvolume(vol.Label) + processVolume(vol, xmlVol, xmlParent, parentName, False) + else: + xmlVol = insertXMLassembly(vol.Label) + processAssembly(vol, xmlVol, xmlParent, parentName, False) -def exportElementAsXML(dirPath, fileName, flag, elemName, elem) : +def exportElementAsXML(dirPath, fileName, flag, elemName, elem): # gdml is a global global gdml, docString, importStr - if elem is not None : - #xmlElem = ET.Element('xml') - #xmlElem.append(elem) - #indent(xmlElem) - if flag == True : - filename = fileName+'-'+elemName+'.xml' - else : - filename = elemName+'.xml' - #ET.ElementTree(xmlElem).write(os.path.join(dirPath,filename)) - ET.ElementTree(elem).write(os.path.join(dirPath,filename)) - docString += '\n' - gdml.append(ET.Entity(elemName)) - -def exportGDMLstructure(dirPath, fileName) : + if elem is not None: + # xmlElem = ET.Element('xml') + # xmlElem.append(elem) + # indent(xmlElem) + if flag is True: + filename = fileName+'-' + elemName + '.xml' + else: + filename = elemName + '.xml' + # ET.ElementTree(xmlElem).write(os.path.join(dirPath,filename)) + ET.ElementTree(elem).write(os.path.join(dirPath, filename)) + docString += '\n' + gdml.append(ET.Entity(elemName)) + + +def exportGDMLstructure(dirPath, fileName): global gdml, docString, importStr print("Write GDML structure to Directory") gdml = initGDML() docString = '\n\n' - #print(docString) - #print(len(docString)) - #gdml = ET.fromstring(docString.encode("UTF-8")) + # print(docString) + # print(len(docString)) + # gdml = ET.fromstring(docString.encode("UTF-8")) indent(gdml) - ET.ElementTree(gdml).write(os.path.join(dirPath,fileName+'.gdml'), \ - doctype=docString.encode('UTF-8')) + ET.ElementTree(gdml).write(os.path.join(dirPath, fileName+'.gdml'), + doctype=docString.encode('UTF-8')) print("GDML file structure written") -def exportGDML(first, filepath, fileExt) : + +def exportGDML(first, filepath, fileExt): from . import GDMLShared + global zOrder - #GDMLShared.setTrace(True) + # GDMLShared.setTrace(True) GDMLShared.trace('exportGDML') print("====> Start GDML Export 1.6") print('File extension : '+fileExt) @@ -2081,265 +2418,269 @@ def exportGDML(first, filepath, fileExt) : zOrder = 1 processMaterials() exportWorldVol(first, fileExt) - # format & write GDML file - #xmlstr = ET.tostring(structure) - #print('Structure : '+str(xmlstr)) - if fileExt == '.gdml' : - indent(gdml) - print("Write to gdml file") - #ET.ElementTree(gdml).write(filepath, 'utf-8', True) - ET.ElementTree(gdml).write(filepath,xml_declaration=True) - #ET.ElementTree(gdml).write(filepath, pretty_print=True, \ - #xml_declaration=True) - print("GDML file written") - - if fileExt == '.GDML' : - filePath = os.path.split(filepath) - print('Input File Path : '+filepath) - fileName = os.path.splitext(filePath[1])[0] - print('File Name : '+fileName) - dirPath = os.path.join(filePath[0],fileName) - print('Directory Path : '+dirPath) - if os.path.exists(dirPath) == False : - if os.path.isdir(dirPath) == False : - os.makedirs(dirPath) - if os.path.isdir(dirPath) == True : - exportGDMLstructure(dirPath, fileName) - else : - print('Invalid Path') - # change to Qt Warning - - if fileExt == '.xml' : - xmlElem = ET.Element('xml') - xmlElem.append(solids) - xmlElem.append(structure) - indent(xmlElem) - ET.ElementTree(xmlElem).write(filepath) - print("XML file written") - -def exportGDMLworld(first,filepath,fileExt) : - if filepath.lower().endswith('.gdml') : - # GDML Export - print('GDML Export') - #if hasattr(first,'InList') : - # print(len(first.InList)) - - if hasattr(first,'OutList') : - cnt = countGDMLObj(first.OutList) - GDMLShared.trace('Count : '+str(cnt)) - if cnt > 1 : - from .GDMLQtDialogs import showInvalidWorldVol - showInvalidWorldVol() - - else : - exportGDML(first,filepath,fileExt) - -def hexInt(f) : + # format & write GDML file + # xmlstr = ET.tostring(structure) + # print('Structure : '+str(xmlstr)) + if fileExt == '.gdml': + indent(gdml) + print("Write to gdml file") + # ET.ElementTree(gdml).write(filepath, 'utf-8', True) + ET.ElementTree(gdml).write(filepath, xml_declaration=True) + # ET.ElementTree(gdml).write(filepath, pretty_print=True, \ + # xml_declaration=True) + print("GDML file written") + + if fileExt == '.GDML': + filePath = os.path.split(filepath) + print('Input File Path : '+filepath) + fileName = os.path.splitext(filePath[1])[0] + print('File Name : '+fileName) + dirPath = os.path.join(filePath[0], fileName) + print('Directory Path : '+dirPath) + if os.path.exists(dirPath) is False: + if os.path.isdir(dirPath) is False: + os.makedirs(dirPath) + if os.path.isdir(dirPath) is True: + exportGDMLstructure(dirPath, fileName) + else: + print('Invalid Path') + # change to Qt Warning + + if fileExt == '.xml': + xmlElem = ET.Element('xml') + xmlElem.append(solids) + xmlElem.append(structure) + indent(xmlElem) + ET.ElementTree(xmlElem).write(filepath) + print("XML file written") + + +def exportGDMLworld(first, filepath, fileExt): + if filepath.lower().endswith('.gdml'): + # GDML Export + print('GDML Export') + # if hasattr(first,'InList') : + # print(len(first.InList)) + + if hasattr(first, 'OutList'): + cnt = countGDMLObj(first.OutList) + GDMLShared.trace('Count : ' + str(cnt)) + if cnt > 1: + from .GDMLQtDialogs import showInvalidWorldVol + showInvalidWorldVol() + else: + exportGDML(first, filepath, fileExt) + + +def hexInt(f): return hex(int(f*255))[2:].zfill(2) + def formatPosition(pos): - s = str(pos[0])+'*mm '+str(pos[1])+'*mm '+str(pos[2])+'*mm' + s = str(pos[0]) + '*mm ' + str(pos[1]) + '*mm ' +str(pos[2]) + '*mm' print(s) return s -def scanForStl(first, gxml, path, flag ): - - from .GDMLColourMap import lookupColour - - # if flag == True ignore Parts that convert - print('scanForStl') - print(first.Name+' : '+first.Label+' : '+first.TypeId) - while switch(first.TypeId) : - - if case("App::Origin") : - #print("App Origin") - return - break - - if case("App::GeoFeature") : - #print("App GeoFeature") - return - break - - if case("App::Line") : - #print("App Line") - return - break - - if case("App::Plane") : - #print("App Plane") - return - break - - break - - if flag == True : - # - # Now deal with objects that map to GDML solids - # - while switch(first.TypeId) : - if case("Part::FeaturePython") : - return - break - if case("Part::Box") : - print(" Box") +def scanForStl(first, gxml, path, flag): + + from .GDMLColourMap import lookupColour + + # if flag == True ignore Parts that convert + print('scanForStl') + print(first.Name+' : '+first.Label+' : '+first.TypeId) + while switch(first.TypeId): + + if case("App::Origin"): + # print("App Origin") return - break - if case("Part::Cylinder") : - print(" Cylinder") + if case("App::GeoFeature"): + # print("App GeoFeature") return - break - if case("Part::Cone") : - print(" Cone") + if case("App::Line"): + # print("App Line") return - break - if case("Part::Sphere") : - print(" Sphere") + if case("App::Plane"): + # print("App Plane") return + + break + + if flag is True: + # + # Now deal with objects that map to GDML solids + # + while switch(first.TypeId): + if case("Part::FeaturePython"): + return + + if case("Part::Box"): + print(" Box") + return + + if case("Part::Cylinder"): + print(" Cylinder") + return + + if case("Part::Cone"): + print(" Cone") + return + + if case("Part::Sphere"): + print(" Sphere") + return + break - break - - # Deal with Booleans which will have Tool - if hasattr(first,'Tool') : - print(first.TypeId) - scanForStl(first.Base, gxml, path, flag) - scanForStl(first.Tool, gxml, path, flag) - - if hasattr(first,'OutList') : - for obj in first.OutList : - scanForStl(obj, gxml, path, flag) - - if first.TypeId != 'App::Part' : - if hasattr(first,'Shape') : - print('Write out stl') - print('===> Name : '+first.Name+' Label : '+first.Label+' \ - Type :'+first.TypeId+' : '+str(hasattr(first,'Shape'))) - newpath = os.path.join(path,first.Label+'.stl') - print('Exporting : '+newpath) - first.Shape.exportStl(newpath) - # Set Defaults - colHex = 'ff0000' - mat = 'G4Si' - if hasattr(first.ViewObject,'ShapeColor') : - #print(dir(first)) - col = first.ViewObject.ShapeColor - colHex = hexInt(col[0]) + hexInt(col[1]) + hexInt(col[2]) - print('===> Colour '+str(col) + ' '+colHex) - mat = lookupColour(col) - print('Material : '+mat) - if hasattr(first,'Placement') : - print(first.Placement.Base) - pos = formatPosition(first.Placement.Base) - ET.SubElement(gxml,'volume',{'name':first.Label, \ - 'color': colHex, 'material':mat, 'position': pos}) - -def exportGXML(first, path, flag) : + # Deal with Booleans which will have Tool + if hasattr(first, 'Tool'): + print(first.TypeId) + scanForStl(first.Base, gxml, path, flag) + scanForStl(first.Tool, gxml, path, flag) + + if hasattr(first, 'OutList'): + for obj in first.OutList: + scanForStl(obj, gxml, path, flag) + + if first.TypeId != 'App::Part': + if hasattr(first, 'Shape'): + print('Write out stl') + print('===> Name : '+first.Name+' Label : '+first.Label+' \ + Type :'+first.TypeId+' : '+str(hasattr(first, 'Shape'))) + newpath = os.path.join(path, first.Label + '.stl') + print('Exporting : ' + newpath) + first.Shape.exportStl(newpath) + # Set Defaults + colHex = 'ff0000' + mat = 'G4Si' + if hasattr(first.ViewObject, 'ShapeColor'): + # print(dir(first)) + col = first.ViewObject.ShapeColor + colHex = hexInt(col[0]) + hexInt(col[1]) + hexInt(col[2]) + print('===> Colour '+str(col) + ' '+colHex) + mat = lookupColour(col) + print('Material : '+mat) + if hasattr(first, 'Placement'): + print(first.Placement.Base) + pos = formatPosition(first.Placement.Base) + ET.SubElement(gxml, 'volume', {'name': first.Label, + 'color': colHex, + 'material': mat, + 'position': pos}) + + +def exportGXML(first, path, flag): print('Path : '+path) - #basename = 'target_'+os.path.basename(path) + # basename = 'target_'+os.path.basename(path) gxml = ET.Element('gxml') print('ScanForStl') scanForStl(first, gxml, path, flag) - # format & write gxml file + # format & write gxml file indent(gxml) print("Write to gxml file") - #ET.ElementTree(gxml).write(os.path.join(path,basename+'.gxml')) - ET.ElementTree(gxml).write(os.path.join(path,'target_cad.gxml')) + # ET.ElementTree(gxml).write(os.path.join(path,basename+'.gxml')) + ET.ElementTree(gxml).write(os.path.join(path, 'target_cad.gxml')) print("gxml file written") -def exportMaterials(first,filename) : - if filename.lower().endswith('.xml') : - print('Export Materials to XML file : '+filename) - xml = ET.Element('xml') - global define - define = ET.SubElement(xml,'define') - global materials - materials = ET.SubElement(xml,'materials') - processMaterials() - indent(xml) - ET.ElementTree(xml).write(filename) - else : - print('File extension must be xml') -def create_gcard(path, flag) : +def exportMaterials(first, filename): + if filename.lower().endswith('.xml'): + print('Export Materials to XML file : '+filename) + xml = ET.Element('xml') + global define + define = ET.SubElement(xml, 'define') + global materials + materials = ET.SubElement(xml, 'materials') + processMaterials() + indent(xml) + ET.ElementTree(xml).write(filename) + else: + print('File extension must be xml') + + +def create_gcard(path, flag): basename = os.path.basename(path) print('Create gcard : '+basename) print('Path : '+path) gcard = ET.Element('gcard') - ET.SubElement(gcard,'detector',{'name':'target_cad','factory':'CAD'}) - if flag == True : - ET.SubElement(gcard,'detector',{'name':'target_gdml','factory':'GDML'}) + ET.SubElement(gcard, 'detector', {'name': 'target_cad', 'factory': 'CAD'}) + if flag is True: + ET.SubElement(gcard, 'detector', { + 'name': 'target_gdml', 'factory': 'GDML'}) indent(gcard) - path = os.path.join(path,basename+'.gcard') + path = os.path.join(path, basename + '.gcard') ET.ElementTree(gcard).write(path) -def checkDirectory(path) : + +def checkDirectory(path): if not os.path.exists(path): - print('Creating Directory : '+path) - os.mkdir(path) + print('Creating Directory : ' + path) + os.mkdir(path) -def exportGEMC(first, path, flag) : + +def exportGEMC(first, path, flag): # flag = True GEMC - GDML # flag = False just CAD global gxml print('Export GEMC') - #basename = os.path.basename(path) + # basename = os.path.basename(path) print(path) print(flag) checkDirectory(path) # Create CAD directory - cadPath = os.path.join(path,'cad') + cadPath = os.path.join(path, 'cad') checkDirectory(cadPath) # Create gcard create_gcard(path, flag) exportGXML(first, cadPath, flag) - if flag == True : - print('Create GDML directory') - gdmlPath = os.path.join(path,'gdml') - checkDirectory(gdmlPath) - #gdmlFilePath = os.path.join(gdmlPath,basename+'.gdml') - gdmlFilePath = os.path.join(gdmlPath,'target_gdml.gdml') - exportGDML(first, gdmlFilePath,'gdml') - #newpath = os.path.join(gdmlPath,basename+'.gxml') - newpath = os.path.join(gdmlPath,'target_gdml.gxml') - indent(gxml) - ET.ElementTree(gxml).write(newpath) - -def export(exportList,filepath) : + if flag is True: + print('Create GDML directory') + gdmlPath = os.path.join(path, 'gdml') + checkDirectory(gdmlPath) + # gdmlFilePath = os.path.join(gdmlPath,basename+'.gdml') + gdmlFilePath = os.path.join(gdmlPath, 'target_gdml.gdml') + exportGDML(first, gdmlFilePath, 'gdml') + # newpath = os.path.join(gdmlPath,basename+'.gxml') + newpath = os.path.join(gdmlPath, 'target_gdml.gxml') + indent(gxml) + ET.ElementTree(gxml).write(newpath) + + +def export(exportList, filepath): "called when FreeCAD exports a file" - + first = exportList[0] print(f'Export Volume: {first.Label}') - + import os path, fileExt = os.path.splitext(filepath) print('filepath : '+path) print('file extension : '+fileExt) - if fileExt.lower() == '.gdml' : - if first.TypeId == "App::Part" : - exportGDMLworld(first,filepath,fileExt) - - elif first.Label == "Materials" : - exportMaterials(first,filepath) - - else : - print("Needs to be a Part for export") - from PySide import QtGui - QtGui.QMessageBox.critical(None,'Need to select a Part for export', \ - 'Press OK') - - elif fileExt.lower == '.xml' : - print('Export XML structure & solids') - exportGDML(first,filepath,'.xml') - - if fileExt == '.gemc' : - exportGEMC(first, path, False) - - elif fileExt == '.GEMC' : - exportGEMC(first, path, True) + if fileExt.lower() == '.gdml': + if first.TypeId == "App::Part": + exportGDMLworld(first, filepath, fileExt) + + elif first.Label == "Materials": + exportMaterials(first, filepath) + + else: + print("Needs to be a Part for export") + from PySide import QtGui + QtGui.QMessageBox.critical(None, + 'Need to select a Part for export', + 'Press OK') + + elif fileExt.lower == '.xml': + print('Export XML structure & solids') + exportGDML(first, filepath, '.xml') + + if fileExt == '.gemc': + exportGEMC(first, path, False) + + elif fileExt == '.GEMC': + exportGEMC(first, path, True) diff --git a/freecad/gdml/importGDML.py b/freecad/gdml/importGDML.py index c61a26368..26631840f 100644 --- a/freecad/gdml/importGDML.py +++ b/freecad/gdml/importGDML.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # emacs insert date command: Ctrl-U ESC-! date -# Sat Jan 29 09:50:15 AM PST 2022 +# Mon Feb 28 12:47:38 PM PST 2022 # ************************************************************************** # * * # * Copyright (c) 2017 Keith Sloan * @@ -34,6 +34,7 @@ import os, io, sys, re import Part, Draft + def joinDir(path): import os __dirname__ = os.path.dirname(__file__) @@ -47,7 +48,7 @@ def joinDir(path): ########################## # global setup, define, mats_xml, solids, structure, extension -# globals constDict, filesDict +# globals constDict, filesDict if FreeCAD.GuiUp: import PartGui, FreeCADGui @@ -173,7 +174,7 @@ def setDisplayMode(obj, mode): def newPartFeature(obj, name): newobj = obj.newObject("Part::FeaturePython", name) - # FreeCAD can change the name i.e. hypen to underscore + # FreeCAD can change the name i.e. hyphen to underscore # So also set the Objects Label newobj.Label = name return(newobj) @@ -181,7 +182,7 @@ def newPartFeature(obj, name): def newGroupPython(obj, name): newobj = obj.newObject("App::DocumentObjectGroupPython", name) - # FreeCAD can change the name i.e. hypen to underscore + # FreeCAD can change the name i.e. hyphen to underscore # So also set the Objects Label newobj.Label = name return(newobj) @@ -532,6 +533,7 @@ def createParaboloid(part, solid, material, colour, px, py, pz, rot, displayMode setDisplayMode(myparaboloid, displayMode) return myparaboloid + def createPolycone(part, solid, material, colour, px, py, pz, rot, displayMode): from .GDMLObjects import GDMLPolycone, GDMLzplane, \ ViewProvider, ViewProviderExtension @@ -612,6 +614,28 @@ def createPolyhedra(part, solid, material, colour, px, py, pz, rot, displayMode) return mypolyhedra +def createScaledSolid(part, solid, material, colour, px, py, pz, rot, displayMode): + print('ScaledSolid') + global solids + solidref = GDMLShared.getRef(solid, 'solidref') + newSolid = solids.find("*[@name='%s']" % solidref) + scaledObj = createSolid(part, newSolid, material, colour, + px, py, pz, rot, displayMode) + scale = solid.find('scale') + scaleName = scale.get('name') + sx = GDMLShared.getVal(scale, 'x') + sy = GDMLShared.getVal(scale, 'y') + sz = GDMLShared.getVal(scale, 'z') + scaleVec = FreeCAD.Vector(sx, sy, sz) + mat = FreeCAD.Matrix() + mat.scale(scaleVec) + scaledObj.recompute() + scaledObj.Shape.transformGeometry(mat) + scaledObj.recompute() + scaledObj.addProperty("App::PropertyVector", "scale","Base","scale"). \ + scale=scaleVec + return scaledObj + def createSphere(part, solid, material, colour, px, py, pz, rot, displayMode): from .GDMLObjects import GDMLSphere, ViewProvider # GDMLShared.setTrace(True) @@ -706,7 +730,7 @@ def createTrap(part, solid, material, colour, px, py, pz, rot, displayMode): aunit = getText(solid, 'aunit', 'rad') lunit = getText(solid, 'lunit', "mm") # print z - mytrap = newPartFeature(part, "GDMLTrap:"+getName(solid)) + mytrap = newPartFeature(part, "GDMLTrap_"+getName(solid)) GDMLTrap(mytrap, z, theta, phi, x1, x2, x3, x4, y1, y2, alpha1, aunit, lunit, material, colour) GDMLShared.trace("Position : "+str(px)+','+str(py)+','+str(pz)) @@ -808,6 +832,7 @@ def createTwistedtrap(part, solid, material, colour, px, py, pz, rot, displayMod setDisplayMode(mytrap, displayMode) return mytrap + def createTwistedtrd(part, solid, material, colour, px, py, pz, rot, displayMode): from .GDMLObjects import GDMLTwistedtrd, ViewProvider GDMLShared.trace("CreateTwistedtrd : ") @@ -859,6 +884,7 @@ def createTwistedtubs(part, solid, material, colour, px, py, pz, rot, displayMod setDisplayMode(mypart, displayMode) return mypart + def createXtru(part, solid, material, colour, px, py, pz, rot, displayMode): from .GDMLObjects import GDMLXtru, GDML2dVertex, GDMLSection, \ ViewProvider, ViewProviderExtension @@ -976,6 +1002,7 @@ def createCutTube(part, solid, material, colour, px, py, pz, rot, displayMode): setDisplayMode(mycuttube, displayMode) return mycuttube + def indexVertex(list, name): try: i = list.index(name) @@ -983,6 +1010,7 @@ def indexVertex(list, name): return -1 return i + def createTessellated(part, solid, material, colour, px, py, pz, rot, displayMode): from .GDMLObjects import GDMLTessellated, GDMLTriangular, \ @@ -1025,7 +1053,6 @@ def createTessellated(part, solid, material, colour, px, py, pz, rot, v3pos = len(vertNames) - 1 vertex.append(v3) # print(v3pos) - vType = elem.get('type') if elem.tag == 'triangular': faces.append([v1pos, v2pos, v3pos]) if elem.tag == 'quadrangular': @@ -1056,6 +1083,7 @@ def createTessellated(part, solid, material, colour, px, py, pz, rot, setDisplayMode(myTess, displayMode) return myTess + def parseMultiUnion(part, solid, material, colour, px, py, pz, rot, displayMode): global solids @@ -1202,6 +1230,10 @@ def createSolid(part, solid, material, colour, px, py, pz, rot, displayMode): return(createGenericPolyhedra(part, solid, material, colour, px, py, pz, rot, displayMode)) + if case('scaledSolid'): + return(createScaledSolid(part, solid, material, colour, \ + px, py, pz, rot, displayMode)) + if case('sphere'): return(createSphere(part, solid, material, colour, px, py, pz, rot, displayMode)) @@ -1372,6 +1404,7 @@ def parseVolume(parent, name, phylvl, displayMode): GDMLShared.trace("ParseVolume : "+name) expandVolume(parent, name, phylvl, displayMode) + def processVol(vol, parent, phylvl, displayMode): # GDMLShared.setTrace(True) from .GDMLObjects import checkMaterial @@ -1473,7 +1506,7 @@ def processVol(vol, parent, phylvl, displayMode): # If negative always parse otherwise increase level parsePhysVol(True, parent, pv, phylvl, displayMode) - else: # Just Add to structure + else: # Just Add to structure volRef = GDMLShared.getRef(pv, "volumeref") print('volRef : '+str(volRef)) nx, ny, nz = GDMLShared.getPosition(pv) @@ -1529,7 +1562,8 @@ def getItem(element, attribute): # returns None if not found return element.get(attribute) -def processIsotopes(isotopesGrp,mats_xml): + +def processIsotopes(isotopesGrp, mats_xml): from .GDMLObjects import GDMLisotope, ViewProvider for isotope in mats_xml.findall('isotope'): N = int(isotope.get('N')) @@ -1553,7 +1587,7 @@ def processIsotopes(isotopesGrp,mats_xml): 'Value').value = value -def processElements(elementsGrp,mats_xml): +def processElements(elementsGrp, mats_xml): from .GDMLObjects import GDMLelement, GDMLfraction, GDMLcomposite for element in mats_xml.findall('element'): name = element.get('name') @@ -1628,7 +1662,7 @@ def processMaterials(materialGrp, mats_xml, subGrp=None): # print(matType) # print(materialGrp.Group) mGrp = materialGrp.Group[subGrp.index(matType)] - + materialObj = newGroupPython(mGrp, name) GDMLmaterial(materialObj, name) formula = material.get('formula') @@ -1673,7 +1707,6 @@ def processMaterials(materialGrp, mats_xml, subGrp=None): materialObj.addProperty("App::PropertyString", 'Tunit', 'GDMLmaterial', "T ZZZUnit").Tunit = Tunit - Tvalue = GDMLShared.getVal(T, 'value') MEE = material.find('MEE') if MEE is not None: Munit = MEE.get('unit') @@ -1796,6 +1829,7 @@ def processGEANT4(doc, filename): geant4Grp = newGroupPython(materials, "Geant4") processMaterialsG4(geant4Grp, root) + def processMaterialsDocSet(doc, root): print('Process Materials') mats_xml = root.find('materials') @@ -1819,7 +1853,7 @@ def processMaterialsDocSet(doc, root): "Materials") processMaterials(materialsGrp, mats_xml) - + def processNewG4(materialsGrp, mats_xml): print('process new G4') matTypes = ['NIST', 'Element', 'HEP', 'Space', 'BioChemical'] @@ -1827,7 +1861,7 @@ def processNewG4(materialsGrp, mats_xml): newGroupPython(materialsGrp, t) processMaterials(materialsGrp, mats_xml, matTypes) - + def processMaterialsG4(G4rp, root): mats_xml = root.find('materials') if mats_xml is not None: @@ -1836,11 +1870,11 @@ def processMaterialsG4(G4rp, root): elementsGrp = newGroupPython(G4rp, "G4Elements") processElements(elementsGrp, mats_xml) materialsGrp = newGroupPython(G4rp, "G4Materials") - materialsGrp.addProperty('App::PropertyFloat','version','Base'). \ - version = 1.0 + materialsGrp.addProperty('App::PropertyFloat', 'version', 'Base'). \ + version = 1.0 processNewG4(materialsGrp, mats_xml) - + def processDefines(root, doc): GDMLShared.trace("Call set Define") GDMLShared.setDefine(root.find('define')) diff --git a/freecad/gdml/importOBJ.py b/freecad/gdml/importOBJ.py index f7a9eff44..bf9288a91 100644 --- a/freecad/gdml/importOBJ.py +++ b/freecad/gdml/importOBJ.py @@ -170,6 +170,6 @@ def processOBJ(doc,filename) : print('Tag : '+items[0]) break - GDMLTessellated(obj,vertex,faces,'mm',getSelectedMaterial()) + GDMLTessellated(obj,vertex,faces,False,'mm',getSelectedMaterial()) ViewProvider(obj.ViewObject) obj.recompute() diff --git a/freecad/gdml/init_gui.py b/freecad/gdml/init_gui.py index 28d98c7ac..f73ef1c82 100644 --- a/freecad/gdml/init_gui.py +++ b/freecad/gdml/init_gui.py @@ -34,6 +34,7 @@ #from FreeCAD import * import FreeCAD import PartGui +import SketcherGui import MeshGui import FreeCADGui from freecad.gdml import GDMLCommands, GDMLResources @@ -80,24 +81,28 @@ def QT_TRANSLATE_NOOP(scope, text): 'BoxCommand','ConeCommand','ElTubeCommand', \ 'EllipsoidCommand','SphereCommand', \ 'TorusCommand','TrapCommand','TubeCommand', \ + 'Sketcher_NewSketch','Part_Extrude', \ 'BooleanCutCommand','BooleanIntersectionCommand', \ 'BooleanUnionCommand', \ - 'AddCompound','TessellateCommand','TessellateGmshCommand', \ + 'TessellateCommand','TessellateGmshCommand', \ 'DecimateCommand', \ 'Mesh_FromPartShape','Mesh_Evaluation', \ - 'Mesh2TessCommand','Tess2MeshCommand', 'TetrahedronCommand'] + 'Mesh2TessCommand','Tess2MeshCommand', 'TetrahedronCommand', \ + 'AddCompound'] toolbarcommands=['CycleCommand','ColourMapCommand','ExpandCommand', 'ExpandMaxCommand', 'SetMaterialCommand', \ - 'BoxCommand','ConeCommand', \ + 'Separator','Std_Part','BoxCommand','ConeCommand', \ 'ElTubeCommand', 'EllipsoidCommand','SphereCommand', \ - 'TorusCommand','TrapCommand','TubeCommand', - 'BooleanCutCommand','BooleanIntersectionCommand', \ - 'BooleanUnionCommand', \ - 'AddCompound','TessellateCommand','TessellateGmshCommand', \ + 'TorusCommand','TrapCommand','TubeCommand', \ + 'Sketcher_NewSketch','Part_Extrude', \ + 'Separator', 'BooleanCutCommand','BooleanIntersectionCommand', \ + 'BooleanUnionCommand','Separator', \ + 'TessellateCommand','TessellateGmshCommand', \ 'DecimateCommand', \ 'Mesh_FromPartShape','Mesh_Evaluation', \ - 'Mesh2TessCommand','Tess2MeshCommand','TetrahedronCommand'] + 'Mesh2TessCommand','Tess2MeshCommand','TetrahedronCommand', \ + 'AddCompound'] #parttoolbarcommands = ['Part_Cut','Part_Fuse','Part_Common'] #meshtoolbarcommands = ['Mesh_FromPartShape','Mesh_Evaluation'] diff --git a/metadata.txt b/metadata.txt new file mode 100644 index 000000000..9c1a4017e --- /dev/null +++ b/metadata.txt @@ -0,0 +1,2 @@ +workbenches=Part, Mesh +pylibs=lxml, gmsh diff --git a/package.xml b/package.xml new file mode 100644 index 000000000..d422d6504 --- /dev/null +++ b/package.xml @@ -0,0 +1,22 @@ + + + GDML workbench + An external workbench for creating GDML models for Geant4 and Root> + 1.8 Beta + 2022-03-06 + Keith Sloan + LGPL-2.1 + https://github.com/KeithSloan/GDML + https://github.com/KeithSloan/GDML/wiki + https://github.com/KeithSloan/GDML/blob/master/README.md + + + + GDMLWorkbench + ./ + freecad/gdml/Resources/icons/GDMLWorkbench.svg + 0.19.3 + + + +