From 5b6db4ca5231ce46821652ff131422097cc4436e Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Fri, 11 Feb 2022 16:46:12 +0000 Subject: [PATCH 01/46] Support of extruded sketchs thanks to Munther Hindi --- freecad/gdml/GDMLColourMap.py | 621 +-- freecad/gdml/GDMLCommands.py | 2203 +++++----- freecad/gdml/GDMLMaterials.py | 173 +- freecad/gdml/GDMLObjects.py | 6886 ++++++++++++++++--------------- freecad/gdml/GDMLShared.py | 714 ++-- freecad/gdml/exportExtrusion.py | 845 ++++ freecad/gdml/exportGDML.py | 3771 +++++++++-------- 7 files changed, 8359 insertions(+), 6854 deletions(-) create mode 100644 freecad/gdml/exportExtrusion.py diff --git a/freecad/gdml/GDMLColourMap.py b/freecad/gdml/GDMLColourMap.py index fc5ed7fc3..ec4a1744a 100644 --- a/freecad/gdml/GDMLColourMap.py +++ b/freecad/gdml/GDMLColourMap.py @@ -1,41 +1,42 @@ -#************************************************************************** -#* * -#* 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 : * -#* * -#************************************************************************** - -__title__="FreeCAD GDML Workbench - GDMLColourMap" +# ************************************************************************** +# * * +# * 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 : * +# * * +# ************************************************************************** + +__title__ = "FreeCAD GDML Workbench - GDMLColourMap" __author__ = "Keith Sloan" __url__ = ["http://www.freecadweb.org"] import FreeCAD import FreeCADGui -#from PySide2 import QtGui, QtCore +# from PySide2 import QtGui, QtCore from PySide import QtGui, QtCore if FreeCADGui: try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text): "convenience function for Qt translator" return QtGui.QApplication.translate(context, text, None, _encoding) @@ -44,288 +45,330 @@ def translate(context, text): "convenience function for Qt translator" return QtGui.QApplication.translate(context, text, None) + def resetGDMLColourMap(): print('Reset Colour Map') global workBenchColourMap - try : - del workBenchColourMap + try: + del workBenchColourMap except NameError: - pass + pass workBenchColourMap = GDMLColourMap(FreeCADGui.getMainWindow()) + def showGDMLColourMap(): print('Display Colour Map') workBenchColourMap.show() -def lookupColour(col) : + +def lookupColour(col): global workBenchColourMap print('Lookup Colour') - try : + try: mat = workBenchColourMap.lookupColour(col) - + except NameError: workBenchColourMap = GDMLColourMap(FreeCADGui.getMainWindow()) mat = workBenchColourMap.lookupColour(col) - + return mat -#class GDMLColour(QtGui.QWidget): -#class GDMLColour(QtGui.QPushButton): + +# class GDMLColour(QtGui.QWidget): +# class GDMLColour(QtGui.QPushButton): class GDMLColour(QtGui.QLineEdit): - - def __init__(self,colour): - super().__init__() - palette = self.palette() - #palette.setColor(QtGui.QPalette.Button, QtGui.QColor(colour)) - palette.setColor(QtGui.QPalette.Base, QtGui.QColor(colour)) - #palette.setColor(QtGui.QPalette.Window, QtGui.QColor(colour)) - self.setAutoFillBackground(True) - #palette.setColor(QtGui.QPalette.Window, QtGui.QColor(QtGui.qRed)) - #palette.setColor(QtGui.QPalette.Window, QtGui.qRed) - self.setStyleSheet("QPushButton {border-color: black; border: 2px;}") - self.setPalette(palette) - #self.setFlat(True) - self.update() + + def __init__(self, colour): + super().__init__() + palette = self.palette() + # palette.setColor(QtGui.QPalette.Button, QtGui.QColor(colour)) + palette.setColor(QtGui.QPalette.Base, QtGui.QColor(colour)) + # palette.setColor(QtGui.QPalette.Window, QtGui.QColor(colour)) + self.setAutoFillBackground(True) + # palette.setColor(QtGui.QPalette.Window, QtGui.QColor(QtGui.qRed)) + # palette.setColor(QtGui.QPalette.Window, QtGui.qRed) + self.setStyleSheet("QPushButton {border-color: black; border: 2px;}") + self.setPalette(palette) + self.setReadOnly(True) + # self.setFlat(True) + self.update() + class GDMLColourHex(QtGui.QLineEdit): - - def __init__(self,colhex): - super().__init__() - self.insert(colhex) - -class GDMLColourMapEntry(QtGui.QWidget) : - - def __init__(self,colour,colhex,material) : - super().__init__() - print('Map Entry : '+str(colour)) - self.colour = colour - self.hbox = QtGui.QHBoxLayout() - self.hbox.addWidget(GDMLColour(colour)) - self.hbox.addWidget(GDMLColourHex(colhex)) - self.hbox.addWidget(material) - self.setLayout(self.hbox) - - def dataPicker(self): - print('DataPicker') - -class GDMLColourMapList(QtGui.QScrollArea) : - - def __init__(self,matList) : - super().__init__() - # Scroll Area which contains the widgets, set as the centralWidget - # Widget that contains the collection of Vertical Box - self.widget = QtGui.QWidget() - self.matList = matList - # The Vertical Box that contains the Horizontal Boxes of labels and buttons - self.vbox = QtGui.QVBoxLayout() - self.widget.setLayout(self.vbox) - - #Scroll Area Properties - self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) - self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setWidgetResizable(True) - self.setWidget(self.widget) - - def addEntry(self, colour,colhex,mat) : - from .GDMLMaterials import GDMLMaterial - print('Add Entry') - matWidget = GDMLMaterial(self.matList,mat) - self.vbox.addWidget(GDMLColourMapEntry(colour,colhex,matWidget)) - - def getMaterial(self, index) : - #print(dir(self.vbox)) - item = self.vbox.itemAt(index).widget() - #item.dumpObjectTree() - #cb = item.findChild(QtGui.QComboBox,'GDMLMaterial') - cb = item.findChildren(QtGui.QComboBox)[0] - m = cb.currentText() - print(m) - return(m) - -class GDMLColourMap(QtGui.QDialog) : -#class GDMLColourMap(QtGui.QMainWindow) : - def __init__(self, parent) : - super(GDMLColourMap, self).__init__(parent, QtCore.Qt.Tool) - self.initUI() - - def initUI(self): - self.result = userCancelled - # create our window - # define window xLoc,yLoc,xDim,yDim - self.setGeometry( 150, 450, 650, 550) - self.setWindowTitle("Map FreeCAD Colours to GDML Materials") - self.setMouseTracking(True) - self.buttonNew = QtGui.QPushButton(translate('GDML','New')) - self.buttonNew.clicked.connect(self.onNew) - self.buttonLoad = QtGui.QPushButton(translate('GDML','Load')) - self.buttonLoad.clicked.connect(self.onLoad) - self.buttonSave = QtGui.QPushButton(translate('GDML','Save')) - self.buttonSave.clicked.connect(self.onSave) - self.buttonScan = QtGui.QPushButton(translate('GDML','Scan')) - self.buttonScan.clicked.connect(self.onScan) - headerLayout = QtGui.QHBoxLayout() - headerLayout.addWidget(self.buttonNew) - headerLayout.addWidget(self.buttonLoad) - headerLayout.addWidget(self.buttonSave) - headerLayout.addWidget(self.buttonScan) - self.coloursLayout = QtGui.QGridLayout() - mainLayout = QtGui.QVBoxLayout(self) - mainLayout.addLayout(headerLayout) - mainLayout.addLayout(self.coloursLayout) - from .GDMLMaterials import getMaterialsList - self.matList = getMaterialsList() - self.mapList = GDMLColourMapList(self.matList) - self.colorDict = {} - self.scanDocument(1) - print(self.colorDict) - #for c in self.colorList : - # self.mapList.addEntry(QtGui.QColor(c[0]*255,c[1]*255,c[2]*255)) - # create Labels - self.label1 = self.mapList - self.coloursLayout.addWidget(self.label1,0,0) - # cancel button - cancelButton = QtGui.QPushButton('Cancel', self) - cancelButton.clicked.connect(self.onCancel) - cancelButton.setAutoDefault(True) - self.coloursLayout.addWidget(cancelButton, 2, 1) - - # OK button - okButton = QtGui.QPushButton('Set Materials', self) - okButton.clicked.connect(self.onOk) - self.coloursLayout.addWidget(okButton, 2, 0) - # now make the window visible - self.setLayout(mainLayout) - self.show() - - def scanDocument(self, action) : - doc = FreeCAD.ActiveDocument - print('Active') - print(doc) - if doc is not None : - #print(dir(doc)) - if hasattr(doc,'Objects') : - #print(doc.Objects) - #self.colorList = [] - for obj in doc.Objects : - #print(dir(obj)) - if hasattr(obj,'ViewObject') : - #print(dir(obj.ViewObject)) - if hasattr(obj.ViewObject,'isVisible') : - if obj.ViewObject.isVisible : - if hasattr(obj.ViewObject,'ShapeColor') : - colour = obj.ViewObject.ShapeColor - #print(colour) - colhex = '#'+''.join('{:02x}'.format(round(v*255)) \ - for v in colour) - if action == 1 : # Build Map - if not( colhex in self.colorDict) : - print(f'Add colour {colhex} {colour}') - if hasattr(obj,'material') : - material = obj.material - else : - material = None - self.addColour2Map(colour,colhex,material) - self.colorDict.update([(colhex,len(self.colorDict))]) - if action == 2 : # Update Object Material - if hasattr(obj,'Shape') : - mapIdx = self.colorDict[colhex] - print(f'Found {colhex} : id {mapIdx}') - print(obj.Label) - m = self.mapList.getMaterial(mapIdx) - # Only add - if not hasattr(obj,'material') : - obj.addProperty("App::PropertyEnumeration"\ - ,"material","GDML","Material") - obj.material = self.matList - # Ignore GDML objects which will have Proxy - if not hasattr(obj,'Proxy' ) : - obj.material=self.matList.index(m) - - def addColour2Map(self,c,hex,material) : - self.mapList.addEntry(QtGui.QColor(c[0]*255,c[1]*255,c[2]*255),hex,material) - - def lookupColour(self, col) : - print('Lookup Colour') - idx = self.colorList.index(col) - print(idx) - entry = self.mapList.vbox.itemAt(idx).widget() - print(entry) - mat = entry.hbox.itemAt(1).widget().currentText() - print(mat) - return mat - - def onCancel(self): - self.result = userCancelled - self.close() - - def onOk(self): - self.result = userOK - print('Set Materials') - self.scanDocument(2) - #self.close() - - def onNew(self) : - print('onNew') - - def onLoad(self) : - print('onLoad') - - def onSave(self) : - print('onSave') - # Save materials - from .exportGDML import exportMaterials - from .init_gui import joinDir - matGrp = FreeCAD.ActiveDocument.getObject('Materials') - if matGrp is not None : - exportMaterials(matGrp,joinDir('Resources/MapMaterials.xml')) - # Save Color Dictionary - f = open(joinDir('Resources/ColorDict.csv'),'w') - for key, value in self.colorDict.items(): - #print(f'key {key} value {value}') - #print(self.mapList.getMaterial(value)) - f.write(f'{key},{self.mapList.getMaterial(value)}\n') - f.close() - - def onScan(self) : - print('onScan') - self.scanDocument(1) - print('Update Layout') - self.coloursLayout.update() - - def getGDMLMaterials(self): - print('getGDMLMaterials') - matList = [] - doc = FreeCAD.activeDocument() - try : - materials = doc.Materials - geant4 = doc.Geant4 - g4Mats = doc.getObject('G4Materials') - - except : - from .importGDML import processXML - from .init_gui import joinDir - - print('Load Geant4 Materials XML') - processXML(doc,joinDir("Resources/Geant4Materials.xml")) - materials = doc.Materials - try : - if materials is not None : - for m in materials.OutList : - matList.append(m.Label) - #print(matList) - except : - pass - - try : - if g4Mats is not None : - for m in g4Mats.OutList : - matList.append(m.Label) - #print(matList) - except : - pass - - return matList + + def __init__(self, colhex): + super().__init__() + self.insert(colhex) + self.setReadOnly(True) + + +class GDMLColourMapEntry(QtGui.QWidget): + + def __init__(self, colour, colhex, material): + super().__init__() + print('Map Entry : '+str(colour)) + self.colour = colour + self.hbox = QtGui.QHBoxLayout() + self.hbox.addWidget(GDMLColour(colour)) + self.hbox.addWidget(GDMLColourHex(colhex)) + self.hbox.addWidget(material) + self.setLayout(self.hbox) + + def dataPicker(self): + print('DataPicker') + + +class GDMLColourMapList(QtGui.QScrollArea): + + def __init__(self, matList): + super().__init__() + # Scroll Area which contains the widgets, set as the centralWidget + # Widget that contains the collection of Vertical Box + self.widget = QtGui.QWidget() + self.matList = matList + # The Vertical Box that contains the Horizontal Boxes of labels and buttons + self.vbox = QtGui.QVBoxLayout() + self.widget.setLayout(self.vbox) + + # Scroll Area Properties + self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.setWidgetResizable(True) + self.setWidget(self.widget) + + def addEntry(self, colour, colhex, mat): + from .GDMLMaterials import GDMLMaterial + print('Add Entry') + matWidget = GDMLMaterial(self.matList, mat) + self.vbox.addWidget(GDMLColourMapEntry(colour, colhex, matWidget)) + + def getMaterial(self, index): + # print(dir(self.vbox)) + item = self.vbox.itemAt(index).widget() + # item.dumpObjectTree() + # cb = item.findChild(QtGui.QComboBox,'GDMLMaterial') + cb = item.findChildren(QtGui.QComboBox)[0] + m = cb.currentText() + print(m) + return(m) + + def setMaterial(self, index, mat): + item = self.vbox.itemAt(index).widget() + cb = item.findChildren(QtGui.QComboBox)[0] + matIndex = cb.findText(mat) + if matIndex != -1: + cb.setCurrentIndex(matIndex) + + +class GDMLColourMap(QtGui.QDialog): + def __init__(self, parent): + super(GDMLColourMap, self).__init__(parent, QtCore.Qt.Tool) + self.initUI() + + def initUI(self): + self.result = userCancelled + # create our window + # define window xLoc,yLoc,xDim,yDim + self.setGeometry(150, 450, 650, 550) + self.setWindowTitle("Map FreeCAD Colours to GDML Materials") + self.setMouseTracking(True) + self.buttonNew = QtGui.QPushButton(translate('GDML', 'New')) + self.buttonNew.clicked.connect(self.onNew) + self.buttonLoad = QtGui.QPushButton(translate('GDML', 'Load')) + self.buttonLoad.clicked.connect(self.onLoad) + self.buttonSave = QtGui.QPushButton(translate('GDML', 'Save')) + self.buttonSave.clicked.connect(self.onSave) + self.buttonScan = QtGui.QPushButton(translate('GDML', 'Scan')) + self.buttonScan.clicked.connect(self.onScan) + headerLayout = QtGui.QHBoxLayout() + headerLayout.addWidget(self.buttonNew) + headerLayout.addWidget(self.buttonLoad) + headerLayout.addWidget(self.buttonSave) + headerLayout.addWidget(self.buttonScan) + self.coloursLayout = QtGui.QGridLayout() + mainLayout = QtGui.QVBoxLayout(self) + mainLayout.addLayout(headerLayout) + mainLayout.addLayout(self.coloursLayout) + from .GDMLMaterials import getMaterialsList + self.matList = getMaterialsList() + self.mapList = GDMLColourMapList(self.matList) + self.colorDict = {} + self.scanDocument(1) + print(self.colorDict) + # for c in self.colorList : + # self.mapList.addEntry(QtGui.QColor(c[0]*255,c[1]*255,c[2]*255)) + # create Labels + self.label1 = self.mapList + self.coloursLayout.addWidget(self.label1, 0, 0) + # cancel button + cancelButton = QtGui.QPushButton('Cancel', self) + cancelButton.clicked.connect(self.onCancel) + cancelButton.setAutoDefault(True) + self.coloursLayout.addWidget(cancelButton, 2, 1) + + # OK button + okButton = QtGui.QPushButton('Set Materials', self) + okButton.clicked.connect(self.onOk) + self.coloursLayout.addWidget(okButton, 2, 0) + # now make the window visible + self.setLayout(mainLayout) + self.show() + + def scanDocument(self, action): + doc = FreeCAD.ActiveDocument + print('Active') + print(doc) + if doc is None: + return + # print(dir(doc)) + if hasattr(doc, 'Objects'): + # print(doc.Objects) + # self.colorList = [] + for obj in doc.Objects: + # print(dir(obj)) + if hasattr(obj, 'ViewObject'): + # print(dir(obj.ViewObject)) + if hasattr(obj.ViewObject, 'isVisible'): + if obj.ViewObject.isVisible: + if hasattr(obj.ViewObject, 'ShapeColor'): + colour = obj.ViewObject.ShapeColor + # print(colour) + colhex = '#'+''.join('{:02x}'.format(round(v*255)) + for v in colour) + if action == 1: # Build Map + if not(colhex in self.colorDict): + print(f'Add colour {colhex} {colour}') + if hasattr(obj, 'material'): + material = obj.material + else: + material = None + self.addColour2Map(colour, colhex, material) + self.colorDict.update([(colhex, len(self.colorDict))]) + if action == 2: # Update Object Material + if hasattr(obj, 'Shape'): + mapIdx = self.colorDict[colhex] + print(f'Found {colhex} : id {mapIdx}') + print(obj.Label) + m = self.mapList.getMaterial(mapIdx) + # Only add + if not hasattr(obj, 'material'): + obj.addProperty("App::PropertyEnumeration", + "material", "GDML", "Material") + obj.material = self.matList + # Ignore GDML objects which will have Proxy + if not hasattr(obj, 'Proxy'): + obj.material = self.matList.index(m) + + def addColour2Map(self, c, hex, material): + self.mapList.addEntry(QtGui.QColor(c[0]*255, c[1]*255, + c[2]*255), hex, material) + + def lookupColour(self, col): + print('Lookup Colour') + idx = self.colorList.index(col) + print(idx) + entry = self.mapList.vbox.itemAt(idx).widget() + print(entry) + mat = entry.hbox.itemAt(1).widget().currentText() + print(mat) + return mat + + def onCancel(self): + self.result = userCancelled + self.close() + + def onOk(self): + self.result = userOK + print('Set Materials') + self.scanDocument(2) + # self.close() + + def onNew(self): + print('onNew') + + def hex_to_rgb(self, value): + value = value.lstrip('#') + lv = len(value) + return tuple(int(value[i:i + lv // 3], 16) + for i in range(0, lv, lv // 3)) + + def onLoad(self): + import csv + from .importGDML import processXML, joinDir + print('onLoad') + processXML(FreeCAD.ActiveDocument, joinDir('Resources/MapMaterials.xml')) + # reset mapList + self.mapList = GDMLColourMapList(self.matList) + with open(joinDir('Resources/ColorDict.csv'), 'r') as file: + reader = csv.reader(file) + for i, row in enumerate(reader): + colhex = row[0] + colour = self.hex_to_rgb(colhex) + print(f'colour : {colour}') + material = row[1] + print(row) + print(row[1]) + idx = self.matList.index(row[1]) + self.addColour2Map(colour, colhex, material) + self.colorDict.update([(colhex, idx)]) + self.mapList.setMaterial(i, material) + + def onSave(self): + print('onSave') + # Save materials + from .exportGDML import exportMaterials + from .init_gui import joinDir + matGrp = FreeCAD.ActiveDocument.getObject('Materials') + if matGrp is not None: + exportMaterials(matGrp, joinDir('Resources/MapMaterials.xml')) + # Save Color Dictionary + f = open(joinDir('Resources/ColorDict.csv'), 'w') + for key, value in self.colorDict.items(): + print(f'key {key} value {value}') + print(self.mapList.getMaterial(value)) + f.write(f'{key},{self.mapList.getMaterial(value)}\n') + f.close() + + def onScan(self): + print('onScan') + self.scanDocument(1) + print('Update Layout') + self.coloursLayout.update() + + def getGDMLMaterials(self): + print('getGDMLMaterials') + matList = [] + doc = FreeCAD.activeDocument() + try: + materials = doc.Materials + geant4 = doc.Geant4 + g4Mats = doc.getObject('G4Materials') + + except: + from .importGDML import processXML + from .init_gui import joinDir + + print('Load Geant4 Materials XML') + processXML(doc, joinDir("Resources/Geant4Materials.xml")) + materials = doc.Materials + try: + if materials is not None: + for m in materials.OutList: + matList.append(m.Label) + # print(matList) + except: + pass + + try: + if g4Mats is not None: + for m in g4Mats.OutList: + matList.append(m.Label) + # print(matList) + except: + pass + + return matList # Class definitions @@ -334,6 +377,6 @@ def getGDMLMaterials(self): # Constant definitions global userCancelled, userOK -userCancelled = "Cancelled" -userOK = "OK" +userCancelled = "Cancelled" +userOK = "OK" diff --git a/freecad/gdml/GDMLCommands.py b/freecad/gdml/GDMLCommands.py index 4f9cb00c9..3e39a30a4 100644 --- a/freecad/gdml/GDMLCommands.py +++ b/freecad/gdml/GDMLCommands.py @@ -1,29 +1,29 @@ -#************************************************************************** -#* * -#* 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 : * -#* * -#************************************************************************** - -__title__="FreeCAD GDML Workbench - GUI Commands" +# ************************************************************************** +# * * +# * 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 : * +# * * +# ************************************************************************** + +__title__ = "FreeCAD GDML Workbench - GUI Commands" __author__ = "Keith Sloan" __url__ = ["http://www.freecadweb.org"] @@ -31,12 +31,14 @@ This Script includes the GUI Commands of the GDML module ''' -import FreeCAD,FreeCADGui +import FreeCAD, FreeCADGui from PySide import QtGui, QtCore + if FreeCAD.GuiUp: try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text): "convenience function for Qt translator" return QtGui.QApplication.translate(context, text, None, _encoding) @@ -45,11 +47,12 @@ def translate(context, text): "convenience function for Qt translator" return QtGui.QApplication.translate(context, text, None) + class importPrompt(QtGui.QDialog): def __init__(self, *args): super(importPrompt, self).__init__() self.initUI() - + def initUI(self): importButton = QtGui.QPushButton('Import') importButton.clicked.connect(self.onImport) @@ -58,15 +61,15 @@ def initUI(self): # buttonBox = QtGui.QDialogButtonBox() buttonBox.setFixedWidth(400) - #buttonBox = Qt.QDialogButtonBox(QtCore.Qt.Vertical) + # buttonBox = Qt.QDialogButtonBox(QtCore.Qt.Vertical) buttonBox.addButton(importButton, QtGui.QDialogButtonBox.ActionRole) buttonBox.addButton(scanButton, QtGui.QDialogButtonBox.ActionRole) # mainLayout = QtGui.QVBoxLayout() mainLayout.addWidget(buttonBox) self.setLayout(mainLayout) - #self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) - # define window xLoc,yLoc,xDim,yDim + # self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) + # define window xLoc,yLoc,xDim,yDim self.setGeometry( 650, 650, 0, 50) self.setWindowTitle("Choose an Option ") self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) @@ -80,362 +83,426 @@ def onScan(self): self.retStatus = 2 self.close() -def getSelectedMaterial() : + +def getSelectedMaterial(): from .exportGDML import nameFromLabel from .GDMLObjects import GDMLmaterial list = FreeCADGui.Selection.getSelection() - if list is not None : - for obj in list : - if hasattr(obj,'Proxy') : - if isinstance(obj.Proxy,GDMLmaterial) == True : - return nameFromLabel(obj.Label) + if list is not None: + for obj in list: + if hasattr(obj, 'Proxy'): + if isinstance(obj.Proxy, GDMLmaterial) is True: + return nameFromLabel(obj.Label) return 0 -def getSelectedPM() : + +def getSelectedPM(): from .exportGDML import nameFromLabel from .GDMLObjects import GDMLmaterial objPart = None material = 0 list = FreeCADGui.Selection.getSelection() - if list is not None : - for obj in list : - if hasattr(obj,'Proxy') : - if isinstance(obj.Proxy,GDMLmaterial) == True and \ - material == 0 : - material = nameFromLabel(obj.Label) - - if obj.TypeId == 'App::Part' and objPart is None : + if list is not None: + for obj in list: + if hasattr(obj, 'Proxy'): + if isinstance(obj.Proxy, GDMLmaterial) is True and \ + material == 0: + material = nameFromLabel(obj.Label) + + if obj.TypeId == 'App::Part' and objPart is None: objPart = obj - if objPart is not None and material !=0 : + if objPart is not None and material != 0: return objPart, material return objPart, material -def createPartVol(obj) : + +def createPartVol(obj): # Create Part(GDML Vol) Shared with a number of Features LVname = 'LV-'+obj.Label - if hasattr(obj,'InList') : - if len(obj.InList) > 0 : - parent = obj.InList[0] - vol=parent.newObject("App::Part",LVname) - else : - vol=FreeCAD.ActiveDocument.addObject("App::Part",LVname) - return vol + if hasattr(obj, 'InList'): + if len(obj.InList) > 0: + parent = obj.InList[0] + vol = parent.newObject("App::Part", LVname) + else: + vol = FreeCAD.ActiveDocument.addObject("App::Part", LVname) + return vol return None + class ColourMapFeature: - def Activated(self): - from PySide import QtGui, QtCore - #import sys - from .GDMLColourMap import resetGDMLColourMap, showGDMLColourMap - - print('Add colour Map') - resetGDMLColourMap() - showGDMLColourMap() - return - - #myWidget = QtGui.QDockWidget() - #mainWin = FreeCADGui.getMainWindow() - #mainWin.addDockWidget(QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.TopDockWidgetArea, \ - mainWin.addDockWidget(QtCore.Qt.LeftDockWidgetArea or QtCore.Qt.TopDockWidgetArea, \ - myWidget) - #mainWin.addDockWidget(Qt::LeftDockWidgetArea or Qt::TopDockWidgetArea, myWidget) - #myWidget.setObjectName("ColourMap") - #myWidget.resize(QtCore.QSize(300,100)) - #title = QtGui.QLabel("Colour Mapping to GDML Materials") - #title.setIndent(100) - #myWidget.setTitleBarWidget(title) - #label = QtGui.QLabel("Colour Mapping to GDML Materials",myWidget) - - def IsActive(self): - if FreeCAD.ActiveDocument is None: - return False - else: - return True - - def GetResources(self): - return {'Pixmap' : 'GDMLColourMapFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLColourMapFeature',\ - 'Add Colour Map'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLColourMapFeature',\ - 'Add Colour Map')} - - -class GDMLSetColour(QtGui.QDialog) : - def __init__(self, selList): - super(GDMLSetColour, self).__init__() - self.SelList = selList - self.initUI() - - def initUI(self) : - from .GDMLMaterials import GDMLMaterial, getMaterialsList - - print('initUI') - self.setGeometry( 150, 150, 250, 250) - self.setWindowTitle("Set GDML Material") - self.setMouseTracking(True) - self.buttonSet = QtGui.QPushButton(translate('GDML','Set Material')) - self.buttonSet.clicked.connect(self.onSet) - self.matList = getMaterialsList() - self.material = GDMLMaterial(self.matList,None) - mainLayout = QtGui.QVBoxLayout() - mainLayout.addWidget(self.material) - mainLayout.addWidget(self.buttonSet) - self.setLayout(mainLayout) - self.show() - - def onSet(self) : - mat = self.material.getItem() - print(f'Set Material {mat}') - for sel in self.SelList : - obj = sel.Object - if hasattr(obj,'material') : - obj.material = mat - else : - obj.addProperty("App::PropertyEnumeration","material", \ - "GDML","Material") - obj.material = self.matList - obj.material = self.matList.index(mat) + def Activated(self): + from PySide import QtGui, QtCore + # import sys + from .GDMLColourMap import resetGDMLColourMap, showGDMLColourMap + + print('Add colour Map') + resetGDMLColourMap() + showGDMLColourMap() + return + + # myWidget = QtGui.QDockWidget() + # mainWin = FreeCADGui.getMainWindow() + # mainWin.addDockWidget(QtCore.Qt.LeftDockWidgetArea | QtCore.Qt.TopDockWidgetArea, \ + mainWin.addDockWidget(QtCore.Qt.LeftDockWidgetArea or QtCore.Qt.TopDockWidgetArea, \ + myWidget) + # mainWin.addDockWidget(Qt::LeftDockWidgetArea or Qt::TopDockWidgetArea, myWidget) + # myWidget.setObjectName("ColourMap") + # myWidget.resize(QtCore.QSize(300,100)) + # title = QtGui.QLabel("Colour Mapping to GDML Materials") + # title.setIndent(100) + # myWidget.setTitleBarWidget(title) + # label = QtGui.QLabel("Colour Mapping to GDML Materials",myWidget) + + def IsActive(self): + if FreeCAD.ActiveDocument is None: + return False + else: + return True + + def GetResources(self): + return {'Pixmap': 'GDMLColourMapFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLColourMapFeature', + 'Add Colour Map'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLColourMapFeature', + 'Add Colour Map')} + + +class GDMLSetMaterial(QtGui.QDialog): + def __init__(self, selList): + super(GDMLSetMaterial, self).__init__() + self.SelList = selList + self.initUI() + + def initUI(self): + from .GDMLMaterials import GDMLMaterial, getGroupedMaterials + + print('initUI') + self.setGeometry(150, 150, 250, 250) + self.setWindowTitle("Set GDML Material") + self.setMouseTracking(True) + self.buttonSet = QtGui.QPushButton(translate('GDML', 'Set Material')) + self.buttonSet.clicked.connect(self.onSet) + self.groupedMaterials = getGroupedMaterials() # this build, then returns all materials + self.groupsCombo = QtGui.QComboBox() + groups = [group for group in self.groupedMaterials] + self.groupsCombo.addItems(groups) + self.groupsCombo.currentIndexChanged.connect(self.groupChanged) + self.materialComboBox = QtGui.QComboBox() + self.materialComboBox.addItems(self.groupedMaterials[groups[0]]) + self.matList = [] + for group in self.groupedMaterials: + self.matList += self.groupedMaterials[group] + self.completer = QtGui.QCompleter(self.matList, self) + self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) + self.materialComboBox.setCompleter(self.completer) + self.materialComboBox.setEditable(True) + self.materialComboBox.currentTextChanged.connect(self.materialChanged) + self.lineedit = QtGui.QLineEdit() + self.lineedit.setCompleter(self.completer) + self.completer.activated.connect(self.completionActivated) + # self.materialComboBox.setEditable(False) + combosLayout = QtGui.QHBoxLayout() + combosLayout.addWidget(self.groupsCombo) + combosLayout.addWidget(self.materialComboBox) + mainLayout = QtGui.QVBoxLayout() + mainLayout.addWidget(self.lineedit) + mainLayout.addItem(combosLayout) + mainLayout.addWidget(self.buttonSet) + self.setLayout(mainLayout) + obj = self.SelList[0].Object + if hasattr(obj, 'material'): + mat = obj.material + self.lineedit.setText(mat) + self.setMaterial(mat) + self.show() + + def setMaterial(self, text): + from .GDMLObjects import GroupedMaterials + for i, group in enumerate(GroupedMaterials): + if text in GroupedMaterials[group]: + self.groupsCombo.blockSignals(True) + self.groupsCombo.setCurrentIndex(i) + self.groupsCombo.blockSignals(False) + self.groupChanged(i) + self.materialComboBox.blockSignals(True) + self.materialComboBox.setCurrentText(text) + self.materialComboBox.blockSignals(False) + + def completionActivated(self, text): + self.setMaterial(text) + + def groupChanged(self, index): + from .GDMLObjects import GroupedMaterials + self.materialComboBox.blockSignals(True) + self.materialComboBox.clear() + group = self.groupsCombo.currentText() + self.materialComboBox.addItems(GroupedMaterials[group]) + self.materialComboBox.blockSignals(False) + + def materialChanged(self, text): + self.lineedit.setText(text) + + def onSet(self): + # mat = self.materialComboBox.currentText() + mat = self.lineedit.text() + if mat not in self.matList: + print(f'Material {mat} not defined') + return + + print(f'Set Material {mat}') + for sel in self.SelList: + obj = sel.Object + if hasattr(obj, 'material'): + obj.material = mat + else: + obj.addProperty("App::PropertyEnumeration", "material", + "GDML", "Material") + obj.material = self.matList + obj.material = self.matList.index(mat) class SetMaterialFeature: - def Activated(self): - from PySide import QtGui, QtCore - - print('Add SetMaterial') - cnt = 0 - sel = FreeCADGui.Selection.getSelectionEx() - #print(sel) - set = [] - for s in sel : - #print(s) - #print(dir(s)) - if hasattr(s.Object,'Shape') : - cnt += 1 - set.append(s) - if cnt > 0 : - dialog = GDMLSetColour(set) - dialog.exec_() - return - - def IsActive(self): - if FreeCAD.ActiveDocument is None: - return False - else: - return True - - def GetResources(self): - return {'Pixmap' : 'GDML_SetMaterial', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_SetMaterial',\ - 'Set Material'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_SetMaterial',\ - 'Set Material')} - -class BooleanCutFeature : - - #def IsActive(self): + def Activated(self): + from PySide import QtGui, QtCore + + print('Add SetMaterial') + cnt = 0 + sel = FreeCADGui.Selection.getSelectionEx() + # print(sel) + set = [] + for s in sel: + # print(s) + # print(dir(s)) + if hasattr(s.Object, 'Shape'): + cnt += 1 + set.append(s) + if cnt > 0: + dialog = GDMLSetMaterial(set) + dialog.exec_() + return + + def IsActive(self): + if FreeCAD.ActiveDocument is None: + return False + else: + return True + + def GetResources(self): + return {'Pixmap': 'GDML_SetMaterial', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_SetMaterial', + 'Set Material'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDML_SetMaterial', + 'Set Material')} + + +class BooleanCutFeature: + + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): - import Part sel = FreeCADGui.Selection.getSelectionEx() - if len(sel) == 2 : - print(sel) - selObj = 'Gui::SelectionObject' - if sel[0].TypeId == selObj and sel[1].TypeId == selObj : - if sel[0].Object.TypeId == 'App::Part' and \ - sel[1].Object.TypeId == 'App::Part' : - print('Boolean Cut') - if len(sel[0].Object.InList) > 0 : - parent = sel[0].Object.InList[0] - print('Parent : '+parent.Label) - baseVol = sel[0].Object - print('Base Vol : '+baseVol.Label) - toolVol = sel[1].Object - print('Tool Vol : '+toolVol.Label) - print(sel[0].Object.OutList) - base = sel[0].Object.OutList[-1] - print('Base : '+base.Label) - tool = sel[1].Object.OutList[-1] - print('Tool : '+tool.Label) - print('Remove Base') - baseVol.removeObject(base) - print('Adjust Base Links') - base.adjustRelativeLinks(baseVol) - toolVol.removeObject(tool) - tool.adjustRelativeLinks(toolVol) - boolVol = parent.newObject('App::Part','Bool-Cut') - boolVol.addObject(base) - boolVol.addObject(tool) - boolObj = boolVol.newObject('Part::Cut','Cut') - boolObj.Placement = sel[0].Object.Placement - boolObj.Base = base - boolObj.Tool = tool - boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ - - sel[0].Object.Placement.Base - boolObj.Tool.setEditorMode('Placement',0) - print('Remove Base Vol') - FreeCAD.ActiveDocument.removeObject(baseVol.Label) - FreeCAD.ActiveDocument.removeObject(toolVol.Label) - boolObj.recompute() - else : + if len(sel) == 2: + print(sel) + selObj = 'Gui::SelectionObject' + if sel[0].TypeId == selObj and sel[1].TypeId == selObj: + if sel[0].Object.TypeId == 'App::Part' and \ + sel[1].Object.TypeId == 'App::Part': + print('Boolean Cut') + if len(sel[0].Object.InList) > 0: + parent = sel[0].Object.InList[0] + print('Parent : '+parent.Label) + baseVol = sel[0].Object + print('Base Vol : '+baseVol.Label) + toolVol = sel[1].Object + print('Tool Vol : '+toolVol.Label) + print(sel[0].Object.OutList) + base = sel[0].Object.OutList[-1] + print('Base : '+base.Label) + tool = sel[1].Object.OutList[-1] + print('Tool : '+tool.Label) + print('Remove Base') + baseVol.removeObject(base) + print('Adjust Base Links') + base.adjustRelativeLinks(baseVol) + toolVol.removeObject(tool) + tool.adjustRelativeLinks(toolVol) + boolVol = parent.newObject('App::Part', 'Bool-Cut') + boolVol.addObject(base) + boolVol.addObject(tool) + boolObj = boolVol.newObject('Part::Cut', 'Cut') + boolObj.Placement = sel[0].Object.Placement + boolObj.Base = base + boolObj.Tool = tool + boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ + - sel[0].Object.Placement.Base + boolObj.Tool.setEditorMode('Placement', 0) + print('Remove Base Vol') + FreeCAD.ActiveDocument.removeObject(baseVol.Label) + FreeCAD.ActiveDocument.removeObject(toolVol.Label) + boolObj.recompute() + else: print('No Parent Volume/Part') def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDML_Cut', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Cut'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Cut')} + return {'Pixmap': 'GDML_Cut', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Cut'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Cut')} + -class BooleanIntersectionFeature : +class BooleanIntersectionFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): import Part sel = FreeCADGui.Selection.getSelectionEx() - if len(sel) == 2 : - print(sel) - selObj = 'Gui::SelectionObject' - if sel[0].TypeId == selObj and sel[1].TypeId == selObj : - if sel[0].Object.TypeId == 'App::Part' and \ - sel[1].Object.TypeId == 'App::Part' : - print('Boolean Intersection') - if len(sel[0].Object.InList) > 0 : - parent = sel[0].Object.InList[0] - print('Parent : '+parent.Label) - baseVol = sel[0].Object - print('Base Vol : '+baseVol.Label) - toolVol = sel[1].Object - print('Tool Vol : '+toolVol.Label) - baseVol = sel[0].Object - print(sel[0].Object.OutList) - base = sel[0].Object.OutList[-1] - print('Base : '+base.Label) - tool = sel[1].Object.OutList[-1] - print('Tool : '+tool.Label) - print('Remove Base') - baseVol.removeObject(base) - print('Adjust Base Links') - base.adjustRelativeLinks(baseVol) - toolVol.removeObject(tool) - tool.adjustRelativeLinks(toolVol) - boolVol = parent.newObject('App::Part','Bool-Intersection') - boolVol.addObject(base) - boolVol.addObject(tool) - boolObj = boolVol.newObject('Part::Common','Common') - boolObj.Placement = sel[0].Object.Placement - boolObj.Base = base - boolObj.Tool = tool - boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ - - sel[0].Object.Placement.Base - boolObj.Tool.setEditorMode('Placement',0) - FreeCAD.ActiveDocument.removeObject(baseVol.Label) - FreeCAD.ActiveDocument.removeObject(toolVol.Label) - boolObj.recompute() - else : - print('No Parent Volume/Part') + if len(sel) == 2: + print(sel) + selObj = 'Gui::SelectionObject' + if sel[0].TypeId == selObj and sel[1].TypeId == selObj: + if sel[0].Object.TypeId == 'App::Part' and \ + sel[1].Object.TypeId == 'App::Part' : + print('Boolean Intersection') + if len(sel[0].Object.InList) > 0: + parent = sel[0].Object.InList[0] + print('Parent : '+parent.Label) + baseVol = sel[0].Object + print('Base Vol : '+baseVol.Label) + toolVol = sel[1].Object + print('Tool Vol : '+toolVol.Label) + baseVol = sel[0].Object + print(sel[0].Object.OutList) + base = sel[0].Object.OutList[-1] + print('Base : '+base.Label) + tool = sel[1].Object.OutList[-1] + print('Tool : '+tool.Label) + print('Remove Base') + baseVol.removeObject(base) + print('Adjust Base Links') + base.adjustRelativeLinks(baseVol) + toolVol.removeObject(tool) + tool.adjustRelativeLinks(toolVol) + boolVol = parent.newObject('App::Part', 'Bool-Intersection') + boolVol.addObject(base) + boolVol.addObject(tool) + boolObj = boolVol.newObject('Part::Common', 'Common') + boolObj.Placement = sel[0].Object.Placement + boolObj.Base = base + boolObj.Tool = tool + boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ + - sel[0].Object.Placement.Base + boolObj.Tool.setEditorMode('Placement', 0) + FreeCAD.ActiveDocument.removeObject(baseVol.Label) + FreeCAD.ActiveDocument.removeObject(toolVol.Label) + boolObj.recompute() + else: + print('No Parent Volume/Part') def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDML_Intersection', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Intersection'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Intersection')} + return {'Pixmap': 'GDML_Intersection', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Intersection'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Intersection')} + -class BooleanUnionFeature : +class BooleanUnionFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): import Part sel = FreeCADGui.Selection.getSelectionEx() - if len(sel) == 2 : - print(sel) - selObj = 'Gui::SelectionObject' - if sel[0].TypeId == selObj and sel[1].TypeId == selObj : - if sel[0].Object.TypeId == 'App::Part' and \ - sel[1].Object.TypeId == 'App::Part' : - print('Boolean Union') - if len(sel[0].Object.InList) > 0 : - print(sel[0].Object.InList) - parent = sel[0].Object.InList[0] - print('Parent : '+parent.Label) - baseVol = sel[0].Object - print('Base Vol : '+baseVol.Label) - toolVol = sel[1].Object - print('Tool Vol : '+toolVol.Label) - baseVol = sel[0].Object - print(f'Base OutList {sel[0].Object.OutList}') - for o in sel[0].Object.OutList : - print(o.Label) - print(f'Tool OutList {sel[1].Object.OutList}') - for o in sel[1].Object.OutList : - print(o.Label) - print(f'True Base {sel[0].Object.OutList[-1].Label}') - base = sel[0].Object.OutList[-1] - print('Base : '+base.Label) - print(f'True Tool {sel[1].Object.OutList[-1].Label}') - tool = sel[1].Object.OutList[-1] - print('Tool : '+tool.Label) - print('Remove Base') - baseVol.removeObject(base) - print('Adjust Base Links') - base.adjustRelativeLinks(baseVol) - toolVol.removeObject(tool) - tool.adjustRelativeLinks(toolVol) - boolVol = parent.newObject('App::Part','Bool-Union') - boolVol.addObject(base) - boolVol.addObject(tool) - boolObj = boolVol.newObject('Part::Fuse','Union') - boolObj.Placement = sel[0].Object.Placement - boolObj.Base = base - boolObj.Tool = tool - boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ - - sel[0].Object.Placement.Base - boolObj.Tool.setEditorMode('Placement',0) - FreeCAD.ActiveDocument.removeObject(baseVol.Label) - FreeCAD.ActiveDocument.removeObject(toolVol.Label) - boolObj.recompute() - else : - print('No Parent Volume') + if len(sel) == 2: + print(sel) + selObj = 'Gui::SelectionObject' + if sel[0].TypeId == selObj and sel[1].TypeId == selObj: + if sel[0].Object.TypeId == 'App::Part' and \ + sel[1].Object.TypeId == 'App::Part': + print('Boolean Union') + if len(sel[0].Object.InList) > 0: + print(sel[0].Object.InList) + parent = sel[0].Object.InList[0] + print('Parent : '+parent.Label) + baseVol = sel[0].Object + print('Base Vol : '+baseVol.Label) + toolVol = sel[1].Object + print('Tool Vol : '+toolVol.Label) + baseVol = sel[0].Object + print(f'Base OutList {sel[0].Object.OutList}') + for o in sel[0].Object.OutList: + print(o.Label) + print(f'Tool OutList {sel[1].Object.OutList}') + for o in sel[1].Object.OutList: + print(o.Label) + print(f'True Base {sel[0].Object.OutList[-1].Label}') + base = sel[0].Object.OutList[-1] + print('Base : '+base.Label) + print(f'True Tool {sel[1].Object.OutList[-1].Label}') + tool = sel[1].Object.OutList[-1] + print('Tool : '+tool.Label) + print('Remove Base') + baseVol.removeObject(base) + print('Adjust Base Links') + base.adjustRelativeLinks(baseVol) + toolVol.removeObject(tool) + tool.adjustRelativeLinks(toolVol) + boolVol = parent.newObject('App::Part', 'Bool-Union') + boolVol.addObject(base) + boolVol.addObject(tool) + boolObj = boolVol.newObject('Part::Fuse', 'Union') + boolObj.Placement = sel[0].Object.Placement + boolObj.Base = base + boolObj.Tool = tool + boolObj.Tool.Placement.Base = sel[1].Object.Placement.Base \ + - sel[0].Object.Placement.Base + boolObj.Tool.setEditorMode('Placement', 0) + FreeCAD.ActiveDocument.removeObject(baseVol.Label) + FreeCAD.ActiveDocument.removeObject(toolVol.Label) + boolObj.recompute() + else: + print('No Parent Volume') def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDML_Union', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Union'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature',\ - 'GDML Union')} + return {'Pixmap': 'GDML_Union', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Union'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('gdmlBooleanFeature', + 'GDML Union')} + class BoxFeature: # def IsActive(self): @@ -444,221 +511,227 @@ class BoxFeature: def Activated(self): from .GDMLObjects import GDMLBox, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Box") - else : - vol=objPart.newObject("App::Part","LV-Box") - obj=vol.newObject("Part::FeaturePython","GDMLBox_Box") - #print("GDMLBox Object - added") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Box") + else: + vol = objPart.newObject("App::Part", "LV-Box") + obj = vol.newObject("Part::FeaturePython", "GDMLBox_Box") + # print("GDMLBox Object - added") # obj, x, y, z, lunits, material - GDMLBox(obj,10.0,10.0,10.0,"mm",material) - #print("GDMLBox initiated") + GDMLBox(obj, 10.0, 10.0, 10.0, "mm", material) + # print("GDMLBox initiated") ViewProvider(obj.ViewObject) FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLBoxFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLBoxFeature',\ - 'Box Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLBoxFeature',\ - 'Box Object')} + return {'Pixmap': 'GDMLBoxFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLBoxFeature', + 'Box Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLBoxFeature', + 'Box Object')} + class ConeFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLCone, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Cone") - else : - vol=objPart.newObject("App::Part","LV-Cone") - obj=vol.newObject("Part::FeaturePython","GDMLCone_Cone") - #print("GDMLCone Object - added") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Cone") + else: + vol = objPart.newObject("App::Part", "LV-Cone") + obj = vol.newObject("Part::FeaturePython", "GDMLCone_Cone") + # print("GDMLCone Object - added") # obj,rmin1,rmax1,rmin2,rmax2,z,startphi,deltaphi,aunit,lunits,material - GDMLCone(obj,1,3,4,7,10.0,0,2,"rads","mm",material) - #print("GDMLCone initiated") + GDMLCone(obj, 1, 3, 4, 7, 10.0, 0, 2, "rads", "mm", material) + # print("GDMLCone initiated") ViewProvider(obj.ViewObject) - #print("GDMLCone ViewProvided - added") + # print("GDMLCone ViewProvided - added") FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLConeFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLConeFeature',\ - 'Cone Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLConeFeature',\ - 'Cone Object')} + return {'Pixmap': 'GDMLConeFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLConeFeature', + 'Cone Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLConeFeature', + 'Cone Object')} + class EllispoidFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLEllipsoid, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Ellipsoid") - else : - vol=objPart.newObject("App::Part","LV-Ellipsoid") - obj=vol.newObject("Part::FeaturePython","GDMLEllipsoid_Ellipsoid") - #print("GDMLEllipsoid Object - added") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Ellipsoid") + else: + vol = objPart.newObject("App::Part", "LV-Ellipsoid") + obj = vol.newObject("Part::FeaturePython", "GDMLEllipsoid_Ellipsoid") + # print("GDMLEllipsoid Object - added") # obj,ax, by, cz, zcut1, zcut2, lunit,material - GDMLEllipsoid(obj,10,20,30,0,0,"mm",material) - #print("GDMLEllipsoid initiated") + GDMLEllipsoid(obj, 10, 20, 30, 0, 0, "mm", material) + # print("GDMLEllipsoid initiated") ViewProvider(obj.ViewObject) - #print("GDMLEllipsoid ViewProvided - added") + # print("GDMLEllipsoid ViewProvided - added") FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLEllipsoidFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLEllipsoidFeature',\ - 'Ellipsoid Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLEllipsoidFeature',\ - 'Ellipsoid Object')} + return {'Pixmap': 'GDMLEllipsoidFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLEllipsoidFeature', + 'Ellipsoid Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLEllipsoidFeature', + 'Ellipsoid Object')} + class ElliTubeFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLElTube, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-EllipticalTube") - else : - vol=objPart.newObject("App::Part","LV-EllipticalTube") - obj=vol.newObject("Part::FeaturePython","GDMLElTube_Eltube") - #print("GDMLElTube Object - added") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-EllipticalTube") + else: + vol = objPart.newObject("App::Part", "LV-EllipticalTube") + obj = vol.newObject("Part::FeaturePython", "GDMLElTube_Eltube") + # print("GDMLElTube Object - added") # obj,dx, dy, dz, lunit, material - GDMLElTube(obj,10,20,30,"mm",material) - #print("GDMLElTube initiated") + GDMLElTube(obj, 10, 20, 30, "mm", material) + # print("GDMLElTube initiated") ViewProvider(obj.ViewObject) - #print("GDMLElTube ViewProvided - added") + # print("GDMLElTube ViewProvided - added") FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLElTubeFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLElTubeFeature',\ - 'ElTube Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLElTubeFeature',\ - 'ElTube Object')} + return {'Pixmap': 'GDMLElTubeFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLElTubeFeature', + 'ElTube Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLElTubeFeature', + 'ElTube Object')} + class SphereFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLSphere, ViewProvider objPart, material = getSelectedPM() - #print(objPart) - #print(material) - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Sphere") - else : - vol=objPart.newObject("App::Part","LV-Sphere") - obj=vol.newObject("Part::FeaturePython","GDMLSphere_Sphere") - #print("GDMLSphere Object - added") + # print(objPart) + # print(material) + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Sphere") + else: + vol = objPart.newObject("App::Part", "LV-Sphere") + obj = vol.newObject("Part::FeaturePython", "GDMLSphere_Sphere") + # print("GDMLSphere Object - added") # obj, rmin, rmax, startphi, deltaphi, starttheta, deltatheta, # aunit, lunits, material - GDMLSphere(obj,10.0, 20.0, 0.0, 2.02, 0.0, 2.02,"rad","mm",material) - #print("GDMLSphere initiated") + GDMLSphere(obj, 10.0, 20.0, 0.0, 2.02, 0.0, 2.02, "rad", "mm", material) + # print("GDMLSphere initiated") ViewProvider(obj.ViewObject) - #print("GDMLSphere ViewProvided - added") + # print("GDMLSphere ViewProvided - added") FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLSphereFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLSphereFeature',\ - 'Sphere Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLSphereFeature',\ - 'Sphere Object')} + return {'Pixmap': 'GDMLSphereFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLSphereFeature', + 'Sphere Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLSphereFeature', + 'Sphere Object')} + class TorusFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLTorus, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Torus") - else : - vol=objPart.newObject("App::Part","LV-Torus") - myTorus=vol.newObject("Part::FeaturePython","GDMLTorus_Torus") - GDMLTorus(myTorus,10,50,50,10,360,"deg","mm",material) - if FreeCAD.GuiUp : - myTorus.ViewObject.Visibility = True - ViewProvider(myTorus.ViewObject) - - FreeCAD.ActiveDocument.recompute() - FreeCADGui.SendMsgToActiveView("ViewFit") - + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Torus") + else: + vol = objPart.newObject("App::Part", "LV-Torus") + myTorus = vol.newObject("Part::FeaturePython", "GDMLTorus_Torus") + GDMLTorus(myTorus, 10, 50, 50, 10, 360, "deg", "mm", material) + if FreeCAD.GuiUp: + myTorus.ViewObject.Visibility = True + ViewProvider(myTorus.ViewObject) + + FreeCAD.ActiveDocument.recompute() + FreeCADGui.SendMsgToActiveView("ViewFit") + def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLTorusFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTorusFeature',\ - 'Torus Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTorusFeature',\ - 'Torus Object')} + return {'Pixmap': 'GDMLTorusFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLTorusFeature', + 'Torus Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLTorusFeature', + 'Torus Object')} + class TrapFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLTrap, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Trap") - else : - vol=objPart.newObject("App::Part","LV-Trap") - obj=vol.newObject("Part::FeaturePython","GDMLTrap_Trap") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Trap") + else: + vol = objPart.newObject("App::Part", "LV-Trap") + obj = vol.newObject("Part::FeaturePython", "GDMLTrap_Trap") print("GDMLTrap Object - added") # obj z, theta, phi, x1, x2, x3, x4, y1, y2, # pAlp2, aunits, lunits, material - GDMLTrap(obj,10.0,0.0,0.0,6.0,6.0,6.0,6.0,7.0,7.0,0.0,"rad","mm", \ - material) + GDMLTrap(obj, 10.0, 0.0, 0.0, 6.0, 6.0, 6.0, 6.0, 7.0, 7.0, 0.0, + "rad", "mm", material) print("GDMLTrap initiated") ViewProvider(obj.ViewObject) print("GDMLTrap ViewProvided - added") @@ -667,99 +740,101 @@ def Activated(self): def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLTrapFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTrapFeature',\ - 'Trap Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTrapFeature',\ - 'Trap Object')} + return {'Pixmap': 'GDMLTrapFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLTrapFeature', + 'Trap Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLTrapFeature', + 'Trap Object')} class TubeFeature: - #def IsActive(self): + # def IsActive(self): # return FreeCADGui.Selection.countObjectsOfType('Part::Feature') > 0 def Activated(self): from .GDMLObjects import GDMLTube, ViewProvider objPart, material = getSelectedPM() - if objPart is None : - vol=FreeCAD.ActiveDocument.addObject("App::Part","LV-Tube") - else : - vol=objPart.newObject("App::Part","LV-Tube") - obj=vol.newObject("Part::FeaturePython","GDMLTube_Tube") - #print("GDMLTube Object - added") + if objPart is None: + vol = FreeCAD.ActiveDocument.addObject("App::Part", "LV-Tube") + else: + vol = objPart.newObject("App::Part", "LV-Tube") + obj = vol.newObject("Part::FeaturePython", "GDMLTube_Tube") + # print("GDMLTube Object - added") # obj, rmin, rmax, z, startphi, deltaphi, aunit, lunits, material - GDMLTube(obj,5.0,8.0,10.0,0.52,1.57,"rad","mm",material) - #print("GDMLTube initiated") + GDMLTube(obj, 5.0, 8.0, 10.0, 0.52, 1.57, "rad", "mm", material) + # print("GDMLTube initiated") ViewProvider(obj.ViewObject) - #print("GDMLTube ViewProvided - added") + # print("GDMLTube ViewProvided - added") FreeCAD.ActiveDocument.recompute() FreeCADGui.SendMsgToActiveView("ViewFit") def IsActive(self): if FreeCAD.ActiveDocument is None: - return False + return False else: - return True + return True def GetResources(self): - return {'Pixmap' : 'GDMLTubeFeature', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTubeFeature',\ - 'Tube Object'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDMLTubeFeature',\ - 'Tube Object')} + return {'Pixmap': 'GDMLTubeFeature', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDMLTubeFeature', + 'Tube Object'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDMLTubeFeature', + 'Tube Object')} -class PolyHedraFeature : - - def Activated(self) : + +class PolyHedraFeature: + + def Activated(self): for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print('Action Poly') - if hasattr(obj,'Shape') : - print(obj.Shape.ShapeType) - if hasattr(obj.Shape,'Vertexes') : - numVert = len(obj.Shape.Vertexes) - print('Number of Vertex : '+str(numVert)) - print(obj.Shape.Vertexes) - if hasattr(obj.Shape,'Faces') : - print('Faces') - #print(dir(obj.Shape.Faces[0])) - print(obj.Shape.Faces) - planar = self.checkPlanar(obj.Shape.Faces) - print(planar) - if hasattr(obj.Shape,'Edges') : - print('Edges') - #print(dir(obj.Shape.Edges[0])) - print(obj.Shape.Edges) - - def checkPlanar(self,faces): + if hasattr(obj, 'Shape'): + print(obj.Shape.ShapeType) + if hasattr(obj.Shape, 'Vertexes'): + numVert = len(obj.Shape.Vertexes) + print('Number of Vertex : '+str(numVert)) + print(obj.Shape.Vertexes) + if hasattr(obj.Shape, 'Faces'): + print('Faces') + # print(dir(obj.Shape.Faces[0])) + print(obj.Shape.Faces) + planar = self.checkPlanar(obj.Shape.Faces) + print(planar) + if hasattr(obj.Shape, 'Edges'): + print('Edges') + # print(dir(obj.Shape.Edges[0])) + print(obj.Shape.Edges) + + def checkPlanar(self, faces): import Part print('Check Planar') - for f in faces : - if not isinstance(f.Surface, Part.Plane) : - return False + for f in faces: + if not isinstance(f.Surface, Part.Plane): + return False return True def GetResources(self): - return {'Pixmap' : 'GDML_Polyhedra', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup',\ - 'Poly Group'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup', \ - 'PolyHedra Selected Object')} - -class iField(QtGui.QWidget) : - def __init__(self,label,len,value,parent=None) : + return {'Pixmap': 'GDML_Polyhedra', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup', + 'Poly Group'), 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup', + 'PolyHedra Selected Object')} + + +class iField(QtGui.QWidget): + def __init__(self, label, len, value, parent=None): super(iField, self).__init__(parent) self.label = QtGui.QLabel(label) self.value = QtGui.QLineEdit() self.value.setMaxLength(len) - self.value.setGeometry(QtCore.QRect(10,20,25,15)) - self.value.setTextMargins(0,0,10,5) + self.value.setGeometry(QtCore.QRect(10, 20, 25, 15)) + self.value.setTextMargins(0, 0, 10, 5) self.value.setText(value) self.label.setBuddy(self.value) layout = QtGui.QHBoxLayout() @@ -767,13 +842,14 @@ def __init__(self,label,len,value,parent=None) : layout.addWidget(self.value) self.setLayout(layout) -class oField(QtGui.QWidget) : - def __init__(self,label,len, value,parent=None) : + +class oField(QtGui.QWidget): + def __init__(self, label, len, value, parent=None): super(oField, self).__init__(parent) self.label = QtGui.QLabel(label) self.value = QtGui.QLineEdit() self.value.setMaxLength(len) - self.value.setGeometry(QtCore.QRect(0,0,10,5)) + self.value.setGeometry(QtCore.QRect(0, 0, 10, 5)) self.value.setReadOnly(True) self.value.setText(value) self.label.setBuddy(self.value) @@ -782,60 +858,62 @@ def __init__(self,label,len, value,parent=None) : layout.addWidget(self.value) self.setLayout(layout) - def sizeHint(self) : - return(QtCore.QSize(10,5)) + def sizeHint(self): + return(QtCore.QSize(10, 5)) + class AddDecimateWidget(QtGui.QWidget): - def __init__(self, Obj,*args): - QtGui.QWidget.__init__(self,*args) - #bboxGroup = QtGui.QGroupBox('Objects Bounding Box') - #laybbox = QtGui.QHBoxLayout() - #laybbox.addWidget(QtGui.QLabel('Width : '+str(Shape.BoundBox.XLength))) - #laybbox.addWidget(QtGui.QLabel('Height : '+str(Shape.BoundBox.YLength))) - #laybbox.addWidget(QtGui.QLabel('Depth : '+str(Shape.BoundBox.ZLength) )) - #bboxGroup.setLayout(laybbox) - #maxl = int((Shape.BoundBox.XLength + Shape.BoundBox.YLength + \ + def __init__(self, Obj, *args): + QtGui.QWidget.__init__(self, *args) + # bboxGroup = QtGui.QGroupBox('Objects Bounding Box') + # laybbox = QtGui.QHBoxLayout() + # laybbox.addWidget(QtGui.QLabel('Width : '+str(Shape.BoundBox.XLength))) + # laybbox.addWidget(QtGui.QLabel('Height : '+str(Shape.BoundBox.YLength))) + # laybbox.addWidget(QtGui.QLabel('Depth : '+str(Shape.BoundBox.ZLength) )) + # bboxGroup.setLayout(laybbox) + # maxl = int((Shape.BoundBox.XLength + Shape.BoundBox.YLength + \ # Shape.BoundBox.ZLength) / 15) - self.type = QtGui.QComboBox() - #self.type.addItems(['sp4cerat','MeshLab','Blender']) + self.type = QtGui.QComboBox() + # self.type.addItems(['sp4cerat','MeshLab','Blender']) self.type.addItems(['sp4cerat']) - self.group1 = QtGui.QGroupBox('Decimate Reduction') - self.tolerance = iField('Tolerance',5,'5.0') - self.reduction = iField('Reduction',5,'0.8') + self.group1 = QtGui.QGroupBox('Decimate Reduction') + self.tolerance = iField('Tolerance', 5, '5.0') + self.reduction = iField('Reduction', 5, '0.8') self.parms1layout = QtGui.QHBoxLayout() self.parms1layout.addWidget(self.tolerance) self.parms1layout.addWidget(self.reduction) self.grpLay1 = QtGui.QVBoxLayout() self.grpLay1.addLayout(self.parms1layout) - self.buttonReduction = QtGui.QPushButton(translate('GDML','Decimate Reduction')) + self.buttonReduction = QtGui.QPushButton(translate('GDML', 'Decimate Reduction')) self.grpLay1.addWidget(self.buttonReduction) self.group1.setLayout(self.grpLay1) - self.group2 = QtGui.QGroupBox('Decimate to Size') - self.targetSize = iField('Target Size',5,'100') + self.group2 = QtGui.QGroupBox('Decimate to Size') + self.targetSize = iField('Target Size', 5, '100') self.grpLay2 = QtGui.QVBoxLayout() self.grpLay2.addWidget(self.targetSize) - self.buttonToSize = QtGui.QPushButton(translate('GDML','Decimate To Size')) + self.buttonToSize = QtGui.QPushButton(translate('GDML', 'Decimate To Size')) self.grpLay2.addWidget(self.buttonToSize) self.group2.setLayout(self.grpLay2) - self.Vlayout= QtGui.QVBoxLayout() + self.Vlayout = QtGui.QVBoxLayout() self.Vlayout.addWidget(self.type) self.Vlayout.addWidget(self.group1) self.Vlayout.addWidget(self.group2) self.setLayout(self.Vlayout) - self.setWindowTitle(translate('GDML','Decimate')) + self.setWindowTitle(translate('GDML', 'Decimate')) - def leaveEvent(self, event) : + def leaveEvent(self, event): print('Leave Event') - QtCore.QTimer.singleShot(0, lambda :FreeCADGui.Control.closeDialog()) + QtCore.QTimer.singleShot(0, lambda: FreeCADGui.Control.closeDialog()) def retranslateUi(self, widget=None): - self.buttonMesh.setText(translate('GDML','Decimate')) - self.setWindowTitle(translate('GDML','Decimate')) + self.buttonMesh.setText(translate('GDML', 'Decimate')) + self.setWindowTitle(translate('GDML', 'Decimate')) + class AddDecimateTask: def __init__(self, Obj): - self.obj = Obj + self.obj = Obj self.form = AddDecimateWidget(Obj) self.form.buttonReduction.clicked.connect(self.actionReduction) self.form.buttonToSize.clicked.connect(self.actionToSize) @@ -852,152 +930,153 @@ def isAllowedAlterView(self): def isAllowedAlterDocument(self): return True - def actionReduction(self) : - from .GmshUtils import TessellatedShape2Mesh + def actionReduction(self): + from .GmshUtils import TessellatedShape2Mesh print('Action Decimate Reduction : '+self.obj.Name) - #print(dir(self)) - if hasattr(self.obj,'Mesh') : - mesh = self.obj.Mesh - else : - mesh = TessellatedShape2Mesh(self.obj) - try : - tolerance = float(self.form.tolerance.value.text()) - reduction = float(self.form.reduction.value.text()) - print('Tolerance : '+str(tolerance)) - print('Reduction : '+str(reduction)) - mesh.decimate(tolerance,reduction) + # print(dir(self)) + if hasattr(self.obj, 'Mesh'): + mesh = self.obj.Mesh + else: + mesh = TessellatedShape2Mesh(self.obj) + try: + tolerance = float(self.form.tolerance.value.text()) + reduction = float(self.form.reduction.value.text()) + print('Tolerance : '+str(tolerance)) + print('Reduction : '+str(reduction)) + mesh.decimate(tolerance, reduction) except Exception as e: - print(e) + print(e) - #print(dir(self.obj)) - self.obj.Proxy.updateParams(mesh.Topology[0],mesh.Topology[1],False) + # print(dir(self.obj)) + self.obj.Proxy.updateParams(mesh.Topology[0], mesh.Topology[1], False) self.obj.recompute() self.obj.ViewObject.Visibility = True FreeCADGui.SendMsgToActiveView("ViewFit") print('Update Gui') FreeCADGui.updateGui() - def actionToSize(self) : - from .GmshUtils import TessellatedShape2Mesh + def actionToSize(self): + from .GmshUtils import TessellatedShape2Mesh print('Action Decimate To Size : '+self.obj.Name) print(dir(self)) - if hasattr(self.obj,'Mesh') : - mesh = self.obj.Mesh - else : - mesh = TessellatedShape2Mesh(self.obj) + if hasattr(self.obj, 'Mesh'): + mesh = self.obj.Mesh + else: + mesh = TessellatedShape2Mesh(self.obj) - try : - targetSize = int(self.form.targetSize.value.text()) - print('Target Size : '+str(targetSize)) - mesh.decimate(targetSize) + try: + targetSize = int(self.form.targetSize.value.text()) + print('Target Size : '+str(targetSize)) + mesh.decimate(targetSize) - except : - print('Invalid Float Values') + except: + print('Invalid Float Values') - def leaveEvent(self, event) : + def leaveEvent(self, event): print('Leave Event II') - def focusOutEvent(self, event) : + def focusOutEvent(self, event): print('Out of Focus II') -class DecimateFeature : - - def Activated(self) : - import Mesh - import MeshPart + +class DecimateFeature: + + def Activated(self): from .GDMLObjects import GDMLTessellated, GDMLTriangular, \ ViewProvider, ViewProviderExtension for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print('Action Decimate') - if self.isDecimatable(obj) : - if FreeCADGui.Control.activeDialog() == False : - print('Build panel for Decimate') - panel = AddDecimateTask(obj) - FreeCADGui.Control.showDialog(panel) - else : - print('Already an Active Task') + if self.isDecimatable(obj): + if FreeCADGui.Control.activeDialog() is False: + print('Build panel for Decimate') + panel = AddDecimateTask(obj) + FreeCADGui.Control.showDialog(panel) + else: + print('Already an Active Task') return - + def GetResources(self): - return {'Pixmap' : 'GDML_Decimate', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'Decimate Selected Object'), 'Decimate': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', \ - 'Decimate Selected Object')} - - def isDecimatable(self, obj) : - if hasattr(obj,'Proxy') : - print(obj.Proxy.Type) - if obj.Proxy.Type == 'GDMLGmshTessellated' or \ - obj.Proxy.Type == 'GDMLTessellated' : - return True - if hasattr(obj,'Mesh') : - return True + return {'Pixmap': 'GDML_Decimate', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Decimate Selected Object'), 'Decimate': + QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Decimate Selected Object')} + + def isDecimatable(self, obj): + if hasattr(obj, 'Proxy'): + print(obj.Proxy.Type) + if obj.Proxy.Type == 'GDMLGmshTessellated' or \ + obj.Proxy.Type == 'GDMLTessellated': + return True + if hasattr(obj, 'Mesh'): + return True return False + class AddTessellateWidget(QtGui.QWidget): - def __init__(self, Shape,*args): - QtGui.QWidget.__init__(self,*args) - bboxGroup = QtGui.QGroupBox('Objects Bounding Box') + def __init__(self, Shape, *args): + QtGui.QWidget.__init__(self, *args) + bboxGroup = QtGui.QGroupBox('Objects Bounding Box') laybbox = QtGui.QHBoxLayout() laybbox.addWidget(QtGui.QLabel('Width : '+str(Shape.BoundBox.XLength))) laybbox.addWidget(QtGui.QLabel('Height : '+str(Shape.BoundBox.YLength))) - laybbox.addWidget(QtGui.QLabel('Depth : '+str(Shape.BoundBox.ZLength) )) + laybbox.addWidget(QtGui.QLabel('Depth : '+str(Shape.BoundBox.ZLength))) bboxGroup.setLayout(laybbox) - maxl = int((Shape.BoundBox.XLength + Shape.BoundBox.YLength + \ + maxl = int((Shape.BoundBox.XLength + Shape.BoundBox.YLength + Shape.BoundBox.ZLength) / 15) - self.type = QtGui.QComboBox() - self.type.addItems(['Triangular','Quadrangular','Parallelograms']) - self.group = QtGui.QGroupBox('Mesh Characteristics') - self.maxLen = iField('Max Length',5,str(maxl)) - self.curveLen = iField('Curve Length',5,'10') - self.pointLen = iField('Length from Point',5,'10') - self.Vertex = oField('Vertex',6,'') - self.Facets = oField('Facets',6,'') - self.meshParmsLayout=QtGui.QGridLayout() - self.meshParmsLayout.addWidget(self.type,0,0) - self.meshParmsLayout.addWidget(self.maxLen,0,1) - self.meshParmsLayout.addWidget(self.curveLen,1,0) - self.meshParmsLayout.addWidget(self.pointLen,1,1) + self.type = QtGui.QComboBox() + self.type.addItems(['Triangular', 'Quadrangular', 'Parallelograms']) + self.group = QtGui.QGroupBox('Mesh Characteristics') + self.maxLen = iField('Max Length', 5, str(maxl)) + self.curveLen = iField('Curve Length', 5, '10') + self.pointLen = iField('Length from Point', 5, '10') + self.Vertex = oField('Vertex', 6, '') + self.Facets = oField('Facets', 6, '') + self.meshParmsLayout = QtGui.QGridLayout() + self.meshParmsLayout.addWidget(self.type, 0, 0) + self.meshParmsLayout.addWidget(self.maxLen, 0, 1) + self.meshParmsLayout.addWidget(self.curveLen, 1, 0) + self.meshParmsLayout.addWidget(self.pointLen, 1, 1) self.group.setLayout(self.meshParmsLayout) - self.buttonMesh = QtGui.QPushButton(translate('GDML','Mesh')) - layoutAction=QtGui.QHBoxLayout() + self.buttonMesh = QtGui.QPushButton(translate('GDML', 'Mesh')) + layoutAction = QtGui.QHBoxLayout() layoutAction.addWidget(self.buttonMesh) - self.Vlayout= QtGui.QVBoxLayout() + self.Vlayout = QtGui.QVBoxLayout() self.Vlayout.addWidget(bboxGroup) self.Vlayout.addWidget(self.group) self.Vlayout.addLayout(layoutAction) self.setLayout(self.Vlayout) - self.setWindowTitle(translate('GDML','Tessellate with Gmsh')) + self.setWindowTitle(translate('GDML', 'Tessellate with Gmsh')) - def leaveEvent(self, event) : + def leaveEvent(self, event): print('Leave Event') - #FreeCADGui.Control.closeDialog() - #closeDialog() - #QtCore.QMetaObject.invokeMethod(FreeCADGui.Control, 'closeDialog', QtCore.Qt.QueuedConnection) - #QtCore.QTimer.singleShot(0, FreeCADGui.Control, SLOT('closeDialog()')) - #QtCore.QTimer.singleShot(0, FreeCADGui.Control, QtCore.SLOT('closeDialog()')) - QtCore.QTimer.singleShot(0, lambda :FreeCADGui.Control.closeDialog()) + # FreeCADGui.Control.closeDialog() + # closeDialog() + # QtCore.QMetaObject.invokeMethod(FreeCADGui.Control, 'closeDialog', QtCore.Qt.QueuedConnection) + # QtCore.QTimer.singleShot(0, FreeCADGui.Control, SLOT('closeDialog()')) + # QtCore.QTimer.singleShot(0, FreeCADGui.Control, QtCore.SLOT('closeDialog()')) + QtCore.QTimer.singleShot(0, lambda: FreeCADGui.Control.closeDialog()) def retranslateUi(self, widget=None): - self.buttonMesh.setText(translate('GDML','Mesh')) - self.setWindowTitle(translate('GDML','Tessellate with Gmsh')) + self.buttonMesh.setText(translate('GDML', 'Mesh')) + self.setWindowTitle(translate('GDML', 'Tessellate with Gmsh')) + class AddTessellateTask: def __init__(self, Obj): - self.obj = Obj + self.obj = Obj self.tess = None self.form = AddTessellateWidget(Obj.Shape) self.form.buttonMesh.clicked.connect(self.actionMesh) - #self.form.buttonload.clicked.connect(self.loadelement) - #self.form.buttonsave.clicked.connect(self.saveelement) - #self.form.buttonrefresh.clicked.connect(self.refreshelement) + # self.form.buttonload.clicked.connect(self.loadelement) + # self.form.buttonsave.clicked.connect(self.saveelement) + # self.form.buttonrefresh.clicked.connect(self.refreshelement) def getStandardButtons(self): return int(QtGui.QDialogButtonBox.Close) @@ -1011,176 +1090,175 @@ def isAllowedAlterView(self): def isAllowedAlterDocument(self): return True - def processMesh(self, vertex, facets) : + def processMesh(self, vertex, facets): from .GDMLObjects import ViewProvider print('Update Tessellated Object') print(dir(self)) - print('Object Name ' +self.obj.Name) - print('Object Type ' +self.obj.TypeId) - if hasattr(self.obj,'Proxy') : - print('Proxy') - print(self.obj.Proxy.Type) - if self.obj.Proxy.Type == 'GDMLGmshTessellated' or \ - self.obj.Proxy.Type == 'GDMLTessellated' : - self.obj.Proxy.updateParams(vertex,facets,False) - #print(dir(self.form)) + print('Object Name ' + self.obj.Name) + print('Object Type ' + self.obj.TypeId) + if hasattr(self.obj, 'Proxy'): + print('Proxy') + print(self.obj.Proxy.Type) + if self.obj.Proxy.Type == 'GDMLGmshTessellated' or \ + self.obj.Proxy.Type == 'GDMLTessellated': + self.obj.Proxy.updateParams(vertex, facets, False) + # print(dir(self.form)) print('Vertex : '+str(len(vertex))) print('Facets : '+str(len(facets))) # Update Info of GDML Tessellated Object - if self.tess is not None : - print('Tesselated Name '+self.tess.Name) - print('Update parms : '+self.tess.Name) - if hasattr(self.tess,'Proxy') : # If GDML object has Proxy - print(dir(self.tess.Proxy)) - self.tess.Proxy.updateParams(vertex,facets,False) - else : - self.tess.updateParams(vertex,facets,False) - #print('Update parms : '+self.tess.Name) - #self.tess.updateParams(vertex,facets,False) - #self.form.Vertex.value.setText(QtCore.QString(len(vertex))) + if self.tess is not None: + print('Tesselated Name '+self.tess.Name) + print('Update parms : '+self.tess.Name) + if hasattr(self.tess, 'Proxy'): # If GDML object has Proxy + print(dir(self.tess.Proxy)) + self.tess.Proxy.updateParams(vertex, facets, False) + else: + self.tess.updateParams(vertex, facets, False) + # print('Update parms : '+self.tess.Name) + # self.tess.updateParams(vertex,facets,False) + # self.form.Vertex.value.setText(QtCore.QString(len(vertex))) self.form.Vertex.value.setText(str(len(vertex))) - #self.form.Facets.value.setText(QtCore.QString(len(facets))) + # self.form.Facets.value.setText(QtCore.QString(len(facets))) self.form.Facets.value.setText(str(len(facets))) - if FreeCAD.GuiUp : - if self.tess is not None : - self.obj.ViewObject.Visibility = False - ViewProvider(self.tess.ViewObject) - self.tess.ViewObject.DisplayMode = "Wireframe" - self.tess.recompute() - #FreeCAD.ActiveDocument.recompute() - else : - print('Recompute : '+self.obj.Name) - self.obj.recompute() - self.obj.ViewObject.Visibility = True - FreeCADGui.SendMsgToActiveView("ViewFit") - FreeCADGui.updateGui() - - def actionMesh(self) : + if FreeCAD.GuiUp: + if self.tess is not None: + self.obj.ViewObject.Visibility = False + ViewProvider(self.tess.ViewObject) + self.tess.ViewObject.DisplayMode = "Wireframe" + self.tess.recompute() + # FreeCAD.ActiveDocument.recompute() + else: + print('Recompute : '+self.obj.Name) + self.obj.recompute() + self.obj.ViewObject.Visibility = True + FreeCADGui.SendMsgToActiveView("ViewFit") + FreeCADGui.updateGui() + + def actionMesh(self): from .GmshUtils import initialize, meshObject, \ getVertex, getFacets, getMeshLen, printMeshInfo, printMyInfo from .GDMLObjects import GDMLGmshTessellated, GDMLTriangular print('Action Gmsh : '+self.obj.Name) initialize() - typeDict = {0:6,1:8,2:9} + typeDict = {0: 6, 1: 8, 2: 9} print(dir(self)) print('Object '+self.obj.Name) - if self.tess is not None : - print('Tessellated '+self.tess.Name) + if self.tess is not None: + print('Tessellated '+self.tess.Name) ty = typeDict[self.form.type.currentIndex()] ml = self.form.maxLen.value.text() cl = self.form.curveLen.value.text() pl = self.form.pointLen.value.text() print('type : '+str(ty)+' ml : '+ml+' cl : '+cl+' pl : '+pl) - if hasattr(self.obj,'Proxy') : - print('has proxy') - if hasattr(self.obj.Proxy,'SourceObj') : - print('Has source Object') - if meshObject(self.obj.Proxy.SourceObj,2,ty,\ - float(ml),float(cl),float(pl)) == True : - facets = getFacets() - vertex = getVertex() - self.processMesh(vertex,facets) - return - - if meshObject(self.obj,2,ty, \ - float(ml),float(cl),float(pl)) == True : - facets = getFacets() - vertex = getVertex() - if self.tess is None : - name ='GDMLTessellate_'+self.obj.Name - parent = None - if hasattr(self.obj,'InList') : - if len(self.obj.InList) > 0 : - parent = self.obj.InList[0] - self.tess = parent.newObject('Part::FeaturePython',name) - if parent is None : - self.tess = FreeCAD.ActiveDocument.addObject( \ - 'Part::FeaturePython',name) - GDMLGmshTessellated(self.tess,self.obj,getMeshLen(self.obj),vertex, facets, \ - "mm", getSelectedMaterial()) - else : - self.processMesh(vertex,facets) - + if hasattr(self.obj, 'Proxy'): + print('has proxy') + if hasattr(self.obj.Proxy, 'SourceObj'): + print('Has source Object') + if meshObject(self.obj.Proxy.SourceObj, 2, ty, + float(ml), float(cl), float(pl)) is True: + facets = getFacets() + vertex = getVertex() + self.processMesh(vertex, facets) + return + + if meshObject(self.obj, 2, ty, + float(ml), float(cl), float(pl)) is True: + facets = getFacets() + vertex = getVertex() + if self.tess is None: + name = 'GDMLTessellate_'+self.obj.Name + parent = None + if hasattr(self.obj, 'InList'): + if len(self.obj.InList) > 0: + parent = self.obj.InList[0] + self.tess = parent.newObject('Part::FeaturePython', name) + if parent is None: + self.tess = FreeCAD.ActiveDocument.addObject( + 'Part::FeaturePython', name) + GDMLGmshTessellated(self.tess, self.obj, + getMeshLen(self.obj), vertex, facets, + "mm", getSelectedMaterial()) + else: + self.processMesh(vertex, facets) + print('Check Form') - #print(dir(self.form)) - if not hasattr(self.form,'infoGroup') : - self.form.infoGroup = QtGui.QGroupBox('Mesh Information') - print('Mesh Info Layout') - layMeshInfo=QtGui.QHBoxLayout() - layMeshInfo.addWidget(self.form.Vertex) - layMeshInfo.addWidget(self.form.Facets) - #layMeshInfo.addWidget(self.form.Nodes) - self.form.infoGroup.setLayout(layMeshInfo) - self.form.Vlayout.addWidget(self.form.infoGroup) - #self.form.setLayout(self.form.Vlayout) - self.processMesh(vertex,facets) - - def leaveEvent(self, event) : + # print(dir(self.form)) + if not hasattr(self.form, 'infoGroup'): + self.form.infoGroup = QtGui.QGroupBox('Mesh Information') + print('Mesh Info Layout') + layMeshInfo = QtGui.QHBoxLayout() + layMeshInfo.addWidget(self.form.Vertex) + layMeshInfo.addWidget(self.form.Facets) + # layMeshInfo.addWidget(self.form.Nodes) + self.form.infoGroup.setLayout(layMeshInfo) + self.form.Vlayout.addWidget(self.form.infoGroup) + # self.form.setLayout(self.form.Vlayout) + self.processMesh(vertex, facets) + + def leaveEvent(self, event): print('Leave Event II') - def focusOutEvent(self, event) : + def focusOutEvent(self, event): print('Out of Focus II') -class TessellateFeature : - - def Activated(self) : - import Mesh + +class TessellateFeature: + + def Activated(self): import MeshPart from .GDMLObjects import GDMLTessellated, GDMLTriangular, \ ViewProvider, ViewProviderExtension for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print('Action Tessellate') - if hasattr(obj,'Shape') : - shape = obj.Shape.copy(False) - try : # Only supported if smesh built with netgen - mesh = MeshPart.meshFromShape(Shape=shape,Fineness=2,\ - SecondOrder=0,Optimize=1,AllowQuad=0) - except : - mesh = MeshPart.meshFromShape(Shape=shape,MaxArea=shape.Area) - print('Points : '+str(mesh.CountPoints)) - #print(mesh.Points) - print('Facets : '+str(mesh.CountFacets)) - #print(mesh.Facets) - name ='GDMLTessellate_'+obj.Label - vol = createPartVol(obj) - print(obj.Label) - print(obj.Placement) - if hasattr(obj,'material') : - mat = obj.material - else : - mat = getSelectedMaterial() - myTess = vol.newObject('Part::FeaturePython',name) - #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() - if FreeCAD.GuiUp : - ViewProvider(myTess.ViewObject) - obj.ViewObject.Visibility = False - myTess.ViewObject.DisplayMode = 'Flat Lines' - FreeCADGui.SendMsgToActiveView("ViewFit") - + if hasattr(obj, 'Shape'): + shape = obj.Shape.copy(False) + mesh = MeshPart.meshFromShape(Shape=shape, Fineness=2, + SecondOrder=0, Optimize=1, + AllowQuad=0) + print('Points : '+str(mesh.CountPoints)) + # print(mesh.Points) + print('Facets : '+str(mesh.CountFacets)) + # print(mesh.Facets) + name = 'GDMLTessellate_'+obj.Label + vol = createPartVol(obj) + print(obj.Label) + print(obj.Placement) + if hasattr(obj, 'material'): + mat = obj.material + else: + mat = getSelectedMaterial() + myTess = vol.newObject('Part::FeaturePython', name) + # 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() + if FreeCAD.GuiUp: + ViewProvider(myTess.ViewObject) + obj.ViewObject.Visibility = False + myTess.ViewObject.DisplayMode = 'Flat Lines' + FreeCADGui.SendMsgToActiveView("ViewFit") + def GetResources(self): - return {'Pixmap' : 'GDML_Tessellate', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'GDML Tessellate Selected Object'), 'Tessellate_Planar': \ - QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup', \ - 'Tesselate Selected Planar Object')} - -class TessellateGmshFeature : - - def Activated(self) : - - import ObjectsFem + return {'Pixmap': 'GDML_Tessellate', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'GDML Tessellate Selected Object'), + 'Tessellate_Planar': QtCore.QT_TRANSLATE_NOOP('GDML_PolyGroup', + 'Tesselate Selected Planar Object')} + + +class TessellateGmshFeature: + + def Activated(self): + from .GmshUtils import initialize, meshObject, \ getVertex, getFacets, getMeshLen, printMeshInfo, printMyInfo @@ -1189,83 +1267,84 @@ def Activated(self) : print('Action Gmsh Activated') for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print('Action Gmsh Tessellate') - #print(dir(obj)) + # print(dir(obj)) print(obj.Name) - if hasattr(obj,'Shape') and obj.TypeId != 'App::Part' : - if FreeCADGui.Control.activeDialog() == False : - print('Build panel for TO BE Gmeshed') - panel = AddTessellateTask(obj) - if hasattr(obj,'Proxy') : - print(obj.Proxy.Type) - if obj.Proxy.Type == 'GDMLGmshTessellated' : - print('Build panel for EXISTING Gmsh Tessellate') - panel.form.meshInfoLayout=QtGui.QHBoxLayout() - panel.form.meshInfoLayout.addWidget(oField('Vertex',6, \ - str(len(obj.Proxy.Vertex)))) - panel.form.meshInfoLayout.addWidget(oField('Facets',6, \ - str(len(obj.Proxy.Facets)))) - panel.form.Vlayout.addLayout(panel.form.meshInfoLayout) - panel.form.setLayout(panel.form.Vlayout) - FreeCADGui.Control.showDialog(panel) - else : - print('Already an Active Task') + if hasattr(obj, 'Shape') and obj.TypeId != 'App::Part': + if FreeCADGui.Control.activeDialog() is False: + print('Build panel for TO BE Gmeshed') + panel = AddTessellateTask(obj) + if hasattr(obj, 'Proxy'): + print(obj.Proxy.Type) + if obj.Proxy.Type == 'GDMLGmshTessellated': + print('Build panel for EXISTING Gmsh Tessellate') + panel.form.meshInfoLayout = QtGui.QHBoxLayout() + panel.form.meshInfoLayout.addWidget(oField('Vertex', 6, + str(len(obj.Proxy.Vertex)))) + panel.form.meshInfoLayout.addWidget(oField('Facets', 6, + str(len(obj.Proxy.Facets)))) + panel.form.Vlayout.addLayout(panel.form.meshInfoLayout) + panel.form.setLayout(panel.form.Vlayout) + FreeCADGui.Control.showDialog(panel) + else: + print('Already an Active Task') return - def GetResources(self): - return {'Pixmap' : 'GDML_Tessellate_Gmsh', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'Gmsh & Tessellate'), 'Tessellate_Gmsh': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', \ - 'Mesh & Tessellate Selected Planar Object')} + return {'Pixmap': 'GDML_Tessellate_Gmsh', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Gmsh & Tessellate'), + 'Tessellate_Gmsh': QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Mesh & Tessellate Selected Planar Object')} + + +class Mesh2TessFeature: + + def Activated(self): -class Mesh2TessFeature : - - def Activated(self) : - from .GDMLObjects import GDMLTessellated, GDMLTriangular, \ ViewProvider, ViewProviderExtension for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print(obj.TypeId) - if hasattr(obj,'Mesh') : - # Mesh Object difficult to determine parent - print('Action Mesh 2 Tessellate') - print('Points : '+str(obj.Mesh.CountPoints)) - print('Facets : '+str(obj.Mesh.CountFacets)) - #print(obj.Mesh.Topology[0]) - #print(obj.Mesh.Topology[1]) - vol = createPartVol(obj) - if hasattr(obj,'material') : - mat = obj.material - else : - mat = getSelectedMaterial() - m2t = vol.newObject('Part::FeaturePython',\ - "GDMLTessellate_Mesh2Tess") - GDMLTessellated(m2t,obj.Mesh.Topology[0],obj.Mesh.Facets,True, \ - "mm",mat) - if FreeCAD.GuiUp : - obj.ViewObject.Visibility = False - #print(dir(obj.ViewObject)) - ViewProvider(m2t.ViewObject) - - FreeCAD.ActiveDocument.recompute() - FreeCADGui.SendMsgToActiveView("ViewFit") - + if hasattr(obj, 'Mesh'): + # Mesh Object difficult to determine parent + print('Action Mesh 2 Tessellate') + print('Points : '+str(obj.Mesh.CountPoints)) + print('Facets : '+str(obj.Mesh.CountFacets)) + # print(obj.Mesh.Topology[0]) + # print(obj.Mesh.Topology[1]) + vol = createPartVol(obj) + if hasattr(obj, 'material'): + mat = obj.material + else: + mat = getSelectedMaterial() + m2t = vol.newObject('Part::FeaturePython', + "GDMLTessellate_Mesh2Tess") + GDMLTessellated(m2t, obj.Mesh.Topology[0], obj.Mesh.Facets, True, + "mm", mat) + if FreeCAD.GuiUp: + obj.ViewObject.Visibility = False + # print(dir(obj.ViewObject)) + ViewProvider(m2t.ViewObject) + + FreeCAD.ActiveDocument.recompute() + FreeCADGui.SendMsgToActiveView("ViewFit") + def GetResources(self): - return {'Pixmap' : 'GDML_Mesh2Tess', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'Mesh 2 Tess'), 'Mesh2Tess': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessyGroup', \ - 'Create GDML Tessellate from FC Mesh')} - -class Tess2MeshFeature : - - def Activated(self) : - + return {'Pixmap': 'GDML_Mesh2Tess', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Mesh 2 Tess'), 'Mesh2Tess': + QtCore.QT_TRANSLATE_NOOP('GDML_TessyGroup', + 'Create GDML Tessellate from FC Mesh')} + + +class Tess2MeshFeature: + + def Activated(self): + from .GDMLObjects import GDMLTessellated, GDMLTriangular, \ ViewProvider, ViewProviderExtension @@ -1274,52 +1353,39 @@ def Activated(self) : for obj in FreeCADGui.Selection.getSelection(): import MeshPart print('Action Tessellate 2 Mesh') - if hasattr(obj,'Proxy') : - if hasattr(obj.Proxy,'Type') : - if obj.Proxy.Type in ['GDMLTessellated', \ - 'GDMLGmshTessellated','GDMLTetrahedron'] : - parent = None - if hasattr(obj,'InList') : - if len(obj.InList) > 0 : - parent = obj.InList[0] - mshObj = parent.newObject('Mesh::Feature',obj.Name) - if parent is None : - mshObj = FreeCAD.ActiveDocument.addObject( \ - 'Mesh::Feature',obj.Name) - mshObj.Mesh = MeshPart.meshFromShape(obj.Shape) - - #if obj.Proxy.Type == 'GDMLTetrahedron' : - # print('Tetrahedron2Mesh') - # mesh = Tetrahedron2Mesh(obj) - - #if mesh is not None : - # print('Add Mesh') - # parent = None - # if hasattr(obj,'InList') : - # if len(obj.InList) > 0 : - # parent = obj.InList[0] - # mshObj = parent.newObject('Mesh::Feature',obj.Name) - # if parent is None : - # mshObj = FreeCAD.ActiveDocument.addObject( \ - # 'Mesh::Feature',obj.Name) - # mshObj.Mesh = mesh - if FreeCAD.GuiUp : - obj.ViewObject.Visibility = False - mshObj.ViewObject.DisplayMode = "Wireframe" - FreeCAD.ActiveDocument.recompute() - FreeCADGui.SendMsgToActiveView("ViewFit") + if hasattr(obj, 'Proxy'): + if hasattr(obj.Proxy, 'Type'): + if obj.Proxy.Type in ['GDMLTessellated', + 'GDMLGmshTessellated', + 'GDMLTetrahedron']: + parent = None + if hasattr(obj, 'InList'): + if len(obj.InList) > 0: + parent = obj.InList[0] + mshObj = parent.newObject('Mesh::Feature', obj.Name) + if parent is None: + mshObj = FreeCAD.ActiveDocument.addObject( + 'Mesh::Feature', obj.Name) + mshObj.Mesh = MeshPart.meshFromShape(obj.Shape) + if FreeCAD.GuiUp: + obj.ViewObject.Visibility = False + mshObj.ViewObject.DisplayMode = "Wireframe" + FreeCAD.ActiveDocument.recompute() + FreeCADGui.SendMsgToActiveView("ViewFit") + def GetResources(self): - return {'Pixmap' : 'GDML_Tess2Mesh', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'Tess2Mesh'), 'Tess 2 Mesh': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', \ - 'Create FC Mesh from GDML Tessellate')} - -class TetrahedronFeature : - - def Activated(self) : - + return {'Pixmap': 'GDML_Tess2Mesh', 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Tess2Mesh'), 'Tess 2 Mesh': + QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Create FC Mesh from GDML Tessellate')} + + +class TetrahedronFeature: + + def Activated(self): + from .GDMLObjects import GDMLTetrahedron, ViewProvider from .GmshUtils import initialize, meshObj, \ getTetrahedrons, printMeshInfo, printMyInfo @@ -1327,264 +1393,267 @@ def Activated(self) : for obj in FreeCADGui.Selection.getSelection(): print('Action Tetrahedron') initialize() - if meshObj(obj,3) == True : - tetraheds = getTetrahedrons() - if tetraheds is not None : - print('tetraheds : '+str(len(tetraheds))) - name ='GDMLTetrahedron_'+obj.Name - parent = None - if hasattr(obj,'InList') : - if len(obj.InList) > 0 : - parent = obj.InList[0] - myTet = parent.newObject('Part::FeaturePython',name) - if parent is None : - myTet = FreeCAD.ActiveDocument.addObject( \ - 'Part::FeaturePython',name) - GDMLTetrahedron(myTet,tetraheds,"mm",getSelectedMaterial()) - if FreeCAD.GuiUp : - obj.ViewObject.Visibility = False - ViewProvider(myTet.ViewObject) - myTet.ViewObject.DisplayMode = "Wireframe" - FreeCAD.ActiveDocument.recompute() - FreeCADGui.SendMsgToActiveView("ViewFit") - else : - FreeCAD.Console.PrintMessage('Not able to produce quandrants for this shape') + if meshObj(obj, 3) is True: + tetraheds = getTetrahedrons() + if tetraheds is not None: + print('tetraheds : '+str(len(tetraheds))) + name = 'GDMLTetrahedron_'+obj.Name + parent = None + if hasattr(obj, 'InList'): + if len(obj.InList) > 0: + parent = obj.InList[0] + myTet = parent.newObject('Part::FeaturePython', name) + if parent is None: + myTet = FreeCAD.ActiveDocument.addObject( + 'Part::FeaturePython', name) + GDMLTetrahedron(myTet, tetraheds, "mm", getSelectedMaterial()) + if FreeCAD.GuiUp: + obj.ViewObject.Visibility = False + ViewProvider(myTet.ViewObject) + myTet.ViewObject.DisplayMode = "Wireframe" + FreeCAD.ActiveDocument.recompute() + FreeCADGui.SendMsgToActiveView("ViewFit") + else: + FreeCAD.Console.PrintMessage('Not able to produce quandrants for this shape') def GetResources(self): - return {'Pixmap' : 'GDML_Tetrahedron', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup',\ - 'Tetrahedron'), 'Tetrehedron': \ - QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', \ - 'Create Tetrahedron from FC Shape')} - -class CycleFeature : - - def Activated(self) : - - def toggle(obj) : - #print ("Toggle "+obj.Label) - #print (obj.ViewObject.DisplayMode) - #print (obj.ViewObject.Visibility) - if obj.ViewObject.Visibility == False : - try : - obj.ViewObject.DisplayMode = 'Shaded' - except : - print(obj.Label+' No Shaded') - obj.ViewObject.Visibility = True - else : - if obj.ViewObject.DisplayMode == 'Shaded' : - obj.ViewObject.DisplayMode = 'Wireframe' - else : - obj.ViewObject.Visibility = False - - def cycle(obj) : - #print ("Toggle : "+ obj.Label) - #print (dir(obj)) - #print("TypeId : "+str(obj.TypeId)) - if obj.TypeId == "App::Part" : - for i in obj.OutList : - #print(i) - #print(dir(i)) - #print (i.TypeId) - if i.TypeId != "App::Origin" : - cycle(i) - elif obj.TypeId =="App::Origin" : + return {'Pixmap': 'GDML_Tetrahedron', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Tetrahedron'), + 'Tetrehedron': QtCore.QT_TRANSLATE_NOOP('GDML_TessGroup', + 'Create Tetrahedron from FC Shape')} + + +class CycleFeature: + + def Activated(self): + + def toggle(obj): + # print ("Toggle "+obj.Label) + # print (obj.ViewObject.DisplayMode) + # print (obj.ViewObject.Visibility) + if obj.ViewObject.Visibility is False: + try: + obj.ViewObject.DisplayMode = 'Shaded' + except: + print(obj.Label+' No Shaded') + obj.ViewObject.Visibility = True + else: + if obj.ViewObject.DisplayMode == 'Shaded': + obj.ViewObject.DisplayMode = 'Wireframe' + else: + obj.ViewObject.Visibility = False + + def cycle(obj): + # print ("Toggle : "+ obj.Label) + # print (dir(obj)) + # print("TypeId : "+str(obj.TypeId)) + if obj.TypeId == "App::Part": + for i in obj.OutList: + # print(i) + # print(dir(i)) + # print (i.TypeId) + if i.TypeId != "App::Origin": + cycle(i) + elif obj.TypeId == "App::Origin": return - #print obj.isDerivedFrom('App::DocumentObjectGroupPython') + # print obj.isDerivedFrom('App::DocumentObjectGroupPython') # Is this a genuine group i.e. Volumes # Not Parts with Groups i.e. GDMLPolycone - elif obj.isDerivedFrom('App::DocumentObjectGroupPython') : - #print "Toggle Group" - for s in obj.Group : - #print s - cycle(s) + elif obj.isDerivedFrom('App::DocumentObjectGroupPython'): + # print "Toggle Group" + for s in obj.Group: + # print s + cycle(s) # Cycle through display options - elif hasattr(obj,'ViewObject') : - toggle(obj) + elif hasattr(obj, 'ViewObject'): + toggle(obj) - if hasattr(obj,'Base') and hasattr(obj,'Tool') : - print ("Boolean") - cycle(obj.Base) - cycle(obj.Tool) - + if hasattr(obj, 'Base') and hasattr(obj, 'Tool'): + print("Boolean") + cycle(obj.Base) + cycle(obj.Tool) for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects cycle(obj) - def GetResources(self): - return {'Pixmap' : 'GDML_Cycle', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_CycleGroup',\ - 'Cycle Group'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_CycleGroup', \ - 'Cycle Object and all children display')} - -def expandFunction(obj, eNum) : + return {'Pixmap': 'GDML_Cycle', + 'MenuText': + QtCore.QT_TRANSLATE_NOOP('GDML_CycleGroup', + 'Cycle Group'), + 'ToolTip': + QtCore.QT_TRANSLATE_NOOP('GDML_CycleGroup', + 'Cycle Object and all children display')} + +def expandFunction(obj, eNum): from .importGDML import expandVolume print('Expand Function') # Get original volume name i.e. loose _ or _nnn name = obj.Label[13:] - if hasattr(obj,'VolRef') : - volRef = obj.VolRef - else : - volRef = name - if obj.TypeId != 'App::Link' : - expandVolume(obj,volRef,eNum,3) - obj.Label = name + if hasattr(obj, 'VolRef'): + volRef = obj.VolRef + else: + volRef = name + if obj.TypeId != 'App::Link': + expandVolume(obj, volRef, eNum, 3) + obj.Label = name -class ExpandFeature : +class ExpandFeature: - def Activated(self) : - - print('Expand Feature') + def Activated(self): + + print('Expand Feature') for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects - # add check for Part i.e. Volume + # if len(obj.InList) == 0: # allowed only for for top level objects + # add check for Part i.e. Volume print("Selected") print(obj.Label[:13]) - if obj.Label[:13] == "NOT_Expanded_" : - expandFunction(obj,0) - if obj.Label[:5] == "Link_" : - if hasattr(obj,'LinkedObject') : - if obj.LinkedObject.Label[0:13] == 'NOT_Expanded_' : - expandFunction(obj.LinkedObject,0) + if obj.Label[:13] == "NOT_Expanded_": + expandFunction(obj, 0) + if obj.Label[:5] == "Link_": + if hasattr(obj, 'LinkedObject'): + if obj.LinkedObject.Label[0:13] == 'NOT_Expanded_': + expandFunction(obj.LinkedObject, 0) def GetResources(self): - return {'Pixmap' : 'GDML_Expand_One', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Expand_One',\ - 'Expand Volume'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Expand_One', \ - 'Expand Volume')} + return {'Pixmap': 'GDML_Expand_One', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_Expand_One', + 'Expand Volume'), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP('GDML_Expand_One', + 'Expand Volume')} + + +class ExpandMaxFeature: -class ExpandMaxFeature : + def Activated(self): - def Activated(self) : - for obj in FreeCADGui.Selection.getSelection(): - #if len(obj.InList) == 0: # allowed only for for top level objects - # add check for Part i.e. Volume + # if len(obj.InList) == 0: # allowed only for for top level objects + # add check for Part i.e. Volume print("Selected") print(obj.Label[:13]) - if obj.Label[:13] == "NOT_Expanded_" : - expandFunction(obj,-1) - if obj.Label[:5] == "Link_" : - if hasattr(obj,'LinkedObject') : - if obj.LinkedObject.Label[0:13] == 'NOT_Expanded_' : - expandFunction(obj.LinkedObject,-1) + if obj.Label[:13] == "NOT_Expanded_": + expandFunction(obj, -1) + if obj.Label[:5] == "Link_": + if hasattr(obj, 'LinkedObject'): + if obj.LinkedObject.Label[0:13] == 'NOT_Expanded_': + expandFunction(obj.LinkedObject, -1) def GetResources(self): - return {'Pixmap' : 'GDML_Expand_Max', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Expand_Max',\ - 'Max Expand Volume'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Expand_Max', \ - 'Max Expand Volume')} + return {'Pixmap': 'GDML_Expand_Max', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_Expand_Max', + 'Max Expand Volume'), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP('GDML_Expand_Max', + 'Max Expand Volume')} -class CompoundFeature : - - def Activated(self) : + +class CompoundFeature: + + def Activated(self): from .GDMLObjects import GDMLcommon import ObjectsFem - - def allocateMaterial(doc, analObj, materials, material) : - print("Allocate Material : ",material) - for n in materials.OutList : - if n.Label == material : - print("Found Material") - matObj = ObjectsFem.makeMaterialSolid(doc, material) - mat = matObj.Material - mat['Name'] = material - mat['Density'] = str(n.density) + " kg/m^3" - mat['ThermalConductivity'] = str(n.conduct) + " W/m/K" - mat['ThermalExpansionCoefficient'] = str(n.expand) + " m/m/K" - mat['SpecificHeat'] = str(n.specific) + " J/kg/K" - #print(mat) - #print(mat['Density']) - matObj.Material = mat - analObj.addObject(matObj) - - def addToList(objList, matList, obj) : - print(obj.Name) - if hasattr(obj,'Proxy') : - #print("Has proxy") - #material_object = ObjectsFem.makeMaterialSolid \ - # (doc,obj.Name+"-Material") - #allocateMaterial(material_object, obj.Material) - if isinstance(obj.Proxy,GDMLcommon) : - objList.append(obj) - if obj.material not in matList : - matList.append(obj.material) - - if obj.TypeId == 'App::Part' and hasattr(obj,'OutList') : - #if hasattr(obj,'OutList') : - #print("Has OutList + len "+str(len(obj.OutList))) - for i in obj.OutList : - #print('Call add to List '+i.Name) - addToList(objList, matList, i) - - def myaddCompound(obj,count) : + + def allocateMaterial(doc, analObj, materials, material): + print("Allocate Material : ", material) + for n in materials.OutList: + if n.Label == material: + print("Found Material") + matObj = ObjectsFem.makeMaterialSolid(doc, material) + mat = matObj.Material + mat['Name'] = material + mat['Density'] = str(n.density) + " kg/m^3" + mat['ThermalConductivity'] = str(n.conduct) + " W/m/K" + mat['ThermalExpansionCoefficient'] = str(n.expand) + " m/m/K" + mat['SpecificHeat'] = str(n.specific) + " J/kg/K" + # print(mat) + # print(mat['Density']) + matObj.Material = mat + analObj.addObject(matObj) + + def addToList(objList, matList, obj): + print(obj.Name) + if hasattr(obj, 'Proxy'): + # print("Has proxy") + # material_object = ObjectsFem.makeMaterialSolid \ + # (doc,obj.Name+"-Material") + # allocateMaterial(material_object, obj.Material) + if isinstance(obj.Proxy, GDMLcommon): + objList.append(obj) + if obj.material not in matList: + matList.append(obj.material) + + if obj.TypeId == 'App::Part' and hasattr(obj, 'OutList'): + # if hasattr(obj,'OutList') : + # print("Has OutList + len "+str(len(obj.OutList))) + for i in obj.OutList: + # print('Call add to List '+i.Name) + addToList(objList, matList, i) + + def myaddCompound(obj, count): # count == 0 World Volume - print ("Add Compound "+obj.Label) + print("Add Compound "+obj.Label) volList = [] matList = [] addToList(volList, matList, obj) - if count == 0 : - del volList[0] - del matList[0] + if count == 0: + del volList[0] + del matList[0] # DO not delete World Material as it may be repeat - print('vol List') + print('vol List') print(volList) print('Material List') - #print(matList) + # print(matList) doc = FreeCAD.activeDocument() - analysis_object = ObjectsFem.makeAnalysis(doc,"Analysis") + analysis_object = ObjectsFem.makeAnalysis(doc, "Analysis") materials = FreeCAD.ActiveDocument.Materials - for m in matList : + for m in matList: allocateMaterial(doc, analysis_object, materials, m) - comp = obj.newObject("Part::Compound","Compound") + comp = obj.newObject("Part::Compound", "Compound") comp.Links = volList FreeCAD.ActiveDocument.recompute() - objs = FreeCADGui.Selection.getSelection() - #if len(obj.InList) == 0: # allowed only for for top level objects + # if len(obj.InList) == 0: # allowed only for for top level objects print(len(objs)) - if len(objs) > 0 : - obj = objs[0] - if obj.TypeId == 'App::Part' : - myaddCompound(obj,len(obj.InList)) + if len(objs) > 0: + obj = objs[0] + if obj.TypeId == 'App::Part': + myaddCompound(obj, len(obj.InList)) def GetResources(self): - return {'Pixmap' : 'GDML_Compound', 'MenuText': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Compound',\ - 'Add compound to Volume'), 'ToolTip': \ - QtCore.QT_TRANSLATE_NOOP('GDML_Compound', \ - 'Add a Compound of Volume')} - -FreeCADGui.addCommand('CycleCommand',CycleFeature()) -FreeCADGui.addCommand('ExpandCommand',ExpandFeature()) -FreeCADGui.addCommand('ExpandMaxCommand',ExpandMaxFeature()) -FreeCADGui.addCommand('ColourMapCommand',ColourMapFeature()) -FreeCADGui.addCommand('SetMaterialCommand',SetMaterialFeature()) -FreeCADGui.addCommand('BooleanCutCommand',BooleanCutFeature()) -FreeCADGui.addCommand('BooleanIntersectionCommand',BooleanIntersectionFeature()) -FreeCADGui.addCommand('BooleanUnionCommand',BooleanUnionFeature()) -FreeCADGui.addCommand('BoxCommand',BoxFeature()) -FreeCADGui.addCommand('EllipsoidCommand',EllispoidFeature()) -FreeCADGui.addCommand('ElTubeCommand',ElliTubeFeature()) -FreeCADGui.addCommand('ConeCommand',ConeFeature()) -FreeCADGui.addCommand('SphereCommand',SphereFeature()) -FreeCADGui.addCommand('TorusCommand',TorusFeature()) -FreeCADGui.addCommand('TrapCommand',TrapFeature()) -FreeCADGui.addCommand('TubeCommand',TubeFeature()) -FreeCADGui.addCommand('PolyHedraCommand',PolyHedraFeature()) -FreeCADGui.addCommand('AddCompound',CompoundFeature()) -FreeCADGui.addCommand('TessellateCommand',TessellateFeature()) -FreeCADGui.addCommand('TessellateGmshCommand',TessellateGmshFeature()) -FreeCADGui.addCommand('DecimateCommand',DecimateFeature()) -FreeCADGui.addCommand('Mesh2TessCommand',Mesh2TessFeature()) -FreeCADGui.addCommand('Tess2MeshCommand',Tess2MeshFeature()) -FreeCADGui.addCommand('TetrahedronCommand',TetrahedronFeature()) + return {'Pixmap': 'GDML_Compound', + 'MenuText': QtCore.QT_TRANSLATE_NOOP('GDML_Compound', + 'Add compound to Volume'), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP('GDML_Compound', + 'Add a Compound of Volume')} + + +FreeCADGui.addCommand('CycleCommand', CycleFeature()) +FreeCADGui.addCommand('ExpandCommand', ExpandFeature()) +FreeCADGui.addCommand('ExpandMaxCommand', ExpandMaxFeature()) +FreeCADGui.addCommand('ColourMapCommand', ColourMapFeature()) +FreeCADGui.addCommand('SetMaterialCommand', SetMaterialFeature()) +FreeCADGui.addCommand('BooleanCutCommand', BooleanCutFeature()) +FreeCADGui.addCommand('BooleanIntersectionCommand', BooleanIntersectionFeature()) +FreeCADGui.addCommand('BooleanUnionCommand', BooleanUnionFeature()) +FreeCADGui.addCommand('BoxCommand', BoxFeature()) +FreeCADGui.addCommand('EllipsoidCommand', EllispoidFeature()) +FreeCADGui.addCommand('ElTubeCommand', ElliTubeFeature()) +FreeCADGui.addCommand('ConeCommand', ConeFeature()) +FreeCADGui.addCommand('SphereCommand', SphereFeature()) +FreeCADGui.addCommand('TorusCommand', TorusFeature()) +FreeCADGui.addCommand('TrapCommand', TrapFeature()) +FreeCADGui.addCommand('TubeCommand', TubeFeature()) +FreeCADGui.addCommand('PolyHedraCommand', PolyHedraFeature()) +FreeCADGui.addCommand('AddCompound', CompoundFeature()) +FreeCADGui.addCommand('TessellateCommand', TessellateFeature()) +FreeCADGui.addCommand('TessellateGmshCommand', TessellateGmshFeature()) +FreeCADGui.addCommand('DecimateCommand', DecimateFeature()) +FreeCADGui.addCommand('Mesh2TessCommand', Mesh2TessFeature()) +FreeCADGui.addCommand('Tess2MeshCommand', Tess2MeshFeature()) +FreeCADGui.addCommand('TetrahedronCommand', TetrahedronFeature()) diff --git a/freecad/gdml/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index 872fe4c7c..da08cc61f 100644 --- a/freecad/gdml/GDMLMaterials.py +++ b/freecad/gdml/GDMLMaterials.py @@ -1,28 +1,29 @@ -#************************************************************************** -#* * -#* Copyright (c) 2021 Keith Sloan * -#* (c) Munther Hindi * -#* (c) 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 : * -#************************************************************************** -__title__="FreeCAD GDML Workbench - GUI Commands" +# Sun Jan 30 11:32:46 AM PST 2022 +# ************************************************************************** +# * * +# * Copyright (c) 2021 Keith Sloan * +# * (c) Munther Hindi * +# * (c) 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 : * +# ************************************************************************** +__title__ = "FreeCAD GDML Workbench - GUI Commands" __author__ = "Keith Sloan" __url__ = ["http://www.freecadweb.org"] @@ -30,51 +31,93 @@ from PySide import QtGui, QtCore + class GDMLMaterial(QtGui.QComboBox): - def __init__(self,matList,mat) : - super().__init__() - self.addItems(matList) - self.setEditable(False) - if mat is not None : - self.setCurrentIndex(matList.index(mat)) + def __init__(self, matList, mat): + super().__init__() + self.addItems(matList) + self.setEditable(False) + if mat is not None: + self.setCurrentIndex(matList.index(mat)) + + def getItem(self): + return str(self.currentText()) - def getItem(self): - return str(self.currentText()) -def getMaterialsList() : +def getMaterialsList(): matList = [] doc = FreeCAD.activeDocument() - try : - materials = doc.Materials - geant4 = doc.Geant4 - g4Mats = doc.getObject('G4Materials') - - except : - from .importGDML import processGEANT4 - from .init_gui import joinDir - - print('Load Geant4 Materials XML') - processGEANT4(doc,joinDir("Resources/Geant4Materials.xml")) - materials = doc.Materials - geant4 = doc.Geant4 - g4Mats = doc.getObject('G4Materials') - try : - if materials is not None : - for m in materials.OutList : - if m.Label != "Geant4" : - matList.append(m.Label) - #print(matList) - - except : - pass - - try : - if g4Mats is not None : - for m in g4Mats.OutList : - matList.append(m.Label) - #print(matList) - except : - pass + try: + materials = doc.Materials + geant4 = doc.Geant4 + g4Mats = doc.getObject('G4Materials') + + except: + from .importGDML import processGEANT4 + from .init_gui import joinDir + + print('Load Geant4 Materials XML') + processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) + materials = doc.Materials + geant4 = doc.Geant4 + g4Mats = doc.getObject('G4Materials') + + try: + if materials is not None: + for m in materials.OutList: + if m.Label != "Geant4": + matList.append(m.Label) + # print(matList) + except: + pass + + try: + if g4Mats is not None: + for m in g4Mats.OutList: + matList.append(m.Label) + # print(matList) + except: + pass return matList + + +def getGroupedMaterials(): + print('getGroupedMaterials') + from .GDMLObjects import GroupedMaterials + from .importGDML import setupEtree + from .init_gui import joinDir + + if len(GroupedMaterials) == 0: + etree, root = setupEtree(joinDir("Resources/Geant4Materials.xml")) + materials = root.find('materials') + + for material in materials.findall('material'): + name = material.get('name') + print(name) + if name is None: + print("Missing Name") + else: + for auxiliary in material.findall('auxiliary'): + auxtype = auxiliary.get('auxtype') + if auxtype == 'Material-type': + auxvalue = auxiliary.get('auxvalue') + if auxvalue in GroupedMaterials: + GroupedMaterials[auxvalue].append(name) + else: + GroupedMaterials[auxvalue] = [name] + + doc = FreeCAD.activeDocument() + docMaterials = doc.Materials + matList = [] + 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 + + return GroupedMaterials diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 85d77b481..0738ad8ba 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -# Mon Dec 6 10:02:57 AM PST 2021 +# insert date with Ctrl-u ESC-! date +# Wed Jan 26 04:44:48 PM PST 2022 # -#************************************************************************** -#* * -#* Copyright (c) 2017 Keith Sloan * -#* (c) Dam Lambert 2020 * -#* (c) Munther Hindi 2021 * -#* * -#* 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 * +# * (c) Munther Hindi 2021 * +# * * +# * 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 : * +# * * +# * * +# ************************************************************************** import FreeCAD, FreeCADGui, Part from pivy import coin @@ -39,230 +40,252 @@ # So need to be able to rebuild from Objects global MaterialsList MaterialsList = [] +global GroupedMaterials +GroupedMaterials = {} # dictionary of material lists by type global LengthQuantityList -LengthQuantityList = ['nm','um', 'mm','cm', 'dm','m', 'km'] +LengthQuantityList = ['nm', 'um', 'mm', 'cm', 'dm', 'm', 'km'] # cf definition https://wiki.freecadweb.org/Quantity -def setLengthQuantity(obj, m) : - if LengthQuantityList is not None : +def setLengthQuantity(obj, m): + if LengthQuantityList is not None: obj.lunit = LengthQuantityList obj.lunit = 0 - if len(LengthQuantityList) > 0 : - if not ( m == 0 or m is None ) : + if len(LengthQuantityList) > 0: + if not (m == 0 or m is None): obj.lunit = LengthQuantityList.index(m) - else : + else: obj.lunit = 2 -def addMaterialsFromGroup(doc, MatList, grpName) : + +def addMaterialsFromGroup(doc, MatList, grpName): mmats = doc.getObject(grpName) - if mmats is not None : - if hasattr(mmats,'Group') : - for i in mmats.Group : - MatList.append(i.Name) + if mmats is not None: + if hasattr(mmats, 'Group'): + for i in mmats.Group: + MatList.append(i.Name) -def rebuildMaterialsList() : + +def rebuildMaterialsList(): global MaterialsList print('Restore MaterialsList from Materials Lists') doc = FreeCAD.ActiveDocument - addMaterialsFromGroup(doc,MaterialsList,"Materials") - addMaterialsFromGroup(doc,MaterialsList,"G4Materials") - #print('MaterialsList') - #print(MaterialsList) + addMaterialsFromGroup(doc, MaterialsList, "Materials") + addMaterialsFromGroup(doc, MaterialsList, "G4Materials") + # print('MaterialsList') + # print(MaterialsList) + -def checkMaterial(material) : +def checkMaterial(material): global MaterialsList - try : - i = MaterialsList.index(material) + try: + i = MaterialsList.index(material) except ValueError: - return False + return False return True -def setMaterial(obj, m) : - #print('setMaterial') - if MaterialsList is not None : - if len(MaterialsList) > 0 : - obj.material = MaterialsList - obj.material = 0 - if not ( m == 0 or m is None ) : - try : - obj.material = MaterialsList.index(m) - except : - print('Not in List') - print(MaterialsList) - obj.material = 0 - return - return + +def setMaterial(obj, m): + # print('setMaterial') + if MaterialsList is not None: + if len(MaterialsList) > 0: + obj.material = MaterialsList + obj.material = 0 + if not (m == 0 or m is None): + try: + obj.material = MaterialsList.index(m) + except: + print('Not in List') + print(MaterialsList) + obj.material = 0 + return + return rebuildMaterialsList() setMaterial(obj, m) -def checkFullCircle(aunit, angle) : - #print(angle) - if aunit == 'deg' and angle == 360 : - return True - if aunit == 'rad' and angle == 2 * math.pi : - return True + +def checkFullCircle(aunit, angle): + # print(angle) + if aunit == 'deg' and angle == 360: + return True + if aunit == 'rad' and angle == 2 * math.pi: + return True return False + # Get angle in Radians -def getAngleRad(aunit,angle) : - #print("aunit : "+str(aunit)) - if aunit == 'deg' : # 0 radians 1 Degrees - return(angle*math.pi/180) - else : - return angle +def getAngleRad(aunit, angle): + # print("aunit : "+str(aunit)) + if aunit == 'deg': # 0 radians 1 Degrees + return(angle*math.pi/180) + else: + return angle + # Get angle in Degrees -def getAngleDeg(aunit,angle) : - #print("aunit : "+str(aunit)) - if aunit == 'rad' : # 0 radians 1 Degrees - return(angle*180/math.pi) - else : - return angle - -def makeRegularPolygon(n,r,z): +def getAngleDeg(aunit, angle): + # print("aunit : "+str(aunit)) + if aunit == 'rad': # 0 radians 1 Degrees + return(angle*180/math.pi) + else: + return angle + + +def makeRegularPolygon(n, r, z): from math import cos, sin, pi - vecs = [FreeCAD.Vector(cos(2*pi*i/n)*r, sin(2*pi*i/n)*r, z) \ + vecs = [FreeCAD.Vector(cos(2*pi*i/n)*r, sin(2*pi*i/n)*r, z) for i in range(n+1)] return vecs -def printPolyVec(n,v) : - print("Polygon : "+n) - for i in v : + +def printPolyVec(n, v): + print("Polygon : " + n) + for i in v: print("Vertex - x : "+str(i[0])+" y : "+str(i[1])+" z : "+str(i[2])) -def translate(shape,base) : + +def translate(shape, base): # Input Object and displacement vector - return a transformed shape - #return shape + # return shape myPlacement = FreeCAD.Placement() myPlacement.move(base) mat1 = myPlacement.toMatrix() - #print(mat1) + # print(mat1) mat2 = shape.Matrix - mat = mat1.multiply(mat2) - #print(mat) + mat = mat1.multiply(mat2) + # print(mat) retShape = shape.copy() retShape.transformShape(mat, True) return retShape -def make_face3(v1,v2,v3): + +def make_face3(v1, v2, v3): # helper method to create the faces - wire = Part.makePolygon([v1,v2,v3,v1]) + wire = Part.makePolygon([v1, v2, v3, v1]) face = Part.Face(wire) return face -def make_face4(v1,v2,v3,v4): + +def make_face4(v1, v2, v3, v4): # helper method to create the faces - wire = Part.makePolygon([v1,v2,v3,v4,v1]) + wire = Part.makePolygon([v1, v2, v3, v4, v1]) face = Part.Face(wire) return face -def makeFrustrum(num,poly0,poly1) : + +def makeFrustrum(num, poly0, poly1): # return list of faces - #print("Make Frustrum : "+str(num)+" Faces") + # print("Make Frustrum : "+str(num)+" Faces") faces = [] - for i in range(num) : - j = i + 1 - #print([poly0[i],poly0[j],poly1[j],poly1[i]]) - w = Part.makePolygon([poly0[i],poly0[j],poly1[j],poly1[i],poly0[i]]) - faces.append(Part.Face(w)) - #print("Number of Faces : "+str(len(faces))) + for i in range(num): + j = i + 1 + # print([poly0[i],poly0[j],poly1[j],poly1[i]]) + w = Part.makePolygon([poly0[i], poly0[j], poly1[j], + poly1[i], poly0[i]]) + faces.append(Part.Face(w)) + # print("Number of Faces : "+str(len(faces))) return(faces) -def angleSectionSolid(fp, rmax, z, shape) : + +def angleSectionSolid(fp, rmax, z, shape): # Different Solids have different rmax and height - #print("angleSectionSolid") - #print('rmax : '+str(rmax)) - #print('z : '+str(z)) - #print("aunit : "+fp.aunit) - startPhiDeg = getAngleDeg(fp.aunit,fp.startphi) - deltaPhiDeg = getAngleDeg(fp.aunit,fp.deltaphi) - #print('delta') - #print(deltaPhiDeg) - #print('start') - #print(startPhiDeg) - v1 = FreeCAD.Vector(0,0,0) - v2 = FreeCAD.Vector(rmax,0,0) - v3 = FreeCAD.Vector(rmax,0,z) - v4 = FreeCAD.Vector(0,0,z) - - f1 = make_face4(v1,v2,v3,v4) - s1 = f1.revolve(v1,v4,360-deltaPhiDeg) + # print("angleSectionSolid") + # print('rmax : '+str(rmax)) + # print('z : '+str(z)) + # print("aunit : "+fp.aunit) + startPhiDeg = getAngleDeg(fp.aunit, fp.startphi) + deltaPhiDeg = getAngleDeg(fp.aunit, fp.deltaphi) + # print('delta') + # print(deltaPhiDeg) + # print('start') + # print(startPhiDeg) + v1 = FreeCAD.Vector(0, 0, 0) + v2 = FreeCAD.Vector(rmax, 0, 0) + v3 = FreeCAD.Vector(rmax, 0, z) + v4 = FreeCAD.Vector(0, 0, z) + + f1 = make_face4(v1, v2, v3, v4) + s1 = f1.revolve(v1, v4, 360-deltaPhiDeg) # Problem with FreeCAD 0.18 - #s2 = s1.rotate(v1,v4,startPhiDeg) - - s2 = s1.rotate(v1,v4,deltaPhiDeg) - - #Part.show(s2) - #return(shape.cut(s2)) - #return(s2) - + # s2 = s1.rotate(v1,v4,startPhiDeg) + + s2 = s1.rotate(v1, v4, deltaPhiDeg) + + # Part.show(s2) + # return(shape.cut(s2)) + # return(s2) + shape = shape.cut(s2) - if startPhiDeg != 0 : - shape.rotate(FreeCAD.Vector(0,0,0), \ - FreeCAD.Vector(0,0,1),startPhiDeg) + if startPhiDeg != 0: + shape.rotate(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), startPhiDeg) return shape -def indiceToRay(indiceIn): # Thanks to Dam - if indiceIn<1: + +def indiceToRay(indiceIn): # Thanks to Dam + if indiceIn < 1: return 0 else: - lray=[0.0, 1.0] - puissanceDown=2 + lray = [0.0, 1.0] + puissanceDown = 2 while len(lray) <= indiceIn: - for indiceTmp in range(1,puissanceDown,2): + for indiceTmp in range(1, puissanceDown, 2): lray.append(float(indiceTmp)/float(puissanceDown)) puissanceDown = 2 * puissanceDown return lray[indiceIn] - -def colorFromRay(rayIn): # Thanks to Dam - coeffR=coeffG=coeffB=1.0 - - if(rayIn<0.2 and rayIn>=0.0): - coeffR=1.0 - coeffG=rayIn*5.0 - coeffB=0.0 - elif(rayIn<0.4): - coeffR=2.0-(5.0*rayIn) - coeffG=1.0 - coeffB=0.0 - elif(rayIn<0.6): - coeffR=0.0 - coeffG=1.0 - coeffB=rayIn*5.0-2.0 - elif(rayIn<0.8): - coeffR=1.0 - coeffG=4.0-(5.0*rayIn) - coeffB=1.0 - elif(rayIn<=1.0): - coeffR=(5.0*rayIn)-4.0 - coeffG=0.0 - coeffB=1.0 - return (coeffR,coeffG,coeffB,0.0) + + +def colorFromRay(rayIn): # Thanks to Dam + coeffR = coeffG = coeffB = 1.0 + + if(rayIn < 0.2 and rayIn >= 0.0): + coeffR = 1.0 + coeffG = rayIn*5.0 + coeffB = 0.0 + elif(rayIn < 0.4): + coeffR = 2.0-(5.0*rayIn) + coeffG = 1.0 + coeffB = 0.0 + elif(rayIn < 0.6): + coeffR = 0.0 + coeffG = 1.0 + coeffB = rayIn*5.0-2.0 + elif(rayIn < 0.8): + coeffR = 1.0 + coeffG = 4.0-(5.0*rayIn) + coeffB = 1.0 + elif(rayIn <= 1.0): + coeffR = (5.0*rayIn)-4.0 + coeffG = 0.0 + coeffB = 1.0 + return (coeffR, coeffG, coeffB, 0.0) + def colourMaterial(m): - if MaterialsList is None : - return (0.5,0.5,0.5,0.0) - else : - if ( m is None ) : - return (0.5,0.5,0.5,0,0) - elif(len(MaterialsList)<=1): - return (0.5,0.5,0.5,0.0) - elif m not in MaterialsList : - return (0.5,0.5,0.5,0.0) - else: - coeffRGB = MaterialsList.index(m) - return colorFromRay(indiceToRay(coeffRGB)) - -def updateColour(obj, colour, material) : - if colour is None : - colour = colourMaterial(material) + if MaterialsList is None: + return (0.5, 0.5, 0.5, 0.0) + else: + if (m is None): + return (0.5, 0.5, 0.5, 0, 0) + elif(len(MaterialsList) <= 1): + return (0.5, 0.5, 0.5, 0.0) + elif m not in MaterialsList: + return (0.5, 0.5, 0.5, 0.0) + else: + coeffRGB = MaterialsList.index(m) + return colorFromRay(indiceToRay(coeffRGB)) + + +def updateColour(obj, colour, material): + if colour is None: + colour = colourMaterial(material) obj.ViewObject.ShapeColor = colour - #print(f'Colour {colour}') - if colour is not None : - obj.ViewObject.Transparency = int(colour[3]*100) + # print(f'Colour {colour}') + if colour is not None: + obj.ViewObject.Transparency = int(colour[3]*100) + def rotateAroundZ(nstep, z, r): ####################################### @@ -272,157 +295,188 @@ def rotateAroundZ(nstep, z, r): # z - list of z coordinates of polylines # r - list of r coordinates of polylines # - faces = [] verts = [] verts.append(FreeCAD.Vector(0, 0, z[0])) - verts.extend([FreeCAD.Vector(r[i], 0, z[i]) for i in range(0,len(z))]) - verts.append(FreeCAD.Vector(0, 0, z[len(z)-1]) ) + verts.extend([FreeCAD.Vector(r[i], 0, z[i]) for i in range(0, len(z))]) + verts.append(FreeCAD.Vector(0, 0, z[len(z)-1])) line = Part.makePolygon(verts) - surf = line.revolve(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 360) + surf = line.revolve(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 360) return Part.makeSolid(surf) - -class GDMLColourMapEntry : - def __init__(self,obj,colour,material) : - obj.addProperty("App::PropertyColor","colour", \ - "GDMLColourMapEntry","colour").colour=colour - obj.addProperty("App::PropertyEnumeration","material", \ - "GDMLColourMapEntry","Material") - setMaterial(obj, material) - -def indexBoolean(list,ln) : - #print('Length : '+str(ln)) - if ln > 3 : - #print(range(ln-3)) - for r in range(ln-2,-1,-1) : - t = list[r].TypeId - #print(t) - if t == 'Part::Cut' or t == 'Part::Fuse' or t == 'Part::Common' : - return r - return -1 - -class GDMLsolid : - def __init__(self, obj): - '''Init''' - #print('>>>>>') - #if hasattr(obj,'Label') : - # print('Label : '+obj.Label) - #print('TypeId : '+obj.TypeId) - #print(dir(obj)) - #if hasattr(obj,'InList') : - # print('InList') - # print(obj.InList) - # for i in obj.InList : - # print(i.TypeId) - # if hasattr(i,'Label') : - # print('Label : '+i.Label) - # if i.TypeId == 'App::Part' : - # print(i.OutList) - # for j in i.OutList : - # print(' ==> Typeid'+str(j.TypeId)) - # if hasattr(j,'Label') : - # print(' ==> Label'+j.Label) - # - #print('<<<<<') - if hasattr(obj,'InList') : - for j in obj.InList : - if hasattr(j,'OutList') : - ln = len(j.OutList) - r = indexBoolean(j.OutList,ln) - #print('index : '+str(r)) - if r >= 0 : - if (ln - r) >= 2 : - #print('Tool : '+obj.Label) - return # Let Placement default to 0 - obj.setEditorMode('Placement',2) - - def getMaterial(self): - return obj.material - - def execute(self, fp): - self.createGeometry(fp) - - def __getstate__(self): - '''When saving the document this object gets stored using Python's json module.\ - Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ - to return a tuple of all serializable objects or None.''' - if hasattr(self,'Type') : - return {'type' : self.Type } - else : - pass - - def __setstate__(self, arg): - '''When restoring the serialized object from document we have the chance to set some internals here. Since no data were serialized nothing needs to be done here.''' - self.Type = arg['type'] - -class GDMLcommon : - def __init__(self, obj): - '''Init''' - - def __getstate__(self): - '''When saving the document this object gets stored using Python's json module.\ - Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ - to return a tuple of all serializable objects or None.''' - if hasattr(self,'Type') : # If not saved just return - return {'type' : self.Type } - else : - pass - - def __setstate__(self,arg): - '''When restoring the serialized object from document we have the chance to set some internals here.\ - Since no data were serialized nothing needs to be done here.''' - if arg is not None : - self.Type = arg['type'] - -class GDMLArb8(GDMLsolid) : # Thanks to Dam Lamb - def __init__(self, obj, v1x, v1y, v2x, v2y, v3x, v3y, v4x, v4y, \ - v5x, v5y, v6x, v6y, v7x, v7y, v8x, v8y, dz, \ - lunit, material, colour = None): - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat","v1x","GDMLArb8","vertex 1 x position").v1x=v1x - obj.addProperty("App::PropertyFloat","v1y","GDMLArb8","vertex 1 y position").v1y=v1y - obj.addProperty("App::PropertyFloat","v2x","GDMLArb8","vertex 2 x position").v2x=v2x - obj.addProperty("App::PropertyFloat","v2y","GDMLArb8","vertex 2 y position").v2y=v2y - obj.addProperty("App::PropertyFloat","v3x","GDMLArb8","vertex 3 x position").v3x=v3x - obj.addProperty("App::PropertyFloat","v3y","GDMLArb8","vertex 3 y position").v3y=v3y - obj.addProperty("App::PropertyFloat","v4x","GDMLArb8","vertex 4 x position").v4x=v4x - obj.addProperty("App::PropertyFloat","v4y","GDMLArb8","vertex 4 y position").v4y=v4y - obj.addProperty("App::PropertyFloat","v5x","GDMLArb8","vertex 5 x position").v5x=v5x - obj.addProperty("App::PropertyFloat","v5y","GDMLArb8","vertex 5 y position").v5y=v5y - obj.addProperty("App::PropertyFloat","v6x","GDMLArb8","vertex 6 x position").v6x=v6x - obj.addProperty("App::PropertyFloat","v6y","GDMLArb8","vertex 6 y position").v6y=v6y - obj.addProperty("App::PropertyFloat","v7x","GDMLArb8","vertex 7 x position").v7x=v7x - obj.addProperty("App::PropertyFloat","v7y","GDMLArb8","vertex 7 y position").v7y=v7y - obj.addProperty("App::PropertyFloat","v8x","GDMLArb8","vertex 8 x position").v8x=v8x - obj.addProperty("App::PropertyFloat","v8y","GDMLArb8","vertex 8 y position").v8y=v8y - obj.addProperty("App::PropertyFloat","dz","GDMLArb8","Half z Length").dz=dz - obj.addProperty("App::PropertyEnumeration","lunit","GDMLArb8","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLArb8","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLArb8' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['v1x', 'v1y', 'v2x','v2y', 'v3x', 'v3y', 'v4x', 'v4y', \ - 'v5x', 'v5y', 'v6x', 'v6y', 'v7x', 'v7y', 'v8x', 'v8y', 'dz','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid + + +class GDMLColourMapEntry: + def __init__(self, obj, colour, material): + obj.addProperty("App::PropertyColor", "colour", + "GDMLColourMapEntry", "colour").colour = colour + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLColourMapEntry", "Material") + setMaterial(obj, material) + + +def indexBoolean(list, ln): + # print('Length : '+str(ln)) + if ln > 3: + # print(range(ln-3)) + for r in range(ln-2, -1, -1): + t = list[r].TypeId + # print(t) + if t == 'Part::Cut' or t == 'Part::Fuse' or t == 'Part::Common': + return r + return -1 + + +class GDMLsolid: + def __init__(self, obj): + '''Init''' + # print('>>>>>') + # if hasattr(obj,'Label') : + # print('Label : '+obj.Label) + # print('TypeId : '+obj.TypeId) + # print(dir(obj)) + # if hasattr(obj,'InList') : + # print('InList') + # print(obj.InList) + # for i in obj.InList : + # print(i.TypeId) + # if hasattr(i,'Label') : + # print('Label : '+i.Label) + # if i.TypeId == 'App::Part' : + # print(i.OutList) + # for j in i.OutList : + # print(' ==> Typeid'+str(j.TypeId)) + # if hasattr(j,'Label') : + # print(' ==> Label'+j.Label) + # + # print('<<<<<') + if hasattr(obj, 'InList'): + for j in obj.InList: + if hasattr(j, 'OutList'): + ln = len(j.OutList) + r = indexBoolean(j.OutList, ln) + # print('index : '+str(r)) + if r >= 0: + if (ln - r) >= 2: + # print('Tool : '+obj.Label) + return # Let Placement default to 0 + obj.setEditorMode('Placement', 2) + + def getMaterial(self): + return obj.material + + def execute(self, fp): + self.createGeometry(fp) + + def __getstate__(self): + '''When saving the document this object gets stored using Python's json + module. + Since we have some un-serializable parts here -- the Coin stuff -- + we must define this method\ + to return a tuple of all serializable objects or None.''' + if hasattr(self, 'Type'): + return {'type': self.Type} + else: + pass + + def __setstate__(self, arg): + '''When restoring the serialized object from document we have the + chance to set some internals here. Since no data were serialized + nothing needs to be done here.''' + self.Type = arg['type'] + + +class GDMLcommon: + def __init__(self, obj): + '''Init''' + + def __getstate__(self): + '''When saving the document this object gets stored using Python's + json module. + Since we have some un-serializable parts here -- the Coin stuff -- + we must define this method + to return a tuple of all serializable objects or None.''' + if hasattr(self, 'Type'): # If not saved just return + return {'type': self.Type} + else: + pass + + def __setstate__(self, arg): + '''When restoring the serialized object from document we have the + chance to set some internals here. + Since no data were serialized nothing needs to be done here.''' + if arg is not None: + self.Type = arg['type'] + + +class GDMLArb8(GDMLsolid): # Thanks to Dam Lamb + def __init__(self, obj, v1x, v1y, v2x, v2y, v3x, v3y, v4x, v4y, + v5x, v5y, v6x, v6y, v7x, v7y, v8x, v8y, dz, + lunit, material, colour=None): + '''Add some custom properties to our Tube feature''' + obj.addProperty("App::PropertyFloat", + "v1x", "GDMLArb8", "vertex 1 x position").v1x = v1x + obj.addProperty("App::PropertyFloat", + "v1y", "GDMLArb8", "vertex 1 y position").v1y = v1y + obj.addProperty("App::PropertyFloat", + "v2x", "GDMLArb8", "vertex 2 x position").v2x = v2x + obj.addProperty("App::PropertyFloat", + "v2y", "GDMLArb8", "vertex 2 y position").v2y = v2y + obj.addProperty("App::PropertyFloat", + "v3x", "GDMLArb8", "vertex 3 x position").v3x = v3x + obj.addProperty("App::PropertyFloat", + "v3y", "GDMLArb8", "vertex 3 y position").v3y = v3y + obj.addProperty("App::PropertyFloat", + "v4x", "GDMLArb8", "vertex 4 x position").v4x = v4x + obj.addProperty("App::PropertyFloat", + "v4y", "GDMLArb8", "vertex 4 y position").v4y = v4y + obj.addProperty("App::PropertyFloat", + "v5x", "GDMLArb8", "vertex 5 x position").v5x = v5x + obj.addProperty("App::PropertyFloat", + "v5y", "GDMLArb8", "vertex 5 y position").v5y = v5y + obj.addProperty("App::PropertyFloat", + "v6x", "GDMLArb8", "vertex 6 x position").v6x = v6x + obj.addProperty("App::PropertyFloat", + "v6y", "GDMLArb8", "vertex 6 y position").v6y = v6y + obj.addProperty("App::PropertyFloat", + "v7x", "GDMLArb8", "vertex 7 x position").v7x = v7x + obj.addProperty("App::PropertyFloat", + "v7y", "GDMLArb8", "vertex 7 y position").v7y = v7y + obj.addProperty("App::PropertyFloat", + "v8x", "GDMLArb8", "vertex 8 x position").v8x = v8x + obj.addProperty("App::PropertyFloat", + "v8y", "GDMLArb8", "vertex 8 y position").v8y = v8y + obj.addProperty("App::PropertyFloat", + "dz", "GDMLArb8", "Half z Length").dz = dz + obj.addProperty("App::PropertyEnumeration", + "lunit", "GDMLArb8", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", + "material", "GDMLArb8", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLArb8' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['v1x', 'v1y', 'v2x', 'v2y', 'v3x', 'v3y', 'v4x', 'v4y', + 'v5x', 'v5y', 'v6x', 'v6y', 'v7x', 'v7y', 'v8x', 'v8y', + 'dz', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid # http://geant4-userdoc.web.cern.ch/geant4-userdoc/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html # The order of specification of the coordinates for the vertices in G4GenericTrap is important. The first four points are the vertices sitting on the -hz plane; the last four points are the vertices sitting on the +hz plane. @@ -438,3092 +492,3170 @@ def onChanged(self, fp, prop): # point 6 is connected with points 2,5,7 # point 7 is connected with points 3,4,6 - def createGeometry(self,fp): + def createGeometry(self, fp): currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - - pt1 = FreeCAD.Vector(fp.v1x*mul,fp.v1y*mul,-fp.dz*mul) - pt2 = FreeCAD.Vector(fp.v2x*mul,fp.v2y*mul,-fp.dz*mul) - pt3 = FreeCAD.Vector(fp.v3x*mul,fp.v3y*mul,-fp.dz*mul) - pt4 = FreeCAD.Vector(fp.v4x*mul,fp.v4y*mul,-fp.dz*mul) - pt5 = FreeCAD.Vector(fp.v5x*mul,fp.v5y*mul,fp.dz*mul) - pt6 = FreeCAD.Vector(fp.v6x*mul,fp.v6y*mul,fp.dz*mul) - pt7 = FreeCAD.Vector(fp.v7x*mul,fp.v7y*mul,fp.dz*mul) - pt8 = FreeCAD.Vector(fp.v8x*mul,fp.v8y*mul,fp.dz*mul) - - faceZmin = Part.Face(Part.makePolygon([pt1,pt2,pt3,pt4,pt1])) - faceZmax = Part.Face(Part.makePolygon([pt5,pt6,pt7,pt8,pt5])) - - faceXminA = Part.Face(Part.makePolygon([pt1,pt2,pt6,pt1])) - faceXminB = Part.Face(Part.makePolygon([pt6,pt5,pt1,pt6])) - faceXmaxA = Part.Face(Part.makePolygon([pt4,pt3,pt7,pt4])) - faceXmaxB = Part.Face(Part.makePolygon([pt8,pt4,pt7,pt8])) - - faceYminA = Part.Face(Part.makePolygon([pt1,pt8,pt4,pt1])) - faceYminB = Part.Face(Part.makePolygon([pt1,pt5,pt8,pt1])) - - faceYmaxA = Part.Face(Part.makePolygon([pt2,pt3,pt7,pt2])) - faceYmaxB = Part.Face(Part.makePolygon([pt2,pt7,pt6,pt2])) - - - fp.Shape = Part.makeSolid(Part.makeShell([faceXminA,faceXminB,faceXmaxA,faceXmaxB, - faceYminA,faceYminB,faceYmaxA,faceYmaxB, - faceZmin,faceZmax])) - fp.Placement = currPlacement - -class GDMLBox(GDMLsolid) : - def __init__(self, obj, x, y, z, lunit, material, colour=None): - super().__init__(obj) - '''Add some custom properties to our Box feature''' - GDMLShared.trace("GDMLBox init") - #GDMLShared.trace("material : "+material) - obj.addProperty("App::PropertyFloat","x","GDMLBox","Length x").x=x - obj.addProperty("App::PropertyFloat","y","GDMLBox","Length y").y=y - obj.addProperty("App::PropertyFloat","z","GDMLBox","Length z").z=z - obj.addProperty("App::PropertyEnumeration","lunit","GDMLBox","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLBox","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLBox' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State) : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['x','y','z','lunit'] : - self.createGeometry(fp) - - # execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - #print('createGeometry') - #print(fp) - - if all((fp.x,fp.y,fp.z)) : - currPlacement = fp.Placement - - #if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : - mul = GDMLShared.getMult(fp) - GDMLShared.trace('mul : '+str(mul)) - x = mul * fp.x - y = mul * fp.y - z = mul * fp.z - box = Part.makeBox(x,y,z) - base = FreeCAD.Vector(-x/2,-y/2,-z/2) - fp.Shape = translate(box,base) - fp.Placement = currPlacement - - def OnDocumentRestored(self,obj) : - print('Doc Restored') - - -class GDMLCone(GDMLsolid) : - def __init__(self, obj, rmin1,rmax1,rmin2,rmax2,z,startphi,deltaphi,aunit, \ - lunit, material, colour = None): - super().__init__(obj) - '''Add some custom properties to our Cone feature''' - obj.addProperty("App::PropertyFloat","rmin1","GDMLCone","Min Radius 1").rmin1=rmin1 - obj.addProperty("App::PropertyFloat","rmax1","GDMLCone","Max Radius 1").rmax1=rmax1 - obj.addProperty("App::PropertyFloat","rmin2","GDMLCone","Min Radius 2").rmin2=rmin2 - obj.addProperty("App::PropertyFloat","rmax2","GDMLCone","Max Radius 2").rmax2=rmax2 - obj.addProperty("App::PropertyFloat","z","GDMLCone","Height of Cone").z=z - obj.addProperty("App::PropertyFloat","startphi","GDMLCone","Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLCone","Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLCone","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLCone","lunit") - setLengthQuantity(obj, lunit) - - - obj.addProperty("App::PropertyEnumeration","material","GDMLCone", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLCone' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin1','rmax1','rmin2','rmax2','z','startphi','deltaphi' \ - ,'aunit', 'lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - #print("fp : ") - #print(vars(fp)) - #if all((fp.rmin1,fp.rmin2,fp.rmax1,fp.rmax2,fp.z)) : - if (hasattr(fp,'rmin1') and hasattr(fp,'rmax1') and \ - hasattr(fp,'rmin2') and hasattr(fp,'rmax2') and \ - hasattr(fp,'z')) : - # Need to add code to check variables will make a valid cone - # i.e.max > min etc etc - #print("execute cone") - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - rmin1 = mul * fp.rmin1 - rmin2 = mul * fp.rmin2 - rmax1 = mul * fp.rmax1 - rmax2 = mul * fp.rmax2 - z = mul * fp.z - #print(mul) - #print(rmax1) - #print(rmax2) - #print(rmin1) - #print(rmin2) - #print(z) - if rmax1 != rmax2 : - cone1 = Part.makeCone(rmax1,rmax2,z) - else : - cone1 = Part.makeCylinder(rmax1,z) - - if (rmin1 != 0 and rmin2 != 0 ) : - if rmin1 != rmin2 : - cone2 = Part.makeCone(rmin1,rmin2,z) - else : - cone2 = Part.makeCylinder(rmin1,z) - - if rmax1 > rmin1 : - cone3 = cone1.cut(cone2) - else : - cone3 = cone2.cut(cone1) - else : - cone3 = cone1 - base = FreeCAD.Vector(0,0,-z/2) - if checkFullCircle(fp.aunit,fp.deltaphi) == False : - rmax = max(rmax1, rmax2) - cone = angleSectionSolid(fp, rmax, z, cone3) - fp.Shape = translate(cone,base) - else : - fp.Shape = translate(cone3,base) - fp.Placement = currPlacement - -class GDMLElCone(GDMLsolid) : - def __init__(self, obj, dx, dy, zmax, zcut, lunit, material, colour = None) : - super().__init__(obj) - '''Add some custom properties to our ElCone feature''' - obj.addProperty("App::PropertyFloat","dx","GDMLElCone", \ - "x semi axis").dx = dx - obj.addProperty("App::PropertyFloat","dy","GDMLElCone", \ - "y semi axis").dy = dy - obj.addProperty("App::PropertyFloat","zmax","GDMLElCone", \ - "z length").zmax = zmax - obj.addProperty("App::PropertyFloat","zcut","GDMLElCone", \ - "z cut").zcut = zcut - obj.addProperty("App::PropertyEnumeration","lunit","GDMLElCone","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLElCone", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLElCone' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['dx','dy','zmax','zcut','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - # Form the Web page documentation page for elliptical cone: - #https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html - # the parametric equation of the elliptical cone: - # x = dx*(zmax - u) * cos(v), v = 0..2Pi (note, as of 2021-11-21, web page mistakingly shows /u) - # y = dy*(zmax - u) * sin(v) - # z = u, u = -zcut..zcut - #Therefore the bottom base of the cone (at z=u=-zcut) has xmax = dxmax = dx*(zmax+zcut) - # and ymax=dymax = dy*(zmax+zcut) - # The ellipse at the top has simi-major axis dx*(zmax-zcut) and semiminor axis dy*(zmax-zcut) - # as per the above, the "bottom of the cone is at z = -zcut - # Note that dx is a SCALING factor for the semi major axis, NOT the actual semi major axis - # ditto for dy - - mul = GDMLShared.getMult(fp) - currPlacement = fp.Placement - rmax = (fp.zmax+fp.zcut)*mul - cone1 = Part.makeCone(rmax, 0, rmax) - mat = FreeCAD.Matrix() - mat.unity() - # Semi axis values so need to double - dx = fp.dx - dy = fp.dy - zcut = fp.zcut * mul - zmax = fp.zmax * mul - mat.A11 = dx - mat.A22 = dy - mat.A33 = 1 - mat.A34 = -zcut # move bottom of cone to -zcut - mat.A44 = 1 - xmax = dx*rmax - ymax = dy*rmax - cone2 = cone1.transformGeometry(mat) - if zcut is not None : - box = Part.makeBox(2*xmax,2*ymax,zmax) - pl = FreeCAD.Placement() - # Only need to move to semi axis - pl.move(FreeCAD.Vector(-xmax,- ymax, zcut)) - box.Placement = pl - fp.Shape = cone2.cut(box) - else : - fp.Shape = cone2 - fp.Placement = currPlacement - -class GDMLEllipsoid(GDMLsolid) : - def __init__(self, obj, ax, by, cz, zcut1, zcut2, lunit, material, colour = None) : - super().__init__(obj) - '''Add some custom properties to our Elliptical Tube feature''' - obj.addProperty("App::PropertyFloat","ax","GDMLEllipsoid", \ - "x semi axis").ax=ax - obj.addProperty("App::PropertyFloat","by","GDMLEllipsoid", \ - "y semi axis").by=by - obj.addProperty("App::PropertyFloat","cz","GDMLEllipsoid", \ - "z semi axis").cz=cz - obj.addProperty("App::PropertyFloat","zcut1","GDMLEllipsoid", \ - "z axis cut1").zcut1=zcut1 - obj.addProperty("App::PropertyFloat","zcut2","GDMLEllipsoid", \ - "z axis1 cut2").zcut2=zcut2 - obj.addProperty("App::PropertyEnumeration","lunit","GDMLEllipsoid","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLEllipsoid", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLEllipsoid' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['ax','by','cz','zcut1','zcut2','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - sphere = Part.makeSphere(100) - 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.A44 = 1 - zcut1 = abs(fp.zcut1*mul) - zcut2 = abs(fp.zcut2*mul) - 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) - pl = FreeCAD.Placement() - # Only need to move to semi axis - pl.move(FreeCAD.Vector(-ax,-by,cz-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) - pl = FreeCAD.Placement() - pl.move(FreeCAD.Vector(-ax,-by,-cz)) - box2.Placement = pl - shape = t2ellipsoid.cut(box2) - else : - shape = t2ellipsoid - - base = FreeCAD.Vector(0,0,cz/4) - fp.Shape = translate(shape,base) - fp.Placement = currPlacement - -class GDMLElTube(GDMLsolid) : - def __init__(self, obj, dx, dy, dz, lunit, material, colour=None) : - super().__init__(obj) - '''Add some custom properties to our Elliptical Tube feature''' - obj.addProperty("App::PropertyFloat","dx","GDMLElTube", \ - "x semi axis1").dx=dx - obj.addProperty("App::PropertyFloat","dy","GDMLElTube", \ - "y semi axis1").dy=dy - obj.addProperty("App::PropertyFloat","dz","GDMLElTube", \ - "z half height").dz=dz - obj.addProperty("App::PropertyEnumeration","lunit","GDMLElTube","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLElTube", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLElTube' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - '''Do something when a property has changed''' - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['dx','dy','dz','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - tube = Part.makeCylinder(100,100) - mat = FreeCAD.Matrix() - mat.unity() - mat.A11 = (fp.dx * mul) / 100 - mat.A22 = (fp.dy * mul) / 100 - mat.A33 = (fp.dz * mul) / 50 - mat.A44 = 1 - #trace mat - newtube = tube.transformGeometry(mat) - base = FreeCAD.Vector(0,0,-(fp.dz*mul)) #dz is half height - fp.Shape = translate(newtube,base) - fp.Placement = currPlacement - -class GDMLOrb(GDMLsolid) : - def __init__(self, obj, r, lunit, material, colour=None) : - super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' - obj.addProperty("App::PropertyFloat","r","GDMLOrb","Radius").r=r - obj.addProperty("App::PropertyEnumeration","lunit","GDMLOrb","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLOrb", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLOrb' - self.Object = obj - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['r', 'lunit'] : - #print(dir(fp)) - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute Orb") - mul = GDMLShared.getMult(fp.lunit) - r = mul * fp.r - fp.Shape = Part.makeSphere(r) - fp.Placement = currPlacement - -class GDMLPara(GDMLsolid) : - def __init__(self, obj, x, y, z, alpha, theta, phi, aunit, lunit, \ - material, colour= None) : - super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' - obj.addProperty("App::PropertyFloat","x","GDMLParapiped","x").x=x - obj.addProperty("App::PropertyFloat","y","GDMLParapiped","y").y=y - obj.addProperty("App::PropertyFloat","z","GDMLParapiped","z").z=z - obj.addProperty("App::PropertyFloat","alpha","GDMLParapiped", \ - "Angle with y axis").alpha=alpha - obj.addProperty("App::PropertyFloat","theta","GDMLParapiped", \ - "Polar Angle with faces").theta=theta - obj.addProperty("App::PropertyFloat","phi","GDMLParapiped", \ - "Azimuthal Angle with faces").phi=phi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLParapiped", \ - "aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLParapiped","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLParapiped", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPara' - self.colour = colour - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['x', 'y', 'z', 'alpha', 'theta', 'phi', 'aunit','lunit'] : - self.createGeometry(fp) - - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute Polyparallepiped") - mul = GDMLShared.getMult(fp) - x = mul * fp.x - y = mul * fp.y - z = mul * fp.z - alpha = getAngleRad(fp.aunit,fp.alpha) - theta = getAngleRad(fp.aunit,fp.theta) - phi = getAngleRad(fp.aunit,fp.phi) - #Vertexes - v1 = FreeCAD.Vector( 0, 0, 0) - v2 = FreeCAD.Vector( x, 0, 0) - v3 = FreeCAD.Vector( x, y, 0) - v4 = FreeCAD.Vector( 0, y, 0) - v5 = FreeCAD.Vector( 0, 0, z) - v6 = FreeCAD.Vector( x, 0, z) - v7 = FreeCAD.Vector( x, y, z) - v8 = FreeCAD.Vector( 0, y, z) - # - # xy faces - # - vxy1 = [v1, v4, v3, v2, v1] - vxy2 = [v5, v6, v7, v8, v5] - # - # zx faces - # - vzx1 = [v1, v2, v6, v5, v1] - vzx2 = [v3, v4, v8, v7, v3] - # - # yz faces - # - vyz1 = [v5, v8, v4, v1, v5] - vyz2 = [v2, v3, v7, v6, v2] - - # Apply alpha angle distortions - # - dx = z*math.tan(alpha) - for i in range(0,4): - vzx2[i][0] += dx - # - # apply theta, phi distortions - # - rho = z*math.tan(theta) - dx = rho*math.cos(phi) - dy = rho*math.sin(phi) - for i in range(0,4): - vxy2[i][0] += dx - vxy2[i][1] += dy - - fxy1 = Part.Face(Part.makePolygon(vxy1)) - fxy2 = Part.Face(Part.makePolygon(vxy2)) - fzx1 = Part.Face(Part.makePolygon(vzx1)) - fzx2 = Part.Face(Part.makePolygon(vzx2)) - fyz1 = Part.Face(Part.makePolygon(vyz1)) - fyz2 = Part.Face(Part.makePolygon(vyz2)) - - shell = Part.makeShell([fxy1, fxy2, fzx1, fzx2, fyz1, fyz2]) - solid = Part.makeSolid(shell) - - # center is mid point of diagonal - # - center = (v7 - v1)/2 - fp.Shape = translate(solid, -center) - fp.Placement = currPlacement - -class GDMLHype(GDMLsolid) : - def __init__(self, obj, rmin, rmax, z, inst, outst, aunit, lunit, \ - material, colour= None) : - super().__init__(obj) - '''Add some custom properties for Hyperbolic Tube feature''' - obj.addProperty("App::PropertyFloat","rmin","GDMLHype","inner radius at z=0").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","GDMLHype","outer radius at z=0").rmax=rmax - obj.addProperty("App::PropertyFloat","z","GDMLHype","Tube length").z=z - obj.addProperty("App::PropertyFloat","inst","GDMLHype", \ - "Inner stereo").inst=inst - obj.addProperty("App::PropertyFloat","outst","GDMLHype", \ - "Outer stero").outst=outst - obj.addProperty("App::PropertyEnumeration","aunit","GDMLHype", \ - "aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLHype","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLHype", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLHype' - self.colour = colour - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'z', 'inst', 'outst', 'aunit','lunit'] : - self.createGeometry(fp) - - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute Hyperbolic Tube") - # this should probably be a global variable, but - # for now adopt the value used in geant4.10.07.p02 - NUMBER_OF_DIVISIONS = 36 - mul = GDMLShared.getMult(fp) - rmin = mul * fp.rmin - rmax = mul * fp.rmax - z = mul * fp.z - inst = getAngleRad(fp.aunit,fp.inst) - outst = getAngleRad(fp.aunit,fp.outst) - sqrtan1 = math.tan(inst) - sqrtan1 *= sqrtan1 - sqrtan2 = math.tan(outst) - sqrtan2 *= sqrtan2 - - # mirroring error checking in HepPolyhedron.cc - k = 0 - if rmin < 0. or rmax < 0. or rmin >= rmax: - k = 1 - if z <= 0.: - k += 2 - - if k != 0: - errmsg = "HepPolyhedronHype: error in input parameters: " - if (k & 1) != 0: - errmsg += " (radii)" - if (k & 2) != 0: - errmsg += " (half-length)" - print(errmsg) - print(f' rmin= {rmin} rmax= {rmax} z= {z}') - return - - # Prepare two polylines - ns = NUMBER_OF_DIVISIONS - if ns < 3: - ns = 3 - if sqrtan1 == 0.: - nz1 = 2 - else: - nz1 = ns + 1 - if sqrtan2 == 0.: - nz2 = 2 - else: - nz2 = ns + 1 - - halfZ = z/2 - # - # solid generated by external hyperbeloid - dz2 = z/(nz2 - 1) - zz = [halfZ - dz2*i for i in range(0, nz2)] - rr = [math.sqrt(sqrtan2*zi*zi + rmax*rmax) for zi in zz] - outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) - fp.Shape = outersolid - - if rmin != 0: - # - # solid generated by internal hyperbeloid - dz1 = z/(nz1 - 1) - zz = [halfZ - dz1*i for i in range(0, nz1)] - rr = [math.sqrt(sqrtan1*zi*zi + rmin*rmin) for zi in zz] - innersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) - fp.Shape = outersolid.cut(innersolid) - - fp.Placement = currPlacement - -class GDMLParaboloid(GDMLsolid) : - def __init__(self, obj, rlo, rhi, dz, lunit, \ - material, colour= None) : - super().__init__(obj) - '''Add some custom properties for the Paraboloid feature''' - obj.addProperty("App::PropertyFloat","rlo","GDMLParaboloid","radius at -z/2").rlo=rlo - obj.addProperty("App::PropertyFloat","rhi","GDMLParaboloid","radius at +z/2").rhi=rhi - obj.addProperty("App::PropertyFloat","dz","GDMLParaboloid","Paraboloid half length").dz=dz - obj.addProperty("App::PropertyEnumeration","lunit","GDMLParaboloid","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLParaboloid", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLParaboloid' - self.colour = colour - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rlo', 'rhi', 'z', 'lunit'] : - self.createGeometry(fp) - - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute Hyperbolic Tube") - # this should probably be a global variable, but - # for now adopt the value used in geant4.10.07.p02 - NUMBER_OF_DIVISIONS = 24 - mul = GDMLShared.getMult(fp) - rlo = mul * fp.rlo - rhi = mul * fp.rhi - dz = mul * fp.dz - if dz < 0 or rlo > rhi: - errmsg = "paraboloid: error in input parameters: dz < 0 and/or rlo > rhi " - print(errmsg) - print(f' rlo= {rlo} rhi= {rhi} dz= {dz}') - return - - # Prepare polylines - ns = NUMBER_OF_DIVISIONS - # paraboloid given by following equation: - # rho^2 = k1 * z + k2, (rho = distance of point on surface from z-axis) - # k1 and k2 can be obtained from requirement: - # rlo^2 = k1*(-dz) + k2 - # rhi^2 = k1*(dz) + k2 - k1 = (rhi*rhi - rlo*rlo)/(2*dz) - k2 = (rhi*rhi + rlo*rlo)/2 - # - # solid generated by external hyperbeloid - deltaz = 2*dz/(ns - 1) - zz = [dz - deltaz*i for i in range(0, ns)] - rr = [math.sqrt(k1*zi+k2) for zi in zz] - outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) - fp.Shape = outersolid - - fp.Placement = currPlacement - -class GDMLPolyhedra(GDMLsolid) : - def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, \ - material, colour = None) : - super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' - obj.addProperty("App::PropertyFloat","startphi","GDMLPolyhedra", \ - "Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLPolyhedra", \ - "Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyInteger","numsides","GDMLPolyhedra", \ - "Number of Side").numsides=numsides - obj.addProperty("App::PropertyEnumeration","aunit","GDMLPolyhedra", \ - "aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLPolyhdera","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLPolyhedra", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPolyhedra' - self.colour = colour - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['startphi', 'deltaphi', 'numsides', 'aunit','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute Polyhedra") - parms = fp.OutList - GDMLShared.trace("Number of parms : "+str(len(parms))) - numsides = fp.numsides - GDMLShared.trace("Number of sides : "+str(numsides)) - mul = GDMLShared.getMult(fp) - z0 = parms[0].z * mul - rmin0 = parms[0].rmin * mul - rmax0 = parms[0].rmax * mul - GDMLShared.trace("Top z : "+str(z0)) - GDMLShared.trace("Top rmin : "+str(rmin0)) - GDMLShared.trace("Top rmax : "+str(rmax0)) - inner_faces = [] - outer_faces = [] - numsides = int(numsides * 360 / getAngleDeg(fp.aunit,fp.deltaphi)) - # Deal with Inner Top Face - # Could be point rmin0 = rmax0 = 0 - if rmin0 > 0 : - inner_poly0 = makeRegularPolygon(numsides,rmin0,z0) - inner_faces.append(Part.Face(Part.makePolygon(inner_poly0))) - # Deal with Outer Top Face - outer_poly0 = makeRegularPolygon(numsides,rmax0,z0) - if rmax0 > 0 : # Only make polygon if not a point + mul = GDMLShared.getMult(fp) + + pt1 = FreeCAD.Vector(fp.v1x*mul, fp.v1y*mul, -fp.dz*mul) + pt2 = FreeCAD.Vector(fp.v2x*mul, fp.v2y*mul, -fp.dz*mul) + pt3 = FreeCAD.Vector(fp.v3x*mul, fp.v3y*mul, -fp.dz*mul) + pt4 = FreeCAD.Vector(fp.v4x*mul, fp.v4y*mul, -fp.dz*mul) + pt5 = FreeCAD.Vector(fp.v5x*mul, fp.v5y*mul, fp.dz*mul) + pt6 = FreeCAD.Vector(fp.v6x*mul, fp.v6y*mul, fp.dz*mul) + pt7 = FreeCAD.Vector(fp.v7x*mul, fp.v7y*mul, fp.dz*mul) + pt8 = FreeCAD.Vector(fp.v8x*mul, fp.v8y*mul, fp.dz*mul) + + faceZmin = Part.Face(Part.makePolygon([pt1, pt2, pt3, pt4, pt1])) + faceZmax = Part.Face(Part.makePolygon([pt5, pt6, pt7, pt8, pt5])) + + faceXminA = Part.Face(Part.makePolygon([pt1, pt2, pt6, pt1])) + faceXminB = Part.Face(Part.makePolygon([pt6, pt5, pt1, pt6])) + faceXmaxA = Part.Face(Part.makePolygon([pt4, pt3, pt7, pt4])) + faceXmaxB = Part.Face(Part.makePolygon([pt8, pt4, pt7, pt8])) + + faceYminA = Part.Face(Part.makePolygon([pt1, pt8, pt4, pt1])) + faceYminB = Part.Face(Part.makePolygon([pt1, pt5, pt8, pt1])) + + faceYmaxA = Part.Face(Part.makePolygon([pt2, pt3, pt7, pt2])) + faceYmaxB = Part.Face(Part.makePolygon([pt2, pt7, pt6, pt2])) + + fp.Shape = Part.makeSolid(Part.makeShell([faceXminA, faceXminB, + faceXmaxA, faceXmaxB, + faceYminA, faceYminB, + faceYmaxA, faceYmaxB, + faceZmin, faceZmax])) + fp.Placement = currPlacement + + +class GDMLBox(GDMLsolid): + def __init__(self, obj, x, y, z, lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Box feature''' + GDMLShared.trace("GDMLBox init") + # GDMLShared.trace("material : "+material) + obj.addProperty("App::PropertyFloat", "x", "GDMLBox", "Length x").x = x + obj.addProperty("App::PropertyFloat", "y", "GDMLBox", "Length y").y = y + obj.addProperty("App::PropertyFloat", "z", "GDMLBox", "Length z").z = z + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLBox", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLBox", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLBox' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # Changing Shape in createGeometry will redrive onChanged + if ('Restore' in fp.State): + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['x', 'y', 'z', 'lunit']: + self.createGeometry(fp) + + # execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # print('createGeometry') + # print(fp) + + if all((fp.x, fp.y, fp.z)): + currPlacement = fp.Placement + + # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : + mul = GDMLShared.getMult(fp) + GDMLShared.trace('mul : '+str(mul)) + x = mul * fp.x + y = mul * fp.y + z = mul * fp.z + box = Part.makeBox(x, y, z) + base = FreeCAD.Vector(-x/2, -y/2, -z/2) + fp.Shape = translate(box, base) + fp.Placement = currPlacement + + def OnDocumentRestored(self, obj): + print('Doc Restored') + + +class GDMLCone(GDMLsolid): + def __init__(self, obj, rmin1, rmax1, rmin2, + rmax2, z, startphi, deltaphi, aunit, + lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Cone feature''' + obj.addProperty("App::PropertyFloat", + "rmin1", "GDMLCone", "Min Radius 1").rmin1 = rmin1 + obj.addProperty("App::PropertyFloat", + "rmax1", "GDMLCone", "Max Radius 1").rmax1 = rmax1 + obj.addProperty("App::PropertyFloat", + "rmin2", "GDMLCone", "Min Radius 2").rmin2 = rmin2 + obj.addProperty("App::PropertyFloat", + "rmax2", "GDMLCone", "Max Radius 2").rmax2 = rmax2 + obj.addProperty("App::PropertyFloat", + "z", "GDMLCone", "Height of Cone").z = z + obj.addProperty("App::PropertyFloat", + "startphi", "GDMLCone", "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", + "deltaphi","GDMLCone", "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyEnumeration", + "aunit", "GDMLCone", "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLCone", "lunit") + setLengthQuantity(obj, lunit) + + obj.addProperty("App::PropertyEnumeration", "material", "GDMLCone", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLCone' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rmin1', 'rmax1', 'rmin2', 'rmax2', + 'z', 'startphi', 'deltaphi', + 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # print("fp : ") + # print(vars(fp)) + # if all((fp.rmin1,fp.rmin2,fp.rmax1,fp.rmax2,fp.z)) : + if (hasattr(fp, 'rmin1') and hasattr(fp, 'rmax1') and + hasattr(fp, 'rmin2') and hasattr(fp, 'rmax2') and + hasattr(fp, 'z')): + # Need to add code to check variables will make a valid cone + # i.e.max > min etc etc + # print("execute cone") + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + rmin1 = mul * fp.rmin1 + rmin2 = mul * fp.rmin2 + rmax1 = mul * fp.rmax1 + rmax2 = mul * fp.rmax2 + z = mul * fp.z + # print(mul) + # print(rmax1) + # print(rmax2) + # print(rmin1) + # print(rmin2) + # print(z) + if rmax1 != rmax2: + cone1 = Part.makeCone(rmax1, rmax2, z) + else: + cone1 = Part.makeCylinder(rmax1, z) + + if (rmin1 != 0 and rmin2 != 0): + if rmin1 != rmin2: + cone2 = Part.makeCone(rmin1, rmin2, z) + else: + cone2 = Part.makeCylinder(rmin1, z) + + if rmax1 > rmin1: + cone3 = cone1.cut(cone2) + else: + cone3 = cone2.cut(cone1) + else: + cone3 = cone1 + base = FreeCAD.Vector(0, 0, -z/2) + if checkFullCircle(fp.aunit, fp.deltaphi) is False: + rmax = max(rmax1, rmax2) + cone = angleSectionSolid(fp, rmax, z, cone3) + fp.Shape = translate(cone, base) + else: + fp.Shape = translate(cone3, base) + fp.Placement = currPlacement + + +class GDMLElCone(GDMLsolid): + def __init__(self, obj, dx, dy, zmax, zcut, lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our ElCone feature''' + obj.addProperty("App::PropertyFloat", "dx", "GDMLElCone", + "x semi axis").dx = dx + obj.addProperty("App::PropertyFloat", "dy", "GDMLElCone", + "y semi axis").dy = dy + obj.addProperty("App::PropertyFloat", "zmax", "GDMLElCone", + "z length").zmax = zmax + obj.addProperty("App::PropertyFloat", "zcut", "GDMLElCone", + "z cut").zcut = zcut + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLElCone", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLElCone", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLElCone' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # Form the Web page documentation page for elliptical cone: + # https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html + # the parametric equation of the elliptical cone: + # x = dx*(zmax - u) * cos(v), v = 0..2Pi (note, as of 2021-11-21, + # web page mistakingly shows /u) + # y = dy*(zmax - u) * sin(v) + # z = u, u = -zcut..zcut + # Therefore the bottom base of the cone (at z=u=-zcut) has + # xmax = dxmax = dx*(zmax+zcut) + # and ymax=dymax = dy*(zmax+zcut) + # The ellipse at the top has simi-major axis dx*(zmax-zcut) and + # semiminor axis dy*(zmax-zcut) + # as per the above, the "bottom of the cone is at z = -zcut + # Note that dx is a SCALING factor for the semi major axis, + # NOT the actual semi major axis + # ditto for dy + + mul = GDMLShared.getMult(fp) + currPlacement = fp.Placement + rmax = (fp.zmax+fp.zcut)*mul + cone1 = Part.makeCone(rmax, 0, rmax) + mat = FreeCAD.Matrix() + mat.unity() + # Semi axis values so need to double + dx = fp.dx + dy = fp.dy + zcut = fp.zcut * mul + zmax = fp.zmax * mul + mat.A11 = dx + mat.A22 = dy + mat.A33 = 1 + mat.A34 = -zcut # move bottom of cone to -zcut + mat.A44 = 1 + xmax = dx*rmax + ymax = dy*rmax + cone2 = cone1.transformGeometry(mat) + if zcut is not None: + box = Part.makeBox(2*xmax, 2*ymax, zmax) + pl = FreeCAD.Placement() + # Only need to move to semi axis + pl.move(FreeCAD.Vector(-xmax, -ymax, zcut)) + box.Placement = pl + fp.Shape = cone2.cut(box) + else: + fp.Shape = cone2 + fp.Placement = currPlacement + + +class GDMLEllipsoid(GDMLsolid): + def __init__(self, obj, ax, by, cz, zcut1, zcut2, lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Elliptical Tube feature''' + obj.addProperty("App::PropertyFloat", "ax", "GDMLEllipsoid", + "x semi axis").ax = ax + obj.addProperty("App::PropertyFloat", "by", "GDMLEllipsoid", + "y semi axis").by = by + obj.addProperty("App::PropertyFloat", "cz", "GDMLEllipsoid", + "z semi axis").cz = cz + obj.addProperty("App::PropertyFloat", "zcut1", "GDMLEllipsoid", + "z axis cut1").zcut1 = zcut1 + obj.addProperty("App::PropertyFloat", "zcut2", "GDMLEllipsoid", + "z axis1 cut2").zcut2 = zcut2 + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLEllipsoid", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLEllipsoid", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLEllipsoid' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['ax', 'by', 'cz', 'zcut1', 'zcut2', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + 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() + + mat.A11 = ax / 100 + mat.A22 = by / 100 + mat.A33 = cz / 100 + mat.A44 = 1 + + 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 > -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, zcut2)) + box1.Placement = pl + t2ellipsoid = t1ellipsoid.cut(box1) + else: + t2ellipsoid = t1ellipsoid + if zcut1 < zcut2 and zcut1 > -cz and zcut1 < cz: + box2 = Part.makeBox(2*ax, 2*by, 2*cz) + pl = FreeCAD.Placement() + # 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, 0) + fp.Shape = translate(shape, base) + fp.Placement = currPlacement + + +class GDMLElTube(GDMLsolid): + def __init__(self, obj, dx, dy, dz, lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Elliptical Tube feature''' + obj.addProperty("App::PropertyFloat", "dx", "GDMLElTube", + "x semi axis1").dx = dx + obj.addProperty("App::PropertyFloat", "dy", "GDMLElTube", + "y semi axis1").dy = dy + obj.addProperty("App::PropertyFloat", "dz", "GDMLElTube", + "z half height").dz = dz + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLElTube", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLElTube", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLElTube' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + '''Do something when a property has changed''' + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['dx', 'dy', 'dz', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + tube = Part.makeCylinder(100, 100) + mat = FreeCAD.Matrix() + mat.unity() + mat.A11 = (fp.dx * mul) / 100 + mat.A22 = (fp.dy * mul) / 100 + mat.A33 = (fp.dz * mul) / 50 + mat.A44 = 1 + # trace mat + newtube = tube.transformGeometry(mat) + base = FreeCAD.Vector(0, 0, -(fp.dz*mul)) # dz is half height + fp.Shape = translate(newtube, base) + fp.Placement = currPlacement + + +class GDMLOrb(GDMLsolid): + def __init__(self, obj, r, lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties for Polyhedra feature''' + obj.addProperty("App::PropertyFloat", "r", "GDMLOrb", "Radius").r = r + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLOrb", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLOrb", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLOrb' + self.Object = obj + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['r', 'lunit']: + # print(dir(fp)) + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # GDMLShared.setTrace(True) + GDMLShared.trace("Execute Orb") + mul = GDMLShared.getMult(fp.lunit) + r = mul * fp.r + fp.Shape = Part.makeSphere(r) + fp.Placement = currPlacement + + +class GDMLPara(GDMLsolid): + def __init__(self, obj, x, y, z, alpha, theta, phi, aunit, lunit, + material, colour=None): + super().__init__(obj) + '''Add some custom properties for Polyhedra feature''' + obj.addProperty("App::PropertyFloat", "x", "GDMLParapiped", "x").x = x + obj.addProperty("App::PropertyFloat", "y", "GDMLParapiped", "y").y = y + obj.addProperty("App::PropertyFloat", "z", "GDMLParapiped", "z").z = z + obj.addProperty("App::PropertyFloat", "alpha", "GDMLParapiped", + "Angle with y axis").alpha = alpha + obj.addProperty("App::PropertyFloat", "theta", "GDMLParapiped", + "Polar Angle with faces").theta = theta + obj.addProperty("App::PropertyFloat", "phi", "GDMLParapiped", + "Azimuthal Angle with faces").phi = phi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLParapiped", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLParapiped", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLParapiped", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLPara' + self.colour = colour + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['x', 'y', 'z', 'alpha', 'theta', 'phi', 'aunit', 'lunit']: + self.createGeometry(fp) + + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + #GDMLShared.setTrace(True) + GDMLShared.trace("Execute Polyparallepiped") + mul = GDMLShared.getMult(fp) + x = mul * fp.x + y = mul * fp.y + z = mul * fp.z + alpha = getAngleRad(fp.aunit, fp.alpha) + theta = getAngleRad(fp.aunit, fp.theta) + phi = getAngleRad(fp.aunit, fp.phi) + # Vertexes + v1 = FreeCAD.Vector(0, 0, 0) + v2 = FreeCAD.Vector(x, 0, 0) + v3 = FreeCAD.Vector(x, y, 0) + v4 = FreeCAD.Vector(0, y, 0) + v5 = FreeCAD.Vector(0, 0, z) + v6 = FreeCAD.Vector(x, 0, z) + v7 = FreeCAD.Vector(x, y, z) + v8 = FreeCAD.Vector(0, y, z) + # + # xy faces + # + vxy1 = [v1, v4, v3, v2, v1] + vxy2 = [v5, v6, v7, v8, v5] + # + # zx faces + # + vzx1 = [v1, v2, v6, v5, v1] + vzx2 = [v3, v4, v8, v7, v3] + # + # yz faces + # + vyz1 = [v5, v8, v4, v1, v5] + vyz2 = [v2, v3, v7, v6, v2] + + # Apply alpha angle distortions + # + dx = z*math.tan(alpha) + for i in range(0, 4): + vzx2[i][0] += dx + # + # apply theta, phi distortions + # + rho = z*math.tan(theta) + dx = rho*math.cos(phi) + dy = rho*math.sin(phi) + for i in range(0, 4): + vxy2[i][0] += dx + vxy2[i][1] += dy + + fxy1 = Part.Face(Part.makePolygon(vxy1)) + fxy2 = Part.Face(Part.makePolygon(vxy2)) + fzx1 = Part.Face(Part.makePolygon(vzx1)) + fzx2 = Part.Face(Part.makePolygon(vzx2)) + fyz1 = Part.Face(Part.makePolygon(vyz1)) + fyz2 = Part.Face(Part.makePolygon(vyz2)) + + shell = Part.makeShell([fxy1, fxy2, fzx1, fzx2, fyz1, fyz2]) + solid = Part.makeSolid(shell) + + # center is mid point of diagonal + # + center = (v7 - v1)/2 + fp.Shape = translate(solid, -center) + fp.Placement = currPlacement + + +class GDMLHype(GDMLsolid): + def __init__(self, obj, rmin, rmax, z, inst, outst, aunit, lunit, + material, colour=None): + super().__init__(obj) + '''Add some custom properties for Hyperbolic Tube feature''' + obj.addProperty("App::PropertyFloat", "rmin", "GDMLHype", + "inner radius at z=0").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "GDMLHype", + "outer radius at z=0").rmax = rmax + obj.addProperty("App::PropertyFloat", "z", "GDMLHype", + "Tube length").z = z + obj.addProperty("App::PropertyFloat", "inst", "GDMLHype", + "Inner stereo").inst = inst + obj.addProperty("App::PropertyFloat", "outst", "GDMLHype", + "Outer stero").outst = outst + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLHype", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLHype", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLHype", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLHype' + self.colour = colour + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rmin', 'rmax', 'z', 'inst', 'outst', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # GDMLShared.setTrace(True) + GDMLShared.trace("Execute Hyperbolic Tube") + # this should probably be a global variable, but + # for now adopt the value used in geant4.10.07.p02 + NUMBER_OF_DIVISIONS = 36 + mul = GDMLShared.getMult(fp) + rmin = mul * fp.rmin + rmax = mul * fp.rmax + z = mul * fp.z + inst = getAngleRad(fp.aunit, fp.inst) + outst = getAngleRad(fp.aunit, fp.outst) + sqrtan1 = math.tan(inst) + sqrtan1 *= sqrtan1 + sqrtan2 = math.tan(outst) + sqrtan2 *= sqrtan2 + + # mirroring error checking in HepPolyhedron.cc + k = 0 + if rmin < 0. or rmax < 0. or rmin >= rmax: + k = 1 + if z <= 0.: + k += 2 + + if k != 0: + errmsg = "HepPolyhedronHype: error in input parameters: " + if (k & 1) != 0: + errmsg += " (radii)" + if (k & 2) != 0: + errmsg += " (half-length)" + print(errmsg) + print(f' rmin= {rmin} rmax= {rmax} z= {z}') + return + + # Prepare two polylines + ns = NUMBER_OF_DIVISIONS + if ns < 3: + ns = 3 + if sqrtan1 == 0.: + nz1 = 2 + else: + nz1 = ns + 1 + if sqrtan2 == 0.: + nz2 = 2 + else: + nz2 = ns + 1 + + halfZ = z/2 + # + # solid generated by external hyperbeloid + dz2 = z/(nz2 - 1) + zz = [halfZ - dz2*i for i in range(0, nz2)] + rr = [math.sqrt(sqrtan2*zi*zi + rmax*rmax) for zi in zz] + outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) + fp.Shape = outersolid + + if rmin != 0: + # + # solid generated by internal hyperbeloid + dz1 = z/(nz1 - 1) + zz = [halfZ - dz1*i for i in range(0, nz1)] + rr = [math.sqrt(sqrtan1*zi*zi + rmin*rmin) for zi in zz] + innersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) + fp.Shape = outersolid.cut(innersolid) + + fp.Placement = currPlacement + + +class GDMLParaboloid(GDMLsolid): + def __init__(self, obj, rlo, rhi, dz, lunit, + material, colour=None): + super().__init__(obj) + '''Add some custom properties for the Paraboloid feature''' + obj.addProperty("App::PropertyFloat", "rlo", "GDMLParaboloid", + "radius at -z/2").rlo = rlo + obj.addProperty("App::PropertyFloat", "rhi", "GDMLParaboloid", + "radius at +z/2").rhi = rhi + obj.addProperty("App::PropertyFloat", "dz", "GDMLParaboloid", + "Paraboloid half length").dz = dz + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLParaboloid", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLParaboloid", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLParaboloid' + self.colour = colour + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rlo', 'rhi', 'z', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # GDMLShared.setTrace(True) + GDMLShared.trace("Execute Hyperbolic Tube") + # this should probably be a global variable, but + # for now adopt the value used in geant4.10.07.p02 + NUMBER_OF_DIVISIONS = 24 + mul = GDMLShared.getMult(fp) + rlo = mul * fp.rlo + rhi = mul * fp.rhi + dz = mul * fp.dz + if dz < 0 or rlo > rhi: + errmsg = "paraboloid: error in input parameters: dz < 0 and/or rlo > rhi" + print(errmsg) + print(f' rlo= {rlo} rhi= {rhi} dz= {dz}') + return + + # Prepare polylines + ns = NUMBER_OF_DIVISIONS + # paraboloid given by following equation: + # rho^2 = k1 * z + k2, (rho = distance of point on surface from z-axis) + # k1 and k2 can be obtained from requirement: + # rlo^2 = k1*(-dz) + k2 + # rhi^2 = k1*(dz) + k2 + k1 = (rhi*rhi - rlo*rlo)/(2*dz) + k2 = (rhi*rhi + rlo*rlo)/2 + # + # solid generated by external hyperbeloid + deltaz = 2*dz/(ns - 1) + zz = [dz - deltaz*i for i in range(0, ns)] + rr = [math.sqrt(k1*zi+k2) for zi in zz] + outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) + fp.Shape = outersolid + + fp.Placement = currPlacement + + +class GDMLPolyhedra(GDMLsolid): + def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, + material, colour=None): + super().__init__(obj) + '''Add some custom properties for Polyhedra feature''' + obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolyhedra", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolyhedra", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyInteger", "numsides", "GDMLPolyhedra", + "Number of Side").numsides = numsides + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolyhedra", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolyhdera", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolyhedra", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLPolyhedra' + self.colour = colour + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # GDMLShared.setTrace(True) + GDMLShared.trace("Execute Polyhedra") + parms = fp.OutList + GDMLShared.trace("Number of parms : " + str(len(parms))) + numsides = fp.numsides + GDMLShared.trace("Number of sides : " + str(numsides)) + mul = GDMLShared.getMult(fp) + z0 = parms[0].z * mul + rmin0 = parms[0].rmin * mul + rmax0 = parms[0].rmax * mul + GDMLShared.trace("Top z : " + str(z0)) + GDMLShared.trace("Top rmin : " + str(rmin0)) + GDMLShared.trace("Top rmax : " + str(rmax0)) + inner_faces = [] + outer_faces = [] + numsides = int(numsides * 360 / getAngleDeg(fp.aunit, fp.deltaphi)) + # Deal with Inner Top Face + # Could be point rmin0 = rmax0 = 0 + if rmin0 > 0: + inner_poly0 = makeRegularPolygon(numsides, rmin0, z0) + inner_faces.append(Part.Face(Part.makePolygon(inner_poly0))) + # Deal with Outer Top Face + outer_poly0 = makeRegularPolygon(numsides, rmax0, z0) + if rmax0 > 0: # Only make polygon if not a point outer_faces.append(Part.Face(Part.makePolygon(outer_poly0))) - for ptr in parms[1:] : - z1 = ptr.z * mul - rmin1 = ptr.rmin * mul - rmax1 = ptr.rmax * mul - GDMLShared.trace("z1 : "+str(z1)) - GDMLShared.trace("rmin1 : "+str(rmin1)) - GDMLShared.trace("rmax1 : "+str(rmax1)) - # Concat face lists - if rmin0 > 0 : - inner_poly1 = makeRegularPolygon(numsides,rmin1,z1) - inner_faces = inner_faces + \ - makeFrustrum(numsides,inner_poly0,inner_poly1) - inner_poly0 = inner_poly1 - inner_faces.append(Part.Face(Part.makePolygon(inner_poly1))) - # Deal with Outer - outer_poly1 = makeRegularPolygon(numsides,rmax1,z1) - outer_faces = outer_faces + \ - makeFrustrum(numsides,outer_poly0,outer_poly1) - # update for next zsection - outer_poly0 = outer_poly1 - z0 = z1 - # add bottom polygon face - outer_faces.append(Part.Face(Part.makePolygon(outer_poly1))) - GDMLShared.trace("Total Faces : "+str(len(inner_faces))) - outer_shell = Part.makeShell(outer_faces) - outer_solid = Part.makeSolid(outer_shell) - if rmin0 > 0 : - inner_shell = Part.makeShell(inner_faces) - inner_solid = Part.makeSolid(inner_shell) - shape = outer_solid.cut(inner_solid) - else : - shape = outer_solid - #fp.Shape = shell - if checkFullCircle(fp.aunit,fp.deltaphi) == False : - newShape = angleSectionSolid(fp, rmax1, z0, shape) - fp.Shape = newShape - else : - fp.Shape = shape - fp.Placement = currPlacement - -class GDMLGenericPolyhedra(GDMLsolid) : - def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, \ - material, colour = None) : - super().__init__(obj) - '''Add some custom properties for Generic Polyhedra feature''' - obj.addProperty("App::PropertyFloat","startphi","GDMLGenericPolyhedra", \ - "Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLGenericPolyhedra", \ - "Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyInteger","numsides","GDMLGenericPolyhedra", \ - "Number of Side").numsides=numsides - obj.addProperty("App::PropertyEnumeration","aunit","GDMLGenericPolyhedra", \ - "aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLGenericPolyhdera","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLGenericPolyhedra", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLGenericPolyhedra' - self.colour = colour - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['startphi', 'deltaphi', 'numsides', 'aunit','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #GDMLShared.setTrace(True) - GDMLShared.trace("Execute GenericPolyhedra") - rzpoints = fp.OutList - if len(rzpoints) < 3: - print("Error in genericPolyhedra: number of rzpoints less than 3") - return - GDMLShared.trace("Number of rzpoints : "+str(len(rzpoints))) - numsides = fp.numsides - GDMLShared.trace("Number of sides : "+str(numsides)) - mul = GDMLShared.getMult(fp) - faces = [] - startphi = getAngleRad(fp.aunit, fp.startphi) - dphi = getAngleRad(fp.aunit, fp.deltaphi)/numsides - # form vertexes - verts = [] - for ptr in rzpoints: - z = ptr.z * mul - r = ptr.r * mul - phi = startphi - for i in range(0,numsides+1): - v = FreeCAD.Vector(r*math.cos(phi), r*math.sin(phi), z) - verts.append(v) - phi += dphi - - numverts = len(verts) - stride = numsides + 1 - #outer faces - for k0 in range(0, numverts-stride, stride): - for i in range(0, numsides): - k = k0 + i - wire = Part.makePolygon([verts[k], verts[k+stride], \ - verts[k+stride+1], verts[k+1], \ - verts[k]]) - faces.append(Part.Face(wire)) - - #inner faces - for i in range(0, numsides): - k = numverts - stride + i - wire = Part.makePolygon([verts[i], verts[k], \ - verts[k+1], verts[i+1], \ - verts[i]]) - faces.append(Part.Face(wire)) - - #side faces - if checkFullCircle(fp.aunit, fp.deltaphi) == False: - verts1 = [verts[k] for k in range(0, numverts - stride + 1, stride)] - verts1.append(verts1[0]) - wire = Part.makePolygon(verts1) - faces.append(Part.Face(wire)) - verts1 = [verts[k] for k in range(numsides, numverts, stride)] - verts1.append(verts1[0]) - wire = Part.makePolygon(verts1) - faces.append(Part.Face(wire)) - - shell = Part.makeShell(faces) - solid = Part.makeSolid(shell) - fp.Shape = solid - fp.Placement = currPlacement - -class GDMLTorus(GDMLsolid) : - def __init__(self, obj, rmin, rmax, rtor, startphi, deltaphi, \ - aunit, lunit, material, colour = None) : - super().__init__(obj) - obj.addProperty("App::PropertyFloat","rmin","GDMLTorus", \ - "rmin").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","GDMLTorus", \ - "rmax").rmax=rmax - obj.addProperty("App::PropertyFloat","rtor","GDMLTorus", \ - "rtor").rtor=rtor - obj.addProperty("App::PropertyFloat","startphi","GDMLTorus", \ - "startphi").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLTorus", \ - "deltaphi").deltaphi=deltaphi - obj.addProperty("App::PropertyString","aunit","GDMLTorus", \ - "aunit").aunit=aunit - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTorus","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTorus", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTorus' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'rtor','startphi','deltaphi', \ - 'aunit','lunit'] : - #print(f'Change Prop : {prop}') - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - GDMLShared.trace("Create Torus") - mul = GDMLShared.getMult(fp) - rmin = mul*fp.rmin - rmax = mul*fp.rmax - rtor = mul*fp.rtor - - spnt = FreeCAD.Vector(0,0,0) - sdir = FreeCAD.Vector(0,0,1) - - outerTorus = Part.makeTorus(rtor,rmax,spnt,sdir,0,360, \ - getAngleDeg(fp.aunit, fp.deltaphi)) - if rmin > 0 : - innerTorus = Part.makeTorus(rtor,rmin,spnt,sdir,0,360, \ - getAngleDeg(fp.aunit, fp.deltaphi)) - torus = outerTorus.cut(innerTorus) - else : - torus = outerTorus - if fp.startphi != 0 : - torus.rotate(spnt,sdir,getAngleDeg(fp.aunit,fp.startphi)) - fp.Shape = torus - fp.Placement = currPlacement - -class GDMLTwistedbox(GDMLsolid) : - def __init__(self, obj, PhiTwist, x, y, z, aunit, lunit, material, colour=None): - super().__init__(obj) - '''Add some custom properties to our Box feature''' - GDMLShared.trace("GDMLTwistedbox init") - #GDMLShared.trace("material : "+material) - obj.addProperty("App::PropertyFloat","x","GDMLTwistedbox","Length x").x=x - obj.addProperty("App::PropertyFloat","y","GDMLTwistedbox","Length y").y=y - obj.addProperty("App::PropertyFloat","z","GDMLTwistedbox","Length z").z=z - angle = getAngleDeg(aunit, PhiTwist) - if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') - angle = 90 - aunit = "deg" - elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') - angle = -90 - aunit = "deg" - else: - angle = PhiTwist - - obj.addProperty("App::PropertyFloat","PhiTwist","GDMLTwistedbox","Twist Angle").PhiTwist=angle - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTwistedbox","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTwistedbox","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTwistedbox","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedbox' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State) : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['x','y','z','PhiTwist', 'lunit', 'aunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - #print('createGeometry') - #print(fp) - - if all((fp.x,fp.y,fp.z, fp.PhiTwist)) : - currPlacement = fp.Placement - - #if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : - mul = GDMLShared.getMult(fp) - GDMLShared.trace('mul : '+str(mul)) - x = mul * fp.x - y = mul * fp.y - z = mul * fp.z - angle = getAngleDeg(fp.aunit,fp.PhiTwist) - # lower rectanngle vertexes - v1 = FreeCAD.Vector(-x/2, -y/2, -z/2) - v2 = FreeCAD.Vector( x/2, -y/2, -z/2) - v3 = FreeCAD.Vector( x/2, y/2, -z/2) - v4 = FreeCAD.Vector(-x/2, y/2, -z/2) - pbot = Part.makePolygon([v1,v2,v3,v4,v1]) - slices = [] - N = 5 - dz = z/(N-1) - dPhi = angle/(N-1) - for i in range(0,N): - p = pbot.translated(FreeCAD.Vector(0,0,i*dz)) - p.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), -angle/2 + i*dPhi) - slices.append(p) - loft = Part.makeLoft(slices, True, False) - - fp.Shape = loft - fp.Placement = currPlacement - - def OnDocumentRestored(self,obj) : - print('Doc Restored') - - -class GDMLTwistedtrap(GDMLsolid) : - def __init__(self, obj, PhiTwist, z, theta, phi, x1, x2, x3, x4, y1, y2, alpha, \ - aunit, lunit, material, colour = None): - super().__init__(obj) - '''General Trapezoid''' - obj.addProperty("App::PropertyFloat","PhiTwist","GDMLTwistedtrap","Twist angle").PhiTwist=PhiTwist - obj.addProperty("App::PropertyFloat","z","GDMLTwistedtrap","z").z=z - obj.addProperty("App::PropertyFloat","Theta","GDMLTwistedtrap","Theta"). \ - Theta=theta - obj.addProperty("App::PropertyFloat","Phi","GDMLTwistedtrap","Phi").Phi=phi - obj.addProperty("App::PropertyFloat","x1","GDMLTwistedtrap", \ - "Length x at y= -y1/2 of face at -z/2").x1=x1 - obj.addProperty("App::PropertyFloat","x2","GDMLTwistedtrap", \ - "Length x at y= +y1/2 of face at -z/2").x2=x2 - obj.addProperty("App::PropertyFloat","x3","GDMLTwistedtrap", \ - "Length x at y= -y2/2 of face at +z/2").x3=x3 - obj.addProperty("App::PropertyFloat","x4","GDMLTwistedtrap", \ - "Length x at y= +y2/2 of face at +z/2").x4=x4 - obj.addProperty("App::PropertyFloat","y1","GDMLTwistedtrap", \ - "Length y at face -z/2").y1=y1 - obj.addProperty("App::PropertyFloat","y2","GDMLTwistedtrap", \ - "Length y at face +z/2").y2=y2 - obj.addProperty("App::PropertyFloat","Alph","GDMLTwistedtrap","Alph"). \ - Alph=alpha - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTwistedtrap","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTwistedtrap","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTwistedtrap","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLTwistedtrap' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['PhiTwist', 'z','theta','phi','x1','x2','x3','x4','y1','y2','alpha', \ - 'aunit', 'lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - # Define six vetices for the shape - alpha = getAngleRad(fp.aunit,fp.Alph) - theta = getAngleRad(fp.aunit,fp.Theta) - phi = getAngleRad(fp.aunit,fp.Phi) - PhiTwist = getAngleDeg(fp.aunit,fp.PhiTwist) - mul = GDMLShared.getMult(fp) - y1 = mul * fp.y1 - x1 = mul * fp.x1 - x2 = mul * fp.x2 - y2 = mul * fp.y2 - x3 = mul * fp.x3 - x4 = mul * fp.x4 - z = mul * fp.z - - N = 9 - dz = z/(N-1) - dTwist = PhiTwist/(N-1) - - tanalpha = math.tan(alpha) - dx1 = y1*math.tan(alpha) - dx2 = y2*math.tan(alpha) - - dt = 1.0/(N-1) - t = 0 - slices = [] - tanthet = math.tan(theta) - cosphi = math.cos(phi) - sinphi = math.sin(phi) - rhomax = z*tanthet - xoffset = -rhomax*cosphi/2 - yoffset = -rhomax*sinphi/2 - for i in range(0,N): - #Vertexes, counter clock wise order - y = y1 + t*(y2-y1) # go continuously from y1 to y2 - dx = y*tanalpha - x13 = x1 + t*(x3-x1) # go continuously from x1 to x3 - x24 = x2 + t*(x4-x2) # go continuously from x1 to x3 - zt = -z/2 +t*z - rho = i*dz*tanthet - dxphi = xoffset + rho*cosphi - dyphi = yoffset + rho*sinphi - v1 = FreeCAD.Vector(-x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) - v2 = FreeCAD.Vector( x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) - v3 = FreeCAD.Vector( x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) - v4 = FreeCAD.Vector(-x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) - p = Part.makePolygon([v1,v2,v3,v4,v1]) - p.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), -PhiTwist/2 + i*dTwist) - slices.append(p) - t += dt - - loft = Part.makeLoft(slices, True, False) - fp.Shape = loft - fp.Placement = currPlacement - - -class GDMLTwistedtrd(GDMLsolid) : - def __init__(self, obj, PhiTwist, z, x1, x2, y1, y2, aunit, lunit, material, colour = None) : - super().__init__(obj) - "3.4.15 : Trapezoid – x & y varying along z" - obj.addProperty("App::PropertyFloat","z","GDMLTwistedtrd`","z").z=z - obj.addProperty("App::PropertyFloat","x1","GDMLTwistedtrd", \ - "Length x at face -z/2").x1=x1 - obj.addProperty("App::PropertyFloat","x2","GDMLTwistedtrd", \ - "Length x at face +z/2").x2=x2 - obj.addProperty("App::PropertyFloat","y1","GDMLTwistedtrd", \ - "Length y at face -z/2").y1=y1 - obj.addProperty("App::PropertyFloat","y2","GDMLTwistedtrd", \ - "Length y at face +z/2").y2=y2 - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTwistedtrd","lunit") - angle = getAngleDeg(aunit, PhiTwist) - if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') - angle = 90 - aunit = "deg" - elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') - angle = -90 - aunit = "deg" - else: - angle = PhiTwist - - obj.addProperty("App::PropertyFloat","PhiTwist","GDMLTwistedtrd","Twist Angle").PhiTwist=angle - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTwistedtrd","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTwistedtrd","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedtrd' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State) : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['x1', 'y1', 'x2', 'y2', 'z','PhiTwist', 'lunit', 'aunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - #print('createGeometry') - #print(fp) - - if all((fp.x1, fp.x2, fp.y1, fp.y2, fp.z, fp.PhiTwist)) : - currPlacement = fp.Placement - - #if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : - mul = GDMLShared.getMult(fp) - x1 = (fp.x1 * mul) - x2 = (fp.x2 * mul) - y1 = (fp.y1 * mul) - y2 = (fp.y2 * mul) - z = (fp.z * mul) - GDMLShared.trace('mul : '+str(mul)) - angle = getAngleDeg(fp.aunit,fp.PhiTwist) - slices = [] - N = 9 #number of slices - dz = z/(N-1) - dPhi = angle/(N-1) - for i in range(0,N): - t = i*1./(N-1) - xside = x1 + t*(x2 - x1) - yside = y1 + t*(y2 - y1) - v1 = FreeCAD.Vector(-xside/2, -yside/2, -z/2 + i*dz) - v2 = FreeCAD.Vector( xside/2, -yside/2, -z/2 + i*dz) - v3 = FreeCAD.Vector( xside/2, yside/2, -z/2 + i*dz) - v4 = FreeCAD.Vector(-xside/2, yside/2, -z/2 + i*dz) - p = Part.makePolygon([v1,v2,v3,v4,v1]) - p.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), -angle/2 + i*dPhi) - slices.append(p) - - loft = Part.makeLoft(slices, True, False) - fp.Shape = loft - fp.Placement = currPlacement - - def OnDocumentRestored(self,obj) : - print('Doc Restored') - - -class GDMLTwistedtubs(GDMLsolid) : - def __init__(self, obj,endinnerrad,endouterrad,zlen,twistedangle,phi,aunit,lunit,material, colour = None) : - super().__init__(obj) - '''Twisted tube''' - obj.addProperty("App::PropertyFloat","zlen","GDMLTwistedtubs","zlen").zlen=zlen - obj.addProperty("App::PropertyFloat","endinnerrad","GDMLTwistedtubs", \ - "Inside radius at caps").endinnerrad=endinnerrad - obj.addProperty("App::PropertyFloat","endouterrad","GDMLTwistedtubs", \ - "Outside radius at caps").endouterrad=endouterrad - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTwistedtubs","lunit") - angle = getAngleDeg(aunit, twistedangle) - if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') - angle = 90 - aunit = "deg" - elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') - angle = -90 - aunit = "deg" - else: - angle = twistedangle - - obj.addProperty("App::PropertyFloat","twistedangle","GDMLTwistedtubs","Twist Angle").twistedangle=angle - obj.addProperty("App::PropertyFloat","phi","GDMLTwistedtubs","Delta phi").phi=phi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTwistedtubs","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTwistedtubs","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedtubs' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State) : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['endinnerrad', 'endouterrad', 'zlen', 'twistedangle', 'phi', 'lunit', 'aunit'] : - self.createGeometry(fp) + for ptr in parms[1:]: + z1 = ptr.z * mul + rmin1 = ptr.rmin * mul + rmax1 = ptr.rmax * mul + GDMLShared.trace("z1 : "+str(z1)) + GDMLShared.trace("rmin1 : "+str(rmin1)) + GDMLShared.trace("rmax1 : "+str(rmax1)) + # Concat face lists + if rmin0 > 0: + inner_poly1 = makeRegularPolygon(numsides, rmin1, z1) + inner_faces = inner_faces + \ + makeFrustrum(numsides, inner_poly0, inner_poly1) + inner_poly0 = inner_poly1 + inner_faces.append(Part.Face(Part.makePolygon(inner_poly1))) + # Deal with Outer + outer_poly1 = makeRegularPolygon(numsides, rmax1, z1) + outer_faces = outer_faces + \ + makeFrustrum(numsides, outer_poly0, outer_poly1) + # update for next zsection + outer_poly0 = outer_poly1 + z0 = z1 + # add bottom polygon face + outer_faces.append(Part.Face(Part.makePolygon(outer_poly1))) + GDMLShared.trace("Total Faces : " + str(len(inner_faces))) + outer_shell = Part.makeShell(outer_faces) + outer_solid = Part.makeSolid(outer_shell) + if rmin0 > 0: + inner_shell = Part.makeShell(inner_faces) + inner_solid = Part.makeSolid(inner_shell) + shape = outer_solid.cut(inner_solid) + else: + shape = outer_solid + # fp.Shape = shell + if checkFullCircle(fp.aunit, fp.deltaphi) is False: + newShape = angleSectionSolid(fp, rmax1, z0, shape) + fp.Shape = newShape + else: + fp.Shape = shape + fp.Placement = currPlacement - #def execute(self, fp): in GDMLsolid - def createGeometry(self,fp): - #print('createGeometry') - #print(fp) +class GDMLGenericPolyhedra(GDMLsolid): + def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, + material, colour=None): + super().__init__(obj) + '''Add some custom properties for Generic Polyhedra feature''' + obj.addProperty("App::PropertyFloat", "startphi", "GDMLGenericPolyhedra", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLGenericPolyhedra", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyInteger", "numsides", "GDMLGenericPolyhedra", + "Number of Side").numsides = numsides + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLGenericPolyhedra", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLGenericPolyhdera", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLGenericPolyhedra", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLGenericPolyhedra' + self.colour = colour + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return - if all((fp.endouterrad, fp.zlen, fp.phi)) : - currPlacement = fp.Placement - - mul = GDMLShared.getMult(fp) - rin = (fp.endinnerrad * mul) - rout = (fp.endouterrad * mul) - if rin > rout: - print(f'Erro: Inner radius ({rin}) greater than outer radius ({rout})') - return - zlen = (fp.zlen * mul) - GDMLShared.trace('mul : '+str(mul)) - angle = getAngleDeg(fp.aunit,fp.twistedangle) - phi = getAngleRad(fp.aunit,fp.phi) - phideg = getAngleDeg(fp.aunit,fp.phi) - slices = [] - N = 9 #number of slices - dz = zlen/(N-1) - dtwist = angle/(N-1) - #construct base wire - # Vertexes - v1 = FreeCAD.Vector(rin, 0, 0) - v2 = FreeCAD.Vector(rout,0, 0) - v3 = FreeCAD.Vector(rout*math.cos(phi), rout*math.sin(phi), 0) - v4 = FreeCAD.Vector(rin*math.cos(phi), rin*math.sin(phi), 0) - #arc center points - vCin = FreeCAD.Vector(rin*math.cos(phi/2), rin*math.sin(phi/2), 0) - vCout = FreeCAD.Vector(rout*math.cos(phi/2), rout*math.sin(phi/2), 0) - # Center of twisting - rc = (rin + rout)/2 - vc = FreeCAD.Vector(rc*math.cos(phi/2), rc*math.sin(phi/2), 0) - # wire - arcin = Part.Arc(v1, vCin, v4) - line1 = Part.LineSegment(v4, v3) - arcout = Part.Arc(v3, vCout, v2) - line2 = Part.LineSegment(v2, v1) - - s = Part.Shape([arcin, line1, arcout, line2]) - w = Part.Wire(s.Edges) - angoffset = -angle/2 -phideg/2 - - for i in range(0,N): - p = w.translated(FreeCAD.Vector(0, 0, -zlen/2 + i*dz)) - p.rotate(vc, FreeCAD.Vector(0,0,1), angoffset + i*dtwist) - slices.append(p) - - loft = Part.makeLoft(slices, True, False) - fp.Shape = loft - fp.Placement = currPlacement - - def OnDocumentRestored(self,obj) : - print('Doc Restored') - - -class GDMLXtru(GDMLsolid) : - def __init__(self, obj, lunit, material, colour = None) : - super().__init__(obj) - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyEnumeration","lunit","GDMLXtru","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLXtru", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLXtru' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['startphi','deltaphi','aunit','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # GDMLShared.setTrace(True) + GDMLShared.trace("Execute GenericPolyhedra") + rzpoints = fp.OutList + if len(rzpoints) < 3: + print("Error in genericPolyhedra: number of rzpoints less than 3") + return + GDMLShared.trace("Number of rzpoints : " + str(len(rzpoints))) + numsides = fp.numsides + GDMLShared.trace("Number of sides : " + str(numsides)) + mul = GDMLShared.getMult(fp) + faces = [] + startphi = getAngleRad(fp.aunit, fp.startphi) + dphi = getAngleRad(fp.aunit, fp.deltaphi)/numsides + # form vertexes + verts = [] + for ptr in rzpoints: + z = ptr.z * mul + r = ptr.r * mul + phi = startphi + for i in range(0, numsides+1): + v = FreeCAD.Vector(r*math.cos(phi), r*math.sin(phi), z) + verts.append(v) + phi += dphi + + numverts = len(verts) + stride = numsides + 1 + # outer faces + for k0 in range(0, numverts-stride, stride): + for i in range(0, numsides): + k = k0 + i + wire = Part.makePolygon([verts[k], verts[k+stride], + verts[k+stride+1], verts[k+1], + verts[k]]) + faces.append(Part.Face(wire)) + + # inner faces + for i in range(0, numsides): + k = numverts - stride + i + wire = Part.makePolygon([verts[i], verts[k], + verts[k+1], verts[i+1], + verts[i]]) + faces.append(Part.Face(wire)) + + # side faces + if checkFullCircle(fp.aunit, fp.deltaphi) is False: + verts1 = [verts[k] for k in range(0, numverts - stride + 1, stride)] + verts1.append(verts1[0]) + wire = Part.makePolygon(verts1) + faces.append(Part.Face(wire)) + verts1 = [verts[k] for k in range(numsides, numverts, stride)] + verts1.append(verts1[0]) + wire = Part.makePolygon(verts1) + faces.append(Part.Face(wire)) + + shell = Part.makeShell(faces) + solid = Part.makeSolid(shell) + fp.Shape = solid + fp.Placement = currPlacement + + +class GDMLTorus(GDMLsolid): + def __init__(self, obj, rmin, rmax, rtor, startphi, deltaphi, + aunit, lunit, material, colour=None): + super().__init__(obj) + obj.addProperty("App::PropertyFloat", "rmin", "GDMLTorus", + "rmin").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "GDMLTorus", + "rmax").rmax = rmax + obj.addProperty("App::PropertyFloat", "rtor", "GDMLTorus", + "rtor").rtor = rtor + obj.addProperty("App::PropertyFloat", "startphi", "GDMLTorus", + "startphi").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLTorus", + "deltaphi").deltaphi = deltaphi + obj.addProperty("App::PropertyString", "aunit", "GDMLTorus", + "aunit").aunit = aunit + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTorus", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTorus", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLTorus' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rmin', 'rmax', 'rtor', 'startphi', 'deltaphi', + 'aunit', 'lunit']: + # print(f'Change Prop : {prop}') + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + GDMLShared.trace("Create Torus") + mul = GDMLShared.getMult(fp) + rmin = mul*fp.rmin + rmax = mul*fp.rmax + rtor = mul*fp.rtor + + spnt = FreeCAD.Vector(0, 0, 0) + sdir = FreeCAD.Vector(0, 0, 1) + + outerTorus = Part.makeTorus(rtor, rmax, spnt, sdir, 0, 360, + getAngleDeg(fp.aunit, fp.deltaphi)) + if rmin > 0: + innerTorus = Part.makeTorus(rtor, rmin, spnt, sdir, 0, 360, + getAngleDeg(fp.aunit, fp.deltaphi)) + torus = outerTorus.cut(innerTorus) + else: + torus = outerTorus + if fp.startphi != 0: + torus.rotate(spnt, sdir, getAngleDeg(fp.aunit, fp.startphi)) + fp.Shape = torus + fp.Placement = currPlacement + + +class GDMLTwistedbox(GDMLsolid): + def __init__(self, obj, PhiTwist, x, y, z, aunit, lunit, material, + colour=None): + super().__init__(obj) + '''Add some custom properties to our Box feature''' + GDMLShared.trace("GDMLTwistedbox init") + # GDMLShared.trace("material : "+material) + obj.addProperty("App::PropertyFloat", "x", "GDMLTwistedbox", + "Length x").x = x + obj.addProperty("App::PropertyFloat", "y", "GDMLTwistedbox", + "Length y").y = y + obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedbox", + "Length z").z = z + angle = getAngleDeg(aunit, PhiTwist) + if angle > 90: + print(f'PhiTwist angle cannot be larger than 90 deg') + angle = 90 + aunit = "deg" + elif angle < -90: + print(f'PhiTwist angle cannot be less than -90 deg') + angle = -90 + aunit = "deg" + else: + angle = PhiTwist + + obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedbox", + "Twist Angle").PhiTwist = angle + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedbox", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedbox", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedbox", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLTwistedbox' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # Changing Shape in createGeometry will redrive onChanged + if ('Restore' in fp.State): + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['x', 'y', 'z', 'PhiTwist', 'lunit', 'aunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # print('createGeometry') + # print(fp) + + if all((fp.x, fp.y, fp.z, fp.PhiTwist)): + currPlacement = fp.Placement + + # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : + mul = GDMLShared.getMult(fp) + GDMLShared.trace('mul : '+str(mul)) + x = mul * fp.x + y = mul * fp.y + z = mul * fp.z + angle = getAngleDeg(fp.aunit, fp.PhiTwist) + # lower rectanngle vertexes + v1 = FreeCAD.Vector(-x/2, -y/2, -z/2) + v2 = FreeCAD.Vector(x/2, -y/2, -z/2) + v3 = FreeCAD.Vector(x/2, y/2, -z/2) + v4 = FreeCAD.Vector(-x/2, y/2, -z/2) + pbot = Part.makePolygon([v1, v2, v3, v4, v1]) + slices = [] + N = 5 + dz = z/(N-1) + dPhi = angle/(N-1) + for i in range(0, N): + p = pbot.translated(FreeCAD.Vector(0, 0, i*dz)) + p.rotate(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), -angle/2 + i*dPhi) + slices.append(p) + loft = Part.makeLoft(slices, True, False) + + fp.Shape = loft + fp.Placement = currPlacement + + def OnDocumentRestored(self, obj): + print('Doc Restored') + + +class GDMLTwistedtrap(GDMLsolid): + def __init__(self, obj, PhiTwist, z, theta, phi, x1, x2, x3, x4, y1, y2, + alpha, aunit, lunit, material, colour=None): + super().__init__(obj) + '''General Trapezoid''' + obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedtrap", + "Twist angle").PhiTwist = PhiTwist + obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedtrap", "z").z = z + obj.addProperty("App::PropertyFloat", "Theta", "GDMLTwistedtrap", + "Theta").Theta = theta + obj.addProperty("App::PropertyFloat", "Phi", "GDMLTwistedtrap", + "Phi").Phi = phi + obj.addProperty("App::PropertyFloat", "x1", "GDMLTwistedtrap", + "Length x at y= -y1/2 of face at -z/2").x1 = x1 + obj.addProperty("App::PropertyFloat", "x2", "GDMLTwistedtrap", + "Length x at y= +y1/2 of face at -z/2").x2 = x2 + obj.addProperty("App::PropertyFloat", "x3", "GDMLTwistedtrap", + "Length x at y= -y2/2 of face at +z/2").x3 = x3 + obj.addProperty("App::PropertyFloat", "x4", "GDMLTwistedtrap", + "Length x at y= +y2/2 of face at +z/2").x4 = x4 + obj.addProperty("App::PropertyFloat", "y1", "GDMLTwistedtrap", + "Length y at face -z/2").y1 = y1 + obj.addProperty("App::PropertyFloat", "y2", "GDMLTwistedtrap", + "Length y at face +z/2").y2 = y2 + obj.addProperty("App::PropertyFloat", "Alph", "GDMLTwistedtrap", + "Alph").Alph = alpha + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtrap", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", + "GDMLTwistedtrap", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedtrap", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLTwistedtrap' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['PhiTwist', 'z', 'theta', 'phi', + 'x1', 'x2', 'x3', 'x4', 'y1', 'y2', 'alpha', + 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + # Define six vetices for the shape + alpha = getAngleRad(fp.aunit, fp.Alph) + theta = getAngleRad(fp.aunit, fp.Theta) + phi = getAngleRad(fp.aunit, fp.Phi) + PhiTwist = getAngleDeg(fp.aunit, fp.PhiTwist) + mul = GDMLShared.getMult(fp) + y1 = mul * fp.y1 + x1 = mul * fp.x1 + x2 = mul * fp.x2 + y2 = mul * fp.y2 + x3 = mul * fp.x3 + x4 = mul * fp.x4 + z = mul * fp.z + + N = 9 + dz = z/(N-1) + dTwist = PhiTwist/(N-1) + + tanalpha = math.tan(alpha) + + dt = 1.0/(N-1) + t = 0 + slices = [] + tanthet = math.tan(theta) + cosphi = math.cos(phi) + sinphi = math.sin(phi) + rhomax = z*tanthet + xoffset = -rhomax*cosphi/2 + yoffset = -rhomax*sinphi/2 + for i in range(0, N): + # Vertexes, counter clock wise order + y = y1 + t*(y2-y1) # go continuously from y1 to y2 + dx = y*tanalpha + x13 = x1 + t*(x3-x1) # go continuously from x1 to x3 + x24 = x2 + t*(x4-x2) # go continuously from x1 to x3 + zt = -z/2 + t*z + rho = i*dz*tanthet + dxphi = xoffset + rho*cosphi + dyphi = yoffset + rho*sinphi + v1 = FreeCAD.Vector(-x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) + v2 = FreeCAD.Vector( x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) + v3 = FreeCAD.Vector( x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) + v4 = FreeCAD.Vector(-x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) + p = Part.makePolygon([v1, v2, v3, v4, v1]) + p.rotate(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), -PhiTwist/2 + i*dTwist) + slices.append(p) + t += dt + + loft = Part.makeLoft(slices, True, False) + fp.Shape = loft + fp.Placement = currPlacement + + +class GDMLTwistedtrd(GDMLsolid): + def __init__(self, obj, PhiTwist, z, x1, x2, y1, y2, aunit, lunit, + material, colour=None): + super().__init__(obj) + "3.4.15 : Trapezoid – x & y varying along z" + obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedtrd", "z").z = z + obj.addProperty("App::PropertyFloat", "x1", "GDMLTwistedtrd", + "Length x at face -z/2").x1 = x1 + obj.addProperty("App::PropertyFloat", "x2", "GDMLTwistedtrd", + "Length x at face +z/2").x2 = x2 + obj.addProperty("App::PropertyFloat", "y1", "GDMLTwistedtrd", + "Length y at face -z/2").y1 = y1 + obj.addProperty("App::PropertyFloat", "y2", "GDMLTwistedtrd", + "Length y at face +z/2").y2 = y2 + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedtrd", + "lunit") + angle = getAngleDeg(aunit, PhiTwist) + if angle > 90: + print(f'PhiTwist angle cannot be larger than 90 deg') + angle = 90 + aunit = "deg" + elif angle < -90: + print(f'PhiTwist angle cannot be less than -90 deg') + angle = -90 + aunit = "deg" + else: + angle = PhiTwist + + obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedtrd", + "Twist Angle").PhiTwist = angle + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtrd", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLTwistedtrd", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLTwistedtrd' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # Changing Shape in createGeometry will redrive onChanged + if ('Restore' in fp.State): + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['x1', 'y1', 'x2', 'y2', 'z', 'PhiTwist', 'lunit', 'aunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # print('createGeometry') + # print(fp) + + if all((fp.x1, fp.x2, fp.y1, fp.y2, fp.z, fp.PhiTwist)): + currPlacement = fp.Placement + + # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : + mul = GDMLShared.getMult(fp) + x1 = (fp.x1 * mul) + x2 = (fp.x2 * mul) + y1 = (fp.y1 * mul) + y2 = (fp.y2 * mul) + z = (fp.z * mul) + GDMLShared.trace('mul : ' + str(mul)) + angle = getAngleDeg(fp.aunit, fp.PhiTwist) + slices = [] + N = 9 # number of slices + dz = z/(N-1) + dPhi = angle/(N-1) + for i in range(0, N): + t = i*1./(N-1) + xside = x1 + t*(x2 - x1) + yside = y1 + t*(y2 - y1) + v1 = FreeCAD.Vector(-xside/2, -yside/2, -z/2 + i*dz) + v2 = FreeCAD.Vector( xside/2, -yside/2, -z/2 + i*dz) + v3 = FreeCAD.Vector( xside/2, yside/2, -z/2 + i*dz) + v4 = FreeCAD.Vector(-xside/2, yside/2, -z/2 + i*dz) + p = Part.makePolygon([v1, v2, v3, v4, v1]) + p.rotate(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), -angle/2 + i*dPhi) + slices.append(p) + + loft = Part.makeLoft(slices, True, False) + fp.Shape = loft + fp.Placement = currPlacement + + def OnDocumentRestored(self, obj): + print('Doc Restored') + + +class GDMLTwistedtubs(GDMLsolid): + def __init__(self, obj, endinnerrad, endouterrad, zlen, twistedangle, + phi, aunit, lunit, material, colour=None): + super().__init__(obj) + '''Twisted tube''' + obj.addProperty("App::PropertyFloat", "zlen", "GDMLTwistedtubs", + "zlen").zlen = zlen + obj.addProperty("App::PropertyFloat", "endinnerrad", "GDMLTwistedtubs", + "Inside radius at caps").endinnerrad = endinnerrad + obj.addProperty("App::PropertyFloat", "endouterrad", "GDMLTwistedtubs", + "Outside radius at caps").endouterrad = endouterrad + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedtubs", + "lunit") + angle = getAngleDeg(aunit, twistedangle) + if angle > 90: + print(f'PhiTwist angle cannot be larger than 90 deg') + angle = 90 + aunit = "deg" + elif angle < -90: + print(f'PhiTwist angle cannot be less than -90 deg') + angle = -90 + aunit = "deg" + else: + angle = twistedangle + + obj.addProperty("App::PropertyFloat", "twistedangle", "GDMLTwistedtubs", + "Twist Angle").twistedangle = angle + obj.addProperty("App::PropertyFloat", "phi", "GDMLTwistedtubs", + "Delta phi").phi = phi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtubs", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedtubs", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLTwistedtubs' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # Changing Shape in createGeometry will redrive onChanged + if ('Restore' in fp.State): + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['endinnerrad', 'endouterrad', 'zlen', 'twistedangle', + 'phi', 'lunit', 'aunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # print('createGeometry') + # print(fp) + + if all((fp.endouterrad, fp.zlen, fp.phi)): + currPlacement = fp.Placement + + mul = GDMLShared.getMult(fp) + rin = (fp.endinnerrad * mul) + rout = (fp.endouterrad * mul) + if rin > rout: + print(f'Erro: Inner radius ({rin}) greater than outer radius ({rout})') + return + zlen = (fp.zlen * mul) + GDMLShared.trace('mul : ' + str(mul)) + angle = getAngleDeg(fp.aunit, fp.twistedangle) + phi = getAngleRad(fp.aunit, fp.phi) + phideg = getAngleDeg(fp.aunit, fp.phi) + slices = [] + N = 9 # number of slices + dz = zlen/(N-1) + dtwist = angle/(N-1) + # construct base wire + # Vertexes + v1 = FreeCAD.Vector(rin, 0, 0) + v2 = FreeCAD.Vector(rout, 0, 0) + v3 = FreeCAD.Vector(rout*math.cos(phi), rout*math.sin(phi), 0) + v4 = FreeCAD.Vector(rin*math.cos(phi), rin*math.sin(phi), 0) + # arc center points + vCin = FreeCAD.Vector(rin*math.cos(phi/2), rin*math.sin(phi/2), 0) + vCout = FreeCAD.Vector(rout*math.cos(phi/2), rout*math.sin(phi/2), 0) + # Center of twisting + rc = (rin + rout)/2 + vc = FreeCAD.Vector(rc*math.cos(phi/2), rc*math.sin(phi/2), 0) + # wire + arcin = Part.Arc(v1, vCin, v4) + line1 = Part.LineSegment(v4, v3) + arcout = Part.Arc(v3, vCout, v2) + line2 = Part.LineSegment(v2, v1) + + s = Part.Shape([arcin, line1, arcout, line2]) + w = Part.Wire(s.Edges) + angoffset = -angle/2 - phideg/2 + + for i in range(0, N): + p = w.translated(FreeCAD.Vector(0, 0, -zlen/2 + i*dz)) + p.rotate(vc, FreeCAD.Vector(0, 0, 1), angoffset + i*dtwist) + slices.append(p) + + loft = Part.makeLoft(slices, True, False) + fp.Shape = loft + fp.Placement = currPlacement + + def OnDocumentRestored(self, obj): + print('Doc Restored') - def layerPoints(self,polyList,sf,xOffset,yOffset,zPosition): - vl = [] - for p in polyList : - #print(p) - vl.append(FreeCAD.Vector(p[0]*sf+xOffset, p[1]*sf+yOffset,zPosition)) - # Close list - vl.append(vl[0]) - return vl - - def createGeometry(self,fp): - #GDMLShared.setTrace(True) - currPlacement = fp.Placement - #print("Create Geometry") - parms = fp.OutList - #print("OutList") - #print(parms) - GDMLShared.trace("Number of parms : "+str(len(parms))) - polyList = [] - faceList = [] - sections = [] - mul = GDMLShared.getMult(fp) - for ptr in parms : - if hasattr(ptr,'x') : - x = ptr.x * mul - y = ptr.y * mul - GDMLShared.trace('x : '+str(x)) - GDMLShared.trace('y : '+str(y)) - polyList.append([x, y]) - if hasattr(ptr,'zOrder') : - zOrder = ptr.zOrder - xOffset = ptr.xOffset * mul - yOffset = ptr.yOffset * mul - zPosition = ptr.zPosition * mul - sf = ptr.scalingFactor * mul - s = [zOrder,xOffset,yOffset,zPosition,sf] - sections.append(s) - #print('sections : '+str(len(sections))) - # - # Deal with Base Face - # - #baseList = layerPoints(polyList,sf,xOffset,yOffset,zPosition): - baseList = self.layerPoints(polyList,sections[0][4],sections[0][1], \ - sections[0][2],sections[0][3]) - #print('baseList') - #print(baseList) - w1 = Part.makePolygon(baseList) - f1 = Part.Face(w1) - f1.reverse() - faceList.append(f1) - #print("base list") - # - # Deal with Sides - # - #print("Start Range "+str(len(sections)-1)) - for s in range(0,len(sections)-1) : - xOffset = sections[s+1][1] - yOffset = sections[s+1][2] - zPosition = sections[s+1][3] - sf2 = sections[s+1][4] - #layerList = layerPoints(polyList,sf,xOffset,yOffset,zPosition) - layerList = self.layerPoints(polyList,sf,xOffset,yOffset,zPosition) - # deal with side faces - # remember first point is added to end of list - #print("Number Sides : "+str(len(baseList)-1)) - for i in range(0,len(baseList)-2) : - sideList = [] - sideList.append(baseList[i]) - sideList.append(baseList[i+1]) - sideList.append(layerList[i+1]) - sideList.append(layerList[i]) - # Close SideList polygon - sideList.append(baseList[i]) - #print("sideList") - #print(sideList) - w1 = Part.makePolygon(sideList) - f1 = Part.Face(w1) - faceList.append(f1) - # - # Deal with Top Face - # - w1 = Part.makePolygon(layerList) - f1 = Part.Face(w1) - #f1.reverse() - faceList.append(f1) - #print("Faces List") - #print(faceList) - shell=Part.makeShell(faceList) - #solid=Part.Solid(shell).removeSplitter() - solid=Part.Solid(shell) - #print("Valid Solid : "+str(solid.isValid())) - if solid.Volume < 0: - solid.reverse() - #print(dir(fp)) - #solid.exportBrep("/tmp/"+fp.Label+".brep") - fp.Shape = solid - fp.Placement = currPlacement - -class GDML2dVertex(GDMLcommon) : - def __init__(self, obj, x, y): - super().__init__(obj) - obj.addProperty("App::PropertyString","Type","Vertex", \ - "twoDimVertex").Type='twoDimVertex' - obj.addProperty("App::PropertyFloat","x","Vertex", \ - "x").x=x - obj.addProperty("App::PropertyFloat","y","Vertex", \ - "y").y=y - obj.setEditorMode("Type", 1) - self.Type = 'Vertex' - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if prop in ['x','y'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLSection(GDMLcommon) : - def __init__(self, obj, zOrder,zPosition,xOffset,yOffset,scalingFactor): - super().__init__(obj) - obj.addProperty("App::PropertyString","Type","section", \ - "section").Type='section' - obj.addProperty("App::PropertyInteger","zOrder","section", \ - "zOrder").zOrder=zOrder - obj.addProperty("App::PropertyFloat","zPosition","section", \ - "zPosition").zPosition=zPosition - obj.addProperty("App::PropertyFloat","xOffset","section", \ - "xOffset").xOffset=xOffset - obj.addProperty("App::PropertyFloat","yOffset","section", \ - "yOffset").yOffset=yOffset - obj.addProperty("App::PropertyFloat","scalingFactor","section", \ - "scalingFactor").scalingFactor=scalingFactor - obj.setEditorMode("Type", 1) - self.Type = 'section' - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if prop in ['zOrder','zPosition','xOffset','yOffset','scaleFactor'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLzplane(GDMLcommon) : - def __init__(self, obj, rmin, rmax, z): - super().__init__(obj) - obj.addProperty("App::PropertyFloat","rmin","zplane", \ - "Inside Radius").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","zplane", \ - "Outside Radius").rmax=rmax - obj.addProperty("App::PropertyFloat","z","zplane","z").z=z - self.Type = 'zplane' - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if not ('Restore' in fp.State) : - #if prop in ['rmin','rmax','z'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLrzpoint(GDMLcommon) : - def __init__(self, obj, r, z): - super().__init__(obj) - obj.addProperty("App::PropertyFloat","r","rzpoint", \ - "r-coordinate").r=r - obj.addProperty("App::PropertyFloat","z","rzpoint","z-coordinate").z=z - self.Type = 'zplane' - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if not ('Restore' in fp.State) : - #if prop in ['rmin','rmax','z'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLPolycone(GDMLsolid) : # Thanks to Dam Lamb - def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, colour = None) : - super().__init__(obj) - '''Add some custom properties to our Polycone feature''' - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyFloat","startphi","GDMLPolycone", \ - "Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLPolycone", \ - "Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLPolycone","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLPolycone","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLPolycone", \ - "Material") - setMaterial(obj, material) - # For debugging - #obj.setEditorMode('Placement',0) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPolycone' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['startphi','deltaphi','aunit','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp) : - - currPlacement = fp.Placement - zplanes = fp.OutList - #GDMLShared.trace("Number of zplanes : "+str(len(zplanes))) - mul = GDMLShared.getMult(fp.lunit) - offset = zplanes[0].z * mul - angleDeltaPhiDeg = 360.0 - if (hasattr(fp,'deltaphi')) : - angleDeltaPhiDeg = min([getAngleDeg(fp.aunit,fp.deltaphi), angleDeltaPhiDeg]) - if(angleDeltaPhiDeg <=0.0): return - - listShape = [0 for i in range((len(zplanes)-1))] - - sinPhi = 0.0 - cosPhi = 1.0 - if fp.startphi != 0 : - angleRad = getAngleRad(fp.aunit,fp.startphi) - sinPhi = math.sin(angleRad) - cosPhi = math.cos(angleRad) - - # loops on each z level - for i in range(len(zplanes)-1) : - GDMLShared.trace('index : '+str(i)) - if i == 0: - rmin1 = zplanes[i].rmin * mul - rmax1 = zplanes[i].rmax * mul - z1 = zplanes[i].z * mul - offset - else: - rmin1 = rmin2 - rmax1 = rmax2 - z1 = z2 - - rmin2 = zplanes[i+1].rmin * mul - rmax2 = zplanes[i+1].rmax * mul - z2 = zplanes[i+1].z * mul - offset - - # def of one face to rotate - face = Part.Face(Part.makePolygon( [ \ - FreeCAD.Vector(rmin1*cosPhi,rmin1*sinPhi,z1), - FreeCAD.Vector(rmax1*cosPhi,rmax1*sinPhi,z1), - FreeCAD.Vector(rmax2*cosPhi,rmax2*sinPhi,z2), - FreeCAD.Vector(rmin2*cosPhi,rmin2*sinPhi,z2), - FreeCAD.Vector(rmin1*cosPhi,rmin1*sinPhi,z1)])) - # rotation of the face - listShape[i] = face.revolve(FreeCAD.Vector(0,0,0),FreeCAD.Vector(0,0,1),angleDeltaPhiDeg) - # compound of all faces - fp.Shape = Part.makeCompound(listShape) - fp.Placement = currPlacement - -class GDMLGenericPolycone(GDMLsolid) : # Thanks to Dam Lamb - def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, colour = None) : - super().__init__(obj) - '''Add some custom properties to our GenericPolycone feature''' - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyFloat","startphi","GDMLPolycone", \ - "Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLPolycone", \ - "Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLPolycone","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLPolycone","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLPolycone", \ - "Material") - setMaterial(obj, material) - # For debugging - #obj.setEditorMode('Placement',0) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLGenericPolycone' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['startphi','deltaphi','aunit','lunit'] : - self.createGeometry(fp) +class GDMLXtru(GDMLsolid): + def __init__(self, obj, lunit, material, colour=None): + super().__init__(obj) + obj.addExtension('App::GroupExtensionPython') + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLXtru", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLXtru", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLXtru' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def layerPoints(self, polyList, sf, xOffset, yOffset, zPosition): + vl = [] + for p in polyList: + # print(p) + vl.append(FreeCAD.Vector(p[0]*sf+xOffset, p[1]*sf+yOffset, + zPosition)) + # Close list + vl.append(vl[0]) + return vl + + def createGeometry(self, fp): + # GDMLShared.setTrace(True) + currPlacement = fp.Placement + # print("Create Geometry") + parms = fp.OutList + # print("OutList") + # print(parms) + GDMLShared.trace("Number of parms : " + str(len(parms))) + polyList = [] + faceList = [] + sections = [] + mul = GDMLShared.getMult(fp) + for ptr in parms: + if hasattr(ptr, 'x'): + x = ptr.x * mul + y = ptr.y * mul + GDMLShared.trace('x : '+str(x)) + GDMLShared.trace('y : '+str(y)) + polyList.append([x, y]) + if hasattr(ptr, 'zOrder'): + zOrder = ptr.zOrder + xOffset = ptr.xOffset * mul + yOffset = ptr.yOffset * mul + zPosition = ptr.zPosition * mul + sf = ptr.scalingFactor * mul + s = [zOrder, xOffset, yOffset, zPosition, sf] + sections.append(s) + # print('sections : '+str(len(sections))) + # + # Deal with Base Face + # + # baseList = layerPoints(polyList,sf,xOffset,yOffset,zPosition): + baseList = self.layerPoints(polyList, sections[0][4], sections[0][1], + sections[0][2], sections[0][3]) + # print('baseList') + # print(baseList) + w1 = Part.makePolygon(baseList) + f1 = Part.Face(w1) + f1.reverse() + faceList.append(f1) + # print("base list") + # + # Deal with Sides + # + # print("Start Range "+str(len(sections)-1)) + for s in range(0, len(sections)-1): + xOffset = sections[s+1][1] + yOffset = sections[s+1][2] + zPosition = sections[s+1][3] + sf2 = sections[s+1][4] + # layerList = layerPoints(polyList,sf,xOffset,yOffset,zPosition) + layerList = self.layerPoints(polyList, sf, xOffset, yOffset, zPosition) + # deal with side faces + # remember first point is added to end of list + # print("Number Sides : "+str(len(baseList)-1)) + for i in range(0, len(baseList)-2): + sideList = [] + sideList.append(baseList[i]) + sideList.append(baseList[i+1]) + sideList.append(layerList[i+1]) + sideList.append(layerList[i]) + # Close SideList polygon + sideList.append(baseList[i]) + # print("sideList") + # print(sideList) + w1 = Part.makePolygon(sideList) + f1 = Part.Face(w1) + faceList.append(f1) + # + # Deal with Top Face + # + w1 = Part.makePolygon(layerList) + f1 = Part.Face(w1) + # f1.reverse() + faceList.append(f1) + # print("Faces List") + # print(faceList) + shell = Part.makeShell(faceList) + # solid=Part.Solid(shell).removeSplitter() + solid = Part.Solid(shell) + # print("Valid Solid : "+str(solid.isValid())) + if solid.Volume < 0: + solid.reverse() + # print(dir(fp)) + # solid.exportBrep("/tmp/"+fp.Label+".brep") + fp.Shape = solid + fp.Placement = currPlacement - #def execute(self, fp): in GDMLsolid - def createGeometry(self,fp) : +class GDML2dVertex(GDMLcommon): + def __init__(self, obj, x, y): + super().__init__(obj) + obj.addProperty("App::PropertyString", "Type", "Vertex", + "twoDimVertex").Type = 'twoDimVertex' + obj.addProperty("App::PropertyFloat", "x", "Vertex", + "x").x = x + obj.addProperty("App::PropertyFloat", "y", "Vertex", + "y").y = y + obj.setEditorMode("Type", 1) + self.Type = 'Vertex' + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if prop in ['x','y'] : + # self.execute(fp) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + pass + + def execute(self, fp): + pass + + +class GDMLSection(GDMLcommon): + def __init__(self, obj, zOrder, zPosition, xOffset, yOffset, scalingFactor): + super().__init__(obj) + obj.addProperty("App::PropertyString", "Type", "section", + "section").Type = 'section' + obj.addProperty("App::PropertyInteger", "zOrder", "section", + "zOrder").zOrder = zOrder + obj.addProperty("App::PropertyFloat", "zPosition", "section", + "zPosition").zPosition = zPosition + obj.addProperty("App::PropertyFloat", "xOffset", "section", + "xOffset").xOffset = xOffset + obj.addProperty("App::PropertyFloat", "yOffset", "section", + "yOffset").yOffset = yOffset + obj.addProperty("App::PropertyFloat", "scalingFactor", "section", + "scalingFactor").scalingFactor = scalingFactor + obj.setEditorMode("Type", 1) + self.Type = 'section' + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if prop in ['zOrder','zPosition','xOffset','yOffset','scaleFactor'] : + # self.execute(fp) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + pass + + def execute(self, fp): + pass + + +class GDMLzplane(GDMLcommon): + def __init__(self, obj, rmin, rmax, z): + super().__init__(obj) + obj.addProperty("App::PropertyFloat", "rmin", "zplane", + "Inside Radius").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "zplane", + "Outside Radius").rmax = rmax + obj.addProperty("App::PropertyFloat", "z", "zplane", "z").z = z + self.Type = 'zplane' + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if not ('Restore' in fp.State) : + # if prop in ['rmin','rmax','z'] : + # self.execute(fp) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + pass + + def execute(self, fp): + pass + + +class GDMLrzpoint(GDMLcommon): + def __init__(self, obj, r, z): + super().__init__(obj) + obj.addProperty("App::PropertyFloat", "r", "rzpoint", + "r-coordinate").r = r + obj.addProperty("App::PropertyFloat", "z", "rzpoint", + "z-coordinate").z = z + self.Type = 'zplane' + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if not ('Restore' in fp.State) : + # if prop in ['rmin','rmax','z'] : + # self.execute(fp) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + pass + + def execute(self, fp): + pass + + +class GDMLPolycone(GDMLsolid): # Thanks to Dam Lamb + def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, + colour=None): + super().__init__(obj) + '''Add some custom properties to our Polycone feature''' + obj.addExtension('App::GroupExtensionPython') + obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolycone", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolycone", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolycone", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolycone", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolycone", + "Material") + setMaterial(obj, material) + # For debugging + # obj.setEditorMode('Placement',0) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLPolycone' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): - currPlacement = fp.Placement - rzpoints = fp.OutList - if len(rzpoints) < 3: - print("Error in genericPolycone: number of rzpoints less than 3") - return + currPlacement = fp.Placement + zplanes = fp.OutList + # GDMLShared.trace("Number of zplanes : "+str(len(zplanes))) + mul = GDMLShared.getMult(fp.lunit) + offset = zplanes[0].z * mul + angleDeltaPhiDeg = 360.0 + if (hasattr(fp, 'deltaphi')): + angleDeltaPhiDeg = min([getAngleDeg(fp.aunit, fp.deltaphi), + angleDeltaPhiDeg]) + if(angleDeltaPhiDeg <= 0.0): + return + + listShape = [0 for i in range((len(zplanes)-1))] + + sinPhi = 0.0 + cosPhi = 1.0 + if fp.startphi != 0: + angleRad = getAngleRad(fp.aunit, fp.startphi) + sinPhi = math.sin(angleRad) + cosPhi = math.cos(angleRad) + + # loops on each z level + for i in range(len(zplanes)-1): + GDMLShared.trace('index : ' + str(i)) + if i == 0: + rmin1 = zplanes[i].rmin * mul + rmax1 = zplanes[i].rmax * mul + z1 = zplanes[i].z * mul - offset + else: + rmin1 = rmin2 # for i > 0, rmin2 will have been defined below + rmax1 = rmax2 + z1 = z2 + + rmin2 = zplanes[i+1].rmin * mul + rmax2 = zplanes[i+1].rmax * mul + z2 = zplanes[i+1].z * mul - offset + + # def of one face to rotate + face = Part.Face(Part.makePolygon([ + FreeCAD.Vector(rmin1*cosPhi, rmin1*sinPhi, z1), + FreeCAD.Vector(rmax1*cosPhi, rmax1*sinPhi, z1), + FreeCAD.Vector(rmax2*cosPhi, rmax2*sinPhi, z2), + FreeCAD.Vector(rmin2*cosPhi, rmin2*sinPhi, z2), + FreeCAD.Vector(rmin1*cosPhi, rmin1*sinPhi, z1)])) + # rotation of the face + listShape[i] = face.revolve(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + angleDeltaPhiDeg) + # compound of all faces + fp.Shape = Part.makeCompound(listShape) + fp.Placement = currPlacement - deltaphi = getAngleDeg(fp.aunit, fp.deltaphi) - startphi = getAngleDeg(fp.aunit, fp.startphi) - - mul = GDMLShared.getMult(fp.lunit) - rr = [rz.r*mul for rz in rzpoints] - zz = [rz.z*mul for rz in rzpoints] - verts = [FreeCAD.Vector(rz.r*mul, 0, rz.z*mul) for rz in rzpoints] - verts.append(FreeCAD.Vector(rzpoints[0].r*mul, 0, rzpoints[0].z*mul) ) - line = Part.makePolygon(verts) - line.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), startphi) - face = Part.Face(line) - surf = face.revolve(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), deltaphi) - solid = Part.makeSolid(surf) - - fp.Shape = solid - fp.Placement = currPlacement - -class GDMLSphere(GDMLsolid) : - def __init__(self, obj, rmin, rmax, startphi, deltaphi, starttheta, \ - deltatheta, aunit, lunit, material, colour = None ): - super().__init__(obj) - '''Add some custom properties to our Sphere feature''' - GDMLShared.trace("GDMLSphere init") - obj.addProperty("App::PropertyFloat","rmin","GDMLSphere", \ - "Inside Radius").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","GDMLSphere", \ - "Outside Radius").rmax=rmax - obj.addProperty("App::PropertyFloat","startphi","GDMLSphere", \ - "Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLSphere", \ - "Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyFloat","starttheta","GDMLSphere", \ - "Start Theta pos").starttheta=starttheta - obj.addProperty("App::PropertyFloat","deltatheta","GDMLSphere", \ - "Delta Angle").deltatheta=deltatheta - obj.addProperty("App::PropertyEnumeration","aunit","GDMLSphere","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLSphere","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLSphere", \ - "Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLSphere' - self.colour = colour - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin','rmax','startphi','deltaphi','starttheta', \ - 'deltatheta','aunit','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - # Based on code by Dam Lamb - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - rmax = mul * fp.rmax - if rmax <= 0.0: return - Rmax = 2 * rmax - rmin = mul * fp.rmin - spos = FreeCAD.Vector(0,0,0) - sdir = FreeCAD.Vector(0,0,1) - HalfPi = math.pi / 2.0 - TwoPi = 2 * math.pi - deltaphi_deg = getAngleDeg(fp.aunit, fp.deltaphi) - if deltaphi_deg < 360.0 and deltaphi_deg > 0: - sphere2 = Part.makeSphere(rmax,spos,sdir, \ - -90.0, 90.0, \ - deltaphi_deg) - if fp.startphi != 0 : - sphere2.rotate(spos, sdir, getAngleDeg(fp.aunit,fp.startphi)) - else : + +class GDMLGenericPolycone(GDMLsolid): # Thanks to Dam Lamb + def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, + colour=None): + super().__init__(obj) + '''Add some custom properties to our GenericPolycone feature''' + obj.addExtension('App::GroupExtensionPython') + obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolycone", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolycone", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolycone", + "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolycone", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolycone", + "Material") + setMaterial(obj, material) + # For debugging + # obj.setEditorMode('Placement',0) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLGenericPolycone' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + + currPlacement = fp.Placement + rzpoints = fp.OutList + if len(rzpoints) < 3: + print("Error in genericPolycone: number of rzpoints less than 3") + return + + deltaphi = getAngleDeg(fp.aunit, fp.deltaphi) + startphi = getAngleDeg(fp.aunit, fp.startphi) + + mul = GDMLShared.getMult(fp.lunit) + verts = [FreeCAD.Vector(rz.r*mul, 0, rz.z*mul) for rz in rzpoints] + verts.append(FreeCAD.Vector(rzpoints[0].r*mul, 0, rzpoints[0].z*mul)) + line = Part.makePolygon(verts) + line.rotate(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), startphi) + face = Part.Face(line) + surf = face.revolve(FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), deltaphi) + solid = Part.makeSolid(surf) + + fp.Shape = solid + fp.Placement = currPlacement + + +class GDMLSphere(GDMLsolid): + def __init__(self, obj, rmin, rmax, startphi, deltaphi, starttheta, + deltatheta, aunit, lunit, material, colour=None ): + super().__init__(obj) + '''Add some custom properties to our Sphere feature''' + GDMLShared.trace("GDMLSphere init") + obj.addProperty("App::PropertyFloat", "rmin", "GDMLSphere", + "Inside Radius").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "GDMLSphere", + "Outside Radius").rmax = rmax + obj.addProperty("App::PropertyFloat", "startphi", "GDMLSphere", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLSphere", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyFloat", "starttheta", "GDMLSphere", + "Start Theta pos").starttheta = starttheta + obj.addProperty("App::PropertyFloat", "deltatheta", "GDMLSphere", + "Delta Angle").deltatheta = deltatheta + obj.addProperty("App::PropertyEnumeration", "aunit", + "GDMLSphere", "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLSphere", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLSphere", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLSphere' + self.colour = colour + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rmin', 'rmax', 'startphi', 'deltaphi', 'starttheta', + 'deltatheta', 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + # Based on code by Dam Lamb + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + rmax = mul * fp.rmax + if rmax <= 0.0: + return + Rmax = 2 * rmax + rmin = mul * fp.rmin + spos = FreeCAD.Vector(0, 0, 0) + sdir = FreeCAD.Vector(0, 0, 1) + HalfPi = math.pi / 2.0 + TwoPi = 2 * math.pi + deltaphi_deg = getAngleDeg(fp.aunit, fp.deltaphi) + if deltaphi_deg < 360.0 and deltaphi_deg > 0: + sphere2 = Part.makeSphere(rmax, spos, sdir, + -90.0, 90.0, + deltaphi_deg) + if fp.startphi != 0: + sphere2.rotate(spos, sdir, getAngleDeg(fp.aunit, fp.startphi)) + else: sphere2 = Part.makeSphere(rmax) - # if starttheta > 0 cut the upper cone - startthetaRad = getAngleRad(fp.aunit, fp.starttheta) - startthetaDeg = getAngleDeg(fp.aunit, fp.starttheta) - - if startthetaDeg > 0.0 : - if startthetaDeg == 90.0 : - cylToCut = Part.makeCylinder(2.0*rmax,rmax, \ - FreeCAD.Vector(0,0,0)) + # if starttheta > 0 cut the upper cone + startthetaRad = getAngleRad(fp.aunit, fp.starttheta) + startthetaDeg = getAngleDeg(fp.aunit, fp.starttheta) + + if startthetaDeg > 0.0: + if startthetaDeg == 90.0: + cylToCut = Part.makeCylinder(2.0*rmax, rmax, + FreeCAD.Vector(0, 0, 0)) sphere2 = sphere2.cut(cylToCut) - elif startthetaDeg < 90.0 : - sphere2 = sphere2.cut(Part.makeCone(0.0, \ - rmax*math.sin(startthetaRad), rmax*math.cos(startthetaRad))) - - cylToCut = Part.makeCylinder(2.0*rmax,rmax, \ - FreeCAD.Vector(0,0,rmax*math.cos(startthetaRad))) - sphere2 = sphere2.cut(cylToCut) - - elif startthetaDeg < 180.0 : - sphere2 = sphere2.common(Part.makeCone(0.0, \ - rmax/math.cos(math.pi-startthetaRad),rmax, spos, \ - FreeCAD.Vector(0,0,-1.0))) - - # if deltatheta -> cut the down cone - deltathetaRad = getAngleRad(fp.aunit, fp.deltatheta) - thetaSumRad= startthetaRad + deltathetaRad - if thetaSumRad < math.pi : - if thetaSumRad > HalfPi : - - sphere2 = sphere2.cut(Part.makeCone(0.0, \ - rmax*math.sin(math.pi - thetaSumRad), \ - rmax*math.cos(math.pi - thetaSumRad), \ - spos, FreeCAD.Vector(0,0,-1.0))) - - cylToCut = Part.makeCylinder(2.0*rmax,rmax, \ - FreeCAD.Vector(0,0,rmax*(-1.0 + math.cos(thetaSumRad))) ) - sphere2 = sphere2.cut(cylToCut) - - elif thetaSumRad == HalfPi : - cylToCut = Part.makeCylinder(2.0*rmax,rmax, \ - FreeCAD.Vector(0,0,-rmax)) + elif startthetaDeg < 90.0: + sphere2 = sphere2.cut(Part.makeCone( + 0.0, + rmax*math.sin(startthetaRad), + rmax*math.cos(startthetaRad))) + + cylToCut = Part.makeCylinder( + 2.0*rmax, rmax, + FreeCAD.Vector(0, 0, rmax*math.cos(startthetaRad))) sphere2 = sphere2.cut(cylToCut) - elif thetaSumRad > 0 : - sphere2 = sphere2.common(Part.makeCone(0.0, \ - 2*rmax*math.tan( thetaSumRad), \ - 2*rmax )) - - if rmin <= 0 or rmin > rmax : - fp.Shape = sphere2 - else : - fp.Shape = sphere2.cut(Part.makeSphere(rmin)) - fp.Placement = currPlacement - - -class GDMLTrap(GDMLsolid) : - def __init__(self, obj, z, theta, phi, x1, x2, x3, x4, y1, y2, alpha, \ - aunit, lunit, material, colour = None): - super().__init__(obj) - "General Trapezoid" - obj.addProperty("App::PropertyFloat","z","GDMLTrap","z").z=z - obj.addProperty("App::PropertyFloat","theta","GDMLTrap","theta"). \ - theta=theta - obj.addProperty("App::PropertyFloat","phi","GDMLTrap","phi").phi=phi - obj.addProperty("App::PropertyFloat","x1","GDMLTrap", \ - "Length x at y= -y1/2 of face at -z/2").x1=x1 - obj.addProperty("App::PropertyFloat","x2","GDMLTrap", \ - "Length x at y= +y1/2 of face at -z/2").x2=x2 - obj.addProperty("App::PropertyFloat","x3","GDMLTrap", \ - "Length x at y= -y2/2 of face at +z/2").x3=x3 - obj.addProperty("App::PropertyFloat","x4","GDMLTrap", \ - "Length x at y= +y2/2 of face at +z/2").x4=x4 - obj.addProperty("App::PropertyFloat","y1","GDMLTrap", \ - "Length y at face -z/2").y1=y1 - obj.addProperty("App::PropertyFloat","y2","GDMLTrap", \ - "Length y at face +z/2").y2=y2 - obj.addProperty("App::PropertyFloat","alpha","GDMLTrap","alpha"). \ - alpha=alpha - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTrap","aunit") - obj.aunit=["rad", "deg"] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTrap","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTrap","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLTrap' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['z','theta','phi','x1','x2','x3','x4','y1','y2','alpha', \ - 'aunit', 'lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - # Define six vetices for the shape - alpha = getAngleRad(fp.aunit,fp.alpha) - theta = getAngleRad(fp.aunit,fp.theta) - phi = getAngleRad(fp.aunit,fp.phi) - mul = GDMLShared.getMult(fp) - y1 = mul * fp.y1 - x1 = mul * fp.x1 - x2 = mul * fp.x2 - y2 = mul * fp.y2 - x3 = mul * fp.x3 - x4 = mul * fp.x4 - z = mul * fp.z - dx1 = y1*math.tan(alpha) - dx2 = y2*math.tan(alpha) - - #Vertexes, counter clock wise order - v1 = FreeCAD.Vector(-x1/2 - dx1/2, -y1/2, -z/2) - v2 = FreeCAD.Vector( x1/2 - dx1/2, -y1/2, -z/2) - v3 = FreeCAD.Vector( x2/2 + dx1/2, y1/2, -z/2) - v4 = FreeCAD.Vector(-x2/2 + dx1/2, y1/2, -z/2) - v5 = FreeCAD.Vector(-x3/2 - dx2/2, -y2/2, z/2) - v6 = FreeCAD.Vector( x3/2 - dx2/2, -y2/2, z/2) - v7 = FreeCAD.Vector( x4/2 + dx2/2, y2/2, z/2) - v8 = FreeCAD.Vector(-x4/2 + dx2/2, y2/2, z/2) - # - # xy faces - # - vxy1 = [v1, v4, v3, v2, v1] - vxy2 = [v5, v6, v7, v8, v5] - # - # zx faces - # - vzx1 = [v1, v2, v6, v5, v1] - vzx2 = [v3, v4, v8, v7, v3] - # - # yz faces - # - vyz1 = [v5, v8, v4, v1, v5] - vyz2 = [v2, v3, v7, v6, v2] - # - # apply theta, phi distortions - # - rho = z*math.tan(theta) - dx = rho*math.cos(phi) - dy = rho*math.sin(phi) - for i in range(0,4): - vxy1[i][0] -= dx/2 - vxy1[i][1] -= dy/2 - vxy2[i][0] += dx/2 - vxy2[i][1] += dy/2 + + elif startthetaDeg < 180.0: + sphere2 = sphere2.common(Part.makeCone( + 0.0, + rmax/math.cos(math.pi-startthetaRad), rmax, spos, + FreeCAD.Vector(0, 0, -1.0))) + + # if deltatheta -> cut the down cone + deltathetaRad = getAngleRad(fp.aunit, fp.deltatheta) + thetaSumRad = startthetaRad + deltathetaRad + if thetaSumRad < math.pi: + if thetaSumRad > HalfPi: + + sphere2 = sphere2.cut(Part.makeCone( + 0.0, + rmax*math.sin(math.pi - thetaSumRad), + rmax*math.cos(math.pi - thetaSumRad), + spos, FreeCAD.Vector(0, 0, -1.0))) + + cylToCut = Part.makeCylinder( + 2.0*rmax, rmax, + FreeCAD.Vector(0, 0, rmax*(-1.0 + math.cos(thetaSumRad)))) + sphere2 = sphere2.cut(cylToCut) + + elif thetaSumRad == HalfPi: + cylToCut = Part.makeCylinder(2.0*rmax, rmax, + FreeCAD.Vector(0, 0, -rmax)) + sphere2 = sphere2.cut(cylToCut) + elif thetaSumRad > 0: + sphere2 = sphere2.common(Part.makeCone( + 0.0, + 2*rmax*math.tan(thetaSumRad), + 2*rmax)) + + if rmin <= 0 or rmin > rmax: + fp.Shape = sphere2 + else: + fp.Shape = sphere2.cut(Part.makeSphere(rmin)) + fp.Placement = currPlacement + + +class GDMLTrap(GDMLsolid): + def __init__(self, obj, z, theta, phi, x1, x2, x3, x4, y1, y2, alpha, + aunit, lunit, material, colour=None): + super().__init__(obj) + "General Trapezoid" + obj.addProperty("App::PropertyFloat", "z", "GDMLTrap", "z").z = z + obj.addProperty("App::PropertyFloat", "theta", "GDMLTrap", + "theta").theta = theta + obj.addProperty("App::PropertyFloat", "phi", "GDMLTrap", + "phi").phi = phi + obj.addProperty("App::PropertyFloat", "x1", "GDMLTrap", + "Length x at y= -y1/2 of face at -z/2").x1 = x1 + obj.addProperty("App::PropertyFloat", "x2", "GDMLTrap", + "Length x at y= +y1/2 of face at -z/2").x2 = x2 + obj.addProperty("App::PropertyFloat", "x3", "GDMLTrap", + "Length x at y= -y2/2 of face at +z/2").x3 = x3 + obj.addProperty("App::PropertyFloat", "x4", "GDMLTrap", + "Length x at y= +y2/2 of face at +z/2").x4 = x4 + obj.addProperty("App::PropertyFloat", "y1", "GDMLTrap", + "Length y at face -z/2").y1 = y1 + obj.addProperty("App::PropertyFloat", "y2", "GDMLTrap", + "Length y at face +z/2").y2 = y2 + obj.addProperty("App::PropertyFloat", "alpha", "GDMLTrap", + "alpha").alpha = alpha + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTrap", "aunit") + obj.aunit = ["rad", "deg"] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTrap", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTrap", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLTrap' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['z', 'theta', 'phi', + 'x1', 'x2', 'x3', 'x4', 'y1', 'y2', 'alpha', + 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self,fp): + currPlacement = fp.Placement + # Define six vetices for the shape + alpha = getAngleRad(fp.aunit,fp.alpha) + theta = getAngleRad(fp.aunit,fp.theta) + phi = getAngleRad(fp.aunit,fp.phi) + mul = GDMLShared.getMult(fp) + y1 = mul * fp.y1 + x1 = mul * fp.x1 + x2 = mul * fp.x2 + y2 = mul * fp.y2 + x3 = mul * fp.x3 + x4 = mul * fp.x4 + z = mul * fp.z + dx1 = y1*math.tan(alpha) + dx2 = y2*math.tan(alpha) - fxy1 = Part.Face(Part.makePolygon(vxy1)) - fxy2 = Part.Face(Part.makePolygon(vxy2)) - fzx1 = Part.Face(Part.makePolygon(vzx1)) - fzx2 = Part.Face(Part.makePolygon(vzx2)) - fyz1 = Part.Face(Part.makePolygon(vyz1)) - fyz2 = Part.Face(Part.makePolygon(vyz2)) - - shell = Part.makeShell([fxy1, fxy2, fzx1, fzx2, fyz1, fyz2]) - solid = Part.makeSolid(shell) - - # center is mid point of diagonal - # - botCenter = ((v3+v4) + (v1+v2))/2 - topCenter = ((v7+v8) + (v5+v6))/2 - center = (topCenter+botCenter)/2 - - fp.Shape = translate(solid, -center) - fp.Placement = currPlacement - - -class GDMLTrd(GDMLsolid) : - def __init__(self, obj, z, x1, x2, y1, y2, lunit, material, colour = None) : - super().__init__(obj) - "3.4.15 : Trapezoid – x & y varying along z" - obj.addProperty("App::PropertyFloat","z","GDMLTrd`","z").z=z - obj.addProperty("App::PropertyFloat","x1","GDMLTrd", \ - "Length x at face -z/2").x1=x1 - obj.addProperty("App::PropertyFloat","x2","GDMLTrd", \ - "Length x at face +z/2").x2=x2 - obj.addProperty("App::PropertyFloat","y1","GDMLTrd", \ - "Length y at face -z/2").y1=y1 - obj.addProperty("App::PropertyFloat","y2","GDMLTrd", \ - "Length y at face +z/2").y2=y2 - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTrd","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTrd","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLTrd' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['z','x1','x2','y1','y2','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - GDMLShared.trace("x2 : "+str(fp.x2)) - - mul = GDMLShared.getMult(fp) - x1 = (fp.x1 * mul)/2 - x2 = (fp.x2 * mul)/2 - y1 = (fp.y1 * mul)/2 - y2 = (fp.y2 * mul)/2 - z = (fp.z * mul)/2 - v1 = FreeCAD.Vector(-x1, -y1, -z) - v2 = FreeCAD.Vector(-x1, +y1, -z) - v3 = FreeCAD.Vector(x1, +y1, -z) - v4 = FreeCAD.Vector(x1, -y1, -z) - - v5 = FreeCAD.Vector(-x2, -y2, z) - v6 = FreeCAD.Vector(-x2, +y2, z) - v7 = FreeCAD.Vector(x2, +y2, z) - v8 = FreeCAD.Vector(x2, -y2, z) - # Make the wires/faces - f1 = make_face4(v1,v2,v3,v4) - f2 = make_face4(v1,v2,v6,v5) - f3 = make_face4(v2,v3,v7,v6) - f4 = make_face4(v3,v4,v8,v7) - f5 = make_face4(v1,v4,v8,v5) - f6 = make_face4(v5,v6,v7,v8) - shell=Part.makeShell([f1,f2,f3,f4,f5,f6]) - solid=Part.makeSolid(shell) - - #solid = Part.makePolygon([v1,v2,v3,v4,v5,v6,v7,v1]) - - fp.Shape = solid - fp.Placement = currPlacement - -class GDMLTube(GDMLsolid) : - def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, \ - lunit, material, colour = None): - super().__init__(obj) - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat","rmin","GDMLTube","Inside Radius").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","GDMLTube","Outside Radius").rmax=rmax - obj.addProperty("App::PropertyFloat","z","GDMLTube","Length z").z=z - obj.addProperty("App::PropertyFloat","startphi","GDMLTube","Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLTube","Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLTube","aunit") - obj.aunit=['rad','deg'] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTube","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTube","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLTube' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin','rmax','z','startphi','deltaphi','aunit', \ - 'lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - rmax = mul * fp.rmax - rmin = mul * fp.rmin - z = mul * fp.z - spos = FreeCAD.Vector(0,0,0) - sdir = FreeCAD.Vector(0,0,1) - #print('mul : '+str(mul)) - #print('rmax : '+str(rmax)) - #print('z : '+str(z)) - #print('deltaPhi : '+str(fp.deltaphi)) - tube = Part.makeCylinder(rmax, z, spos, sdir, - getAngleDeg(fp.aunit, fp.deltaphi)) - - if fp.startphi != 0 : - tube.rotate(spos, sdir, getAngleDeg(fp.aunit,fp.startphi)) - - if rmin > 0 : - tube = tube.cut(Part.makeCylinder(rmin, z)) - - base = FreeCAD.Vector(0,0,-z/2) - fp.Shape = translate(tube,base) - fp.Placement = currPlacement - -class GDMLcutTube(GDMLsolid) : - def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, \ - lowX, lowY, lowZ, highX, highY, highZ, \ - lunit, material, colour = None): - super().__init__(obj) - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat","rmin","GDMLcutTube","Inside Radius").rmin=rmin - obj.addProperty("App::PropertyFloat","rmax","GDMLcutTube","Outside Radius").rmax=rmax - obj.addProperty("App::PropertyFloat","z","GDMLcutTube","Length z").z=z - obj.addProperty("App::PropertyFloat","startphi","GDMLcutTube","Start Angle").startphi=startphi - obj.addProperty("App::PropertyFloat","deltaphi","GDMLcutTube","Delta Angle").deltaphi=deltaphi - obj.addProperty("App::PropertyEnumeration","aunit","GDMLcutTube","aunit") - obj.aunit=['rad','deg'] - obj.aunit=['rad','deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyFloat","lowX","GDMLcutTube","low X").lowX=lowX - obj.addProperty("App::PropertyFloat","lowY","GDMLcutTube","low Y").lowY=lowY - obj.addProperty("App::PropertyFloat","lowZ","GDMLcutTube","low Z").lowZ=lowZ - obj.addProperty("App::PropertyFloat","highX","GDMLcutTube","high X").highX=highX - obj.addProperty("App::PropertyFloat","highY","GDMLcutTube","high Y").highY=highY - obj.addProperty("App::PropertyFloat","highZ","GDMLcutTube","high Z").highZ=highZ - obj.addProperty("App::PropertyEnumeration","lunit","GDMLcutTube","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLcutTube","Material") - #print('Add material') - #print(material) - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - #print(MaterialsList) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - obj.Proxy = self - self.Type = 'GDMLcutTube' - self.colour = colour - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return + # Vertexes, counter clock wise order + v1 = FreeCAD.Vector(-x1/2 - dx1/2, -y1/2, -z/2) + v2 = FreeCAD.Vector( x1/2 - dx1/2, -y1/2, -z/2) + v3 = FreeCAD.Vector( x2/2 + dx1/2, y1/2, -z/2) + v4 = FreeCAD.Vector(-x2/2 + dx1/2, y1/2, -z/2) + v5 = FreeCAD.Vector(-x3/2 - dx2/2, -y2/2, z/2) + v6 = FreeCAD.Vector( x3/2 - dx2/2, -y2/2, z/2) + v7 = FreeCAD.Vector( x4/2 + dx2/2, y2/2, z/2) + v8 = FreeCAD.Vector(-x4/2 + dx2/2, y2/2, z/2) + # + # xy faces + # + vxy1 = [v1, v4, v3, v2, v1] + vxy2 = [v5, v6, v7, v8, v5] + # + # zx faces + # + vzx1 = [v1, v2, v6, v5, v1] + vzx2 = [v3, v4, v8, v7, v3] + # + # yz faces + # + vyz1 = [v5, v8, v4, v1, v5] + vyz2 = [v2, v3, v7, v6, v2] + # + # apply theta, phi distortions + # + rho = z*math.tan(theta) + dx = rho*math.cos(phi) + dy = rho*math.sin(phi) + for i in range(0, 4): + vxy1[i][0] -= dx/2 + vxy1[i][1] -= dy/2 + vxy2[i][0] += dx/2 + vxy2[i][1] += dy/2 + + fxy1 = Part.Face(Part.makePolygon(vxy1)) + fxy2 = Part.Face(Part.makePolygon(vxy2)) + fzx1 = Part.Face(Part.makePolygon(vzx1)) + fzx2 = Part.Face(Part.makePolygon(vzx2)) + fyz1 = Part.Face(Part.makePolygon(vyz1)) + fyz2 = Part.Face(Part.makePolygon(vyz2)) + + shell = Part.makeShell([fxy1, fxy2, fzx1, fzx2, fyz1, fyz2]) + solid = Part.makeSolid(shell) + + # center is mid point of diagonal + # + botCenter = ((v3+v4) + (v1+v2))/2 + topCenter = ((v7+v8) + (v5+v6))/2 + center = (topCenter+botCenter)/2 + + fp.Shape = translate(solid, -center) + fp.Placement = currPlacement + + +class GDMLTrd(GDMLsolid): + def __init__(self, obj, z, x1, x2, y1, y2, lunit, material, colour=None): + super().__init__(obj) + "3.4.15 : Trapezoid – x & y varying along z" + obj.addProperty("App::PropertyFloat", "z", "GDMLTrd", + "z").z = z + obj.addProperty("App::PropertyFloat", "x1", "GDMLTrd", + "Length x at face -z/2").x1 = x1 + obj.addProperty("App::PropertyFloat", "x2", "GDMLTrd", + "Length x at face +z/2").x2 = x2 + obj.addProperty("App::PropertyFloat", "y1", "GDMLTrd", + "Length y at face -z/2").y1 = y1 + obj.addProperty("App::PropertyFloat", "y2", "GDMLTrd", + "Length y at face +z/2").y2 = y2 + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTrd", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTrd", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLTrd' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['z', 'x1', 'x2', 'y1', 'y2', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + GDMLShared.trace("x2 : " + str(fp.x2)) + + mul = GDMLShared.getMult(fp) + x1 = (fp.x1 * mul)/2 + x2 = (fp.x2 * mul)/2 + y1 = (fp.y1 * mul)/2 + y2 = (fp.y2 * mul)/2 + z = (fp.z * mul)/2 + v1 = FreeCAD.Vector(-x1, -y1, -z) + v2 = FreeCAD.Vector(-x1, +y1, -z) + v3 = FreeCAD.Vector(x1, +y1, -z) + v4 = FreeCAD.Vector(x1, -y1, -z) + + v5 = FreeCAD.Vector(-x2, -y2, z) + v6 = FreeCAD.Vector(-x2, +y2, z) + v7 = FreeCAD.Vector(x2, +y2, z) + v8 = FreeCAD.Vector(x2, -y2, z) + # Make the wires/faces + f1 = make_face4(v1, v2, v3, v4) + f2 = make_face4(v1, v2, v6, v5) + f3 = make_face4(v2, v3, v7, v6) + f4 = make_face4(v3, v4, v8, v7) + f5 = make_face4(v1, v4, v8, v5) + f6 = make_face4(v5, v6, v7, v8) + shell = Part.makeShell([f1, f2, f3, f4, f5, f6]) + solid = Part.makeSolid(shell) + + # solid = Part.makePolygon([v1,v2,v3,v4,v5,v6,v7,v1]) + + fp.Shape = solid + fp.Placement = currPlacement + + +class GDMLTube(GDMLsolid): + def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, + lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Tube feature''' + obj.addProperty("App::PropertyFloat", "rmin", "GDMLTube", + "Inside Radius").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "GDMLTube", + "Outside Radius").rmax = rmax + obj.addProperty("App::PropertyFloat", "z", "GDMLTube", + "Length z").z = z + obj.addProperty("App::PropertyFloat", "startphi", "GDMLTube", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLTube", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTube", + "aunit") + obj.aunit = ['rad', 'deg'] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTube", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLTube", + "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLTube' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['rmin','rmax','z','startphi','deltaphi','aunit', \ - 'lowX', 'lowY', 'lowZ', \ - 'highX','highY','highZ','lunit'] : - self.createGeometry(fp) + if prop in ['rmin', 'rmax', 'z', 'startphi', 'deltaphi', + 'aunit', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + rmax = mul * fp.rmax + rmin = mul * fp.rmin + z = mul * fp.z + spos = FreeCAD.Vector(0, 0, 0) + sdir = FreeCAD.Vector(0, 0, 1) + # print('mul : '+str(mul)) + # print('rmax : '+str(rmax)) + # print('z : '+str(z)) + # print('deltaPhi : '+str(fp.deltaphi)) + tube = Part.makeCylinder(rmax, z, spos, sdir, + getAngleDeg(fp.aunit, fp.deltaphi)) + + if fp.startphi != 0: + tube.rotate(spos, sdir, getAngleDeg(fp.aunit, fp.startphi)) + + if rmin > 0: + tube = tube.cut(Part.makeCylinder(rmin, z)) + + base = FreeCAD.Vector(0, 0, -z/2) + fp.Shape = translate(tube, base) + fp.Placement = currPlacement - #def execute(self, fp): in GDMLsolid - def cutShapeWithPlane(self, shape, plane, depth): +class GDMLcutTube(GDMLsolid): + def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, + lowX, lowY, lowZ, highX, highY, highZ, + lunit, material, colour=None): + super().__init__(obj) + '''Add some custom properties to our Tube feature''' + obj.addProperty("App::PropertyFloat", "rmin", "GDMLcutTube", + "Inside Radius").rmin = rmin + obj.addProperty("App::PropertyFloat", "rmax", "GDMLcutTube", + "Outside Radius").rmax = rmax + obj.addProperty("App::PropertyFloat", "z", "GDMLcutTube", + "Length z").z = z + obj.addProperty("App::PropertyFloat", "startphi", "GDMLcutTube", + "Start Angle").startphi = startphi + obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLcutTube", + "Delta Angle").deltaphi = deltaphi + obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLcutTube", + "aunit") + obj.aunit = ['rad', 'deg'] + obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty("App::PropertyFloat", "lowX", "GDMLcutTube", + "low X").lowX = lowX + obj.addProperty("App::PropertyFloat", "lowY", "GDMLcutTube", + "low Y").lowY = lowY + obj.addProperty("App::PropertyFloat", "lowZ", "GDMLcutTube", + "low Z").lowZ = lowZ + obj.addProperty("App::PropertyFloat", "highX", "GDMLcutTube", + "high X").highX = highX + obj.addProperty("App::PropertyFloat", "highY", "GDMLcutTube", + "high Y").highY = highY + obj.addProperty("App::PropertyFloat", "highZ", "GDMLcutTube", + "high Z").highZ = highZ + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLcutTube", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", "GDMLcutTube", + "Material") + # print('Add material') + # print(material) + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # print(MaterialsList) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + obj.Proxy = self + self.Type = 'GDMLcutTube' + self.colour = colour + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['rmin', 'rmax', 'z', 'startphi', 'deltaphi', 'aunit', + 'lowX', 'lowY', 'lowZ', + 'highX', 'highY', 'highZ', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def cutShapeWithPlane(self, shape, plane, depth): "Cut a shape with a plane" - #print('Cut Shape with Plane') - #print('depth : '+str(depth)) - #so = plane.extrude(plane.*1e10) - #so = plane.extrude(plane.normalAt(1,1)*1e10) - #so = plane.extrude(plane.normalAt(1,1)*100) - so = plane.extrude(plane.normalAt(1,1)*depth) - #print('Plane extruded') - #print(plane.normalAt(1,1)) - #return so - #print('Extrude made - Now Cut') + # print('Cut Shape with Plane') + # print('depth : '+str(depth)) + # so = plane.extrude(plane.*1e10) + # so = plane.extrude(plane.normalAt(1,1)*1e10) + # so = plane.extrude(plane.normalAt(1,1)*100) + so = plane.extrude(plane.normalAt(1, 1)*depth) + # print('Plane extruded') + # print(plane.normalAt(1,1)) + # return so + # print('Extrude made - Now Cut') cut = shape.cut(so) - #print('Return Cut') + # print('Return Cut') return cut - def createGeometry(self,fp): + def createGeometry(self, fp): currPlacement = fp.Placement - angle = getAngleDeg(fp.aunit,fp.deltaphi) - pntC = FreeCAD.Vector(0,0,0) - dirC = FreeCAD.Vector(0,0,1) - mul = GDMLShared.getMult(fp) - #print('mul : '+str(mul)) + angle = getAngleDeg(fp.aunit, fp.deltaphi) + pntC = FreeCAD.Vector(0, 0, 0) + dirC = FreeCAD.Vector(0, 0, 1) + mul = GDMLShared.getMult(fp) rmin = mul * fp.rmin - #print('rmin : '+str(rmin)) - #print(type(fp.rmin)) rmax = mul * fp.rmax - #print('rmax : '+str(rmax)) - z = mul * fp.z - #print('z : '+str(z)) - depth = 2 * max(rmax,z) - #print('depth : '+str(depth)) - #print(fp.lowX) - #print(fp.lowY) - #print(fp.lowZ) + z = mul * fp.z + depth = 2 * max(rmax, z) botDir = FreeCAD.Vector(fp.lowX, fp.lowY, fp.lowZ) topDir = FreeCAD.Vector(fp.highX, fp.highY, fp.highZ) - tube1 = Part.makeCylinder(rmax,z,pntC,dirC,angle) - tube2 = Part.makeCylinder(rmin,z,pntC,dirC,angle) + tube1 = Part.makeCylinder(rmax, z, pntC, dirC, angle) + tube2 = Part.makeCylinder(rmin, z, pntC, dirC, angle) tube = tube1.cut(tube2) - #Part.show(tube1) - #print('Create top Plane') - topPlane = Part.makePlane(depth, depth, \ - FreeCAD.Vector(-rmax,-rmax,z),topDir) - #Part.show(topPlane) - #print('Cut top Plane') - cutTube1 = self.cutShapeWithPlane(tube, topPlane, depth) - #Part.show(cutTube1) - #print('Create BottomPlane') - botPlane = Part.makePlane(depth, depth, \ - FreeCAD.Vector(rmax,rmax,0.0),botDir) - #botPlane = Part.makePlane(500, 500, \ - # FreeCAD.Vector(rmax,rmax,0.0),FreeCAD.Vector(0.0,-0.7,-0.71)) - #Part.show(botPlane) - #print('Cut Top Plane') - cutTube2 = self.cutShapeWithPlane(cutTube1,botPlane,depth) - #print('Return result') - #fp.Shape = Part.makeBox(2,2,2) - base = FreeCAD.Vector(0,0,-z/2) - fp.Shape = translate(cutTube2,base) - #fp.Shape = topPlane - #fp.Shape = botPlane + topPlane = Part.makePlane(depth, depth, + FreeCAD.Vector(-rmax, -rmax, z), topDir) + cutTube1 = self.cutShapeWithPlane(tube, topPlane, depth) + botPlane = Part.makePlane(depth, depth, + FreeCAD.Vector(rmax, rmax, 0.0), botDir) + cutTube2 = self.cutShapeWithPlane(cutTube1, botPlane, depth) + base = FreeCAD.Vector(0, 0, -z/2) + fp.Shape = translate(cutTube2, base) fp.Placement = currPlacement - def createGeometry_hardcoded(self,fp): - angle = getAngleDeg(fp.aunit,fp.deltaphi) - pntC = FreeCAD.Vector(0,0,0) - dirC = FreeCAD.Vector(0,0,1) - #pntP = FreeCAD.Vector(-5,-5,5) + def createGeometry_hardcoded(self, fp): + angle = getAngleDeg(fp.aunit, fp.deltaphi) + pntC = FreeCAD.Vector(0, 0, 0) + dirC = FreeCAD.Vector(0, 0, 1) - tube1 = Part.makeCylinder(20,60,pntC,dirC,angle) - tube2 = Part.makeCylinder(12,60,pntC,dirC,angle) + tube1 = Part.makeCylinder(20, 60, pntC, dirC, angle) + tube2 = Part.makeCylinder(12, 60, pntC, dirC, angle) tube = tube1.cut(tube2) - #Part.show(tube1) - #print('Create top Plane') - topPlane = Part.makePlane(100, 100, \ - FreeCAD.Vector(-20,-20,60),FreeCAD.Vector(0.7,0,0.71)) - #Part.show(topPlane) - print('Cut top Plane') - cutTube1 = self.cutShapeWithPlane(tube, topPlane) - #Part.show(cutTube1) - print('Create BottomPlane') - botPlane = Part.makePlane(100, 100, \ - FreeCAD.Vector(20,20,0),FreeCAD.Vector(0,-0.7,-0.71)) + topPlane = Part.makePlane(100, 100, + FreeCAD.Vector(-20, -20, 60), + FreeCAD.Vector(0.7, 0, 0.71)) + cutTube1 = self.cutShapeWithPlane(tube, topPlane) + botPlane = Part.makePlane(100, 100, + FreeCAD.Vector(20, 20, 0), + FreeCAD.Vector(0, -0.7, -0.71)) Part.show(botPlane) - print('Cut Top Plane') - cutTube2 = self.cutShapeWithPlane(cutTube1,botPlane) - #cutTube2 = self.cutShapeWithPlane(tube,botPlane) + cutTube2 = self.cutShapeWithPlane(cutTube1, botPlane) print('Return result') - #fp.Shape = Part.makeBox(2,2,2) fp.Shape = cutTube2 - #fp.Shape = tube - #fp.Shape = topPlane - -class GDMLVertex(GDMLcommon) : - def __init__(self, obj, x, y, z, lunit): - super().__init__(obj) - obj.addProperty("App::PropertyFloat","x","GDMLVertex", \ - "x").x=x - obj.addProperty("App::PropertyFloat","y","GDMLVertex", \ - "y").y=y - obj.addProperty("App::PropertyFloat","z","GDMLVertex", \ - "z").z=z - self.Type = 'GDMLVertex' - self.Object = obj - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if not ('Restore' in fp.State) : - # if prop in ['x','y', 'z'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLTriangular(GDMLcommon) : - def __init__(self, obj, v1, v2, v3, vtype): - super().__init__(obj) - obj.addProperty("App::PropertyVector","v1","Triangular", \ - "v1").v1=v1 - obj.addProperty("App::PropertyVector","v2","Triangular", \ - "v1").v2=v2 - obj.addProperty("App::PropertyVector","v3","Triangular", \ - "v1").v3=v3 - obj.addProperty("App::PropertyEnumeration","vtype","Triangular","vtype") - obj.vtype=["ABSOLUTE", "RELATIVE"] - obj.vtype=["ABSOLUTE", "RELATIVE"].index(vtype) - self.Type = 'GDMLTriangular' - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if not ('Restore' in fp.State) : - # if prop in ['v1','v2','v3','type'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLQuadrangular(GDMLcommon) : - def __init__(self, obj, v1, v2, v3, v4, vtype): - super().__init__(obj) - obj.addProperty("App::PropertyVector","v1","Quadrang", \ - "v1").v1=v1 - obj.addProperty("App::PropertyVector","v2","Quadrang", \ - "v2").v2=v2 - obj.addProperty("App::PropertyVector","v3","Quadrang", \ - "v3").v3=v3 - obj.addProperty("App::PropertyVector","v4","Quadrang", \ - "v4").v4=v4 - obj.addProperty("App::PropertyEnumeration","vtype","Quadrang","vtype") - obj.vtype=["ABSOLUTE", "RELATIVE"] - obj.vtype=0 - self.Type = 'GDMLQuadrangular' - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - #if prop in ['v1','v2','v3','v4','type'] : - # self.execute(fp) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - pass - - def execute(self, fp): - pass - -class GDMLGmshTessellated(GDMLsolid) : - - def __init__(self, obj, sourceObj,meshLen, vertex, facets, lunit, \ - material, colour = None) : - super().__init__(obj) - #obj.addProperty('App::PropertyBool','editable','GDMLGmshTessellated', \ - # 'Editable').editable = False - obj.addProperty('App::PropertyInteger','facets','GDMLGmshTessellated', \ - 'Facets').facets = len(facets) - obj.setEditorMode('facets',1) - obj.addProperty('App::PropertyInteger','vertex','GDMLGmshTessellated', \ - 'Vertex').vertex = len(vertex) - obj.setEditorMode('vertex',1) - obj.addProperty('App::PropertyFloat','m_maxLength', \ - 'GDMLGmshTessellated', \ - 'Max Length').m_maxLength = meshLen - obj.addProperty('App::PropertyFloat','m_curveLen','GDMLGmshTessellated', \ - 'Curve Length').m_curveLen = meshLen - obj.addProperty('App::PropertyFloat','m_pointLen','GDMLGmshTessellated', \ - 'Point Length').m_pointLen = meshLen - #obj.addProperty('App::PropertyBool','m_Remesh','GDMLGmshTessellated', \ - # 'ReMesh').m_Remesh = False - obj.addProperty("App::PropertyEnumeration","lunit","GDMLGmshTessellated","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material", \ - "GDMLTessellated","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - #print(MaterialsList) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - #obj.addExtension('App::GroupExtensionPython') - self.Type = 'GDMLGmshTessellated' - self.SourceObj = sourceObj - self.Vertex = vertex - self.Facets = facets - self.Object = obj - self.colour = colour - obj.Proxy = self - - def updateParams(self, vertex, facets) : - #print('Update Params') - self.Vertex = vertex - self.Facets = facets - self.facets = len(facets) - self.vertex = len(vertex) - print(f"Vertex : {self.vertex} Facets : {self.facets}") - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['editable'] : - if fp.editable == True : - self.addProperties() - - if prop in ['m_Remesh'] : - if fp.m_Remesh == True : - self.reMesh(fp) - self.execute(fp) - - #if prop in ['v1','v2','v3','v4','type','lunit'] : - # self.createGeometry(fp) - - def execute(self, fp): # Here for remesh? - self.createGeometry(fp) - - def addProperties(self) : - print('Add Properties') - - def reMesh(self,fp) : - from .GmshUtils import initialize, meshObj, getVertex, getFacets - - initialize() - meshObj(fp.Proxy.SourceObj,2,True,fp.Proxy.Object) - facets = getFacets() - vertex = getVertex() - fp.Proxy.Vertex = vertex - self.Object.vertex = len(vertex) - fp.Proxy.Facets = facets - self.Object.facets = len(facets) - FreeCADGui.updateGui() - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - currPlacement = fp.Placement - #print("Tessellated") - mul = GDMLShared.getMult(fp) - FCfaces = [] - #print(self.Vertex) - i = 0 - for f in self.Facets : - #print('Facet') - #print(f) - if len(f) == 3 : - FCfaces.append(GDMLShared.triangle( \ - mul*self.Vertex[f[0]], \ - mul*self.Vertex[f[1]], \ - mul*self.Vertex[f[2]])) - else : # len should then be 4 - FCfaces.append(GDMLShared.quad( \ - mul*self.Vertex[f[0]], \ - mul*self.Vertex[f[1]], \ - mul*self.Vertex[f[2]], \ - mul*self.Vertex[f[3]])) - shell=Part.makeShell(FCfaces) - if shell.isValid == False : - FreeCAD.Console.PrintWarning('Not a valid Shell/n') - - #shell.check() - #solid=Part.Solid(shell).removeSplitter() - try : - solid=Part.Solid(shell) - except : - # make compound rather than just barf - # visually able to view at least - FreeCAD.Console.PrintWarning('Problem making Solid/n') - solid = Part.makeCompound(FCfaces) - #if solid.Volume < 0: - # solid.reverse() - #print(dir(solid)) - #bbox = solid.BoundBox - #base = FreeCAD.Vector(-(bbox.XMin+bbox.XMax)/2, \ - # -(bbox.YMin+bbox.YMax)/2 \ - # -(bbox.ZMin+bbox.ZMax)/2) - #print(base) - - #base = FreeCAD.Vector(0,0,0) - #fp.Shape = translate(solid,base) - fp.Shape = solid - fp.Placement = currPlacement - -class GDMLTessellated(GDMLsolid) : - - def __init__(self, obj, vertex, facets, flag, lunit, material, colour = None) : - super().__init__(obj) - # ######################################## - # if flag == True - facets is Mesh.Facets - with Normals - # if flag == False - facets is Faces i.e. from import GDMLTessellated - # ######################################## - obj.addProperty('App::PropertyInteger','facets','GDMLTessellated', \ - 'Facets').facets = len(facets) - obj.setEditorMode('facets',1) - obj.addProperty('App::PropertyInteger','vertex','GDMLTessellated', \ - 'Vertex').vertex = len(vertex) - obj.setEditorMode('vertex',1) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTessellated","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material", \ - "GDMLTessellated","Material") - setMaterial(obj, material) - self.updateParams(vertex, facets, flag) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - self.Type = 'GDMLTessellated' - self.colour = colour - obj.Proxy = self - - def updateParams(self, vertex, facets, flag) : - #print('Update Params & Shape') - self.pshape = self.createShape(vertex,facets,flag) - #print(f"Pshape vertex {len(self.pshape.Vertexes)}") - self.facets = len(facets) - self.vertex = len(vertex) - #print(f"Vertex : {self.vertex} Facets : {self.facets}") - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['editable'] : - if fp.editable == True : - self.addProperties() - - #if prop in ['v1','v2','v3','v4','type','lunit'] : - # self.createGeometry(fp) - - def addProperties(self) : - print('Add Properties') - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp): - #currPlacement = fp.Placement - #print("Tessellated") - #print(self.Type) - #print('self') - #print(dir(self)) - #print('fp') - #print(dir(fp)) - if hasattr(self,'pshape') : - #print('Update Shape') - fp.Shape = self.pshape - if hasattr(fp,'pshape') : - fp.pshape = self.pshape - fp.vertex = self.vertex - fp.facets = self.facets - #print(len(fp.Shape.Vertexes)) - #print(fp.Shape) - #fp.Placement = currPlacement - - def createShape(self,vertex,facets,flag) : - # Viewing outside of face vertex must be counter clockwise - # if flag == True - facets is Mesh.Facets - # if flag == False - factes is Faces i.e. from import GDMLTessellated - #mul = GDMLShared.getMult(fp) - mul = GDMLShared.getMult(self) - #print('Create Shape') - FCfaces = [] - i = 0 - for f in facets : - #print('Facet') - #print(f) - if flag == True : - FCfaces.append(GDMLShared.facet(f)) - else : - if len(f) == 3 : - FCfaces.append(GDMLShared.triangle( \ - mul*vertex[f[0]], \ - 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]])) - #print(FCfaces) - shell=Part.makeShell(FCfaces) - if shell.isValid == False : - FreeCAD.Console.PrintWarning('Not a valid Shell/n') - - #shell.check() - #solid=Part.Solid(shell).removeSplitter() - try : - solid=Part.Solid(shell) - except : - # make compound rather than just barf - # visually able to view at least - FreeCAD.Console.PrintWarning('Problem making Solid/n') - solid = Part.makeCompound(FCfaces) - #if solid.Volume < 0: - # solid.reverse() - #print(dir(solid)) - #bbox = solid.BoundBox - #base = FreeCAD.Vector(-(bbox.XMin+bbox.XMax)/2, \ - # -(bbox.YMin+bbox.YMax)/2 \ - # -(bbox.ZMin+bbox.ZMax)/2) - #print(base) - - #base = FreeCAD.Vector(0,0,0) - #fp.Shape = translate(solid,base) - #fp.Shape = solid - - return solid - -class GDMLTetra(GDMLsolid) : # 4 point Tetrahedron - - def __init__(self, obj, v1, v2, v3, v4, lunit, material, colour = None ): - super().__init__(obj) - obj.addProperty("App::PropertyVector","v1","GDMLTra", \ - "v1").v1=v1 - obj.addProperty("App::PropertyVector","v2","GDMLTra", \ - "v2").v2=v2 - obj.addProperty("App::PropertyVector","v3","GDMLTra", \ - "v3").v3=v3 - obj.addProperty("App::PropertyVector","v4","GDMLTra", \ - "v4").v4=v4 - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTra","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material","GDMLTra","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTetra' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['v1','v2','v3','v4','lunit'] : - self.createGeometry(fp) - - #def execute(self, fp): in GDMLsolid - - def createGeometry(self,fp) : - currPlacement = fp.Placement - mul = GDMLShared.getMult(fp) - pt1 = mul * fp.v1 - pt2 = mul * fp.v2 - pt3 = mul * fp.v3 - pt4 = mul * fp.v4 - face1 = Part.Face(Part.makePolygon([pt1,pt2,pt3,pt1])) - face2 = Part.Face(Part.makePolygon([pt1,pt2,pt4,pt1])) - 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])) - fp.Placement = currPlacement - -class GDMLTetrahedron(GDMLsolid) : - - ''' Does not exist as a GDML solid, but export as an Assembly of G4Tet ''' - ''' See paper Poole at al - Fast Tessellated solid navigation in GEANT4 ''' - - def __init__(self, obj, tetra, lunit, material, colour=None) : - super().__init__(obj) - #obj.addProperty('App::PropertyBool','editable','GDMLTetrahedron', \ - # 'Editable').editable = False - obj.addProperty('App::PropertyInteger','tetra','GDMLTetrahedron', \ - 'Tetra').tetra = len(tetra) - obj.setEditorMode('tetra',1) - obj.addProperty("App::PropertyEnumeration","lunit","GDMLTetrahedron","lunit") - setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration","material", \ - "GDMLTetrahedron","Material") - setMaterial(obj, material) - if FreeCAD.GuiUp : - updateColour(obj,colour,material) - # Suppress Placement - position & Rotation via parent App::Part - # this makes Placement via Phyvol easier and allows copies etc - #obj.addExtension('App::GroupExtensionPython') - self.Tetra = tetra - self.Object = obj - self.Type = 'GDMLTetrahedron' - self.colour = colour - obj.Proxy = self - - def onChanged(self, fp, prop): - '''Do something when a property has changed''' - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State : - return - - if prop in ['material'] : - if FreeCAD.GuiUp : - if hasattr(self,'colour') : - if self.colour is None : - fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['lunit'] : - self.createGeometry(fp) - #def execute(self, fp): in GDMLsolid - - def makeTetra(self,pt1,pt2,pt3,pt4) : - face1 = Part.Face(Part.makePolygon([pt1,pt2,pt3,pt1])) - face2 = Part.Face(Part.makePolygon([pt1,pt2,pt4,pt1])) - face3 = Part.Face(Part.makePolygon([pt4,pt2,pt3,pt4])) - face4 = Part.Face(Part.makePolygon([pt1,pt3,pt4,pt1])) - return(Part.makeShell([face1,face2,face3,face4])) - #return(face1,face2,face3,face4) - - def createGeometry(self,fp): - currPlacement = fp.Placement - print("Tetrahedron") - mul = GDMLShared.getMult(fp) - print(len(self.Tetra)) - tetraShells = [] - for t in self.Tetra : - pt1 = mul * t[0] - pt2 = mul * t[1] - pt3 = mul * t[2] - pt4 = mul * t[3] - tetraShells.append(self.makeTetra(pt1,pt2,pt3,pt4)) - fp.Shape = Part.makeCompound(tetraShells) - fp.Placement = currPlacement - -class GDMLFiles(GDMLcommon) : - def __init__(self,obj,FilesEntity,sectionDict) : - super().__init__(obj) - '''Add some custom properties to our Cone feature''' - GDMLShared.trace("GDML Files") - GDMLShared.trace(FilesEntity) - obj.addProperty("App::PropertyBool","active","GDMLFiles", \ - "split option").active=FilesEntity - obj.addProperty("App::PropertyString","define","GDMLFiles", \ - "define section").define=sectionDict.get('define',"") - obj.addProperty("App::PropertyString","materials","GDMLFiles", \ - "materials section").materials=sectionDict.get('materials',"") - obj.addProperty("App::PropertyString","solids","GDMLFiles", \ - "solids section").solids=sectionDict.get('solids',"") - obj.addProperty("App::PropertyString","structure","GDMLFiles", \ - "structure section").structure=sectionDict.get('structure',"") - self.Type = 'GDMLFiles' - obj.Proxy = self - - def execute(self, fp): - '''Do something when doing a recomputation, this method is mandatory''' - pass - - def onChanged(self, fp, prop): - #print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - #if not ('Restore' in fp.State) : - # if not hasattr(fp,'onchange') or not fp.onchange : return - pass - -class GDMLvolume : - def __init__(self,obj) : - obj.Proxy = self - self.Object = obj - -class GDMLconstant(GDMLcommon) : - def __init__(self,obj,name,value) : - super().__init__(obj) - obj.addProperty("App::PropertyString","name",'GDMLconstant','name').name = name - obj.addProperty("App::PropertyString","value",'GDMLconstant','value').value = value - obj.Proxy = self - self.Object = obj - -class GDMLvariable(GDMLcommon) : - def __init__(self,obj,name,value) : - super().__init__(obj) - obj.addProperty("App::PropertyString","name",'GDMLvariable','name').name = name - obj.addProperty("App::PropertyString","value",'GDMLvariable','value').value = value - obj.Proxy = self - self.Object = obj - -class GDMLquantity(GDMLcommon) : - def __init__(self,obj,name,type,unit,value) : - super().__init__(obj) - obj.addProperty("App::PropertyString","name",'GDMLvariable','name').name = name - obj.addProperty("App::PropertyString","type",'GDMLvariable','type').type = type - obj.addProperty("App::PropertyString","unit",'GDMLvariable','unit').unit = unit - obj.addProperty("App::PropertyString","value",'GDMLvariable','value').value = value - obj.Proxy = self - self.Object = obj - - -class GDMLmaterial(GDMLcommon) : - def __init__(self,obj,name,density=1.0,conduct=2.0,expand=3.0,specific=4.0) : - super().__init__(obj) - # Add most properties later - obj.addProperty("App::PropertyString","name",'GDMLmaterial','name').name = name - obj.addProperty("App::PropertyFloat","density","GDMLmaterial", \ - "Density kg/m^3").density = density - obj.addProperty("App::PropertyFloat","conduct","GDMLmaterial", \ - "Thermal Conductivity W/m/K").conduct = conduct - obj.addProperty("App::PropertyFloat","expand","GDMLmaterial", \ - "Expansion Coefficient m/m/K").expand = expand - obj.addProperty("App::PropertyFloat","specific","GDMLmaterial", - "Specific Heat J/kg/K").specific = specific - - obj.Proxy = self - self.Object = obj - -class GDMLfraction(GDMLcommon) : - def __init__(self,obj,ref,n) : - super().__init__(obj) - obj.addProperty("App::PropertyFloat",'n',ref).n = n - obj.Proxy = self - self.Object = obj - -class GDMLcomposite(GDMLcommon) : - def __init__(self,obj,name,n,ref) : - super().__init__(obj) - obj.addProperty("App::PropertyInteger","n",name).n = n - obj.addProperty("App::PropertyString","ref",name).ref = ref - obj.Proxy = self - self.Object = obj - -class GDMLelement(GDMLcommon) : - def __init__(self,obj,name) : - super().__init__(obj) - obj.addProperty("App::PropertyString","name",name).name = name - obj.Proxy = self - self.Object = obj - -class GDMLisotope(GDMLcommon) : - def __init__(self,obj,name,N,Z) : - super().__init__(obj) - obj.addProperty("App::PropertyString","name",name).name = name - obj.addProperty("App::PropertyInteger","N",name).N=N - obj.addProperty("App::PropertyInteger","Z",name).Z=Z - # Name, N and Z are minimum other values are added by import - #obj.addProperty("App::PropertyString","unit",name).unit = unit - #obj.addProperty("App::PropertyFloat","value",name).value = value - obj.Proxy = self - self.Object = obj - -class ViewProviderExtension(GDMLcommon) : - def __init__(self, obj): - super().__init__(obj) - obj.addExtension("Gui::ViewProviderGroupExtensionPython") - obj.Proxy = self - - def getDisplayModes(self,obj): - '''Return a list of display modes.''' - modes=[] - modes.append("Shaded") - modes.append("Wireframe") - return modes - - def updateData(self, fp, prop): - '''If a property of the handled feature has changed we have the chance to handle this here''' - # fp is the handled feature, prop is the name of the property that has changed - #l = fp.getPropertyByName("Length") - #w = fp.getPropertyByName("Width") - #h = fp.getPropertyByName("Height") - #self.scale.scaleFactor.setValue(float(l),float(w),float(h)) - pass - - def getDefaultDisplayMode(self): - '''Return the name of the default display mode. It must be defined in getDisplayModes.''' - return "Shaded" - +class GDMLVertex(GDMLcommon): + def __init__(self, obj, x, y, z, lunit): + super().__init__(obj) + obj.addProperty("App::PropertyFloat", "x", "GDMLVertex", + "x").x = x + obj.addProperty("App::PropertyFloat", "y", "GDMLVertex", + "y").y = y + obj.addProperty("App::PropertyFloat", "z", "GDMLVertex", + "z").z = z + self.Type = 'GDMLVertex' + self.Object = obj + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if not ('Restore' in fp.State) : + # if prop in ['x','y', 'z'] : + # self.execute(fp) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + pass + + def execute(self, fp): + pass + + +class GDMLTriangular(GDMLcommon): + def __init__(self, obj, v1, v2, v3, vtype): + super().__init__(obj) + obj.addProperty("App::PropertyVector", "v1", "Triangular", + "v1").v1 = v1 + obj.addProperty("App::PropertyVector", "v2", "Triangular", + "v1").v2 = v2 + obj.addProperty("App::PropertyVector", "v3", "Triangular", + "v1").v3 = v3 + obj.addProperty("App::PropertyEnumeration", "vtype", "Triangular", + "vtype") + obj.vtype = ["ABSOLUTE", "RELATIVE"] + obj.vtype = ["ABSOLUTE", "RELATIVE"].index(vtype) + self.Type = 'GDMLTriangular' + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + pass + + def execute(self, fp): + pass + + +class GDMLQuadrangular(GDMLcommon): + def __init__(self, obj, v1, v2, v3, v4, vtype): + super().__init__(obj) + obj.addProperty("App::PropertyVector", "v1", "Quadrang", + "v1").v1 = v1 + obj.addProperty("App::PropertyVector", "v2", "Quadrang", + "v2").v2 = v2 + obj.addProperty("App::PropertyVector", "v3", "Quadrang", + "v3").v3 = v3 + obj.addProperty("App::PropertyVector", "v4", "Quadrang", + "v4").v4 = v4 + obj.addProperty("App::PropertyEnumeration", "vtype", + "Quadrang", "vtype") + obj.vtype = ["ABSOLUTE", "RELATIVE"] + obj.vtype = 0 + self.Type = 'GDMLQuadrangular' + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + if 'Restore' in fp.State: + return + + pass + + def execute(self, fp): + pass + + +class GDMLGmshTessellated(GDMLsolid): + + def __init__(self, obj, sourceObj, meshLen, vertex, facets, lunit, + material, colour=None): + super().__init__(obj) + obj.addProperty('App::PropertyInteger', 'facets', 'GDMLGmshTessellated', + 'Facets').facets = len(facets) + obj.setEditorMode('facets', 1) + obj.addProperty('App::PropertyInteger', 'vertex', 'GDMLGmshTessellated', + 'Vertex').vertex = len(vertex) + obj.setEditorMode('vertex', 1) + obj.addProperty('App::PropertyFloat', 'm_maxLength', + 'GDMLGmshTessellated', + 'Max Length').m_maxLength = meshLen + obj.addProperty('App::PropertyFloat', 'm_curveLen', + 'GDMLGmshTessellated', + 'Curve Length').m_curveLen = meshLen + obj.addProperty('App::PropertyFloat', 'm_pointLen', + 'GDMLGmshTessellated', + 'Point Length').m_pointLen = meshLen + obj.addProperty("App::PropertyEnumeration", "lunit", + "GDMLGmshTessellated", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLTessellated", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + self.Type = 'GDMLGmshTessellated' + self.SourceObj = sourceObj + self.Vertex = vertex + self.Facets = facets + self.Object = obj + self.colour = colour + obj.Proxy = self + + def updateParams(self, vertex, facets): + self.Vertex = vertex + self.Facets = facets + self.facets = len(facets) + self.vertex = len(vertex) + print(f"Vertex : {self.vertex} Facets : {self.facets}") + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['editable']: + if fp.editable is True: + self.addProperties() + + if prop in ['m_Remesh']: + if fp.m_Remesh is True: + self.reMesh(fp) + self.execute(fp) + + def execute(self, fp): # Here for remesh? + self.createGeometry(fp) + + def addProperties(self): + print('Add Properties') + + def reMesh(self, fp): + from .GmshUtils import initialize, meshObj, getVertex, getFacets + + initialize() + meshObj(fp.Proxy.SourceObj, 2, True, fp.Proxy.Object) + facets = getFacets() + vertex = getVertex() + fp.Proxy.Vertex = vertex + self.Object.vertex = len(vertex) + fp.Proxy.Facets = facets + self.Object.facets = len(facets) + FreeCADGui.updateGui() + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + FCfaces = [] + i = 0 + for f in self.Facets: + if len(f) == 3: + FCfaces.append(GDMLShared.triangle(mul*self.Vertex[f[0]], + mul*self.Vertex[f[1]], + mul*self.Vertex[f[2]])) + else: # len should then be 4 + FCfaces.append(GDMLShared.quad(mul*self.Vertex[f[0]], + mul*self.Vertex[f[1]], + mul*self.Vertex[f[2]], + mul*self.Vertex[f[3]])) + shell = Part.makeShell(FCfaces) + if shell.isValid is False: + FreeCAD.Console.PrintWarning('Not a valid Shell/n') + + try: + solid = Part.Solid(shell) + except: + # make compound rather than just barf + # visually able to view at least + FreeCAD.Console.PrintWarning('Problem making Solid/n') + solid = Part.makeCompound(FCfaces) + # if solid.Volume < 0: + # solid.reverse() + # print(dir(solid)) + # bbox = solid.BoundBox + # base = FreeCAD.Vector(-(bbox.XMin+bbox.XMax)/2, \ + # -(bbox.YMin+bbox.YMax)/2 \ + # -(bbox.ZMin+bbox.ZMax)/2) + # print(base) + + # base = FreeCAD.Vector(0,0,0) + # fp.Shape = translate(solid,base) + fp.Shape = solid + fp.Placement = currPlacement + + +class GDMLTessellated(GDMLsolid): + + def __init__(self, obj, vertex, facets, flag, lunit, material, + colour=None): + super().__init__(obj) + # ######################################## + # if flag == True - facets is Mesh.Facets - with Normals + # if flag == False - facets is Faces i.e. from import GDMLTessellated + # ######################################## + obj.addProperty('App::PropertyInteger', 'facets', 'GDMLTessellated', + 'Facets').facets = len(facets) + obj.setEditorMode('facets', 1) + obj.addProperty('App::PropertyInteger', 'vertex', 'GDMLTessellated', + 'Vertex').vertex = len(vertex) + obj.setEditorMode('vertex', 1) + obj.addProperty("App::PropertyEnumeration", "lunit", + "GDMLTessellated", "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLTessellated", "Material") + setMaterial(obj, material) + self.updateParams(vertex, facets, flag) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + self.Type = 'GDMLTessellated' + self.colour = colour + obj.Proxy = self + + def updateParams(self, vertex, facets, flag): + # print('Update Params & Shape') + self.pshape = self.createShape(vertex, facets, flag) + # print(f"Pshape vertex {len(self.pshape.Vertexes)}") + self.facets = len(facets) + self.vertex = len(vertex) + # print(f"Vertex : {self.vertex} Facets : {self.facets}") + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['editable']: + if fp.editable is True: + self.addProperties() + + def addProperties(self): + print('Add Properties') + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + if hasattr(self, 'pshape'): + # print('Update Shape') + fp.Shape = self.pshape + if hasattr(fp, 'pshape'): + fp.pshape = self.pshape + fp.vertex = self.vertex + fp.facets = self.facets + + def createShape(self, vertex, facets, flag): + # Viewing outside of face vertex must be counter clockwise + # if flag == True - facets is Mesh.Facets + # if flag == False - factes is Faces i.e. from import GDMLTessellated + # mul = GDMLShared.getMult(fp) + mul = GDMLShared.getMult(self) + # print('Create Shape') + FCfaces = [] + for f in facets: + # print('Facet') + # print(f) + if flag is True: + FCfaces.append(GDMLShared.facet(f)) + else: + if len(f) == 3: + FCfaces.append(GDMLShared.triangle( + mul*vertex[f[0]], + 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]])) + shell = Part.makeShell(FCfaces) + if shell.isValid is False: + FreeCAD.Console.PrintWarning('Not a valid Shell/n') + + # shell.check() + # solid=Part.Solid(shell).removeSplitter() + try: + solid = Part.Solid(shell) + except: + # make compound rather than just barf + # visually able to view at least + FreeCAD.Console.PrintWarning('Problem making Solid/n') + solid = Part.makeCompound(FCfaces) + + return solid + + +class GDMLTetra(GDMLsolid): # 4 point Tetrahedron + + def __init__(self, obj, v1, v2, v3, v4, lunit, material, colour=None): + super().__init__(obj) + obj.addProperty("App::PropertyVector", "v1", "GDMLTra", + "v1").v1 = v1 + obj.addProperty("App::PropertyVector", "v2", "GDMLTra", + "v2").v2 = v2 + obj.addProperty("App::PropertyVector", "v3", "GDMLTra", + "v3").v3 = v3 + obj.addProperty("App::PropertyVector", "v4", "GDMLTra", + "v4").v4 = v4 + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTra", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLTra", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + self.Type = 'GDMLTetra' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['v1', 'v2', 'v3', 'v4', 'lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + pt1 = mul * fp.v1 + pt2 = mul * fp.v2 + pt3 = mul * fp.v3 + pt4 = mul * fp.v4 + face1 = Part.Face(Part.makePolygon([pt1, pt2, pt3, pt1])) + face2 = Part.Face(Part.makePolygon([pt1, pt2, pt4, pt1])) + 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])) + fp.Placement = currPlacement + + +class GDMLTetrahedron(GDMLsolid): + + ''' Does not exist as a GDML solid, but export as an Assembly of G4Tet ''' + ''' See paper Poole at al - Fast Tessellated solid navigation in GEANT4 ''' + + def __init__(self, obj, tetra, lunit, material, colour=None): + super().__init__(obj) + obj.addProperty('App::PropertyInteger', 'tetra', 'GDMLTetrahedron', + 'Tetra').tetra = len(tetra) + obj.setEditorMode('tetra', 1) + obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTetrahedron", + "lunit") + setLengthQuantity(obj, lunit) + obj.addProperty("App::PropertyEnumeration", "material", + "GDMLTetrahedron", "Material") + setMaterial(obj, material) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + # Suppress Placement - position & Rotation via parent App::Part + # this makes Placement via Phyvol easier and allows copies etc + # obj.addExtension('App::GroupExtensionPython') + self.Tetra = tetra + self.Object = obj + self.Type = 'GDMLTetrahedron' + self.colour = colour + obj.Proxy = self + + def onChanged(self, fp, prop): + '''Do something when a property has changed''' + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if 'Restore' in fp.State: + return + + if prop in ['material']: + if FreeCAD.GuiUp: + if hasattr(self, 'colour'): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + + if prop in ['lunit']: + self.createGeometry(fp) + + # def execute(self, fp): in GDMLsolid + + def makeTetra(self, pt1, pt2, pt3, pt4): + face1 = Part.Face(Part.makePolygon([pt1, pt2, pt3, pt1])) + face2 = Part.Face(Part.makePolygon([pt1, pt2, pt4, pt1])) + face3 = Part.Face(Part.makePolygon([pt4, pt2, pt3, pt4])) + face4 = Part.Face(Part.makePolygon([pt1, pt3, pt4, pt1])) + return(Part.makeShell([face1, face2, face3, face4])) + + def createGeometry(self, fp): + currPlacement = fp.Placement + print("Tetrahedron") + mul = GDMLShared.getMult(fp) + print(len(self.Tetra)) + tetraShells = [] + for t in self.Tetra: + pt1 = mul * t[0] + pt2 = mul * t[1] + pt3 = mul * t[2] + pt4 = mul * t[3] + tetraShells.append(self.makeTetra(pt1, pt2, pt3, pt4)) + fp.Shape = Part.makeCompound(tetraShells) + fp.Placement = currPlacement + + +class GDMLFiles(GDMLcommon): + def __init__(self, obj, FilesEntity, sectionDict): + super().__init__(obj) + '''Add some custom properties to our Cone feature''' + GDMLShared.trace("GDML Files") + GDMLShared.trace(FilesEntity) + obj.addProperty("App::PropertyBool", "active", "GDMLFiles", + "split option").active = FilesEntity + obj.addProperty("App::PropertyString", "define", "GDMLFiles", + "define section").define = sectionDict.get('define', "") + obj.addProperty("App::PropertyString", "materials", "GDMLFiles", + "materials section").materials = sectionDict.get('materials', "") + obj.addProperty("App::PropertyString", "solids", "GDMLFiles", + "solids section").solids = sectionDict.get('solids', "") + obj.addProperty("App::PropertyString", "structure", "GDMLFiles", + "structure section").structure = sectionDict.get('structure', "") + self.Type = 'GDMLFiles' + obj.Proxy = self + + def execute(self, fp): + '''Do something when doing a recomputation, this method is mandatory''' + pass + + def onChanged(self, fp, prop): + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + # if not ('Restore' in fp.State) : + # if not hasattr(fp,'onchange') or not fp.onchange : return + pass + + +class GDMLvolume: + def __init__(self, obj): + obj.Proxy = self + self.Object = obj + + +class GDMLconstant(GDMLcommon): + def __init__(self, obj, name, value): + super().__init__(obj) + obj.addProperty("App::PropertyString", "name", 'GDMLconstant', + 'name').name = name + obj.addProperty("App::PropertyString", "value", 'GDMLconstant', + 'value').value = value + obj.Proxy = self + self.Object = obj + + +class GDMLvariable(GDMLcommon): + def __init__(self, obj, name, value): + super().__init__(obj) + obj.addProperty("App::PropertyString", "name", 'GDMLvariable', + 'name').name = name + obj.addProperty("App::PropertyString", "value", 'GDMLvariable', + 'value').value = value + obj.Proxy = self + self.Object = obj + + +class GDMLquantity(GDMLcommon): + def __init__(self, obj, name, type, unit, value): + super().__init__(obj) + obj.addProperty("App::PropertyString", "name", 'GDMLvariable', + 'name').name = name + obj.addProperty("App::PropertyString", "type", 'GDMLvariable', + 'type').type = type + obj.addProperty("App::PropertyString", "unit", 'GDMLvariable', + 'unit').unit = unit + obj.addProperty("App::PropertyString", "value", 'GDMLvariable', + 'value').value = value + obj.Proxy = self + self.Object = obj + + +class GDMLmaterial(GDMLcommon): + def __init__(self, obj, name, density=1.0, conduct=2.0, expand=3.0, + specific=4.0): + super().__init__(obj) + # Add most properties later + obj.addProperty("App::PropertyString", "name", 'GDMLmaterial', + 'name').name = name + obj.addProperty("App::PropertyFloat", "density", "GDMLmaterial", + "Density kg/m^3").density = density + obj.addProperty("App::PropertyFloat", "conduct", "GDMLmaterial", + "Thermal Conductivity W/m/K").conduct = conduct + obj.addProperty("App::PropertyFloat", "expand", "GDMLmaterial", + "Expansion Coefficient m/m/K").expand = expand + obj.addProperty("App::PropertyFloat", "specific", "GDMLmaterial", + "Specific Heat J/kg/K").specific = specific + + obj.Proxy = self + self.Object = obj + + +class GDMLfraction(GDMLcommon): + def __init__(self, obj, ref, n): + super().__init__(obj) + obj.addProperty("App::PropertyFloat", 'n', ref).n = n + obj.Proxy = self + self.Object = obj + + +class GDMLcomposite(GDMLcommon): + def __init__(self, obj, name, n, ref): + super().__init__(obj) + obj.addProperty("App::PropertyInteger", "n", name).n = n + obj.addProperty("App::PropertyString", "ref", name).ref = ref + obj.Proxy = self + self.Object = obj + + +class GDMLelement(GDMLcommon): + def __init__(self, obj, name): + super().__init__(obj) + obj.addProperty("App::PropertyString", "name", name).name = name + obj.Proxy = self + self.Object = obj + + +class GDMLisotope(GDMLcommon): + def __init__(self, obj, name, N, Z): + super().__init__(obj) + obj.addProperty("App::PropertyString", "name", name).name = name + obj.addProperty("App::PropertyInteger", "N", name).N = N + obj.addProperty("App::PropertyInteger", "Z", name).Z = Z + # Name, N and Z are minimum other values are added by import + # obj.addProperty("App::PropertyString","unit",name).unit = unit + # obj.addProperty("App::PropertyFloat","value",name).value = value + obj.Proxy = self + self.Object = obj + + +class ViewProviderExtension(GDMLcommon): + def __init__(self, obj): + super().__init__(obj) + obj.addExtension("Gui::ViewProviderGroupExtensionPython") + obj.Proxy = self + + def getDisplayModes(self, obj): + '''Return a list of display modes.''' + modes = [] + modes.append("Shaded") + modes.append("Wireframe") + return modes + + def updateData(self, fp, prop): + '''If a property of the handled feature has changed we have the chance to handle this here''' + # fp is the handled feature, prop is the name of the property that has changed + # l = fp.getPropertyByName("Length") + # w = fp.getPropertyByName("Width") + # h = fp.getPropertyByName("Height") + # self.scale.scaleFactor.setValue(float(l),float(w),float(h)) + pass + + def getDefaultDisplayMode(self): + '''Return the name of the default display mode. It must be defined in getDisplayModes.''' + return "Shaded" + # use general ViewProvider if poss class ViewProvider(GDMLcommon): - def __init__(self, obj): - super().__init__(obj) - '''Set this object to the proxy object of the actual view provider''' - obj.Proxy = self + def __init__(self, obj): + super().__init__(obj) + '''Set this object to the proxy object of the actual view provider''' + obj.Proxy = self + + def updateData(self, fp, prop): + '''If a property of the handled feature has changed we have the chance to handle this here''' + # print("updateData") + # fp is the handled feature, prop is the name of the property that has changed + # l = fp.getPropertyByName("Length") + # w = fp.getPropertyByName("Width") + # h = fp.getPropertyByName("Height") + # self.scale.scaleFactor.setValue(float(l),float(w),float(h)) + pass + + def getDisplayModes(self, obj): + '''Return a list of display modes.''' + # print("getDisplayModes") + modes = [] + modes.append("Shaded") + modes.append("Wireframe") + return modes - def updateData(self, fp, prop): - '''If a property of the handled feature has changed we have the chance to handle this here''' - #print("updateData") - # fp is the handled feature, prop is the name of the property that has changed - #l = fp.getPropertyByName("Length") - #w = fp.getPropertyByName("Width") - #h = fp.getPropertyByName("Height") - #self.scale.scaleFactor.setValue(float(l),float(w),float(h)) - pass + def getDefaultDisplayMode(self): + '''Return the name of the default display mode. It must be defined in getDisplayModes.''' + return "Shaded" - def getDisplayModes(self,obj): - '''Return a list of display modes.''' - #print("getDisplayModes") - modes=[] - modes.append("Shaded") - modes.append("Wireframe") - return modes - - def getDefaultDisplayMode(self): - '''Return the name of the default display mode. It must be defined in getDisplayModes.''' - return "Shaded" - - def setDisplayMode(self,mode): - '''Map the display mode defined in attach with those defined in getDisplayModes.\ + def setDisplayMode(self,mode): + '''Map the display mode defined in attach with those defined in getDisplayModes.\ Since they have the same names nothing needs to be done. This method is optional''' - return mode + return mode - def onChanged(self, vp, prop): - '''Here we can do something when a single property got changed''' - #if hasattr(vp,'Name') : - # print("View Provider : "+vp.Name+" State : "+str(vp.State)+" prop : "+prop) - #else : - # print("View Provider : prop : "+prop) - #GDMLShared.trace("Change property: " + str(prop) + "\n") - #if prop == "Color": - # c = vp.getPropertyByName("Color") -# self.color.rgb.setValue(c[0],c[1],c[2]) - - def getIcon(self): - '''Return the icon in XPM format which will appear in the tree view. This method is\ + def onChanged(self, vp, prop): + '''Here we can do something when a single property got changed''' + # if hasattr(vp,'Name') : + # print("View Provider : "+vp.Name+" State : "+str(vp.State)+" prop : "+prop) + # else : + # print("View Provider : prop : "+prop) + # GDMLShared.trace("Change property: " + str(prop) + "\n") + # if prop == "Color": + # c = vp.getPropertyByName("Color") + # self.color.rgb.setValue(c[0],c[1],c[2]) + + def getIcon(self): + '''Return the icon in XPM format which will appear in the tree view. This method is\ optional and if not defined a default icon is shown.''' - return """ + return """ /* XPM */ static const char * ViewProviderBox_xpm[] = { "16 16 6 1", @@ -3550,36 +3682,40 @@ def getIcon(self): " ##$$$$$# ", " ####### "}; """ - def __getstate__(self): - '''When saving the document this object gets stored using Python's json module.\ + + def __getstate__(self): + '''When saving the document this object gets stored using Python's json module.\ Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ to return a tuple of all serializable objects or None.''' - return None + return None - def __setstate__(self,state): - '''When restoring the serialized object from document we have the chance to set some internals here.\ + def __setstate__(self, state): + '''When restoring the serialized object from document we have the chance to set some internals here.\ Since no data were serialized nothing needs to be done here.''' - return None + return None # # Need to add variables to these functions or delete? # def makeBox(): - a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","GDMLBox") + a = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "GDMLBox") GDMLBox(a) ViewProvider(a.ViewObject) + def makeCone(): - a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","GDMLCone") + a = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "GDMLCone") GDMLCone(a) ViewProvider(a.ViewObject) + def makecSphere(): - a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","GDMLSphere") + a = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "GDMLSphere") GDMLSphere(a) ViewProvider(a.ViewObject) + def makeTube(): - a=FreeCAD.ActiveDocument.addObject("App::FeaturePython","GDMLTube") + a = FreeCAD.ActiveDocument.addObject("App::FeaturePython", "GDMLTube") GDMLTube(a) ViewProvider(a.ViewObject) diff --git a/freecad/gdml/GDMLShared.py b/freecad/gdml/GDMLShared.py index 0fd5ba8ac..b6ddcab27 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,198 @@ 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 = 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 +425,163 @@ 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 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): # 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..ddc488960 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'}) + 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'}) 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) From 701fbb27f70a9d75dddfbb50b3acf7f4a74b4ebd Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 12 Feb 2022 09:34:43 +0000 Subject: [PATCH 02/46] Use Label for material Name --- freecad/gdml/GDMLMaterials.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freecad/gdml/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index 6d393e67e..fb4ec0cd8 100644 --- a/freecad/gdml/GDMLMaterials.py +++ b/freecad/gdml/GDMLMaterials.py @@ -106,13 +106,13 @@ def newGetGroupedMaterials(): refreshG4Materials(doc) docG4Materials = doc.G4Materials for g in docG4Materials.Group: - # print(f'g : {g.Name}') + # print(f'g : {g.Label}') for s in g.Group: - # print(f's : {s.Name}') + # print(f's : {s.Label}') if g.Name in GroupedMaterials: - GroupedMaterials[g.Name].append(s.Name) + GroupedMaterials[g.Label].append(s.Label) else: - GroupedMaterials[g.Name] = [s.Name] + GroupedMaterials[g.Label] = [s.Label] matList = [] docMaterials = doc.Materials if docMaterials is not None: From e1ce58730a3c72db6d0d8c4bd23f95093537a89a Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 12 Feb 2022 10:47:10 +0000 Subject: [PATCH 03/46] Fix no materials --- freecad/gdml/.gitignore | 1 + freecad/gdml/GDMLMaterials.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) 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/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index fb4ec0cd8..2d6cb88ed 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,10 +96,14 @@ 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() + if not hasattr(doc, 'Materials') or not hasattr(doc, 'G4Materials'): + processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) docG4Materials = doc.G4Materials if not hasattr(docG4Materials, 'version'): refreshG4Materials(doc) @@ -126,6 +129,7 @@ def newGetGroupedMaterials(): return GroupedMaterials + def getGroupedMaterials(): print('getGroupedMaterials') from .GDMLObjects import GroupedMaterials @@ -154,7 +158,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: From 6ae8506218a0e61b84c384854426c02c0c1f6546 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 12 Feb 2022 19:05:44 +0000 Subject: [PATCH 04/46] Add creation of GDML object from extruded sketch - thanks to Munther Hindi --- freecad/gdml/GDMLShared.py | 2 +- freecad/gdml/init_gui.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/freecad/gdml/GDMLShared.py b/freecad/gdml/GDMLShared.py index b6ddcab27..23f3d7417 100644 --- a/freecad/gdml/GDMLShared.py +++ b/freecad/gdml/GDMLShared.py @@ -159,7 +159,7 @@ def processVariables(doc): except: globals()[name] = value # print('Value String : '+value) - variableObj = variablesGrp.newObject("App::DocumentObjectGroupPython", + variableObj = variablesGrp.newObject("App::DocumentObjectGroupPython", name) GDMLvariable(variableObj, name, value) # print("Globals") diff --git a/freecad/gdml/init_gui.py b/freecad/gdml/init_gui.py index 28d98c7ac..4e5d0ef32 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 @@ -82,22 +83,25 @@ def QT_TRANSLATE_NOOP(scope, text): 'TorusCommand','TrapCommand','TubeCommand', \ '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'] From 99296289387fd7f79e6d79586b1b3248f303eebf Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 12 Feb 2022 20:02:07 +0000 Subject: [PATCH 05/46] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c151e86e9..0b4e71175 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ will create * 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)
From 6f7e90cb8a01c63f6156126b339a92e366b7513e Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 17 Feb 2022 07:18:03 +0000 Subject: [PATCH 06/46] Fix materials add Extrude as an icon --- freecad/gdml/GDMLCommands.py | 2 ++ freecad/gdml/GDMLMaterials.py | 49 ++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/freecad/gdml/GDMLCommands.py b/freecad/gdml/GDMLCommands.py index bfaa16aab..581a1a28e 100644 --- a/freecad/gdml/GDMLCommands.py +++ b/freecad/gdml/GDMLCommands.py @@ -196,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) diff --git a/freecad/gdml/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index 2d6cb88ed..8c527e2b8 100644 --- a/freecad/gdml/GDMLMaterials.py +++ b/freecad/gdml/GDMLMaterials.py @@ -101,31 +101,32 @@ def newGetGroupedMaterials(): from .importGDML import joinDir, processGEANT4 print('New getGroupedMaterials') from .GDMLObjects import GroupedMaterials - doc = FreeCAD.activeDocument() - if not hasattr(doc, 'Materials') or not hasattr(doc, 'G4Materials'): - processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) - docG4Materials = doc.G4Materials - if not hasattr(docG4Materials, 'version'): - refreshG4Materials(doc) - docG4Materials = doc.G4Materials - 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] - 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'): + processGEANT4(doc, joinDir("Resources/Geant4Materials.xml")) + docG4Materials = doc.G4Materials + if not hasattr(docG4Materials, 'version'): + refreshG4Materials(doc) + docG4Materials = doc.G4Materials + 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] + 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 From df3a68df13963ad3ae0377af6921118f10261c5a Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 17 Feb 2022 20:28:28 +0000 Subject: [PATCH 07/46] Add sketch and extrude to commands --- freecad/gdml/init_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freecad/gdml/init_gui.py b/freecad/gdml/init_gui.py index 4e5d0ef32..f73ef1c82 100644 --- a/freecad/gdml/init_gui.py +++ b/freecad/gdml/init_gui.py @@ -81,6 +81,7 @@ def QT_TRANSLATE_NOOP(scope, text): 'BoxCommand','ConeCommand','ElTubeCommand', \ 'EllipsoidCommand','SphereCommand', \ 'TorusCommand','TrapCommand','TubeCommand', \ + 'Sketcher_NewSketch','Part_Extrude', \ 'BooleanCutCommand','BooleanIntersectionCommand', \ 'BooleanUnionCommand', \ 'TessellateCommand','TessellateGmshCommand', \ From cafafa38f1950e7258c315de22d8d526967c8af2 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 1 Mar 2022 12:06:25 +0000 Subject: [PATCH 08/46] Stack --- freecad/gdml/GDMLObjects.py | 15 +++++++-- freecad/gdml/importGDML.py | 62 ++++++++++++++++++++++++++++--------- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 95cdfe294..af7d3f35f 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -562,7 +562,7 @@ def onChanged(self, fp, prop): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['x', 'y', 'z', 'lunit']: + if prop in ['x', 'y', 'z', 'lunit', 'scale']: self.createGeometry(fp) # execute(self, fp): in GDMLsolid @@ -583,6 +583,11 @@ def createGeometry(self, fp): box = Part.makeBox(x, y, z) base = FreeCAD.Vector(-x/2, -y/2, -z/2) fp.Shape = translate(box, base) + if hasattr(fp,'scale'): + print('Rescale') + mat = FreeCAD.Matrix() + mat.scale(fp.scale) + fp.Shape = fp.Shape.transformGeometry(mat) fp.Placement = currPlacement def OnDocumentRestored(self, obj): @@ -733,7 +738,7 @@ def onChanged(self, fp, prop): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit']: + if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit', 'scale']: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -784,6 +789,12 @@ def createGeometry(self, fp): fp.Shape = cone2.cut(box) else: fp.Shape = cone2 + if hasattr(fp,'scale'): + print('Update Scale') + mat = FreeCAD.Matrix() + mat.scale(fp.scale) + fp.Shape = fp.Shape.transformGeometry(mat) + fp.Placement = currPlacement diff --git a/freecad/gdml/importGDML.py b/freecad/gdml/importGDML.py index c61a26368..305db0993 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 @@ -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')) From 1746c0b41101032935013a3910d4b95966f208da Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 1 Mar 2022 19:28:42 +0000 Subject: [PATCH 09/46] Fix Rotation on import --- freecad/gdml/GDMLShared.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freecad/gdml/GDMLShared.py b/freecad/gdml/GDMLShared.py index 23f3d7417..7846d8138 100644 --- a/freecad/gdml/GDMLShared.py +++ b/freecad/gdml/GDMLShared.py @@ -369,7 +369,8 @@ def processPlacement(base, rot): rotY = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), -y) rotZ = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), -z) - rot = rotZ*rotY*rotX + 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 From 59da9f0913a9d18c11fab760a6df37de6dcd3e48 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 1 Mar 2022 20:12:00 +0000 Subject: [PATCH 10/46] Add scaled support to all GDMLObjects --- freecad/gdml/GDMLObjects.py | 127 +++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index af7d3f35f..e885ef5e3 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -363,6 +363,12 @@ def __init__(self, obj): def getMaterial(self): return obj.material + def scale(self,fp): + print('Rescale') + mat = FreeCAD.Matrix() + mat.scale(fp.scale) + fp.Shape = fp.Shape.transformGeometry(mat) + def execute(self, fp): self.createGeometry(fp) @@ -476,6 +482,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 @@ -562,9 +572,13 @@ def onChanged(self, fp, prop): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['x', 'y', 'z', 'lunit', 'scale']: + if prop in ['x', 'y', 'z', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -649,6 +663,10 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -738,9 +756,13 @@ def onChanged(self, fp, prop): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit', 'scale']: + if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -841,6 +863,10 @@ def onChanged(self, fp, prop): if prop in ['ax', 'by', 'cz', 'zcut1', 'zcut2', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -932,6 +958,11 @@ def onChanged(self, fp, prop): if prop in ['dx', 'dy', 'dz', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -986,6 +1017,10 @@ def onChanged(self, fp, prop): # print(dir(fp)) self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1046,6 +1081,10 @@ 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) + super().scale(fp) + # def execute(self, fp): in GDMLsolid @@ -1166,6 +1205,10 @@ 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) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1278,6 +1321,10 @@ def onChanged(self, fp, prop): if prop in ['rlo', 'rhi', 'z', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1362,6 +1409,10 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1477,6 +1528,10 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1588,6 +1643,10 @@ def onChanged(self, fp, prop): # print(f'Change Prop : {prop}') self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1675,6 +1734,10 @@ def onChanged(self, fp, prop): if prop in ['x', 'y', 'z', 'PhiTwist', 'lunit', 'aunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1775,6 +1838,10 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1894,6 +1961,10 @@ 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) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -1998,6 +2069,10 @@ def onChanged(self, fp, prop): 'phi', 'lunit', 'aunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2089,6 +2164,10 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def layerPoints(self, polyList, sf, xOffset, yOffset, zPosition): @@ -2339,6 +2418,10 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2441,6 +2524,10 @@ def onChanged(self, fp, prop): if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2521,6 +2608,10 @@ def onChanged(self, fp, prop): 'deltatheta', 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2662,6 +2753,10 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self,fp): @@ -2779,6 +2874,10 @@ def onChanged(self, fp, prop): if prop in ['z', 'x1', 'x2', 'y1', 'y2', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2864,6 +2963,10 @@ def onChanged(self, fp, prop): 'aunit', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -2957,6 +3060,10 @@ def onChanged(self, fp, prop): 'highX', 'highY', 'highZ', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def cutShapeWithPlane(self, shape, plane, depth): @@ -3164,6 +3271,10 @@ def onChanged(self, fp, prop): self.reMesh(fp) self.execute(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + def execute(self, fp): # Here for remesh? self.createGeometry(fp) @@ -3278,6 +3389,10 @@ def onChanged(self, fp, prop): if fp.editable is True: self.addProperties() + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + def addProperties(self): print('Add Properties') @@ -3375,6 +3490,10 @@ def onChanged(self, fp, prop): if prop in ['v1', 'v2', 'v3', 'v4', 'lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): @@ -3434,6 +3553,10 @@ def onChanged(self, fp, prop): if prop in ['lunit']: self.createGeometry(fp) + if prop in ['scale']: + self.createGeometry(fp) + super().scale(fp) + # def execute(self, fp): in GDMLsolid def makeTetra(self, pt1, pt2, pt3, pt4): From 0a3fd2ac07778e91ae138e4077fcc699ad54503d Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Wed, 2 Mar 2022 18:03:37 +0000 Subject: [PATCH 11/46] Fix Tessellate Placements and Build Materials list --- freecad/gdml/GDMLCommands.py | 15 +++++++++------ freecad/gdml/GDMLObjects.py | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/freecad/gdml/GDMLCommands.py b/freecad/gdml/GDMLCommands.py index 581a1a28e..4fd1d92e8 100644 --- a/freecad/gdml/GDMLCommands.py +++ b/freecad/gdml/GDMLCommands.py @@ -1236,12 +1236,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/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 95cdfe294..d58fb7354 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,7 +366,7 @@ 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 From 62d768b62dabcbf80c52e411903c2093fdbee22a Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 3 Mar 2022 14:48:41 +0000 Subject: [PATCH 12/46] Gmsh --- freecad/gdml/GDMLObjects.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index d58fb7354..9b9d265c0 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -3134,7 +3134,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) From 0477a4d52a811b445d8d07a03cf891c5c3ccdac9 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 3 Mar 2022 15:22:53 +0000 Subject: [PATCH 13/46] Fix tess Quad --- freecad/gdml/exportGDML.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freecad/gdml/exportGDML.py b/freecad/gdml/exportGDML.py index ddc488960..437f59bf8 100644 --- a/freecad/gdml/exportGDML.py +++ b/freecad/gdml/exportGDML.py @@ -1085,10 +1085,10 @@ def processGDMLTessellatedObject(obj): for f in obj.Shape.Faces: # print(f'Normal at : {n} dot {dot} {clockWise}') vertexes = f.OuterWire.OrderedVertexes + i0 = vertexHashcodeDict[vertexes[0].hashCode()] + i1 = vertexHashcodeDict[vertexes[1].hashCode()] + i2 = vertexHashcodeDict[vertexes[2].hashCode()] 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), From 0ee6c4c39801b97e824833b1757704105e3001dc Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 5 Mar 2022 07:50:37 +0000 Subject: [PATCH 14/46] stack --- freecad/gdml/GDMLObjects.py | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index e885ef5e3..4e0c66d82 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -364,7 +364,7 @@ def getMaterial(self): return obj.material def scale(self,fp): - print('Rescale') + print(f'Rescale : {fp.scale}') mat = FreeCAD.Matrix() mat.scale(fp.scale) fp.Shape = fp.Shape.transformGeometry(mat) @@ -561,7 +561,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 @@ -577,13 +577,12 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(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 @@ -597,12 +596,9 @@ def createGeometry(self, fp): box = Part.makeBox(x, y, z) base = FreeCAD.Vector(-x/2, -y/2, -z/2) fp.Shape = translate(box, base) - if hasattr(fp,'scale'): - print('Rescale') - mat = FreeCAD.Matrix() - mat.scale(fp.scale) - fp.Shape = fp.Shape.transformGeometry(mat) fp.Placement = currPlacement + if hasattr(fp,'scale'): + super().scale(fp) def OnDocumentRestored(self, obj): print('Doc Restored') @@ -811,12 +807,6 @@ def createGeometry(self, fp): fp.Shape = cone2.cut(box) else: fp.Shape = cone2 - if hasattr(fp,'scale'): - print('Update Scale') - mat = FreeCAD.Matrix() - mat.scale(fp.scale) - fp.Shape = fp.Shape.transformGeometry(mat) - fp.Placement = currPlacement From e7e37d6eef78b40eaaed2b96981f4b06b68523f5 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sun, 6 Mar 2022 12:58:18 +0000 Subject: [PATCH 15/46] Scaled Solid Support --- freecad/gdml/GDMLObjects.py | 62 +++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 4e0c66d82..428bb54fb 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -535,6 +535,7 @@ def createGeometry(self, fp): faceYminA, faceYminB, faceYmaxA, faceYmaxB, faceZmin, faceZmax])) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -597,8 +598,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) + if hasattr(fp,'scale'): super().scale(fp) def OnDocumentRestored(self, obj): print('Doc Restored') @@ -661,7 +661,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -712,6 +711,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 @@ -757,7 +757,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -807,6 +806,7 @@ def createGeometry(self, fp): fp.Shape = cone2.cut(box) else: fp.Shape = cone2 + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -855,7 +855,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -906,6 +905,7 @@ def createGeometry(self, fp): base = FreeCAD.Vector(0, 0, 0) fp.Shape = translate(shape, base) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -950,8 +950,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) - # def execute(self, fp): in GDMLsolid @@ -969,6 +967,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 @@ -1009,7 +1008,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1020,6 +1018,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 @@ -1073,8 +1072,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) - # def execute(self, fp): in GDMLsolid @@ -1143,6 +1140,7 @@ def createGeometry(self, fp): # center = (v7 - v1)/2 fp.Shape = translate(solid, -center) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1197,7 +1195,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1266,7 +1263,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 @@ -1313,7 +1310,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1350,7 +1346,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 @@ -1401,7 +1397,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1470,6 +1465,7 @@ def createGeometry(self, fp): fp.Shape = newShape else: fp.Shape = shape + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1520,7 +1516,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1583,6 +1578,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 @@ -1635,7 +1631,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1661,6 +1656,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 @@ -1726,7 +1722,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1762,6 +1757,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): @@ -1830,7 +1826,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1887,6 +1882,7 @@ def createGeometry(self, fp): loft = Part.makeLoft(slices, True, False) fp.Shape = loft + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -1953,7 +1949,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1992,6 +1987,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): @@ -2061,7 +2057,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2116,6 +2111,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): @@ -2156,7 +2152,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2256,6 +2251,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 @@ -2410,7 +2406,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2466,6 +2461,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 @@ -2516,7 +2512,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2543,6 +2538,7 @@ def createGeometry(self, fp): solid = Part.makeSolid(surf) fp.Shape = solid + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2600,7 +2596,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2684,6 +2679,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 @@ -2745,7 +2741,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2819,6 +2814,7 @@ def createGeometry(self,fp): center = (topCenter+botCenter)/2 fp.Shape = translate(solid, -center) + if hasattr(fp,'scale'): super().scale(fp) fp.Placement = currPlacement @@ -2866,7 +2862,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2902,6 +2897,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 @@ -2955,7 +2951,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -2982,6 +2977,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 @@ -3052,7 +3048,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -3096,6 +3091,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): @@ -3263,7 +3259,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) def execute(self, fp): # Here for remesh? self.createGeometry(fp) @@ -3324,6 +3319,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 @@ -3381,7 +3377,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) def addProperties(self): print('Add Properties') @@ -3396,6 +3391,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 @@ -3482,7 +3478,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -3498,6 +3493,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 @@ -3545,7 +3541,6 @@ def onChanged(self, fp, prop): if prop in ['scale']: self.createGeometry(fp) - super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -3569,6 +3564,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 From 393c92b01c52bf54b93ad9d066614a357b0d4b1a Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sun, 6 Mar 2022 15:39:35 +0000 Subject: [PATCH 16/46] Add package.xml --- freecad/gdml/package.xml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 freecad/gdml/package.xml diff --git a/freecad/gdml/package.xml b/freecad/gdml/package.xml new file mode 100644 index 000000000..c191d526b --- /dev/null +++ b/freecad/gdml/package.xml @@ -0,0 +1,26 @@ + + + GDML workbench + An external workbench for creating GDML models for Geant4 and Root> + 1.8 Beta + Keith Sloan + LGPL-2.1 + https://github.com/KeithSloan/GDML.git + https://github.com/KeithSloan/GDML.git + https://github.com/KeithSloan/GDML/wiki + + + + GDML workbench + GDMLWorkbench + A workbench for creating GDML models for Geant4 & ROOT. + 1.8 Beta + ./ + freecad/gdml/Resources/icons/GDMLWorkbench.svg + 0.19.3 + lxml + gmsh + + + + From af0835784bd7a57c4b5c337a4281502156e77514 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sun, 6 Mar 2022 16:13:51 +0000 Subject: [PATCH 17/46] Add package --- metadata.txt | 2 ++ freecad/gdml/package.xml => package.xml | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 metadata.txt rename freecad/gdml/package.xml => package.xml (67%) diff --git a/metadata.txt b/metadata.txt new file mode 100644 index 000000000..50559ffab --- /dev/null +++ b/metadata.txt @@ -0,0 +1,2 @@ +workbenches=Part,Mesh +pylibs=lxml,gmsh diff --git a/freecad/gdml/package.xml b/package.xml similarity index 67% rename from freecad/gdml/package.xml rename to package.xml index c191d526b..79e74542c 100644 --- a/freecad/gdml/package.xml +++ b/package.xml @@ -1,25 +1,21 @@ - + 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.git - https://github.com/KeithSloan/GDML.git + https://github.com/KeithSloan/GDML https://github.com/KeithSloan/GDML/wiki + https://github.com/KeithSloan/GDML/blob/master/README.md - GDML workbench GDMLWorkbench - A workbench for creating GDML models for Geant4 & ROOT. - 1.8 Beta ./ freecad/gdml/Resources/icons/GDMLWorkbench.svg 0.19.3 - lxml - gmsh From b7e20bf1349fe5e1f9e14ad56e9d38c69eb80efb Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Mon, 7 Mar 2022 12:50:34 +0000 Subject: [PATCH 18/46] Fix package definition --- package.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 79e74542c..d422d6504 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ GDML workbench - An external workbench for creating GDML models for Geant4 and Root> + An external workbench for creating GDML models for Geant4 and Root> 1.8 Beta 2022-03-06 Keith Sloan From 92586ec6681ac9a4ef79a509b0a6fc93e0062d25 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 08:47:29 +0000 Subject: [PATCH 19/46] Update README.md Update README with details of new branch beta2 which has a number of exciting new enhancements and facilities. --- README.md | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0b4e71175..09b0fafa2 100644 --- a/README.md +++ b/README.md @@ -32,38 +32,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 a sketch + * Extrude + * Revolve +* Creation of Arrays +* Creation of Mirrors +* Export of GDML object takes into account the Placements of the GDML objects and the App::Part ( GDML Volume ) -* 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 +#### Enhancements with Realthunder LinkDaily branch) -You should also see a dramatic improvement especially with LinkDaily in import times with these builds. +For installation see the FreeCAD_Assembly3 release STABLE or DAILY see https://github.com/realthunder/FreeCAD_assembly3/releases +scroll down to Assets. -#### 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: +Realthunders LinkDaily branch has the following enhancements + +* Faster import of GDML objects +* Add extra toEulerAngles function ( Note: as of branch beta2 this is no longer neede ) +* Enhanced Rendering which helps with complex models. + +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 ) + +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)** +#### Changes to Placement (GDML Position & Rotation) [Fixed in beta2] In order to support copies of GDML Volumes the following changes have been made From 902f750908701526844fe6b81c20156c725531ed Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 08:53:02 +0000 Subject: [PATCH 20/46] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09b0fafa2..691a4098a 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,13 @@ A new branch beta2 has a lot of exciting enhancements. The plan is that in time 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 a sketch - * Extrude + * [Extruded sketch](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches) * Revolve * Creation of Arrays * Creation of Mirrors * Export of GDML object takes into account the Placements of the GDML objects and the App::Part ( GDML Volume ) -#### Enhancements with Realthunder LinkDaily branch) +#### 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. From 7c7eef5b853388076b691effbd911ee3cfa63e98 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 08:54:20 +0000 Subject: [PATCH 21/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 691a4098a..0bacd52cd 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ A new branch beta2 has a lot of exciting enhancements. The plan is that in time 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 a sketch +* Creation of GDML solids from a FreeCAD sketch * [Extruded sketch](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches) * Revolve * Creation of Arrays From 649548df89a8e5d1d9b475c3ca1f3eb71ca03a7b Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 08:56:19 +0000 Subject: [PATCH 22/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0bacd52cd..df406352c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ scroll down to Assets. Realthunders LinkDaily branch has the following enhancements * Faster import of GDML objects -* Add extra toEulerAngles function ( Note: as of branch beta2 this is no longer neede ) +* Add extra toEulerAngles function ( Note: as of branch beta2 this is no longer needed ) * Enhanced Rendering which helps with complex models. To enable enhanced rendering in LinkDaily: From 328cb202462039d680ad70bc395f080c95ddf539 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 16:27:58 +0000 Subject: [PATCH 23/46] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index df406352c..914ba0b28 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ A new branch beta2 has a lot of exciting enhancements. The plan is that in time 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 a FreeCAD sketch - * [Extruded sketch](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches) - * Revolve -* Creation of Arrays -* Creation of Mirrors +* Creation of GDML solids from a [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 From 5dffa69ffcbc22bb3f85dd92053b9ca8087c0088 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 8 Mar 2022 16:30:35 +0000 Subject: [PATCH 24/46] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 914ba0b28..5108b53e8 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ A new branch beta2 has a lot of exciting enhancements. The plan is that in time 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 a [FreeCAD sketches](https://github.com/KeithSloan/GDML/wiki/GDML-Object-from-FreeCAD-sketches) +* 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) +* 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 From 3ff62e8ee4b9861d9676f18f59927c240b3dc15a Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Mon, 14 Mar 2022 20:14:03 +0000 Subject: [PATCH 25/46] Fix GDMLObjects getMaterial --- freecad/gdml/GDMLObjects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index abcd147cc..c2228f381 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -369,7 +369,7 @@ def __init__(self, obj): #obj.setEditorMode('Placement', 2) def getMaterial(self): - return obj.material + return self.obj.material def scale(self,fp): print(f'Rescale : {fp.scale}') From b08ac6c231d059cf3088d762440aa96c73a59ac7 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Mon, 21 Mar 2022 17:44:23 +0000 Subject: [PATCH 26/46] Avoid barf on import Obj --- freecad/gdml/importOBJ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() From de5d79b7a308409851cdb93dff6cac0710ddd12f Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Wed, 23 Mar 2022 20:26:55 +0000 Subject: [PATCH 27/46] Fix import OBJ --- freecad/gdml/GDMLObjects.py | 45 ++++++++++++++++++++++++++++++++----- freecad/gdml/GDMLShared.py | 18 --------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index c2228f381..366158154 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -3414,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(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( @@ -3422,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') diff --git a/freecad/gdml/GDMLShared.py b/freecad/gdml/GDMLShared.py index 7846d8138..4f5db8ad2 100644 --- a/freecad/gdml/GDMLShared.py +++ b/freecad/gdml/GDMLShared.py @@ -555,24 +555,6 @@ def getVertex(v): 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): # passed vertex return face # print('v1 : '+str(v1)+' v2 : '+str(v2)+' v3 : '+str(v3)) From 56c85b0ea0aa0a69dee44234dbb712a227e81d2b Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 24 Mar 2022 21:29:48 +0000 Subject: [PATCH 28/46] convertObj --- Utils | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils b/Utils index e41bc6839..3708b43ef 160000 --- a/Utils +++ b/Utils @@ -1 +1 @@ -Subproject commit e41bc6839b7231ad1007fee3976d27df1fb3b565 +Subproject commit 3708b43ef735406755828c59b9c8f2ba93dc300e From 3d3cf5bed766abf095588cdb1c6a31cd05faca04 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 24 Mar 2022 21:50:59 +0000 Subject: [PATCH 29/46] Add CommandLine directory --- CommandLine/combineGDML.py | 16 +++ CommandLine/convertObj.py | 262 +++++++++++++++++++++++++++++++++++++ CommandLine/extractVol.py | 174 ++++++++++++++++++++++++ CommandLine/listSolids.py | 22 ++++ CommandLine/listVols.py | 18 +++ 5 files changed, 492 insertions(+) create mode 100644 CommandLine/combineGDML.py create mode 100644 CommandLine/convertObj.py create mode 100644 CommandLine/extractVol.py create mode 100644 CommandLine/listSolids.py create mode 100644 CommandLine/listVols.py 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..fab1cbca8 --- /dev/null +++ b/CommandLine/convertObj.py @@ -0,0 +1,262 @@ +# ************************************************************************** +# * * +# * 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 - Covert 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, gdmlStr, name): + self.tess = gdmlStr.defineTessellated(name) + + def addTriFace(self, vrt1, vrt2, vrt3): + ET.SubElement(self.tess,'triangular',{ + 'vertex1' : vrt1, 'vertex2' : vrt2, 'vertex3' : vrt3, + 'type':'ABSOLUTE'}) + + def addQuadFace(self, vrt1, vrt2, vrt3, vrt4): + ET.SubElement(self.tess,'quadrangular',{ + 'vertex1' : vrt1, 'vertex2' : vrt2, 'vertex3' : vrt3, 'vertex4': vrt4, + 'type':'ABSOLUTE'}) + +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}) + return(self.addVol(name, 'G4_AIR',self.addWorldBox(1000,1000,1000))) + + def addVol(self, name, material, solid): + vol = ET.SubElement(self.structure, 'volume',{'name': name}) + ET.SubElement(vol, 'materialref',{'ref': material}) + ET.SubElement(vol, 'solidref',{'ref': solid}) + return vol + + def addPhysVol(self, vol, name): + pvol = ET.SubElement(vol,'physvol', {'name': 'PV'+name}) + ET.SubElement(pvol, 'volumeref', {'ref': name}) + ET.SubElement(pvol, 'positionref', {'ref': 'center'}) + ET.SubElement(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 defineTessellated(self, name): + # return Tessellated Element + return ET.SubElement(self.solids,'tessellated',{'name': name}) + + 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): + tess = tessellated(xmlStr, name) + for line in objFp: + #print(line) + items = line.split(' ') + 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('#') : # Comment ignore + break + + if case('vt') : + break + + if case('vn') : + break + + print('Tag : '+items[0]) + break + +def convert2GDML(objFp, outPath, tessName, material): + print('Creating GDML from Obj') + gdmlStr = xmlStructure() + gdmlStr.initGDML() + gdmlStr.init() + tessVol = gdmlStr.addVol('LV_'+tessName, material, tessName) + worldVol = gdmlStr.addWorldVol('worldVol') + gdmlStr.addPhysVol(worldVol, 'LV_'+tessName) + processObjFile(gdmlStr, objFp, tessName) + gdmlStr.writeElement(outPath) + +def convert2XML(objFp, outPath, tessName, material): + print('Creating XML from Obj') + xmlStr = xmlStructure() + xmlStr.initXML() + xmlStr.init() + processObjFile(xmlStr, objFp, tessName) + 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 == 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/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) From 7113e1e270393439c83c46965a9ab3f9f53e9675 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 24 Mar 2022 21:53:06 +0000 Subject: [PATCH 30/46] Add Commands --- gdml2FC.py | 31 +++++++++++++++++++++++++++++++ gdml2step.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 gdml2FC.py create mode 100644 gdml2step.py diff --git a/gdml2FC.py b/gdml2FC.py new file mode 100644 index 000000000..8fd129306 --- /dev/null +++ b/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/gdml2step.py b/gdml2step.py new file mode 100644 index 000000000..e82c168eb --- /dev/null +++ b/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) + From 1f4588165b002c64277126af59be2522a5be954e Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 24 Mar 2022 21:57:02 +0000 Subject: [PATCH 31/46] remove submodule --- .gitmodules | 3 --- gdml2FC.py => CommandLine/gdml2FC.py | 0 gdml2step.py => CommandLine/gdml2step.py | 0 Utils | 1 - 4 files changed, 4 deletions(-) rename gdml2FC.py => CommandLine/gdml2FC.py (100%) rename gdml2step.py => CommandLine/gdml2step.py (100%) delete mode 160000 Utils 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/gdml2FC.py b/CommandLine/gdml2FC.py similarity index 100% rename from gdml2FC.py rename to CommandLine/gdml2FC.py diff --git a/gdml2step.py b/CommandLine/gdml2step.py similarity index 100% rename from gdml2step.py rename to CommandLine/gdml2step.py diff --git a/Utils b/Utils deleted file mode 160000 index 3708b43ef..000000000 --- a/Utils +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3708b43ef735406755828c59b9c8f2ba93dc300e From 0a8cca4c50eca9ad14bd1c131dc98f91f5c07c47 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Thu, 24 Mar 2022 22:00:07 +0000 Subject: [PATCH 32/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5108b53e8..388989ade 100644 --- a/README.md +++ b/README.md @@ -527,7 +527,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. From 68d06a33c39987c2921c06999fbaadaf5e46feb6 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 29 Mar 2022 08:17:29 +0100 Subject: [PATCH 33/46] Fix convertObj.py --- CommandLine/convertObj.py | 169 +++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 64 deletions(-) diff --git a/CommandLine/convertObj.py b/CommandLine/convertObj.py index fab1cbca8..833683dfb 100644 --- a/CommandLine/convertObj.py +++ b/CommandLine/convertObj.py @@ -36,18 +36,32 @@ def case(*args): return any((arg == switch.value for arg in args)) class tessellated: - def __init__(self, gdmlStr, name): - self.tess = gdmlStr.defineTessellated(name) + 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.tess,'triangular',{ + 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.tess,'quadrangular',{ + 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): @@ -102,20 +116,20 @@ def addWorldBox(self, x, y, z): def addWorldVol(self,name): ET.SubElement(self.setup, 'world', {'ref': name}) - return(self.addVol(name, 'G4_AIR',self.addWorldBox(1000,1000,1000))) + self.world = self.addVol(name, 'G4_AIR',self.addWorldBox(1000,1000,1000)) def addVol(self, name, material, solid): - vol = ET.SubElement(self.structure, 'volume',{'name': name}) - ET.SubElement(vol, 'materialref',{'ref': material}) - ET.SubElement(vol, 'solidref',{'ref': solid}) - return vol - - def addPhysVol(self, vol, name): - pvol = ET.SubElement(vol,'physvol', {'name': 'PV'+name}) - ET.SubElement(pvol, 'volumeref', {'ref': name}) - ET.SubElement(pvol, 'positionref', {'ref': 'center'}) - ET.SubElement(pvol, 'rotationref', {'ref': 'identity'}) + 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 @@ -137,73 +151,100 @@ def addQuadFace(self, tess, items): self.vertex[int(items[3])], \ self.vertex[int(items[4])]) - def defineTessellated(self, name): - # return Tessellated Element - return ET.SubElement(self.solids,'tessellated',{'name': name}) - 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): - tess = tessellated(xmlStr, name) +def processObjFile(xmlStr, objFp, name, material): + xmlStr.addWorldVol('worldVol') + tessName = name + tess = tessellated(tessName, material) for line in objFp: #print(line) - items = line.split(' ') - 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 + 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('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('#') : # Comment ignore - break + if case('g'): + print('Group Name') + tessName = items[1] + print(items) + break - if case('vt') : - 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 - if case('vn') : + print('Tag : '+str(items)) break - print('Tag : '+items[0]) - break + tess.flush(xmlStr) def convert2GDML(objFp, outPath, tessName, material): print('Creating GDML from Obj') gdmlStr = xmlStructure() gdmlStr.initGDML() gdmlStr.init() - tessVol = gdmlStr.addVol('LV_'+tessName, material, tessName) - worldVol = gdmlStr.addWorldVol('worldVol') - gdmlStr.addPhysVol(worldVol, 'LV_'+tessName) - processObjFile(gdmlStr, objFp, tessName) + processObjFile(gdmlStr, objFp, tessName, material) gdmlStr.writeElement(outPath) def convert2XML(objFp, outPath, tessName, material): @@ -211,7 +252,7 @@ def convert2XML(objFp, outPath, tessName, material): xmlStr = xmlStructure() xmlStr.initXML() xmlStr.init() - processObjFile(xmlStr, objFp, tessName) + processObjFile(xmlStr, objFp, tessName, material) gdmlStr.writeElement(outPath) argLen = len(sys.argv) From 71406de892eed245e5895b9a07edad0c9d3a4515 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 29 Mar 2022 14:31:34 +0100 Subject: [PATCH 34/46] test gmsh-dev --- metadata.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metadata.txt b/metadata.txt index 50559ffab..a8c459500 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,2 +1,2 @@ -workbenches=Part,Mesh -pylibs=lxml,gmsh +workbenches=Part, Mesh +pylibs=lxml, gmsh-dev From 0ecacd0f6cc5e409e182824f4ac7b8d6591b194e Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 29 Mar 2022 15:13:58 +0100 Subject: [PATCH 35/46] Fix Tess --- freecad/gdml/GDMLObjects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 366158154..2ca31a974 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -3416,8 +3416,8 @@ def createShape(self, vertex, facets, flag): if flag is True: #FCfaces.append(GDMLShared.facet(f)) if len(f.Points) == 3: - face = GDMLShared.triangle(triangle(f.Points[0], \ - f.Points[1], f.Points[2])) + 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: From 2cfbe041e3f7f03191d894a3018f9ef1a3c79360 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sat, 2 Apr 2022 19:11:35 +0100 Subject: [PATCH 36/46] Reset Materials --- freecad/gdml/GDMLCommands.py | 17 +++++++++++++---- freecad/gdml/GDMLMaterials.py | 3 +++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/freecad/gdml/GDMLCommands.py b/freecad/gdml/GDMLCommands.py index 4fd1d92e8..26c61ec4a 100644 --- a/freecad/gdml/GDMLCommands.py +++ b/freecad/gdml/GDMLCommands.py @@ -219,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() @@ -240,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): @@ -259,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) diff --git a/freecad/gdml/GDMLMaterials.py b/freecad/gdml/GDMLMaterials.py index 8c527e2b8..77243c061 100644 --- a/freecad/gdml/GDMLMaterials.py +++ b/freecad/gdml/GDMLMaterials.py @@ -104,11 +104,13 @@ def newGetGroupedMaterials(): 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: @@ -117,6 +119,7 @@ def newGetGroupedMaterials(): 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: From c0bbcc17d9d8a75e97848fd91a530d43a78ba9f3 Mon Sep 17 00:00:00 2001 From: luz paz Date: Sat, 2 Apr 2022 14:51:13 -0400 Subject: [PATCH 37/46] Fix misc. typos --- CommandLine/convertObj.py | 2 +- freecad/gdml/importGDML.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CommandLine/convertObj.py b/CommandLine/convertObj.py index 833683dfb..bb803ff65 100644 --- a/CommandLine/convertObj.py +++ b/CommandLine/convertObj.py @@ -20,7 +20,7 @@ # * * # * * # *************************************************************************** -__title__ = "convertOBJ - Covert Obj to GDML Tessellated" +__title__ = "convertOBJ - Convert Obj to GDML Tessellated" __author__ = "Keith Sloan " __url__ = ["https://github.com/KeithSloan/GDML/Utils"] diff --git a/freecad/gdml/importGDML.py b/freecad/gdml/importGDML.py index 305db0993..26631840f 100644 --- a/freecad/gdml/importGDML.py +++ b/freecad/gdml/importGDML.py @@ -174,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) @@ -182,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) From 0626d04dc8ae85bb2d977cf7f7b0856ceb6a6b38 Mon Sep 17 00:00:00 2001 From: luz paz Date: Sat, 2 Apr 2022 14:51:53 -0400 Subject: [PATCH 38/46] Fix 'testing equality to None' --- CommandLine/convertObj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommandLine/convertObj.py b/CommandLine/convertObj.py index bb803ff65..67494a63c 100644 --- a/CommandLine/convertObj.py +++ b/CommandLine/convertObj.py @@ -290,7 +290,7 @@ def convert2XML(objFp, outPath, tessName, material): print('Invalid Obj file extension') sys.exit() objFp = open(iPath,"r") -if objFp == None: +if objFp is None: print('Failed to open :'+iPath) sys.exit() path, fileExt = os.path.splitext(oPath) From 9a44a93197f7aedeb16fa93d2b82038e007ccebe Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Tue, 17 May 2022 11:02:08 +0100 Subject: [PATCH 39/46] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 388989ade..d45420f86 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,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. From 76a509e0e63212bf796bb9f45a0e8c3f8f700f59 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Fri, 20 May 2022 13:39:05 +0100 Subject: [PATCH 40/46] Update README.md --- README.md | 150 ++++++++++++++---------------------------------------- 1 file changed, 37 insertions(+), 113 deletions(-) diff --git a/README.md b/README.md index d45420f86..0c3ce7410 100644 --- a/README.md +++ b/README.md @@ -94,29 +94,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) @@ -125,73 +102,52 @@ 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: - - * `FreeCAD_0.19.19424` and above - * `FreeCAD_0.19.19409_x64_Conda_Py3QT5-WinVS2015.7z` and above. - - -##### Checking if `lxml` is installed - -To discover if **`lxml`** is installed and accessible by FreeCAD: - -1. Open the CLI -2. Invoke the following: - ```python - freecad -c - import sys - print(sys.path) - ``` - Result: - - -**Note:** To check if **`lxml`** is installed correctly: - - ```python - freecad -c - import lxml - from lxml import etree - print(etree.LXML_VERSION) - ``` - -##### Manual install of lxml - -In case there is a need to manually install `lxml`: - - ```bash - pip3 install lxml -t < directory > - ``` - +### Prerequisites -##### FreeCAD v0.18 +* FreeCAD (https://freecad.org/) +* `lxml` (bundled in to FreeCAD v0.19) +* `gmsh` python library -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) +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 -### `Gmsh` +* import lxml +* import gmsh -[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 shared library -| OCC | FreeCAD | gmsh | -|---------|-------------|---------------| -| `7.4` | `0.19.1` |`4.7.0 - 4.7.1`| -| `7.6` | |`4.8.0 - 4.8.4`| +Gmsh shared library is also required otherwise you will get the following error message in report View +AttributeError: dlsym(RTLD_DEFAULT, gmshInitialize): symbol not found -At the time of writing a prebuilt version of FreeCAD with OCC 7.6 is not available +To download the gmsh shared library you need to obtain a copy of the Gmsh SDK see https://gmsh.info -Note: The version of OCC with FreeCAD 0.19.2 has a regression with STEP functionality +It should then copied to FreeCAD as follows -You can check the version FreeCAD is using with About FreeCAD, copy to clipboard, paste. +#### MacOS +Copy from lib + * gmsh.py + * libgmsh.4.9.4.dylib +To : + /Applications/FreeCAD_0.20.app/Contents/Resources/lib + +#### Linux + +Copy from lib + * gmsh.py + * libgmsh.so +To : + ?????? + +#### Windows + +Copy from lib + * 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 @@ -203,38 +159,6 @@ 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) From d6ae1094f259ca5d32901616343c6e34d04aab92 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Fri, 20 May 2022 13:46:19 +0100 Subject: [PATCH 41/46] Update README.md --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0c3ce7410..a32ebc84c 100644 --- a/README.md +++ b/README.md @@ -126,27 +126,35 @@ It should then copied to FreeCAD as follows #### MacOS Copy from lib + * gmsh.py * libgmsh.4.9.4.dylib -To : + +To : + /Applications/FreeCAD_0.20.app/Contents/Resources/lib #### Linux Copy from lib + * gmsh.py * libgmsh.so -To : - ?????? +To : + + ?????? #### Windows Copy from lib + * gmsh.py * gmsh.lib * gmsh-4.10.dll -To : - ?????? + +To : + + ?????? #### Checking what version of Gmsh is installed From b7360da0089b7bab075378c765c9228287482a21 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Fri, 20 May 2022 13:48:11 +0100 Subject: [PATCH 42/46] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a32ebc84c..850341481 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,8 @@ by from the FreeCAD python console ### gmsh shared library Gmsh shared library is also required otherwise you will get the following error message in report View -AttributeError: dlsym(RTLD_DEFAULT, gmshInitialize): symbol not found + + AttributeError: dlsym(RTLD_DEFAULT, gmshInitialize): symbol not found To download the gmsh shared library you need to obtain a copy of the Gmsh SDK see https://gmsh.info From db1fd194302546e69cd0e180317e16121e4a45cc Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sun, 19 Jun 2022 16:02:59 +0100 Subject: [PATCH 43/46] Update ReadMe for FreeCAD 0.20 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 850341481..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. From dd87d8bbc397dc9c227753eaccc8fc99524c4d01 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Sun, 19 Jun 2022 18:13:42 +0100 Subject: [PATCH 44/46] Stack --- Utils | 1 + 1 file changed, 1 insertion(+) create mode 160000 Utils diff --git a/Utils b/Utils new file mode 160000 index 000000000..10d3925eb --- /dev/null +++ b/Utils @@ -0,0 +1 @@ +Subproject commit 10d3925eb7c7b7cafebdb43beca1e0a335587300 From ae993fb8868adbd5551eb77da840058eb51d3237 Mon Sep 17 00:00:00 2001 From: Keith Sloan Date: Wed, 6 Jul 2022 18:49:10 +0100 Subject: [PATCH 45/46] Avoid gmsh-dev --- metadata.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata.txt b/metadata.txt index a8c459500..9c1a4017e 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,2 +1,2 @@ workbenches=Part, Mesh -pylibs=lxml, gmsh-dev +pylibs=lxml, gmsh From 099826c9461069451d408eebfddb563bc693a82c Mon Sep 17 00:00:00 2001 From: Munther Hindi <95139464+mhindi2@users.noreply.github.com> Date: Tue, 25 Feb 2025 08:59:03 -0800 Subject: [PATCH 46/46] Add files via upload Fix to work with Windows 11. Also works on Linux --- freecad/gdml/GDMLObjects.py | 4877 ++++++++++++++++-------- freecad/gdml/exportGDML.py | 7201 +++++++++++++++++++++++++---------- 2 files changed, 8657 insertions(+), 3421 deletions(-) diff --git a/freecad/gdml/GDMLObjects.py b/freecad/gdml/GDMLObjects.py index 2ca31a974..78f59b8c2 100644 --- a/freecad/gdml/GDMLObjects.py +++ b/freecad/gdml/GDMLObjects.py @@ -1,5 +1,7 @@ -# -*- coding: utf-8 -*- # insert date with Ctrl-u ESC-! date +# Sun Jul 28 06:02:04 AM PDT 2024 +# Fri Dec 1 11:36:24 AM PST 2023 +# Fri Sep 15 10:00:44 AM PDT 2023 # Wed Jan 26 04:44:48 PM PST 2022 # # ************************************************************************** @@ -28,9 +30,9 @@ # * * # * * # ************************************************************************** +from operator import indexOf import FreeCAD, FreeCADGui, Part -from pivy import coin import math from . import GDMLShared @@ -43,12 +45,18 @@ global GroupedMaterials GroupedMaterials = {} # dictionary of material lists by type +global SurfsList +SurfsList = [] + global LengthQuantityList -LengthQuantityList = ['nm', 'um', 'mm', 'cm', 'dm', 'm', 'km'] +LengthQuantityList = ["nm", "um", "mm", "cm", "m", "km"] # cf definition https://wiki.freecadweb.org/Quantity - +# BUT, geant does not support dm (decimeter), so I removed it from above. (MMH - 2024-09-08) +AngleQuantityList = ["rad", "radian", "deg", "degree", "mrad"] +# geant also accepts mrad (for millirad), but since FreeCAD does not, we skip that. def setLengthQuantity(obj, m): + global LengthQuantityList if LengthQuantityList is not None: obj.lunit = LengthQuantityList obj.lunit = 0 @@ -58,31 +66,89 @@ def setLengthQuantity(obj, m): else: obj.lunit = 2 +def setAngleQuantity(obj, m): + global AngleQuantityList + if AngleQuantityList is not None: + obj.aunit = AngleQuantityList + obj.aunit = 0 + if len(AngleQuantityList) > 0: + if not (m == 0 or m is None): + if m == "rad": + obj.aunit = AngleQuantityList.index("rad") + elif m == "radian": + obj.aunit = AngleQuantityList.index("radian") + elif m=="deg": + obj.aunit = AngleQuantityList.index("deg") + elif m == "degree": + obj.aunit = AngleQuantityList.index("degree") + elif m=="mrad": + obj.aunit = AngleQuantityList.index("mrad") + + else: + obj.aunit = 0 # in geant if angle is not given, it is assumed radians + +def getSurfsListFromGroup(doc): + SurfsList = ["None"] + surfs = doc.getObject("Surfaces") + if surfs is not None: + if hasattr(surfs, "Group"): + for i in surfs.Group: + SurfsList.append(i.Label) + return SurfsList + return None + def addMaterialsFromGroup(doc, MatList, grpName): mmats = doc.getObject(grpName) if mmats is not None: - if hasattr(mmats, 'Group'): + if hasattr(mmats, "Group"): for i in mmats.Group: - if i.Label != 'Geant4': - MatList.append(i.Label) + if i.Label != "Geant4": + MatList.append(i.Label) + else: + # rebuild Materials from scratch + buildDefaultGDMLDoc(doc) + + +def buildDefaultGDMLDoc(doc): + from .importGDML import processGDML, joinDir + print(f"Rebuilding Materials Structure") + + processGDML( + doc, + False, # Open / Insert + joinDir("Resources/Default.gdml"), + False, # Prompt + 1, # Process type = 1 + True, + ) def rebuildMaterialsList(): global MaterialsList - print('Restore MaterialsList from Materials Lists') + print("Restore MaterialsList from Materials Lists") doc = FreeCAD.ActiveDocument addMaterialsFromGroup(doc, MaterialsList, "Materials") - #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) + 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) +def checkMaterialDefinitionsExist(): + doc = FreeCAD.ActiveDocument + if doc is None: + buildDefaultGDMLDoc(doc) + else: + G4Materials = doc.getObject("G4Materials") + if G4Materials is None: + buildDefaultGDMLDoc(doc) + + def checkMaterial(material): global MaterialsList try: @@ -93,18 +159,23 @@ def checkMaterial(material): def setMaterial(obj, m): - print('setMaterial') + # print(f'setMaterial {obj} {m}') + if FreeCAD.GuiUp: + if m in ['G4_AIR', 'AIR']: + print(f"Material {m}") + if hasattr(obj, "ViewObject"): + print("Set transparency") + obj.ViewObject.Transparency = 98 + 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): try: obj.material = MaterialsList.index(m) except: - print('Not in List') + print("Not in List") print(MaterialsList) obj.material = 0 return @@ -115,18 +186,23 @@ def setMaterial(obj, m): def checkFullCircle(aunit, angle): # print(angle) - if aunit == 'deg' and angle == 360: + if (aunit == "deg" or aunit == "degree") and angle == 360: + return True + if (aunit == "rad" or aunit == "radian") and angle == 2 * math.pi: return True - if aunit == 'rad' and angle == 2 * math.pi: + if aunit == "mrad" and angle/1000 == 2 * math.pi: return True + return False # Get angle in Radians def getAngleRad(aunit, angle): # print("aunit : "+str(aunit)) - if aunit == 'deg': # 0 radians 1 Degrees - return(angle*math.pi/180) + if aunit == "deg" or aunit == "degree": # 0 radians 1 Degrees + return angle * math.pi / 180 + elif aunit == "mrad": + return angle/1000. else: return angle @@ -134,23 +210,35 @@ def getAngleRad(aunit, angle): # Get angle in Degrees def getAngleDeg(aunit, angle): # print("aunit : "+str(aunit)) - if aunit == 'rad': # 0 radians 1 Degrees - return(angle*180/math.pi) + if aunit == "rad" or aunit == "radian": # 0 radians 1 Degrees + return angle * 180 / math.pi + elif aunit == "mrad": + return angle/1000. * 180 /math.pi else: return angle def makeRegularPolygon(n, r, z): from math import cos, sin, pi - vecs = [FreeCAD.Vector(cos(2*pi*i/n)*r, sin(2*pi*i/n)*r, z) - for i in range(n+1)] + + vecs = [ + FreeCAD.Vector(cos(2 * pi * i / n) * r, sin(2 * pi * i / n) * r, z) + for i in range(n + 1) + ] return vecs def printPolyVec(n, v): print("Polygon : " + n) for i in v: - print("Vertex - x : "+str(i[0])+" y : "+str(i[1])+" z : "+str(i[2])) + print( + "Vertex - x : " + + str(i[0]) + + " y : " + + str(i[1]) + + " z : " + + str(i[2]) + ) def translate(shape, base): @@ -189,11 +277,12 @@ def makeFrustrum(num, poly0, poly1): for i in range(num): j = i + 1 # print([poly0[i],poly0[j],poly1[j],poly1[i]]) - w = Part.makePolygon([poly0[i], poly0[j], poly1[j], - poly1[i], poly0[i]]) + w = Part.makePolygon( + [poly0[i], poly0[j], poly1[j], poly1[i], poly0[i]] + ) faces.append(Part.Face(w)) # print("Number of Faces : "+str(len(faces))) - return(faces) + return faces def angleSectionSolid(fp, rmax, z, shape): @@ -214,7 +303,7 @@ def angleSectionSolid(fp, rmax, z, shape): v4 = FreeCAD.Vector(0, 0, z) f1 = make_face4(v1, v2, v3, v4) - s1 = f1.revolve(v1, v4, 360-deltaPhiDeg) + s1 = f1.revolve(v1, v4, 360 - deltaPhiDeg) # Problem with FreeCAD 0.18 # s2 = s1.rotate(v1,v4,startPhiDeg) @@ -226,8 +315,9 @@ def angleSectionSolid(fp, rmax, z, shape): shape = shape.cut(s2) if startPhiDeg != 0: - shape.rotate(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), startPhiDeg) + shape.rotate( + FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), startPhiDeg + ) return shape @@ -239,7 +329,7 @@ def indiceToRay(indiceIn): # Thanks to Dam puissanceDown = 2 while len(lray) <= indiceIn: for indiceTmp in range(1, puissanceDown, 2): - lray.append(float(indiceTmp)/float(puissanceDown)) + lray.append(float(indiceTmp) / float(puissanceDown)) puissanceDown = 2 * puissanceDown return lray[indiceIn] @@ -247,24 +337,24 @@ def indiceToRay(indiceIn): # Thanks to Dam def colorFromRay(rayIn): # Thanks to Dam coeffR = coeffG = coeffB = 1.0 - if(rayIn < 0.2 and rayIn >= 0.0): + if rayIn < 0.2 and rayIn >= 0.0: coeffR = 1.0 - coeffG = rayIn*5.0 + coeffG = rayIn * 5.0 coeffB = 0.0 - elif(rayIn < 0.4): - coeffR = 2.0-(5.0*rayIn) + elif rayIn < 0.4: + coeffR = 2.0 - (5.0 * rayIn) coeffG = 1.0 coeffB = 0.0 - elif(rayIn < 0.6): + elif rayIn < 0.6: coeffR = 0.0 coeffG = 1.0 - coeffB = rayIn*5.0-2.0 - elif(rayIn < 0.8): + coeffB = rayIn * 5.0 - 2.0 + elif rayIn < 0.8: coeffR = 1.0 - coeffG = 4.0-(5.0*rayIn) + coeffG = 4.0 - (5.0 * rayIn) coeffB = 1.0 - elif(rayIn <= 1.0): - coeffR = (5.0*rayIn)-4.0 + elif rayIn <= 1.0: + coeffR = (5.0 * rayIn) - 4.0 coeffG = 0.0 coeffB = 1.0 return (coeffR, coeffG, coeffB, 0.0) @@ -275,9 +365,9 @@ def colourMaterial(m): if MaterialsList is None: return (0.5, 0.5, 0.5, 0.0) else: - if (m is None): + if m is None: return (0.5, 0.5, 0.5, 0, 0) - elif(len(MaterialsList) <= 1): + elif len(MaterialsList) <= 1: return (0.5, 0.5, 0.5, 0.0) elif m not in MaterialsList: return (0.5, 0.5, 0.5, 0.0) @@ -292,7 +382,12 @@ def updateColour(obj, colour, material): obj.ViewObject.ShapeColor = colour # print(f'Colour {colour}') if colour is not None: - obj.ViewObject.Transparency = int(colour[3]*100) + obj.ViewObject.Transparency = int(colour[3] * 100) + + +def setTransparency(obj, value=70): + obj.ViewObject.Transparency = value + def rotateAroundZ(nstep, z, r): @@ -306,7 +401,7 @@ def rotateAroundZ(nstep, z, r): verts = [] verts.append(FreeCAD.Vector(0, 0, z[0])) verts.extend([FreeCAD.Vector(r[i], 0, z[i]) for i in range(0, len(z))]) - verts.append(FreeCAD.Vector(0, 0, z[len(z)-1])) + verts.append(FreeCAD.Vector(0, 0, z[len(z) - 1])) line = Part.makePolygon(verts) surf = line.revolve(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), 360) return Part.makeSolid(surf) @@ -314,10 +409,15 @@ def rotateAroundZ(nstep, z, r): class GDMLColourMapEntry: def __init__(self, obj, colour, material): - obj.addProperty("App::PropertyColor", "colour", - "GDMLColourMapEntry", "colour").colour = colour - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLColourMapEntry", "Material") + obj.addProperty( + "App::PropertyColor", "colour", "GDMLColourMapEntry", "colour" + ).colour = colour + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLColourMapEntry", + "Material", + ) setMaterial(obj, material) @@ -325,54 +425,34 @@ def indexBoolean(list, ln): # print('Length : '+str(ln)) if ln > 3: # print(range(ln-3)) - for r in range(ln-2, -1, -1): + for r in range(ln - 2, -1, -1): t = list[r].TypeId # print(t) - if t == 'Part::Cut' or t == 'Part::Fuse' or t == 'Part::Common': + if t == "Part::Cut" or t == "Part::Fuse" or t == "Part::Common": return r return -1 class GDMLsolid: def __init__(self, obj): - '''Init''' - # print('>>>>>') - # if hasattr(obj,'Label') : - # print('Label : '+obj.Label) - # print('TypeId : '+obj.TypeId) - # print(dir(obj)) - # if hasattr(obj,'InList') : - # print('InList') - # print(obj.InList) - # for i in obj.InList : - # print(i.TypeId) - # if hasattr(i,'Label') : - # print('Label : '+i.Label) - # if i.TypeId == 'App::Part' : - # print(i.OutList) - # for j in i.OutList : - # print(' ==> Typeid'+str(j.TypeId)) - # if hasattr(j,'Label') : - # print(' ==> Label'+j.Label) - # - # print('<<<<<') - if hasattr(obj, 'InList'): + """Init""" + if hasattr(obj, "InList"): for j in obj.InList: - if hasattr(j, 'OutList'): + if hasattr(j, "OutList"): ln = len(j.OutList) r = indexBoolean(j.OutList, ln) # print('index : '+str(r)) if r >= 0: if (ln - r) >= 2: # print('Tool : '+obj.Label) - return # Let Placement default to 0 - #obj.setEditorMode('Placement', 2) + return # Let Placement default to 0 + # obj.setEditorMode('Placement', 2) def getMaterial(self): return self.obj.material - def scale(self,fp): - print(f'Rescale : {fp.scale}') + def scale(self, fp): + print(f"Rescale : {fp.scale}") mat = FreeCAD.Matrix() mat.scale(fp.scale) fp.Shape = fp.Shape.transformGeometry(mat) @@ -381,293 +461,464 @@ def execute(self, fp): self.createGeometry(fp) def __getstate__(self): - '''When saving the document this object gets stored using Python's json + """When saving the document this object gets stored using Python's json module. - Since we have some un-serializable parts here -- the Coin stuff -- + Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ - to return a tuple of all serializable objects or None.''' - if hasattr(self, 'Type'): - return {'type': self.Type} + to return a tuple of all serializable objects or None.""" + if hasattr(self, "Type"): + # print(f"getstate : Type {self.Type}") + return {"type": self.Type} + elif hasattr(self.Proxy, "Type"): + # print(f"getstate : Type {self.Proxy.Type}") + return {"type": self.Proxy.Type} + else: + print(f"Error GDMLsolid should have Type") + #print(f" self {self}") pass def __setstate__(self, arg): - '''When restoring the serialized object from document we have the + """When restoring the serialized object from document we have the chance to set some internals here. Since no data were serialized - nothing needs to be done here.''' - self.Type = arg['type'] + nothing needs to be done here.""" + #print(f"setstate : arg {arg} type {type(arg)}") + # Handle bug in FreeCAD 0.21.2 handling of json + if arg is not None and arg != {}: + if 'type' in arg: + self.Type = arg["type"] + else: #elif 'Type' in arg: + self.Type = arg["Type"] + #print(self.Type) class GDMLcommon: def __init__(self, obj): - '''Init''' + """Init""" def __getstate__(self): - '''When saving the document this object gets stored using Python's + """When saving the document this object gets stored using Python's json module. Since we have some un-serializable parts here -- the Coin stuff -- we must define this method - to return a tuple of all serializable objects or None.''' - if hasattr(self, 'Type'): # If not saved just return - return {'type': self.Type} + to return a tuple of all serializable objects or None.""" + if hasattr(self, "Type"): # If not saved just return + return {"type": self.Type} else: pass def __setstate__(self, arg): - '''When restoring the serialized object from document we have the + """When restoring the serialized object from document we have the chance to set some internals here. - Since no data were serialized nothing needs to be done here.''' - if arg is not None: - self.Type = arg['type'] + Since no data were serialized nothing needs to be done here.""" + # Handle bug in FreeCAD 0.21.2 handling of json + #print(f"setstate : arg {arg} type {type(arg)}") + if arg is not None and arg != {}: + if 'type' in arg: + self.Type = arg["type"] + else: #elif 'Type' in arg: + self.Type = arg["Type"] + #print(self.Type) class GDMLArb8(GDMLsolid): # Thanks to Dam Lamb - def __init__(self, obj, v1x, v1y, v2x, v2y, v3x, v3y, v4x, v4y, - v5x, v5y, v6x, v6y, v7x, v7y, v8x, v8y, dz, - lunit, material, colour=None): - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat", - "v1x", "GDMLArb8", "vertex 1 x position").v1x = v1x - obj.addProperty("App::PropertyFloat", - "v1y", "GDMLArb8", "vertex 1 y position").v1y = v1y - obj.addProperty("App::PropertyFloat", - "v2x", "GDMLArb8", "vertex 2 x position").v2x = v2x - obj.addProperty("App::PropertyFloat", - "v2y", "GDMLArb8", "vertex 2 y position").v2y = v2y - obj.addProperty("App::PropertyFloat", - "v3x", "GDMLArb8", "vertex 3 x position").v3x = v3x - obj.addProperty("App::PropertyFloat", - "v3y", "GDMLArb8", "vertex 3 y position").v3y = v3y - obj.addProperty("App::PropertyFloat", - "v4x", "GDMLArb8", "vertex 4 x position").v4x = v4x - obj.addProperty("App::PropertyFloat", - "v4y", "GDMLArb8", "vertex 4 y position").v4y = v4y - obj.addProperty("App::PropertyFloat", - "v5x", "GDMLArb8", "vertex 5 x position").v5x = v5x - obj.addProperty("App::PropertyFloat", - "v5y", "GDMLArb8", "vertex 5 y position").v5y = v5y - obj.addProperty("App::PropertyFloat", - "v6x", "GDMLArb8", "vertex 6 x position").v6x = v6x - obj.addProperty("App::PropertyFloat", - "v6y", "GDMLArb8", "vertex 6 y position").v6y = v6y - obj.addProperty("App::PropertyFloat", - "v7x", "GDMLArb8", "vertex 7 x position").v7x = v7x - obj.addProperty("App::PropertyFloat", - "v7y", "GDMLArb8", "vertex 7 y position").v7y = v7y - obj.addProperty("App::PropertyFloat", - "v8x", "GDMLArb8", "vertex 8 x position").v8x = v8x - obj.addProperty("App::PropertyFloat", - "v8y", "GDMLArb8", "vertex 8 y position").v8y = v8y - obj.addProperty("App::PropertyFloat", - "dz", "GDMLArb8", "Half z Length").dz = dz - obj.addProperty("App::PropertyEnumeration", - "lunit", "GDMLArb8", "lunit") + def __init__( + self, + obj, + v1x, + v1y, + v2x, + v2y, + v3x, + v3y, + v4x, + v4y, + v5x, + v5y, + v6x, + v6y, + v7x, + v7y, + v8x, + v8y, + dz, + lunit, + material, + colour=None, + ): + """Add some custom properties to our Tube feature""" + obj.addProperty( + "App::PropertyFloat", "v1x", "GDMLArb8", "vertex 1 x position" + ).v1x = v1x + obj.addProperty( + "App::PropertyFloat", "v1y", "GDMLArb8", "vertex 1 y position" + ).v1y = v1y + obj.addProperty( + "App::PropertyFloat", "v2x", "GDMLArb8", "vertex 2 x position" + ).v2x = v2x + obj.addProperty( + "App::PropertyFloat", "v2y", "GDMLArb8", "vertex 2 y position" + ).v2y = v2y + obj.addProperty( + "App::PropertyFloat", "v3x", "GDMLArb8", "vertex 3 x position" + ).v3x = v3x + obj.addProperty( + "App::PropertyFloat", "v3y", "GDMLArb8", "vertex 3 y position" + ).v3y = v3y + obj.addProperty( + "App::PropertyFloat", "v4x", "GDMLArb8", "vertex 4 x position" + ).v4x = v4x + obj.addProperty( + "App::PropertyFloat", "v4y", "GDMLArb8", "vertex 4 y position" + ).v4y = v4y + obj.addProperty( + "App::PropertyFloat", "v5x", "GDMLArb8", "vertex 5 x position" + ).v5x = v5x + obj.addProperty( + "App::PropertyFloat", "v5y", "GDMLArb8", "vertex 5 y position" + ).v5y = v5y + obj.addProperty( + "App::PropertyFloat", "v6x", "GDMLArb8", "vertex 6 x position" + ).v6x = v6x + obj.addProperty( + "App::PropertyFloat", "v6y", "GDMLArb8", "vertex 6 y position" + ).v6y = v6y + obj.addProperty( + "App::PropertyFloat", "v7x", "GDMLArb8", "vertex 7 x position" + ).v7x = v7x + obj.addProperty( + "App::PropertyFloat", "v7y", "GDMLArb8", "vertex 7 y position" + ).v7y = v7y + obj.addProperty( + "App::PropertyFloat", "v8x", "GDMLArb8", "vertex 8 x position" + ).v8x = v8x + obj.addProperty( + "App::PropertyFloat", "v8y", "GDMLArb8", "vertex 8 y position" + ).v8y = v8y + obj.addProperty( + "App::PropertyFloat", "dz", "GDMLArb8", "Half z Length" + ).dz = dz + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLArb8", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", - "material", "GDMLArb8", "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLArb8", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLArb8' + self.Type = "GDMLArb8" + obj.Proxy.Type = "GDMLArb8" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['v1x', 'v1y', 'v2x', 'v2y', 'v3x', 'v3y', 'v4x', 'v4y', - 'v5x', 'v5y', 'v6x', 'v6y', 'v7x', 'v7y', 'v8x', 'v8y', - 'dz', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "v1x", + "v1y", + "v2x", + "v2y", + "v3x", + "v3y", + "v4x", + "v4y", + "v5x", + "v5y", + "v6x", + "v6y", + "v7x", + "v7y", + "v8x", + "v8y", + "dz", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + 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 -# The order of specification of the coordinates for the vertices in G4GenericTrap is important. The first four points are the vertices sitting on the -hz plane; the last four points are the vertices sitting on the +hz plane. -# -#The order of defining the vertices of the solid is the following: -# -# point 0 is connected with points 1,3,4 -# point 1 is connected with points 0,2,5 -# point 2 is connected with points 1,3,6 -# point 3 is connected with points 0,2,7 -# point 4 is connected with points 0,5,7 -# point 5 is connected with points 1,4,6 -# point 6 is connected with points 2,5,7 -# point 7 is connected with points 3,4,6 + # http://geant4-userdoc.web.cern.ch/geant4-userdoc/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html + # The order of specification of the coordinates for the vertices in G4GenericTrap is important. The first four points are the vertices sitting on the -hz plane; the last four points are the vertices sitting on the +hz plane. + # + # The order of defining the vertices of the solid is the following: + # + # point 0 is connected with points 1,3,4 + # point 1 is connected with points 0,2,5 + # point 2 is connected with points 1,3,6 + # point 3 is connected with points 0,2,7 + # point 4 is connected with points 0,5,7 + # point 5 is connected with points 1,4,6 + # point 6 is connected with points 2,5,7 + # point 7 is connected with points 3,4,6 + + def isTwisted(self, fp): + ''' test if the upper face is twisted relative to the lower face + Computation here mimics that in G4GenericTrap + ''' + verts2D = [(fp.v1x, fp.v1y), (fp.v2x, fp.v2y), (fp.v3x, fp.v3y), (fp.v4x, fp.v4y), + (fp.v5x, fp.v5y), (fp.v6x, fp.v6y), (fp.v7x, fp.v7y), (fp.v8x, fp.v8y)] + + nv = 4 + + tolerance = 1.E-03 + twisted = False + for i in range(4): + dx1 = verts2D[(i+1) % nv][0] - verts2D[i][0] + dy1 = verts2D[(i+1) % nv][1] - verts2D[i][1] + if dx1 == 0 and dy1 == 0: + continue + dx2 = verts2D[nv + (i+1) % nv][0] - verts2D[nv + i][0] + dy2 = verts2D[nv + (i+1) % nv][1] - verts2D[nv + i][1] + if dx2 == 0 and dy2 == 0: + continue + twist_angle = abs(dy1*dx2 - dx1*dy2) # this is sin(angle) + if twist_angle < tolerance: + continue + twisted = True + break + + return twisted def createGeometry(self, fp): currPlacement = fp.Placement mul = GDMLShared.getMult(fp) + subdivisions = 0 + if self.isTwisted(fp): + subdivisions = 8 + + # old construction was giving a Volume that was off by about 3% + # compared to geant4's. So imitate geant4's construction + + pt1 = mul * FreeCAD.Vector(fp.v1x, fp.v1y, -fp.dz) + pt2 = mul * FreeCAD.Vector(fp.v2x, fp.v2y, -fp.dz) + pt3 = mul * FreeCAD.Vector(fp.v3x, fp.v3y, -fp.dz) + pt4 = mul * FreeCAD.Vector(fp.v4x, fp.v4y, -fp.dz) + pt5 = mul * FreeCAD.Vector(fp.v5x, fp.v5y, fp.dz) + pt6 = mul * FreeCAD.Vector(fp.v6x, fp.v6y, fp.dz) + pt7 = mul * FreeCAD.Vector(fp.v7x, fp.v7y, fp.dz) + pt8 = mul * FreeCAD.Vector(fp.v8x, fp.v8y, fp.dz) + + verts3D = [pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8] + + + faces = [] + faces.append(Part.Face(Part.makePolygon([verts3D[0], verts3D[3], verts3D[2], verts3D[1], verts3D[0]]))) # -fz plane + # breakpoint() + t = 0 + dt = 1./(subdivisions+1) + u0 = verts3D[4] - verts3D[0] + u1 = verts3D[5] - verts3D[1] + u2 = verts3D[6] - verts3D[2] + u3 = verts3D[7] - verts3D[3] + for i in range(subdivisions+1): + j = i*4 + faces.append(Part.Face(Part.makePolygon([verts3D[0] + t * u0, verts3D[1] + t * u1, verts3D[0] + (t + dt) * u0, verts3D[0] + t * u0]))) + faces.append(Part.Face(Part.makePolygon([verts3D[0] + (t + dt) * u0, verts3D[1] + t * u1, verts3D[1] + (t + dt) * u1, verts3D[0] + (t + dt) * u0]))) + + faces.append(Part.Face(Part.makePolygon([verts3D[1] + t * u1, verts3D[2] + t * u2, verts3D[1] + (t + dt) * u1, verts3D[1] + t * u1]))) + faces.append(Part.Face(Part.makePolygon([verts3D[1] + (t + dt) * u1, verts3D[2] + t * u2, verts3D[2] + (t + dt) * u2, verts3D[1] + (t + dt) * u1]))) + + faces.append(Part.Face(Part.makePolygon([verts3D[2] + t * u2, verts3D[3] + t * u3, verts3D[2] + (t + dt) * u2, verts3D[2] + t * u2]))) + faces.append(Part.Face(Part.makePolygon([verts3D[2] + (t + dt) * u2, verts3D[3] + t * u3, verts3D[3] + (t + dt) * u3, verts3D[2] + (t + dt) * u2]))) + + faces.append(Part.Face(Part.makePolygon([verts3D[3] + t * u3, verts3D[0] + t * u0, verts3D[3] + (t + dt) * u3, verts3D[3] + t * u3]))) + faces.append(Part.Face(Part.makePolygon([verts3D[3] + (t + dt) * u3, verts3D[0] + t * u0, verts3D[0] + (t + dt) * u0, verts3D[3] + (t + dt) * u3]))) + + t += dt - pt1 = FreeCAD.Vector(fp.v1x*mul, fp.v1y*mul, -fp.dz*mul) - pt2 = FreeCAD.Vector(fp.v2x*mul, fp.v2y*mul, -fp.dz*mul) - pt3 = FreeCAD.Vector(fp.v3x*mul, fp.v3y*mul, -fp.dz*mul) - pt4 = FreeCAD.Vector(fp.v4x*mul, fp.v4y*mul, -fp.dz*mul) - pt5 = FreeCAD.Vector(fp.v5x*mul, fp.v5y*mul, fp.dz*mul) - pt6 = FreeCAD.Vector(fp.v6x*mul, fp.v6y*mul, fp.dz*mul) - pt7 = FreeCAD.Vector(fp.v7x*mul, fp.v7y*mul, fp.dz*mul) - pt8 = FreeCAD.Vector(fp.v8x*mul, fp.v8y*mul, fp.dz*mul) - - faceZmin = Part.Face(Part.makePolygon([pt1, pt2, pt3, pt4, pt1])) - faceZmax = Part.Face(Part.makePolygon([pt5, pt6, pt7, pt8, pt5])) - - faceXminA = Part.Face(Part.makePolygon([pt1, pt2, pt6, pt1])) - faceXminB = Part.Face(Part.makePolygon([pt6, pt5, pt1, pt6])) - faceXmaxA = Part.Face(Part.makePolygon([pt4, pt3, pt7, pt4])) - faceXmaxB = Part.Face(Part.makePolygon([pt8, pt4, pt7, pt8])) - - faceYminA = Part.Face(Part.makePolygon([pt1, pt8, pt4, pt1])) - faceYminB = Part.Face(Part.makePolygon([pt1, pt5, pt8, pt1])) - - faceYmaxA = Part.Face(Part.makePolygon([pt2, pt3, pt7, pt2])) - faceYmaxB = Part.Face(Part.makePolygon([pt2, pt7, pt6, pt2])) - - fp.Shape = Part.makeSolid(Part.makeShell([faceXminA, faceXminB, - faceXmaxA, faceXmaxB, - faceYminA, faceYminB, - faceYmaxA, faceYmaxB, - faceZmin, faceZmax])) - if hasattr(fp,'scale'): super().scale(fp) + faces.append(Part.Face(Part.makePolygon([verts3D[4], verts3D[5], verts3D[6], verts3D[7], verts3D[4]]))) # +fz plane + + fp.Shape = Part.makeSolid(Part.makeShell(faces)) + + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLBox(GDMLsolid): def __init__(self, obj, x, y, z, lunit, material, colour=None): super().__init__(obj) - '''Add some custom properties to our Box feature''' + """Add some custom properties to our Box feature""" GDMLShared.trace("GDMLBox init") # GDMLShared.trace("material : "+material) obj.addProperty("App::PropertyFloat", "x", "GDMLBox", "Length x").x = x obj.addProperty("App::PropertyFloat", "y", "GDMLBox", "Length y").y = y obj.addProperty("App::PropertyFloat", "z", "GDMLBox", "Length z").z = z - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLBox", "lunit") + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLBox", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLBox", "Material") + #obj.lunit = LengthQuantityList.index(lunit) + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLBox", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLBox' + self.Type = "GDMLBox" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLBox" def onChanged(self, fp, prop): - '''Do something when a property has changed''' - print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + """Do something when a property has changed""" + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State): + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['x', 'y', 'z', 'lunit']: + if prop in ["x", "y", "z", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) - # execute(self, fp): in GDMLsolid + # execute(self, fp): in GDMLsolid def createGeometry(self, fp): - print('createGeometry') - print(fp) + # print('createGeometry') - if all((fp.x, fp.y, fp.z)): - currPlacement = fp.Placement + if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : - # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : + currPlacement = fp.Placement mul = GDMLShared.getMult(fp) - GDMLShared.trace('mul : '+str(mul)) + GDMLShared.trace("mul : " + str(mul)) x = mul * fp.x y = mul * fp.y z = mul * fp.z box = Part.makeBox(x, y, z) - base = FreeCAD.Vector(-x/2, -y/2, -z/2) + base = FreeCAD.Vector(-x / 2, -y / 2, -z / 2) fp.Shape = translate(box, base) fp.Placement = currPlacement - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) def OnDocumentRestored(self, obj): - print('Doc Restored') + print("Doc Restored") class GDMLCone(GDMLsolid): - def __init__(self, obj, rmin1, rmax1, rmin2, - rmax2, z, startphi, deltaphi, aunit, - lunit, material, colour=None): + def __init__( + self, + obj, + rmin1, + rmax1, + rmin2, + rmax2, + z, + startphi, + deltaphi, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties to our Cone feature''' - obj.addProperty("App::PropertyFloat", - "rmin1", "GDMLCone", "Min Radius 1").rmin1 = rmin1 - obj.addProperty("App::PropertyFloat", - "rmax1", "GDMLCone", "Max Radius 1").rmax1 = rmax1 - obj.addProperty("App::PropertyFloat", - "rmin2", "GDMLCone", "Min Radius 2").rmin2 = rmin2 - obj.addProperty("App::PropertyFloat", - "rmax2", "GDMLCone", "Max Radius 2").rmax2 = rmax2 - obj.addProperty("App::PropertyFloat", - "z", "GDMLCone", "Height of Cone").z = z - obj.addProperty("App::PropertyFloat", - "startphi", "GDMLCone", "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", - "deltaphi","GDMLCone", "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyEnumeration", - "aunit", "GDMLCone", "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLCone", "lunit") + """Add some custom properties to our Cone feature""" + obj.addProperty( + "App::PropertyFloat", "rmin1", "GDMLCone", "Min Radius 1" + ).rmin1 = rmin1 + obj.addProperty( + "App::PropertyFloat", "rmax1", "GDMLCone", "Max Radius 1" + ).rmax1 = rmax1 + obj.addProperty( + "App::PropertyFloat", "rmin2", "GDMLCone", "Min Radius 2" + ).rmin2 = rmin2 + obj.addProperty( + "App::PropertyFloat", "rmax2", "GDMLCone", "Max Radius 2" + ).rmax2 = rmax2 + obj.addProperty( + "App::PropertyFloat", "z", "GDMLCone", "Height of Cone" + ).z = z + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLCone", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLCone", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLCone", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLCone", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLCone", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLCone", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLCone' + self.Type = "GDMLCone" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLCone" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin1', 'rmax1', 'rmin2', 'rmax2', - 'z', 'startphi', 'deltaphi', - 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "rmin1", + "rmax1", + "rmin2", + "rmax2", + "z", + "startphi", + "deltaphi", + "aunit", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -676,9 +927,13 @@ def createGeometry(self, fp): # print("fp : ") # print(vars(fp)) # if all((fp.rmin1,fp.rmin2,fp.rmax1,fp.rmax2,fp.z)) : - if (hasattr(fp, 'rmin1') and hasattr(fp, 'rmax1') and - hasattr(fp, 'rmin2') and hasattr(fp, 'rmax2') and - hasattr(fp, 'z')): + if ( + hasattr(fp, "rmin1") + and hasattr(fp, "rmax1") + and hasattr(fp, "rmin2") + and hasattr(fp, "rmax2") + and hasattr(fp, "z") + ): # Need to add code to check variables will make a valid cone # i.e.max > min etc etc # print("execute cone") @@ -700,7 +955,7 @@ def createGeometry(self, fp): else: cone1 = Part.makeCylinder(rmax1, z) - if (rmin1 != 0 and rmin2 != 0): + if rmin1 != 0 and rmin2 != 0: if rmin1 != rmin2: cone2 = Part.makeCone(rmin1, rmin2, z) else: @@ -712,68 +967,81 @@ def createGeometry(self, fp): cone3 = cone2.cut(cone1) else: cone3 = cone1 - base = FreeCAD.Vector(0, 0, -z/2) + base = FreeCAD.Vector(0, 0, -z / 2) if checkFullCircle(fp.aunit, fp.deltaphi) is False: rmax = max(rmax1, rmax2) cone = angleSectionSolid(fp, rmax, z, cone3) fp.Shape = translate(cone, base) else: fp.Shape = translate(cone3, base) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLElCone(GDMLsolid): def __init__(self, obj, dx, dy, zmax, zcut, lunit, material, colour=None): super().__init__(obj) - '''Add some custom properties to our ElCone feature''' - obj.addProperty("App::PropertyFloat", "dx", "GDMLElCone", - "x semi axis").dx = dx - obj.addProperty("App::PropertyFloat", "dy", "GDMLElCone", - "y semi axis").dy = dy - obj.addProperty("App::PropertyFloat", "zmax", "GDMLElCone", - "z length").zmax = zmax - obj.addProperty("App::PropertyFloat", "zcut", "GDMLElCone", - "z cut").zcut = zcut - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLElCone", "lunit") + """Add some custom properties to our ElCone feature""" + obj.addProperty( + "App::PropertyFloat", "dx", "GDMLElCone", "x semi axis" + ).dx = dx + obj.addProperty( + "App::PropertyFloat", "dy", "GDMLElCone", "y semi axis" + ).dy = dy + obj.addProperty( + "App::PropertyFloat", "zmax", "GDMLElCone", "z length" + ).zmax = zmax + obj.addProperty( + "App::PropertyFloat", "zcut", "GDMLElCone", "z cut" + ).zcut = zcut + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLElCone", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLElCone", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLElCone", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLElCone' + self.Type = "GDMLElCone" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLElCone" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['dx', 'dy', 'zmax', 'zcut', 'lunit']: + if prop in ["dx", "dy", "zmax", "zcut", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid + ''' def createGeometry(self, fp): # Form the Web page documentation page for elliptical cone: # https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html # the parametric equation of the elliptical cone: # x = dx*(zmax - u) * cos(v), v = 0..2Pi (note, as of 2021-11-21, - # web page mistakingly shows /u) + # web page mistakenly shows /u) # y = dy*(zmax - u) * sin(v) # z = u, u = -zcut..zcut # Therefore the bottom base of the cone (at z=u=-zcut) has @@ -788,7 +1056,7 @@ def createGeometry(self, fp): mul = GDMLShared.getMult(fp) currPlacement = fp.Placement - rmax = (fp.zmax+fp.zcut)*mul + rmax = (fp.zmax + fp.zcut) * mul cone1 = Part.makeCone(rmax, 0, rmax) mat = FreeCAD.Matrix() mat.unity() @@ -802,11 +1070,11 @@ def createGeometry(self, fp): mat.A33 = 1 mat.A34 = -zcut # move bottom of cone to -zcut mat.A44 = 1 - xmax = dx*rmax - ymax = dy*rmax + xmax = dx * rmax + ymax = dy * rmax cone2 = cone1.transformGeometry(mat) if zcut is not None: - box = Part.makeBox(2*xmax, 2*ymax, zmax) + box = Part.makeBox(2 * xmax, 2 * ymax, zmax) pl = FreeCAD.Placement() # Only need to move to semi axis pl.move(FreeCAD.Vector(-xmax, -ymax, zcut)) @@ -814,54 +1082,125 @@ def createGeometry(self, fp): fp.Shape = cone2.cut(box) else: fp.Shape = cone2 - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) + fp.Placement = currPlacement + ''' + + + def createGeometry(self, fp): + # Form the Web page documentation page for elliptical cone: + # https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html + # the parametric equation of the elliptical cone: + # x = dx*(zmax - u) * cos(v), v = 0..2Pi (note, as of 2021-11-21, + # web page mistakenly shows /u) + # y = dy*(zmax - u) * sin(v) + # z = u, u = -zcut..zcut + # Therefore the bottom base of the cone (at z=u=-zcut) has + # xmax = dxmax = dx*(zmax+zcut) + # and ymax=dymax = dy*(zmax+zcut) + # The ellipse at the top has simi-major axis dx*(zmax-zcut) and + # semiminor axis dy*(zmax-zcut) + # as per the above, the "bottom of the cone is at z = -zcut + # Note that dx is a SCALING factor for the semi major axis, + # NOT the actual semi major axis + # ditto for dy + + mul = GDMLShared.getMult(fp) + currPlacement = fp.Placement + # Semi axis values so need to double + dx = fp.dx + dy = fp.dy + zcut = fp.zcut * mul + zmax = fp.zmax * mul + a_bot = dx*(zmax + zcut) + a_top = dx*(zmax - zcut) + b_bot = dy*(zmax + zcut) + b_top = dy*(zmax - zcut) + + if dx > dy: + ellipse_bot = Part.Ellipse(FreeCAD.Vector(0, 0, 0), a_bot, b_bot) + ellipse_top = Part.Ellipse(FreeCAD.Vector(0, 0, 0), a_top, b_top) + else: + ellipse_bot = Part.Ellipse(FreeCAD.Vector(0, 0, 0), b_bot, a_bot) + ellipse_top = Part.Ellipse(FreeCAD.Vector(0, 0, 0), b_top, a_top) + + edge_bot = Part.Edge(ellipse_bot) + edge_top = Part.Edge(ellipse_top) + + if dy > dx: + edge_bot.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 90) + edge_top.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 90) + edge_bot.translate(FreeCAD.Vector(0, 0, -zcut)) + edge_top.translate(FreeCAD.Vector(0, 0, zcut)) + + wire_bot = Part.Wire(edge_bot) + wire_top = Part.Wire(edge_top) + solid = Part.makeLoft([wire_bot, wire_top], True, False) + + fp.Shape = solid + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLEllipsoid(GDMLsolid): - def __init__(self, obj, ax, by, cz, zcut1, zcut2, lunit, material, colour=None): + def __init__( + self, obj, ax, by, cz, zcut1, zcut2, lunit, material, colour=None + ): super().__init__(obj) - '''Add some custom properties to our Elliptical Tube feature''' - obj.addProperty("App::PropertyFloat", "ax", "GDMLEllipsoid", - "x semi axis").ax = ax - obj.addProperty("App::PropertyFloat", "by", "GDMLEllipsoid", - "y semi axis").by = by - obj.addProperty("App::PropertyFloat", "cz", "GDMLEllipsoid", - "z semi axis").cz = cz - obj.addProperty("App::PropertyFloat", "zcut1", "GDMLEllipsoid", - "z axis cut1").zcut1 = zcut1 - obj.addProperty("App::PropertyFloat", "zcut2", "GDMLEllipsoid", - "z axis1 cut2").zcut2 = zcut2 - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLEllipsoid", - "lunit") + """Add some custom properties to our Elliptical Tube feature""" + obj.addProperty( + "App::PropertyFloat", "ax", "GDMLEllipsoid", "x semi axis" + ).ax = ax + obj.addProperty( + "App::PropertyFloat", "by", "GDMLEllipsoid", "y semi axis" + ).by = by + obj.addProperty( + "App::PropertyFloat", "cz", "GDMLEllipsoid", "z semi axis" + ).cz = cz + obj.addProperty( + "App::PropertyFloat", "zcut1", "GDMLEllipsoid", "z axis cut1" + ).zcut1 = zcut1 + obj.addProperty( + "App::PropertyFloat", "zcut2", "GDMLEllipsoid", "z axis1 cut2" + ).zcut2 = zcut2 + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLEllipsoid", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLEllipsoid", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLEllipsoid", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLEllipsoid' + self.Type = "GDMLEllipsoid" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLEllipsoid" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['ax', 'by', 'cz', 'zcut1', 'zcut2', 'lunit']: + if prop in ["ax", "by", "cz", "zcut1", "zcut2", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -881,19 +1220,19 @@ def createGeometry(self, fp): mat.A44 = 1 if fp.zcut1 is not None: - zcut1 = fp.zcut1*mul + zcut1 = fp.zcut1 * mul else: - zcut1 = -2*cz + zcut1 = -2 * cz if fp.zcut2 is not None: - zcut2 = fp.zcut2*mul + zcut2 = fp.zcut2 * mul else: - zcut2 = 2*cz + zcut2 = 2 * cz GDMLShared.trace("zcut2 : " + str(zcut2)) t1ellipsoid = sphere.transformGeometry(mat) - if zcut2 > -cz and zcut2 < cz: # Remove from upper z - box1 = Part.makeBox(2*ax, 2*by, 2*cz) + 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, zcut2)) @@ -902,10 +1241,10 @@ def createGeometry(self, fp): else: t2ellipsoid = t1ellipsoid if zcut1 < zcut2 and zcut1 > -cz and zcut1 < cz: - box2 = Part.makeBox(2*ax, 2*by, 2*cz) + box2 = Part.makeBox(2 * ax, 2 * by, 2 * cz) pl = FreeCAD.Placement() # cut with the upper edge of the box - pl.move(FreeCAD.Vector(-ax, -by, -2*cz+zcut1)) + pl.move(FreeCAD.Vector(-ax, -by, -2 * cz + zcut1)) box2.Placement = pl shape = t2ellipsoid.cut(box2) else: @@ -913,54 +1252,65 @@ def createGeometry(self, fp): base = FreeCAD.Vector(0, 0, 0) fp.Shape = translate(shape, base) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLElTube(GDMLsolid): def __init__(self, obj, dx, dy, dz, lunit, material, colour=None): super().__init__(obj) - '''Add some custom properties to our Elliptical Tube feature''' - obj.addProperty("App::PropertyFloat", "dx", "GDMLElTube", - "x semi axis1").dx = dx - obj.addProperty("App::PropertyFloat", "dy", "GDMLElTube", - "y semi axis1").dy = dy - obj.addProperty("App::PropertyFloat", "dz", "GDMLElTube", - "z half height").dz = dz - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLElTube", - "lunit") + """Add some custom properties to our Elliptical Tube feature""" + obj.addProperty( + "App::PropertyFloat", "dx", "GDMLElTube", "x semi axis1" + ).dx = dx + obj.addProperty( + "App::PropertyFloat", "dy", "GDMLElTube", "y semi axis1" + ).dy = dy + obj.addProperty( + "App::PropertyFloat", "dz", "GDMLElTube", "z half height" + ).dz = dz + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLElTube", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLElTube", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLElTube", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLElTube' + self.Type = "GDMLElTube" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLElTube" def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - '''Do something when a property has changed''' - if 'Restore' in fp.State: + """Do something when a property has changed""" + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['dx', 'dy', 'dz', 'lunit']: + if prop in ["dx", "dy", "dz", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid + ''' def createGeometry(self, fp): currPlacement = fp.Placement mul = GDMLShared.getMult(fp) @@ -973,48 +1323,79 @@ def createGeometry(self, fp): mat.A44 = 1 # trace mat newtube = tube.transformGeometry(mat) - base = FreeCAD.Vector(0, 0, -(fp.dz*mul)) # dz is half height + base = FreeCAD.Vector(0, 0, -(fp.dz * mul)) # dz is half height fp.Shape = translate(newtube, base) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) + fp.Placement = currPlacement + ''' + def createGeometry(self, fp): + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + dx = fp.dx * mul + dy = fp.dy * mul + h = 2 * fp.dz * mul + if dy > dx: + ellipse = Part.Ellipse(FreeCAD.Vector(0, 0, 0), dy, dx) + else: + ellipse = Part.Ellipse(FreeCAD.Vector(0, 0, 0), dx, dy) + edge = Part.Edge(ellipse) + edge.translate(FreeCAD.Vector(0,0,-h/2)) + if dy > dx: + edge.rotate(FreeCAD.Vector(0,0,0), FreeCAD.Vector(0,0,1), 90) + wire = Part.Wire(edge) + face = Part.Face(wire) + solid = face.extrude(FreeCAD.Vector(0, 0, h)) + + fp.Shape = solid + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLOrb(GDMLsolid): def __init__(self, obj, r, lunit, material, colour=None): super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' + """Add some custom properties for Polyhedra feature""" obj.addProperty("App::PropertyFloat", "r", "GDMLOrb", "Radius").r = r - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLOrb", "lunit") + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLOrb", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLOrb", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLOrb", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLOrb' + self.Type = "GDMLOrb" self.Object = obj self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLOrb" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['r', 'lunit']: + if prop in ["r", "lunit"]: # print(dir(fp)) self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1026,66 +1407,94 @@ 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLPara(GDMLsolid): - def __init__(self, obj, x, y, z, alpha, theta, phi, aunit, lunit, - material, colour=None): + def __init__( + self, + obj, + x, + y, + z, + alpha, + theta, + phi, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' + """Add some custom properties for Polyhedra feature""" obj.addProperty("App::PropertyFloat", "x", "GDMLParapiped", "x").x = x obj.addProperty("App::PropertyFloat", "y", "GDMLParapiped", "y").y = y obj.addProperty("App::PropertyFloat", "z", "GDMLParapiped", "z").z = z - obj.addProperty("App::PropertyFloat", "alpha", "GDMLParapiped", - "Angle with y axis").alpha = alpha - obj.addProperty("App::PropertyFloat", "theta", "GDMLParapiped", - "Polar Angle with faces").theta = theta - obj.addProperty("App::PropertyFloat", "phi", "GDMLParapiped", - "Azimuthal Angle with faces").phi = phi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLParapiped", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLParapiped", - "lunit") + obj.addProperty( + "App::PropertyFloat", "alpha", "GDMLParapiped", "Angle with y axis" + ).alpha = alpha + obj.addProperty( + "App::PropertyFloat", + "theta", + "GDMLParapiped", + "Polar Angle with faces", + ).theta = theta + obj.addProperty( + "App::PropertyFloat", + "phi", + "GDMLParapiped", + "Azimuthal Angle with faces", + ).phi = phi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLParapiped", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLParapiped", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLParapiped", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLParapiped", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPara' + self.Type = "GDMLPara" self.colour = colour self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLPara" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['x', 'y', 'z', 'alpha', 'theta', 'phi', 'aunit', 'lunit']: + if prop in ["x", "y", "z", "alpha", "theta", "phi", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): currPlacement = fp.Placement - #GDMLShared.setTrace(True) + # GDMLShared.setTrace(True) GDMLShared.trace("Execute Polyparallepiped") mul = GDMLShared.getMult(fp) x = mul * fp.x @@ -1121,15 +1530,15 @@ def createGeometry(self, fp): # Apply alpha angle distortions # - dx = z*math.tan(alpha) + dx = z * math.tan(alpha) for i in range(0, 4): vzx2[i][0] += dx # # apply theta, phi distortions # - rho = z*math.tan(theta) - dx = rho*math.cos(phi) - dy = rho*math.sin(phi) + rho = z * math.tan(theta) + dx = rho * math.cos(phi) + dy = rho * math.sin(phi) for i in range(0, 4): vxy2[i][0] += dx vxy2[i][1] += dy @@ -1146,62 +1555,85 @@ def createGeometry(self, fp): # center is mid point of diagonal # - center = (v7 - v1)/2 + center = (v7 - v1) / 2 fp.Shape = translate(solid, -center) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLHype(GDMLsolid): - def __init__(self, obj, rmin, rmax, z, inst, outst, aunit, lunit, - material, colour=None): + def __init__( + self, + obj, + rmin, + rmax, + z, + inst, + outst, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties for Hyperbolic Tube feature''' - obj.addProperty("App::PropertyFloat", "rmin", "GDMLHype", - "inner radius at z=0").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "GDMLHype", - "outer radius at z=0").rmax = rmax - obj.addProperty("App::PropertyFloat", "z", "GDMLHype", - "Tube length").z = z - obj.addProperty("App::PropertyFloat", "inst", "GDMLHype", - "Inner stereo").inst = inst - obj.addProperty("App::PropertyFloat", "outst", "GDMLHype", - "Outer stero").outst = outst - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLHype", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLHype", - "lunit") + """Add some custom properties for Hyperbolic Tube feature""" + obj.addProperty( + "App::PropertyFloat", "rmin", "GDMLHype", "inner radius at z=0" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "GDMLHype", "outer radius at z=0" + ).rmax = rmax + obj.addProperty( + "App::PropertyFloat", "z", "GDMLHype", "Tube length" + ).z = z + obj.addProperty( + "App::PropertyFloat", "inst", "GDMLHype", "Inner stereo" + ).inst = inst + obj.addProperty( + "App::PropertyFloat", "outst", "GDMLHype", "Outer stero" + ).outst = outst + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLHype", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLHype", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLHype", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLHype", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLHype' + self.Type = "GDMLHype" self.colour = colour self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLHype" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['rmin', 'rmax', 'z', 'inst', 'outst', 'aunit', 'lunit']: + if prop in ["rmin", "rmax", "z", "inst", "outst", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1226,9 +1658,9 @@ def createGeometry(self, fp): # mirroring error checking in HepPolyhedron.cc k = 0 - if rmin < 0. or rmax < 0. or rmin >= rmax: + if rmin < 0.0 or rmax < 0.0 or rmin >= rmax: k = 1 - if z <= 0.: + if z <= 0.0: k += 2 if k != 0: @@ -1238,85 +1670,100 @@ def createGeometry(self, fp): if (k & 2) != 0: errmsg += " (half-length)" print(errmsg) - print(f' rmin= {rmin} rmax= {rmax} z= {z}') + print(f" rmin= {rmin} rmax= {rmax} z= {z}") return # Prepare two polylines ns = NUMBER_OF_DIVISIONS if ns < 3: ns = 3 - if sqrtan1 == 0.: + if sqrtan1 == 0.0: nz1 = 2 else: nz1 = ns + 1 - if sqrtan2 == 0.: + if sqrtan2 == 0.0: nz2 = 2 else: nz2 = ns + 1 - halfZ = z/2 + halfZ = z / 2 # # solid generated by external hyperbeloid - dz2 = z/(nz2 - 1) - zz = [halfZ - dz2*i for i in range(0, nz2)] - rr = [math.sqrt(sqrtan2*zi*zi + rmax*rmax) for zi in zz] + dz2 = z / (nz2 - 1) + zz = [halfZ - dz2 * i for i in range(0, nz2)] + rr = [math.sqrt(sqrtan2 * zi * zi + rmax * rmax) for zi in zz] outersolid = rotateAroundZ(NUMBER_OF_DIVISIONS, zz, rr) fp.Shape = outersolid if rmin != 0: # # solid generated by internal hyperbeloid - dz1 = z/(nz1 - 1) - zz = [halfZ - dz1*i for i in range(0, nz1)] - rr = [math.sqrt(sqrtan1*zi*zi + rmin*rmin) for zi in zz] + dz1 = z / (nz1 - 1) + zz = [halfZ - dz1 * i for i in range(0, nz1)] + 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLParaboloid(GDMLsolid): - def __init__(self, obj, rlo, rhi, dz, lunit, - material, colour=None): + def __init__(self, obj, rlo, rhi, dz, lunit, material, colour=None): super().__init__(obj) - '''Add some custom properties for the Paraboloid feature''' - obj.addProperty("App::PropertyFloat", "rlo", "GDMLParaboloid", - "radius at -z/2").rlo = rlo - obj.addProperty("App::PropertyFloat", "rhi", "GDMLParaboloid", - "radius at +z/2").rhi = rhi - obj.addProperty("App::PropertyFloat", "dz", "GDMLParaboloid", - "Paraboloid half length").dz = dz - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLParaboloid", - "lunit") + """Add some custom properties for the Paraboloid feature""" + obj.addProperty( + "App::PropertyFloat", "rlo", "GDMLParaboloid", "radius at -z/2" + ).rlo = rlo + obj.addProperty( + "App::PropertyFloat", "rhi", "GDMLParaboloid", "radius at +z/2" + ).rhi = rhi + obj.addProperty( + "App::PropertyFloat", + "dz", + "GDMLParaboloid", + "Paraboloid half length", + ).dz = dz + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLParaboloid", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLParaboloid", - "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLParaboloid", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLParaboloid' + self.Type = "GDMLParaboloid" self.colour = colour self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLParaboloid" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['rlo', 'rhi', 'z', 'lunit']: + if prop in ["rlo", "rhi", "z", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1335,7 +1782,7 @@ def createGeometry(self, fp): if dz < 0 or rlo > rhi: errmsg = "paraboloid: error in input parameters: dz < 0 and/or rlo > rhi" print(errmsg) - print(f' rlo= {rlo} rhi= {rhi} dz= {dz}') + print(f" rlo= {rlo} rhi= {rhi} dz= {dz}") return # Prepare polylines @@ -1345,70 +1792,94 @@ def createGeometry(self, fp): # k1 and k2 can be obtained from requirement: # rlo^2 = k1*(-dz) + k2 # rhi^2 = k1*(dz) + k2 - k1 = (rhi*rhi - rlo*rlo)/(2*dz) - k2 = (rhi*rhi + rlo*rlo)/2 + k1 = (rhi * rhi - rlo * rlo) / (2 * dz) + k2 = (rhi * rhi + rlo * rlo) / 2 # # solid generated by external hyperbeloid - deltaz = 2*dz/(ns - 1) - zz = [dz - deltaz*i for i in range(0, ns)] - rr = [math.sqrt(k1*zi+k2) for zi in zz] + deltaz = 2 * dz / (ns - 1) + zz = [dz - deltaz * i for i in range(0, ns)] + 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLPolyhedra(GDMLsolid): - def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, - material, colour=None): + def __init__( + self, + obj, + startphi, + deltaphi, + numsides, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties for Polyhedra feature''' - obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolyhedra", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolyhedra", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyInteger", "numsides", "GDMLPolyhedra", - "Number of Side").numsides = numsides - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolyhedra", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolyhdera", - "lunit") + """Add some custom properties for Polyhedra feature""" + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLPolyhedra", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLPolyhedra", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyInteger", + "numsides", + "GDMLPolyhedra", + "Number of Side", + ).numsides = numsides + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLPolyhedra", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLPolyhdera", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolyhedra", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLPolyhedra", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPolyhedra' + self.Type = "GDMLPolyhedra" self.colour = colour self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLPolyhedra" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: + if prop in ["startphi", "deltaphi", "numsides", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): + from math import sin, cos, pi + currPlacement = fp.Placement # GDMLShared.setTrace(True) GDMLShared.trace("Execute Polyhedra") @@ -1417,112 +1888,191 @@ def createGeometry(self, fp): numsides = fp.numsides GDMLShared.trace("Number of sides : " + str(numsides)) mul = GDMLShared.getMult(fp) - z0 = parms[0].z * mul + z0 = parms[0].z * mul rmin0 = parms[0].rmin * mul rmax0 = parms[0].rmax * mul GDMLShared.trace("Top z : " + str(z0)) GDMLShared.trace("Top rmin : " + str(rmin0)) GDMLShared.trace("Top rmax : " + str(rmax0)) - inner_faces = [] - outer_faces = [] - numsides = int(numsides * 360 / getAngleDeg(fp.aunit, fp.deltaphi)) + fullCircle = checkFullCircle(fp.aunit, fp.deltaphi) + faces = [] + # numsides = int(numsides * 360 / getAngleDeg(fp.aunit, fp.deltaphi)) # Deal with Inner Top Face # Could be point rmin0 = rmax0 = 0 - if rmin0 > 0: - inner_poly0 = makeRegularPolygon(numsides, rmin0, z0) - inner_faces.append(Part.Face(Part.makePolygon(inner_poly0))) - # Deal with Outer Top Face - outer_poly0 = makeRegularPolygon(numsides, rmax0, z0) - if rmax0 > 0: # Only make polygon if not a point - outer_faces.append(Part.Face(Part.makePolygon(outer_poly0))) + dPhi = getAngleRad(fp.aunit, fp.deltaphi) / numsides + phi0 = getAngleRad(fp.aunit, fp.startphi) + rp = rmin0 / cos(dPhi / 2) + inner_poly0 = [ + FreeCAD.Vector( + rp * cos(phi0 + i * dPhi), rp * sin(phi0 + i * dPhi), z0 + ) + for i in range(numsides + 1) + ] + rp = rmax0 / cos(dPhi / 2) + outer_poly0 = [ + FreeCAD.Vector( + rp * cos(phi0 + i * dPhi), rp * sin(phi0 + i * dPhi), z0 + ) + for i in range(numsides + 1) + ] + bottom_verts = inner_poly0 + outer_poly0[::-1] + bottom_verts.append(bottom_verts[0]) + if rmax0 > 0: + faces.append(Part.Face(Part.makePolygon(bottom_verts))) for ptr in parms[1:]: z1 = ptr.z * mul rmin1 = ptr.rmin * mul rmax1 = ptr.rmax * mul - GDMLShared.trace("z1 : "+str(z1)) - GDMLShared.trace("rmin1 : "+str(rmin1)) - GDMLShared.trace("rmax1 : "+str(rmax1)) + GDMLShared.trace("z1 : " + str(z1)) + GDMLShared.trace("rmin1 : " + str(rmin1)) + GDMLShared.trace("rmax1 : " + str(rmax1)) # Concat face lists - if rmin0 > 0: - inner_poly1 = makeRegularPolygon(numsides, rmin1, z1) - inner_faces = inner_faces + \ - makeFrustrum(numsides, inner_poly0, inner_poly1) - inner_poly0 = inner_poly1 - inner_faces.append(Part.Face(Part.makePolygon(inner_poly1))) + rp = rmin1 / cos(dPhi / 2) + inner_poly1 = [ + FreeCAD.Vector( + rp * cos(phi0 + i * dPhi), rp * sin(phi0 + i * dPhi), z1 + ) + for i in range(numsides + 1) + ] + faces = faces + makeFrustrum(numsides, inner_poly0, inner_poly1) + inner_poly0 = inner_poly1 # Deal with Outer - outer_poly1 = makeRegularPolygon(numsides, rmax1, z1) - outer_faces = outer_faces + \ - makeFrustrum(numsides, outer_poly0, outer_poly1) + rp = rmax1 / cos(dPhi / 2) + outer_poly1 = [ + FreeCAD.Vector( + rp * cos(phi0 + i * dPhi), rp * sin(phi0 + i * dPhi), z1 + ) + for i in range(numsides + 1) + ] + faces = faces + makeFrustrum(numsides, outer_poly0, outer_poly1) # update for next zsection outer_poly0 = outer_poly1 z0 = z1 - # add bottom polygon face - outer_faces.append(Part.Face(Part.makePolygon(outer_poly1))) - GDMLShared.trace("Total Faces : " + str(len(inner_faces))) - outer_shell = Part.makeShell(outer_faces) - outer_solid = Part.makeSolid(outer_shell) - if rmin0 > 0: - inner_shell = Part.makeShell(inner_faces) - inner_solid = Part.makeSolid(inner_shell) - shape = outer_solid.cut(inner_solid) - else: - shape = outer_solid - # fp.Shape = shell - if checkFullCircle(fp.aunit, fp.deltaphi) is False: - newShape = angleSectionSolid(fp, rmax1, z0, shape) - fp.Shape = newShape - else: - fp.Shape = shape - if hasattr(fp,'scale'): super().scale(fp) + + if not fullCircle: # build side faces + side0_verts = [] + for p in parms: + r = p.rmax * mul / cos(dPhi / 2) + side0_verts.append( + FreeCAD.Vector(r * cos(phi0), r * sin(phi0), p.z) + ) + for p in reversed(parms): + r = p.rmin * mul / cos(dPhi / 2) + side0_verts.append( + FreeCAD.Vector(r * cos(phi0), r * sin(phi0), p.z) + ) + side0_verts.append(side0_verts[0]) + faces.append(Part.Face(Part.makePolygon(side0_verts))) + siden_verts = [] + phi = phi0 + numsides * dPhi + for p in parms: + r = p.rmax * mul / cos(dPhi / 2) + siden_verts.append( + FreeCAD.Vector(r * cos(phi), r * sin(phi), p.z) + ) + for p in reversed(parms): + r = p.rmin * mul / cos(dPhi / 2) + siden_verts.append( + FreeCAD.Vector(r * cos(phi), r * sin(phi), p.z) + ) + siden_verts.append(siden_verts[0]) + faces.append(Part.Face(Part.makePolygon(siden_verts))) + + # add top polygon face + top_verts = outer_poly1 + inner_poly1[::-1] + top_verts.append(top_verts[0]) + if rmax1 > 0: + faces.append(Part.Face(Part.makePolygon(top_verts))) + GDMLShared.trace("Total Faces : " + str(len(faces))) + shell = Part.makeShell(faces) + fp.Shape = Part.makeSolid(shell) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLGenericPolyhedra(GDMLsolid): - def __init__(self, obj, startphi, deltaphi, numsides, aunit, lunit, - material, colour=None): + def __init__( + self, + obj, + startphi, + deltaphi, + numsides, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties for Generic Polyhedra feature''' - obj.addProperty("App::PropertyFloat", "startphi", "GDMLGenericPolyhedra", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLGenericPolyhedra", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyInteger", "numsides", "GDMLGenericPolyhedra", - "Number of Side").numsides = numsides - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLGenericPolyhedra", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLGenericPolyhdera", - "lunit") + """Add some custom properties for Generic Polyhedra feature""" + obj.addProperty( + "App::PropertyFloat", + "startphi", + "GDMLGenericPolyhedra", + "Start Angle", + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", + "deltaphi", + "GDMLGenericPolyhedra", + "Delta Angle", + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyInteger", + "numsides", + "GDMLGenericPolyhedra", + "Number of Side", + ).numsides = numsides + obj.addProperty( + "App::PropertyEnumeration", + "aunit", + "GDMLGenericPolyhedra", + "aunit", + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", + "lunit", + "GDMLGenericPolyhdera", + "lunit", + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLGenericPolyhedra", - "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLGenericPolyhedra", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLGenericPolyhedra' + self.Type = "GDMLGenericPolyhedra" self.colour = colour self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLGenericPolyhedra" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - - if prop in ['material']: + + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['startphi', 'deltaphi', 'numsides', 'aunit', 'lunit']: + if prop in ["startphi", "deltaphi", "numsides", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1541,103 +2091,148 @@ def createGeometry(self, fp): mul = GDMLShared.getMult(fp) faces = [] startphi = getAngleRad(fp.aunit, fp.startphi) - dphi = getAngleRad(fp.aunit, fp.deltaphi)/numsides + dphi = getAngleRad(fp.aunit, fp.deltaphi) / numsides # form vertexes verts = [] for ptr in rzpoints: z = ptr.z * mul r = ptr.r * mul phi = startphi - for i in range(0, numsides+1): - v = FreeCAD.Vector(r*math.cos(phi), r*math.sin(phi), z) + for i in range(0, numsides + 1): + v = FreeCAD.Vector(r * math.cos(phi), r * math.sin(phi), z) verts.append(v) phi += dphi numverts = len(verts) stride = numsides + 1 # outer faces - for k0 in range(0, numverts-stride, stride): + for k0 in range(0, numverts - stride, stride): for i in range(0, numsides): k = k0 + i - wire = Part.makePolygon([verts[k], verts[k+stride], - verts[k+stride+1], verts[k+1], - verts[k]]) - faces.append(Part.Face(wire)) + wire = Part.makePolygon( + [ + verts[k], + verts[k + stride], + verts[k + stride + 1], + verts[k + 1], + verts[k], + ] + ) + f = Part.Face(wire) + if f.Area != 0: + faces.append(f) # inner faces for i in range(0, numsides): k = numverts - stride + i - wire = Part.makePolygon([verts[i], verts[k], - verts[k+1], verts[i+1], - verts[i]]) - faces.append(Part.Face(wire)) + wire = Part.makePolygon( + [verts[i], verts[k], verts[k + 1], verts[i + 1], verts[i]] + ) + f = Part.Face(wire) + if f.Area != 0: + faces.append(f) # side faces if checkFullCircle(fp.aunit, fp.deltaphi) is False: - verts1 = [verts[k] for k in range(0, numverts - stride + 1, stride)] + verts1 = [ + verts[k] for k in range(0, numverts - stride + 1, stride) + ] verts1.append(verts1[0]) wire = Part.makePolygon(verts1) faces.append(Part.Face(wire)) verts1 = [verts[k] for k in range(numsides, numverts, stride)] verts1.append(verts1[0]) wire = Part.makePolygon(verts1) - faces.append(Part.Face(wire)) + f = Part.Face(wire) + if f.Area != 0: + faces.append(f) shell = Part.makeShell(faces) solid = Part.makeSolid(shell) fp.Shape = solid - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTorus(GDMLsolid): - def __init__(self, obj, rmin, rmax, rtor, startphi, deltaphi, - aunit, lunit, material, colour=None): + def __init__( + self, + obj, + rmin, + rmax, + rtor, + startphi, + deltaphi, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - obj.addProperty("App::PropertyFloat", "rmin", "GDMLTorus", - "rmin").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "GDMLTorus", - "rmax").rmax = rmax - obj.addProperty("App::PropertyFloat", "rtor", "GDMLTorus", - "rtor").rtor = rtor - obj.addProperty("App::PropertyFloat", "startphi", "GDMLTorus", - "startphi").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLTorus", - "deltaphi").deltaphi = deltaphi - obj.addProperty("App::PropertyString", "aunit", "GDMLTorus", - "aunit").aunit = aunit - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTorus", - "lunit") + obj.addProperty( + "App::PropertyFloat", "rmin", "GDMLTorus", "rmin" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "GDMLTorus", "rmax" + ).rmax = rmax + obj.addProperty( + "App::PropertyFloat", "rtor", "GDMLTorus", "rtor" + ).rtor = rtor + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLTorus", "startphi" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLTorus", "deltaphi" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyString", "aunit", "GDMLTorus", "aunit" + ).aunit = aunit + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTorus", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTorus", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLTorus", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTorus' + self.Type = "GDMLTorus" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTorus" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'rtor', 'startphi', 'deltaphi', - 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("G4_AIR - Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "rmin", + "rmax", + "rtor", + "startphi", + "deltaphi", + "aunit", + "lunit", + ]: # print(f'Change Prop : {prop}') self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1646,89 +2241,112 @@ def createGeometry(self, fp): currPlacement = fp.Placement GDMLShared.trace("Create Torus") mul = GDMLShared.getMult(fp) - rmin = mul*fp.rmin - rmax = mul*fp.rmax - rtor = mul*fp.rtor + rmin = mul * fp.rmin + rmax = mul * fp.rmax + rtor = mul * fp.rtor spnt = FreeCAD.Vector(0, 0, 0) sdir = FreeCAD.Vector(0, 0, 1) - outerTorus = Part.makeTorus(rtor, rmax, spnt, sdir, 0, 360, - getAngleDeg(fp.aunit, fp.deltaphi)) + outerTorus = Part.makeTorus( + rtor, rmax, spnt, sdir, 0, 360, getAngleDeg(fp.aunit, fp.deltaphi) + ) if rmin > 0: - innerTorus = Part.makeTorus(rtor, rmin, spnt, sdir, 0, 360, - getAngleDeg(fp.aunit, fp.deltaphi)) + innerTorus = Part.makeTorus( + rtor, + rmin, + spnt, + sdir, + 0, + 360, + getAngleDeg(fp.aunit, fp.deltaphi), + ) torus = outerTorus.cut(innerTorus) else: torus = outerTorus if fp.startphi != 0: torus.rotate(spnt, sdir, getAngleDeg(fp.aunit, fp.startphi)) fp.Shape = torus - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTwistedbox(GDMLsolid): - def __init__(self, obj, PhiTwist, x, y, z, aunit, lunit, material, - colour=None): + def __init__( + self, obj, PhiTwist, x, y, z, aunit, lunit, material, colour=None + ): super().__init__(obj) - '''Add some custom properties to our Box feature''' + """Add some custom properties to our Box feature""" GDMLShared.trace("GDMLTwistedbox init") # GDMLShared.trace("material : "+material) - obj.addProperty("App::PropertyFloat", "x", "GDMLTwistedbox", - "Length x").x = x - obj.addProperty("App::PropertyFloat", "y", "GDMLTwistedbox", - "Length y").y = y - obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedbox", - "Length z").z = z + obj.addProperty( + "App::PropertyFloat", "x", "GDMLTwistedbox", "Length x" + ).x = x + obj.addProperty( + "App::PropertyFloat", "y", "GDMLTwistedbox", "Length y" + ).y = y + obj.addProperty( + "App::PropertyFloat", "z", "GDMLTwistedbox", "Length z" + ).z = z angle = getAngleDeg(aunit, PhiTwist) if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') + print("PhiTwist angle cannot be larger than 90 deg") angle = 90 aunit = "deg" elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') + print("PhiTwist angle cannot be less than -90 deg") angle = -90 aunit = "deg" else: angle = PhiTwist - obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedbox", - "Twist Angle").PhiTwist = angle - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedbox", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedbox", - "lunit") + obj.addProperty( + "App::PropertyFloat", "PhiTwist", "GDMLTwistedbox", "Twist Angle" + ).PhiTwist = angle + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTwistedbox", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTwistedbox", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedbox", - "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTwistedbox", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedbox' + self.Type = "GDMLTwistedbox" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTwistedbox" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State): + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("G4_AIR - Set Transparency 98") + fp.ViewObject.Transparency = 98 - if prop in ['x', 'y', 'z', 'PhiTwist', 'lunit', 'aunit']: + if prop in ["x", "y", "z", "PhiTwist", "lunit", "aunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1742,97 +2360,169 @@ def createGeometry(self, fp): # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : mul = GDMLShared.getMult(fp) - GDMLShared.trace('mul : '+str(mul)) + GDMLShared.trace("mul : " + str(mul)) x = mul * fp.x y = mul * fp.y z = mul * fp.z angle = getAngleDeg(fp.aunit, fp.PhiTwist) # lower rectanngle vertexes - v1 = FreeCAD.Vector(-x/2, -y/2, -z/2) - v2 = FreeCAD.Vector(x/2, -y/2, -z/2) - v3 = FreeCAD.Vector(x/2, y/2, -z/2) - v4 = FreeCAD.Vector(-x/2, y/2, -z/2) + v1 = FreeCAD.Vector(-x / 2, -y / 2, -z / 2) + v2 = FreeCAD.Vector(x / 2, -y / 2, -z / 2) + v3 = FreeCAD.Vector(x / 2, y / 2, -z / 2) + v4 = FreeCAD.Vector(-x / 2, y / 2, -z / 2) pbot = Part.makePolygon([v1, v2, v3, v4, v1]) slices = [] N = 5 - dz = z/(N-1) - dPhi = angle/(N-1) + dz = z / (N - 1) + dPhi = angle / (N - 1) for i in range(0, N): - p = pbot.translated(FreeCAD.Vector(0, 0, i*dz)) - p.rotate(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), -angle/2 + i*dPhi) + p = pbot.translated(FreeCAD.Vector(0, 0, i * dz)) + p.rotate( + FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + -angle / 2 + i * dPhi, + ) slices.append(p) loft = Part.makeLoft(slices, True, False) fp.Shape = loft - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): - print('Doc Restored') + print("Doc Restored") class GDMLTwistedtrap(GDMLsolid): - def __init__(self, obj, PhiTwist, z, theta, phi, x1, x2, x3, x4, y1, y2, - alpha, aunit, lunit, material, colour=None): + def __init__( + self, + obj, + PhiTwist, + z, + theta, + phi, + x1, + x2, + x3, + x4, + y1, + y2, + alpha, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''General Trapezoid''' - obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedtrap", - "Twist angle").PhiTwist = PhiTwist - obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedtrap", "z").z = z - obj.addProperty("App::PropertyFloat", "Theta", "GDMLTwistedtrap", - "Theta").Theta = theta - obj.addProperty("App::PropertyFloat", "Phi", "GDMLTwistedtrap", - "Phi").Phi = phi - obj.addProperty("App::PropertyFloat", "x1", "GDMLTwistedtrap", - "Length x at y= -y1/2 of face at -z/2").x1 = x1 - obj.addProperty("App::PropertyFloat", "x2", "GDMLTwistedtrap", - "Length x at y= +y1/2 of face at -z/2").x2 = x2 - obj.addProperty("App::PropertyFloat", "x3", "GDMLTwistedtrap", - "Length x at y= -y2/2 of face at +z/2").x3 = x3 - obj.addProperty("App::PropertyFloat", "x4", "GDMLTwistedtrap", - "Length x at y= +y2/2 of face at +z/2").x4 = x4 - obj.addProperty("App::PropertyFloat", "y1", "GDMLTwistedtrap", - "Length y at face -z/2").y1 = y1 - obj.addProperty("App::PropertyFloat", "y2", "GDMLTwistedtrap", - "Length y at face +z/2").y2 = y2 - obj.addProperty("App::PropertyFloat", "Alph", "GDMLTwistedtrap", - "Alph").Alph = alpha - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtrap", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", - "GDMLTwistedtrap", "lunit") + """General Trapezoid""" + obj.addProperty( + "App::PropertyFloat", "PhiTwist", "GDMLTwistedtrap", "Twist angle" + ).PhiTwist = PhiTwist + obj.addProperty( + "App::PropertyFloat", "z", "GDMLTwistedtrap", "z" + ).z = z + obj.addProperty( + "App::PropertyFloat", "Theta", "GDMLTwistedtrap", "Theta" + ).Theta = theta + obj.addProperty( + "App::PropertyFloat", "Phi", "GDMLTwistedtrap", "Phi" + ).Phi = phi + obj.addProperty( + "App::PropertyFloat", + "x1", + "GDMLTwistedtrap", + "Length x at y= -y1/2 of face at -z/2", + ).x1 = x1 + obj.addProperty( + "App::PropertyFloat", + "x2", + "GDMLTwistedtrap", + "Length x at y= +y1/2 of face at -z/2", + ).x2 = x2 + obj.addProperty( + "App::PropertyFloat", + "x3", + "GDMLTwistedtrap", + "Length x at y= -y2/2 of face at +z/2", + ).x3 = x3 + obj.addProperty( + "App::PropertyFloat", + "x4", + "GDMLTwistedtrap", + "Length x at y= +y2/2 of face at +z/2", + ).x4 = x4 + obj.addProperty( + "App::PropertyFloat", + "y1", + "GDMLTwistedtrap", + "Length y at face -z/2", + ).y1 = y1 + obj.addProperty( + "App::PropertyFloat", + "y2", + "GDMLTwistedtrap", + "Length y at face +z/2", + ).y2 = y2 + obj.addProperty( + "App::PropertyFloat", "Alph", "GDMLTwistedtrap", "Alph" + ).Alph = alpha + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTwistedtrap", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTwistedtrap", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedtrap", - "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTwistedtrap", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLTwistedtrap' + self.Type = "GDMLTwistedtrap" + obj.Proxy.Type = "GDMLTwistedtrap" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['PhiTwist', 'z', 'theta', 'phi', - 'x1', 'x2', 'x3', 'x4', 'y1', 'y2', 'alpha', - 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("G4_AIR - Set Transparency 98") + fp.ViewObject.Transparency = 98 + + if prop in [ + "PhiTwist", + "z", + "theta", + "phi", + "x1", + "x2", + "x3", + "x4", + "y1", + "y2", + "alpha", + "aunit", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -1854,109 +2544,152 @@ def createGeometry(self, fp): z = mul * fp.z N = 9 - dz = z/(N-1) - dTwist = PhiTwist/(N-1) + dz = z / (N - 1) + dTwist = PhiTwist / (N - 1) tanalpha = math.tan(alpha) - dt = 1.0/(N-1) + dt = 1.0 / (N - 1) t = 0 slices = [] tanthet = math.tan(theta) cosphi = math.cos(phi) sinphi = math.sin(phi) - rhomax = z*tanthet - xoffset = -rhomax*cosphi/2 - yoffset = -rhomax*sinphi/2 + rhomax = z * tanthet + xoffset = -rhomax * cosphi / 2 + yoffset = -rhomax * sinphi / 2 for i in range(0, N): # Vertexes, counter clock wise order - y = y1 + t*(y2-y1) # go continuously from y1 to y2 - dx = y*tanalpha - x13 = x1 + t*(x3-x1) # go continuously from x1 to x3 - x24 = x2 + t*(x4-x2) # go continuously from x1 to x3 - zt = -z/2 + t*z - rho = i*dz*tanthet - dxphi = xoffset + rho*cosphi - dyphi = yoffset + rho*sinphi - v1 = FreeCAD.Vector(-x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) - v2 = FreeCAD.Vector( x13/2 - dx/2 + dxphi, -y/2 + dyphi, zt) - v3 = FreeCAD.Vector( x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) - v4 = FreeCAD.Vector(-x24/2 + dx/2 + dxphi, y/2 + dyphi, zt) + y = y1 + t * (y2 - y1) # go continuously from y1 to y2 + dx = y * tanalpha + x13 = x1 + t * (x3 - x1) # go continuously from x1 to x3 + x24 = x2 + t * (x4 - x2) # go continuously from x1 to x3 + zt = -z / 2 + t * z + rho = i * dz * tanthet + dxphi = xoffset + rho * cosphi + dyphi = yoffset + rho * sinphi + v1 = FreeCAD.Vector(-x13 / 2 - dx / 2 + dxphi, -y / 2 + dyphi, zt) + v2 = FreeCAD.Vector(x13 / 2 - dx / 2 + dxphi, -y / 2 + dyphi, zt) + v3 = FreeCAD.Vector(x24 / 2 + dx / 2 + dxphi, y / 2 + dyphi, zt) + v4 = FreeCAD.Vector(-x24 / 2 + dx / 2 + dxphi, y / 2 + dyphi, zt) p = Part.makePolygon([v1, v2, v3, v4, v1]) - p.rotate(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), -PhiTwist/2 + i*dTwist) + p.rotate( + FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + -PhiTwist / 2 + i * dTwist, + ) slices.append(p) t += dt loft = Part.makeLoft(slices, True, False) fp.Shape = loft - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTwistedtrd(GDMLsolid): - def __init__(self, obj, PhiTwist, z, x1, x2, y1, y2, aunit, lunit, - material, colour=None): + def __init__( + self, + obj, + PhiTwist, + z, + x1, + x2, + y1, + y2, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) "3.4.15 : Trapezoid – x & y varying along z" obj.addProperty("App::PropertyFloat", "z", "GDMLTwistedtrd", "z").z = z - obj.addProperty("App::PropertyFloat", "x1", "GDMLTwistedtrd", - "Length x at face -z/2").x1 = x1 - obj.addProperty("App::PropertyFloat", "x2", "GDMLTwistedtrd", - "Length x at face +z/2").x2 = x2 - obj.addProperty("App::PropertyFloat", "y1", "GDMLTwistedtrd", - "Length y at face -z/2").y1 = y1 - obj.addProperty("App::PropertyFloat", "y2", "GDMLTwistedtrd", - "Length y at face +z/2").y2 = y2 - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedtrd", - "lunit") + obj.addProperty( + "App::PropertyFloat", + "x1", + "GDMLTwistedtrd", + "Length x at face -z/2", + ).x1 = x1 + obj.addProperty( + "App::PropertyFloat", + "x2", + "GDMLTwistedtrd", + "Length x at face +z/2", + ).x2 = x2 + obj.addProperty( + "App::PropertyFloat", + "y1", + "GDMLTwistedtrd", + "Length y at face -z/2", + ).y1 = y1 + obj.addProperty( + "App::PropertyFloat", + "y2", + "GDMLTwistedtrd", + "Length y at face +z/2", + ).y2 = y2 + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTwistedtrd", "lunit" + ) angle = getAngleDeg(aunit, PhiTwist) if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') + print("PhiTwist angle cannot be larger than 90 deg") angle = 90 aunit = "deg" elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') + print("PhiTwist angle cannot be less than -90 deg") angle = -90 aunit = "deg" else: angle = PhiTwist - obj.addProperty("App::PropertyFloat", "PhiTwist", "GDMLTwistedtrd", - "Twist Angle").PhiTwist = angle - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtrd", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty( + "App::PropertyFloat", "PhiTwist", "GDMLTwistedtrd", "Twist Angle" + ).PhiTwist = angle + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTwistedtrd", "aunit" + ) + setAngleQuantity(obj, aunit) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLTwistedtrd", "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTwistedtrd", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedtrd' + self.Type = "GDMLTwistedtrd" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTwistedtrd" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State): + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("G4_AIR - Set Transparency 98") + fp.ViewObject.Transparency = 98 - if prop in ['x1', 'y1', 'x2', 'y2', 'z', 'PhiTwist', 'lunit', 'aunit']: + if prop in ["x1", "y1", "x2", "y2", "z", "PhiTwist", "lunit", "aunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) + super().scale(fp) # def execute(self, fp): in GDMLsolid @@ -1969,101 +2702,145 @@ def createGeometry(self, fp): # if (hasattr(fp,'x') and hasattr(fp,'y') and hasattr(fp,'z')) : mul = GDMLShared.getMult(fp) - x1 = (fp.x1 * mul) - x2 = (fp.x2 * mul) - y1 = (fp.y1 * mul) - y2 = (fp.y2 * mul) - z = (fp.z * mul) - GDMLShared.trace('mul : ' + str(mul)) + x1 = fp.x1 * mul + x2 = fp.x2 * mul + y1 = fp.y1 * mul + y2 = fp.y2 * mul + z = fp.z * mul + GDMLShared.trace("mul : " + str(mul)) angle = getAngleDeg(fp.aunit, fp.PhiTwist) slices = [] N = 9 # number of slices - dz = z/(N-1) - dPhi = angle/(N-1) + dz = z / (N - 1) + dPhi = angle / (N - 1) for i in range(0, N): - t = i*1./(N-1) - xside = x1 + t*(x2 - x1) - yside = y1 + t*(y2 - y1) - v1 = FreeCAD.Vector(-xside/2, -yside/2, -z/2 + i*dz) - v2 = FreeCAD.Vector( xside/2, -yside/2, -z/2 + i*dz) - v3 = FreeCAD.Vector( xside/2, yside/2, -z/2 + i*dz) - v4 = FreeCAD.Vector(-xside/2, yside/2, -z/2 + i*dz) + t = i * 1.0 / (N - 1) + xside = x1 + t * (x2 - x1) + yside = y1 + t * (y2 - y1) + v1 = FreeCAD.Vector(-xside / 2, -yside / 2, -z / 2 + i * dz) + v2 = FreeCAD.Vector(xside / 2, -yside / 2, -z / 2 + i * dz) + v3 = FreeCAD.Vector(xside / 2, yside / 2, -z / 2 + i * dz) + v4 = FreeCAD.Vector(-xside / 2, yside / 2, -z / 2 + i * dz) p = Part.makePolygon([v1, v2, v3, v4, v1]) - p.rotate(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), -angle/2 + i*dPhi) + p.rotate( + FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + -angle / 2 + i * dPhi, + ) slices.append(p) loft = Part.makeLoft(slices, True, False) fp.Shape = loft - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): - print('Doc Restored') + print("Doc Restored") class GDMLTwistedtubs(GDMLsolid): - def __init__(self, obj, endinnerrad, endouterrad, zlen, twistedangle, - phi, aunit, lunit, material, colour=None): + def __init__( + self, + obj, + endinnerrad, + endouterrad, + zlen, + twistedangle, + phi, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Twisted tube''' - obj.addProperty("App::PropertyFloat", "zlen", "GDMLTwistedtubs", - "zlen").zlen = zlen - obj.addProperty("App::PropertyFloat", "endinnerrad", "GDMLTwistedtubs", - "Inside radius at caps").endinnerrad = endinnerrad - obj.addProperty("App::PropertyFloat", "endouterrad", "GDMLTwistedtubs", - "Outside radius at caps").endouterrad = endouterrad - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTwistedtubs", - "lunit") + """Twisted tube""" + obj.addProperty( + "App::PropertyFloat", "zlen", "GDMLTwistedtubs", "zlen" + ).zlen = zlen + obj.addProperty( + "App::PropertyFloat", + "endinnerrad", + "GDMLTwistedtubs", + "Inside radius at caps", + ).endinnerrad = endinnerrad + obj.addProperty( + "App::PropertyFloat", + "endouterrad", + "GDMLTwistedtubs", + "Outside radius at caps", + ).endouterrad = endouterrad + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTwistedtubs", "lunit" + ) angle = getAngleDeg(aunit, twistedangle) if angle > 90: - print(f'PhiTwist angle cannot be larger than 90 deg') + print("PhiTwist angle cannot be larger than 90 deg") angle = 90 aunit = "deg" elif angle < -90: - print(f'PhiTwist angle cannot be less than -90 deg') + print("PhiTwist angle cannot be less than -90 deg") angle = -90 aunit = "deg" else: angle = twistedangle - obj.addProperty("App::PropertyFloat", "twistedangle", "GDMLTwistedtubs", - "Twist Angle").twistedangle = angle - obj.addProperty("App::PropertyFloat", "phi", "GDMLTwistedtubs", - "Delta phi").phi = phi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTwistedtubs", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) + obj.addProperty( + "App::PropertyFloat", + "twistedangle", + "GDMLTwistedtubs", + "Twist Angle", + ).twistedangle = angle + obj.addProperty( + "App::PropertyFloat", "phi", "GDMLTwistedtubs", "Delta phi" + ).phi = phi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTwistedtubs", "aunit" + ) + setAngleQuantity(obj, aunit) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTwistedtubs", - "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTwistedtubs", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTwistedtubs' + self.Type = "GDMLTwistedtubs" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTwistedtubs" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # Changing Shape in createGeometry will redrive onChanged - if ('Restore' in fp.State): + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['endinnerrad', 'endouterrad', 'zlen', 'twistedangle', - 'phi', 'lunit', 'aunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + if prop in [ + "endinnerrad", + "endouterrad", + "zlen", + "twistedangle", + "phi", + "lunit", + "aunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2076,32 +2853,40 @@ def createGeometry(self, fp): currPlacement = fp.Placement mul = GDMLShared.getMult(fp) - rin = (fp.endinnerrad * mul) - rout = (fp.endouterrad * mul) + rin = fp.endinnerrad * mul + rout = fp.endouterrad * mul if rin > rout: - print(f'Erro: Inner radius ({rin}) greater than outer radius ({rout})') + print( + f"Error: Inner radius ({rin}) greater than outer radius ({rout})" + ) return - zlen = (fp.zlen * mul) - GDMLShared.trace('mul : ' + str(mul)) + zlen = fp.zlen * mul + GDMLShared.trace("mul : " + str(mul)) angle = getAngleDeg(fp.aunit, fp.twistedangle) phi = getAngleRad(fp.aunit, fp.phi) phideg = getAngleDeg(fp.aunit, fp.phi) slices = [] - N = 9 # number of slices - dz = zlen/(N-1) - dtwist = angle/(N-1) + N = 20 # number of slices + dz = zlen / (N - 1) + dtwist = angle / (N - 1) # construct base wire # Vertexes v1 = FreeCAD.Vector(rin, 0, 0) v2 = FreeCAD.Vector(rout, 0, 0) - v3 = FreeCAD.Vector(rout*math.cos(phi), rout*math.sin(phi), 0) - v4 = FreeCAD.Vector(rin*math.cos(phi), rin*math.sin(phi), 0) + v3 = FreeCAD.Vector(rout * math.cos(phi), rout * math.sin(phi), 0) + v4 = FreeCAD.Vector(rin * math.cos(phi), rin * math.sin(phi), 0) # arc center points - vCin = FreeCAD.Vector(rin*math.cos(phi/2), rin*math.sin(phi/2), 0) - vCout = FreeCAD.Vector(rout*math.cos(phi/2), rout*math.sin(phi/2), 0) + vCin = FreeCAD.Vector( + rin * math.cos(phi / 2), rin * math.sin(phi / 2), 0 + ) + vCout = FreeCAD.Vector( + rout * math.cos(phi / 2), rout * math.sin(phi / 2), 0 + ) # Center of twisting - rc = (rin + rout)/2 - vc = FreeCAD.Vector(rc*math.cos(phi/2), rc*math.sin(phi/2), 0) + rc = (rin + rout) / 2 + vc = FreeCAD.Vector( + rc * math.cos(phi / 2), rc * math.sin(phi / 2), 0 + ) # wire arcin = Part.Arc(v1, vCin, v4) line1 = Part.LineSegment(v4, v3) @@ -2110,55 +2895,64 @@ def createGeometry(self, fp): s = Part.Shape([arcin, line1, arcout, line2]) w = Part.Wire(s.Edges) - angoffset = -angle/2 - phideg/2 + angoffset = -angle / 2 - phideg / 2 for i in range(0, N): - p = w.translated(FreeCAD.Vector(0, 0, -zlen/2 + i*dz)) - p.rotate(vc, FreeCAD.Vector(0, 0, 1), angoffset + i*dtwist) + p = w.translated(FreeCAD.Vector(0, 0, -zlen / 2 + i * dz)) + # p.rotate(vc, FreeCAD.Vector(0, 0, 1), angoffset + i * dtwist) + p.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), angoffset + i * dtwist) slices.append(p) loft = Part.makeLoft(slices, True, False) fp.Shape = loft - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement def OnDocumentRestored(self, obj): - print('Doc Restored') - + print("Doc Restored") + class GDMLXtru(GDMLsolid): def __init__(self, obj, lunit, material, colour=None): super().__init__(obj) - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLXtru", "lunit") + obj.addExtension("App::GroupExtensionPython") + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLXtru", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLXtru", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLXtru", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLXtru' + self.Type = "GDMLXtru" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLXtru" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + if prop in ["startphi", "deltaphi", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2167,8 +2961,11 @@ def layerPoints(self, polyList, sf, xOffset, yOffset, zPosition): vl = [] for p in polyList: # print(p) - vl.append(FreeCAD.Vector(p[0]*sf+xOffset, p[1]*sf+yOffset, - zPosition)) + vl.append( + FreeCAD.Vector( + p[0] * sf + xOffset, p[1] * sf + yOffset, zPosition + ) + ) # Close list vl.append(vl[0]) return vl @@ -2186,18 +2983,18 @@ def createGeometry(self, fp): sections = [] mul = GDMLShared.getMult(fp) for ptr in parms: - if hasattr(ptr, 'x'): + if hasattr(ptr, "x"): x = ptr.x * mul y = ptr.y * mul - GDMLShared.trace('x : '+str(x)) - GDMLShared.trace('y : '+str(y)) + GDMLShared.trace("x : " + str(x)) + GDMLShared.trace("y : " + str(y)) polyList.append([x, y]) - if hasattr(ptr, 'zOrder'): + if hasattr(ptr, "zOrder"): zOrder = ptr.zOrder xOffset = ptr.xOffset * mul yOffset = ptr.yOffset * mul zPosition = ptr.zPosition * mul - sf = ptr.scalingFactor * mul + sf = ptr.scalingFactor s = [zOrder, xOffset, yOffset, zPosition, sf] sections.append(s) # print('sections : '+str(len(sections))) @@ -2205,80 +3002,59 @@ def createGeometry(self, fp): # Deal with Base Face # # baseList = layerPoints(polyList,sf,xOffset,yOffset,zPosition): - baseList = self.layerPoints(polyList, sections[0][4], sections[0][1], - sections[0][2], sections[0][3]) - # print('baseList') - # print(baseList) - w1 = Part.makePolygon(baseList) - f1 = Part.Face(w1) - f1.reverse() - faceList.append(f1) - # print("base list") - # - # Deal with Sides - # - # print("Start Range "+str(len(sections)-1)) - for s in range(0, len(sections)-1): - xOffset = sections[s+1][1] - yOffset = sections[s+1][2] - zPosition = sections[s+1][3] - sf2 = sections[s+1][4] - # layerList = layerPoints(polyList,sf,xOffset,yOffset,zPosition) - layerList = self.layerPoints(polyList, sf, xOffset, yOffset, zPosition) - # deal with side faces - # remember first point is added to end of list - # print("Number Sides : "+str(len(baseList)-1)) - for i in range(0, len(baseList)-2): - sideList = [] - sideList.append(baseList[i]) - sideList.append(baseList[i+1]) - sideList.append(layerList[i+1]) - sideList.append(layerList[i]) - # Close SideList polygon - sideList.append(baseList[i]) - # print("sideList") - # print(sideList) - w1 = Part.makePolygon(sideList) - f1 = Part.Face(w1) - faceList.append(f1) - # - # Deal with Top Face - # - w1 = Part.makePolygon(layerList) - f1 = Part.Face(w1) - # f1.reverse() - faceList.append(f1) - # print("Faces List") - # print(faceList) + # form all vertexes + verts = [] + for s in sections: + verts += self.layerPoints(polyList, s[4], s[1], s[2], s[3]) + + numverts = len(verts) + numsides = len(polyList) + stride = numsides + 1 + # side faces + for k0 in range(0, numverts - stride, stride): + for i in range(0, numsides): + k = k0 + i + wire = Part.makePolygon( + [ + verts[k], + verts[k + stride], + verts[k + stride + 1], + verts[k + 1], + verts[k], + ] + ) + faceList.append(Part.Face(wire)) + # bottom face + wire = Part.makePolygon(verts[0 : numsides + 1]) + faceList.append(Part.Face(wire)) + # Top face + wire = Part.makePolygon(verts[numverts - numsides - 1 :]) + faceList.append(Part.Face(wire)) + shell = Part.makeShell(faceList) - # solid=Part.Solid(shell).removeSplitter() - solid = Part.Solid(shell) - # print("Valid Solid : "+str(solid.isValid())) - if solid.Volume < 0: - solid.reverse() - # print(dir(fp)) - # solid.exportBrep("/tmp/"+fp.Label+".brep") + solid = Part.makeSolid(shell) fp.Shape = solid - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDML2dVertex(GDMLcommon): def __init__(self, obj, x, y): super().__init__(obj) - obj.addProperty("App::PropertyString", "Type", "Vertex", - "twoDimVertex").Type = 'twoDimVertex' - obj.addProperty("App::PropertyFloat", "x", "Vertex", - "x").x = x - obj.addProperty("App::PropertyFloat", "y", "Vertex", - "y").y = y + obj.addProperty( + "App::PropertyString", "Type", "Vertex", "twoDimVertex" + ).Type = "twoDimVertex" + obj.addProperty("App::PropertyFloat", "x", "Vertex", "x").x = x + obj.addProperty("App::PropertyFloat", "y", "Vertex", "y").y = y obj.setEditorMode("Type", 1) - self.Type = 'Vertex' + self.Type = "Vertex" self.Object = obj obj.Proxy = self + obj.Proxy.Type = "Vertex" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # if prop in ['x','y'] : # self.execute(fp) @@ -2290,26 +3066,35 @@ def execute(self, fp): class GDMLSection(GDMLcommon): - def __init__(self, obj, zOrder, zPosition, xOffset, yOffset, scalingFactor): + def __init__( + self, obj, zOrder, zPosition, xOffset, yOffset, scalingFactor + ): super().__init__(obj) - obj.addProperty("App::PropertyString", "Type", "section", - "section").Type = 'section' - obj.addProperty("App::PropertyInteger", "zOrder", "section", - "zOrder").zOrder = zOrder - obj.addProperty("App::PropertyFloat", "zPosition", "section", - "zPosition").zPosition = zPosition - obj.addProperty("App::PropertyFloat", "xOffset", "section", - "xOffset").xOffset = xOffset - obj.addProperty("App::PropertyFloat", "yOffset", "section", - "yOffset").yOffset = yOffset - obj.addProperty("App::PropertyFloat", "scalingFactor", "section", - "scalingFactor").scalingFactor = scalingFactor + obj.addProperty( + "App::PropertyString", "Type", "section", "section" + ).Type = "section" + obj.addProperty( + "App::PropertyInteger", "zOrder", "section", "zOrder" + ).zOrder = zOrder + obj.addProperty( + "App::PropertyFloat", "zPosition", "section", "zPosition" + ).zPosition = zPosition + obj.addProperty( + "App::PropertyFloat", "xOffset", "section", "xOffset" + ).xOffset = xOffset + obj.addProperty( + "App::PropertyFloat", "yOffset", "section", "yOffset" + ).yOffset = yOffset + obj.addProperty( + "App::PropertyFloat", "scalingFactor", "section", "scalingFactor" + ).scalingFactor = scalingFactor obj.setEditorMode("Type", 1) - self.Type = 'section' + self.Type = "section" obj.Proxy = self + obj.Proxy.Type = "section" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # if prop in ['zOrder','zPosition','xOffset','yOffset','scaleFactor'] : # self.execute(fp) @@ -2323,16 +3108,19 @@ def execute(self, fp): class GDMLzplane(GDMLcommon): def __init__(self, obj, rmin, rmax, z): super().__init__(obj) - obj.addProperty("App::PropertyFloat", "rmin", "zplane", - "Inside Radius").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "zplane", - "Outside Radius").rmax = rmax + obj.addProperty( + "App::PropertyFloat", "rmin", "zplane", "Inside Radius" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "zplane", "Outside Radius" + ).rmax = rmax obj.addProperty("App::PropertyFloat", "z", "zplane", "z").z = z - self.Type = 'zplane' + self.Type = "zplane" obj.Proxy = self + obj.Proxy.Type = "zplane" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # if not ('Restore' in fp.State) : # if prop in ['rmin','rmax','z'] : @@ -2347,15 +3135,18 @@ def execute(self, fp): class GDMLrzpoint(GDMLcommon): def __init__(self, obj, r, z): super().__init__(obj) - obj.addProperty("App::PropertyFloat", "r", "rzpoint", - "r-coordinate").r = r - obj.addProperty("App::PropertyFloat", "z", "rzpoint", - "z-coordinate").z = z - self.Type = 'zplane' + obj.addProperty( + "App::PropertyFloat", "r", "rzpoint", "r-coordinate" + ).r = r + obj.addProperty( + "App::PropertyFloat", "z", "rzpoint", "z-coordinate" + ).z = z + self.Type = "zplane" obj.Proxy = self + obj.Proxy.Type = "zplane" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # if not ('Restore' in fp.State) : # if prop in ['rmin','rmax','z'] : @@ -2368,24 +3159,29 @@ def execute(self, fp): class GDMLPolycone(GDMLsolid): # Thanks to Dam Lamb - def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, - colour=None): + def __init__( + self, obj, startphi, deltaphi, aunit, lunit, material, colour=None + ): super().__init__(obj) - '''Add some custom properties to our Polycone feature''' - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolycone", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolycone", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolycone", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolycone", - "lunit") + """Add some custom properties to our Polycone feature""" + obj.addExtension("App::GroupExtensionPython") + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLPolycone", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLPolycone", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLPolycone", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLPolycone", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolycone", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLPolycone", "Material" + ) setMaterial(obj, material) # For debugging # obj.setEditorMode('Placement',0) @@ -2393,26 +3189,30 @@ def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLPolycone' + self.Type = "GDMLPolycone" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLPolycone" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + if prop in ["startphi", "deltaphi", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2425,13 +3225,14 @@ def createGeometry(self, fp): mul = GDMLShared.getMult(fp.lunit) offset = zplanes[0].z * mul angleDeltaPhiDeg = 360.0 - if (hasattr(fp, 'deltaphi')): - angleDeltaPhiDeg = min([getAngleDeg(fp.aunit, fp.deltaphi), - angleDeltaPhiDeg]) - if(angleDeltaPhiDeg <= 0.0): + if hasattr(fp, "deltaphi"): + angleDeltaPhiDeg = min( + [getAngleDeg(fp.aunit, fp.deltaphi), angleDeltaPhiDeg] + ) + if angleDeltaPhiDeg <= 0.0: return - listShape = [0 for i in range((len(zplanes)-1))] + listShape = [0 for i in range((len(zplanes) - 1))] sinPhi = 0.0 cosPhi = 1.0 @@ -2441,57 +3242,70 @@ def createGeometry(self, fp): cosPhi = math.cos(angleRad) # loops on each z level - for i in range(len(zplanes)-1): - GDMLShared.trace('index : ' + str(i)) + for i in range(len(zplanes) - 1): + GDMLShared.trace("index : " + str(i)) if i == 0: rmin1 = zplanes[i].rmin * mul rmax1 = zplanes[i].rmax * mul - z1 = zplanes[i].z * mul - offset + z1 = zplanes[i].z * mul else: rmin1 = rmin2 # for i > 0, rmin2 will have been defined below rmax1 = rmax2 z1 = z2 - rmin2 = zplanes[i+1].rmin * mul - rmax2 = zplanes[i+1].rmax * mul - z2 = zplanes[i+1].z * mul - offset + rmin2 = zplanes[i + 1].rmin * mul + rmax2 = zplanes[i + 1].rmax * mul + z2 = zplanes[i + 1].z * mul # def of one face to rotate - face = Part.Face(Part.makePolygon([ - FreeCAD.Vector(rmin1*cosPhi, rmin1*sinPhi, z1), - FreeCAD.Vector(rmax1*cosPhi, rmax1*sinPhi, z1), - FreeCAD.Vector(rmax2*cosPhi, rmax2*sinPhi, z2), - FreeCAD.Vector(rmin2*cosPhi, rmin2*sinPhi, z2), - FreeCAD.Vector(rmin1*cosPhi, rmin1*sinPhi, z1)])) + face = Part.Face( + Part.makePolygon( + [ + FreeCAD.Vector(rmin1 * cosPhi, rmin1 * sinPhi, z1), + FreeCAD.Vector(rmax1 * cosPhi, rmax1 * sinPhi, z1), + FreeCAD.Vector(rmax2 * cosPhi, rmax2 * sinPhi, z2), + FreeCAD.Vector(rmin2 * cosPhi, rmin2 * sinPhi, z2), + FreeCAD.Vector(rmin1 * cosPhi, rmin1 * sinPhi, z1), + ] + ) + ) # rotation of the face - listShape[i] = face.revolve(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), - angleDeltaPhiDeg) + listShape[i] = face.revolve( + FreeCAD.Vector(0, 0, 0), + FreeCAD.Vector(0, 0, 1), + angleDeltaPhiDeg, + ) # compound of all faces fp.Shape = Part.makeCompound(listShape) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLGenericPolycone(GDMLsolid): # Thanks to Dam Lamb - def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, - colour=None): + def __init__( + self, obj, startphi, deltaphi, aunit, lunit, material, colour=None + ): super().__init__(obj) - '''Add some custom properties to our GenericPolycone feature''' - obj.addExtension('App::GroupExtensionPython') - obj.addProperty("App::PropertyFloat", "startphi", "GDMLPolycone", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLPolycone", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLPolycone", - "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLPolycone", - "lunit") + """Add some custom properties to our GenericPolycone feature""" + obj.addExtension("App::GroupExtensionPython") + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLPolycone", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLPolycone", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLPolycone", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLPolycone", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLPolycone", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLPolycone", "Material" + ) setMaterial(obj, material) # For debugging # obj.setEditorMode('Placement',0) @@ -2499,26 +3313,30 @@ def __init__(self, obj, startphi, deltaphi, aunit, lunit, material, updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLGenericPolycone' - self.colour = colour - obj.Proxy = self + self.Type = "GDMLGenericPolycone" + self.colour = colour + obj.Proxy = self + obj.Proxy.Type = "GDMLGenericPolycone" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['startphi', 'deltaphi', 'aunit', 'lunit']: + if prop in ["startphi", "deltaphi", "aunit", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2535,74 +3353,109 @@ def createGeometry(self, fp): startphi = getAngleDeg(fp.aunit, fp.startphi) mul = GDMLShared.getMult(fp.lunit) - verts = [FreeCAD.Vector(rz.r*mul, 0, rz.z*mul) for rz in rzpoints] - verts.append(FreeCAD.Vector(rzpoints[0].r*mul, 0, rzpoints[0].z*mul)) + verts = [FreeCAD.Vector(rz.r * mul, 0, rz.z * mul) for rz in rzpoints] + verts.append( + FreeCAD.Vector(rzpoints[0].r * mul, 0, rzpoints[0].z * mul) + ) line = Part.makePolygon(verts) - line.rotate(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), startphi) + line.rotate(FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), startphi) face = Part.Face(line) - surf = face.revolve(FreeCAD.Vector(0, 0, 0), - FreeCAD.Vector(0, 0, 1), deltaphi) + surf = face.revolve( + FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(0, 0, 1), deltaphi + ) solid = Part.makeSolid(surf) fp.Shape = solid - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLSphere(GDMLsolid): - def __init__(self, obj, rmin, rmax, startphi, deltaphi, starttheta, - deltatheta, aunit, lunit, material, colour=None ): + def __init__( + self, + obj, + rmin, + rmax, + startphi, + deltaphi, + starttheta, + deltatheta, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties to our Sphere feature''' + """Add some custom properties to our Sphere feature""" GDMLShared.trace("GDMLSphere init") - obj.addProperty("App::PropertyFloat", "rmin", "GDMLSphere", - "Inside Radius").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "GDMLSphere", - "Outside Radius").rmax = rmax - obj.addProperty("App::PropertyFloat", "startphi", "GDMLSphere", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLSphere", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyFloat", "starttheta", "GDMLSphere", - "Start Theta pos").starttheta = starttheta - obj.addProperty("App::PropertyFloat", "deltatheta", "GDMLSphere", - "Delta Angle").deltatheta = deltatheta - obj.addProperty("App::PropertyEnumeration", "aunit", - "GDMLSphere", "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLSphere", - "lunit") + obj.addProperty( + "App::PropertyFloat", "rmin", "GDMLSphere", "Inside Radius" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "GDMLSphere", "Outside Radius" + ).rmax = rmax + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLSphere", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLSphere", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyFloat", "starttheta", "GDMLSphere", "Start Theta pos" + ).starttheta = starttheta + obj.addProperty( + "App::PropertyFloat", "deltatheta", "GDMLSphere", "Delta Angle" + ).deltatheta = deltatheta + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLSphere", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLSphere", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLSphere", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLSphere", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLSphere' + self.Type = "GDMLSphere" + obj.Proxy.Type = "GDMLSphere" self.colour = colour def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'startphi', 'deltaphi', 'starttheta', - 'deltatheta', 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "rmin", + "rmax", + "startphi", + "deltaphi", + "starttheta", + "deltatheta", + "aunit", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2614,17 +3467,15 @@ def createGeometry(self, fp): rmax = mul * fp.rmax if rmax <= 0.0: return - Rmax = 2 * rmax rmin = mul * fp.rmin spos = FreeCAD.Vector(0, 0, 0) sdir = FreeCAD.Vector(0, 0, 1) HalfPi = math.pi / 2.0 - TwoPi = 2 * math.pi deltaphi_deg = getAngleDeg(fp.aunit, fp.deltaphi) if deltaphi_deg < 360.0 and deltaphi_deg > 0: - sphere2 = Part.makeSphere(rmax, spos, sdir, - -90.0, 90.0, - deltaphi_deg) + sphere2 = Part.makeSphere( + rmax, spos, sdir, -90.0, 90.0, deltaphi_deg + ) if fp.startphi != 0: sphere2.rotate(spos, sdir, getAngleDeg(fp.aunit, fp.startphi)) else: @@ -2636,25 +3487,36 @@ def createGeometry(self, fp): if startthetaDeg > 0.0: if startthetaDeg == 90.0: - cylToCut = Part.makeCylinder(2.0*rmax, rmax, - FreeCAD.Vector(0, 0, 0)) + cylToCut = Part.makeCylinder( + 2.0 * rmax, rmax, FreeCAD.Vector(0, 0, 0) + ) sphere2 = sphere2.cut(cylToCut) elif startthetaDeg < 90.0: - sphere2 = sphere2.cut(Part.makeCone( - 0.0, - rmax*math.sin(startthetaRad), - rmax*math.cos(startthetaRad))) + sphere2 = sphere2.cut( + Part.makeCone( + 0.0, + rmax * math.sin(startthetaRad), + rmax * math.cos(startthetaRad), + ) + ) cylToCut = Part.makeCylinder( - 2.0*rmax, rmax, - FreeCAD.Vector(0, 0, rmax*math.cos(startthetaRad))) + 2.0 * rmax, + rmax, + FreeCAD.Vector(0, 0, rmax * math.cos(startthetaRad)), + ) sphere2 = sphere2.cut(cylToCut) elif startthetaDeg < 180.0: - sphere2 = sphere2.common(Part.makeCone( - 0.0, - rmax/math.cos(math.pi-startthetaRad), rmax, spos, - FreeCAD.Vector(0, 0, -1.0))) + sphere2 = sphere2.common( + Part.makeCone( + 0.0, + rmax / math.cos(math.pi - startthetaRad), + rmax, + spos, + FreeCAD.Vector(0, 0, -1.0), + ) + ) # if deltatheta -> cut the down cone deltathetaRad = getAngleRad(fp.aunit, fp.deltatheta) @@ -2662,103 +3524,170 @@ def createGeometry(self, fp): if thetaSumRad < math.pi: if thetaSumRad > HalfPi: - sphere2 = sphere2.cut(Part.makeCone( - 0.0, - rmax*math.sin(math.pi - thetaSumRad), - rmax*math.cos(math.pi - thetaSumRad), - spos, FreeCAD.Vector(0, 0, -1.0))) + sphere2 = sphere2.cut( + Part.makeCone( + 0.0, + rmax * math.sin(math.pi - thetaSumRad), + rmax * math.cos(math.pi - thetaSumRad), + spos, + FreeCAD.Vector(0, 0, -1.0), + ) + ) cylToCut = Part.makeCylinder( - 2.0*rmax, rmax, - FreeCAD.Vector(0, 0, rmax*(-1.0 + math.cos(thetaSumRad)))) + 2.0 * rmax, + rmax, + FreeCAD.Vector( + 0, 0, rmax * (-1.0 + math.cos(thetaSumRad)) + ), + ) sphere2 = sphere2.cut(cylToCut) elif thetaSumRad == HalfPi: - cylToCut = Part.makeCylinder(2.0*rmax, rmax, - FreeCAD.Vector(0, 0, -rmax)) + cylToCut = Part.makeCylinder( + 2.0 * rmax, rmax, FreeCAD.Vector(0, 0, -rmax) + ) sphere2 = sphere2.cut(cylToCut) elif thetaSumRad > 0: - sphere2 = sphere2.common(Part.makeCone( - 0.0, - 2*rmax*math.tan(thetaSumRad), - 2*rmax)) + sphere2 = sphere2.common( + Part.makeCone( + 0.0, 2 * rmax * math.tan(thetaSumRad), 2 * rmax + ) + ) if rmin <= 0 or rmin > rmax: fp.Shape = sphere2 else: fp.Shape = sphere2.cut(Part.makeSphere(rmin)) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTrap(GDMLsolid): - def __init__(self, obj, z, theta, phi, x1, x2, x3, x4, y1, y2, alpha, - aunit, lunit, material, colour=None): + def __init__( + self, + obj, + z, + theta, + phi, + x1, + x2, + x3, + x4, + y1, + y2, + alpha, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) "General Trapezoid" obj.addProperty("App::PropertyFloat", "z", "GDMLTrap", "z").z = z - obj.addProperty("App::PropertyFloat", "theta", "GDMLTrap", - "theta").theta = theta - obj.addProperty("App::PropertyFloat", "phi", "GDMLTrap", - "phi").phi = phi - obj.addProperty("App::PropertyFloat", "x1", "GDMLTrap", - "Length x at y= -y1/2 of face at -z/2").x1 = x1 - obj.addProperty("App::PropertyFloat", "x2", "GDMLTrap", - "Length x at y= +y1/2 of face at -z/2").x2 = x2 - obj.addProperty("App::PropertyFloat", "x3", "GDMLTrap", - "Length x at y= -y2/2 of face at +z/2").x3 = x3 - obj.addProperty("App::PropertyFloat", "x4", "GDMLTrap", - "Length x at y= +y2/2 of face at +z/2").x4 = x4 - obj.addProperty("App::PropertyFloat", "y1", "GDMLTrap", - "Length y at face -z/2").y1 = y1 - obj.addProperty("App::PropertyFloat", "y2", "GDMLTrap", - "Length y at face +z/2").y2 = y2 - obj.addProperty("App::PropertyFloat", "alpha", "GDMLTrap", - "alpha").alpha = alpha - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTrap", "aunit") - obj.aunit = ["rad", "deg"] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTrap", "lunit") + obj.addProperty( + "App::PropertyFloat", "theta", "GDMLTrap", "theta" + ).theta = theta + obj.addProperty( + "App::PropertyFloat", "phi", "GDMLTrap", "phi" + ).phi = phi + obj.addProperty( + "App::PropertyFloat", + "x1", + "GDMLTrap", + "Length x at y= -y1/2 of face at -z/2", + ).x1 = x1 + obj.addProperty( + "App::PropertyFloat", + "x2", + "GDMLTrap", + "Length x at y= +y1/2 of face at -z/2", + ).x2 = x2 + obj.addProperty( + "App::PropertyFloat", + "x3", + "GDMLTrap", + "Length x at y= -y2/2 of face at +z/2", + ).x3 = x3 + obj.addProperty( + "App::PropertyFloat", + "x4", + "GDMLTrap", + "Length x at y= +y2/2 of face at +z/2", + ).x4 = x4 + obj.addProperty( + "App::PropertyFloat", "y1", "GDMLTrap", "Length y at face -z/2" + ).y1 = y1 + obj.addProperty( + "App::PropertyFloat", "y2", "GDMLTrap", "Length y at face +z/2" + ).y2 = y2 + obj.addProperty( + "App::PropertyFloat", "alpha", "GDMLTrap", "alpha" + ).alpha = alpha + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTrap", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTrap", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTrap", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLTrap", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLTrap' + self.Type = "GDMLTrap" + obj.Proxy.Type = "GDMLTrap" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['z', 'theta', 'phi', - 'x1', 'x2', 'x3', 'x4', 'y1', 'y2', 'alpha', - 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "z", + "theta", + "phi", + "x1", + "x2", + "x3", + "x4", + "y1", + "y2", + "alpha", + "aunit", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid - def createGeometry(self,fp): + def createGeometry(self, fp): currPlacement = fp.Placement # Define six vetices for the shape - alpha = getAngleRad(fp.aunit,fp.alpha) - theta = getAngleRad(fp.aunit,fp.theta) - phi = getAngleRad(fp.aunit,fp.phi) - mul = GDMLShared.getMult(fp) + alpha = getAngleRad(fp.aunit, fp.alpha) + theta = getAngleRad(fp.aunit, fp.theta) + phi = getAngleRad(fp.aunit, fp.phi) + mul = GDMLShared.getMult(fp) y1 = mul * fp.y1 x1 = mul * fp.x1 x2 = mul * fp.x2 @@ -2766,18 +3695,18 @@ def createGeometry(self,fp): x3 = mul * fp.x3 x4 = mul * fp.x4 z = mul * fp.z - dx1 = y1*math.tan(alpha) - dx2 = y2*math.tan(alpha) - + dx1 = y1 * math.tan(alpha) + dx2 = y2 * math.tan(alpha) + # Vertexes, counter clock wise order - v1 = FreeCAD.Vector(-x1/2 - dx1/2, -y1/2, -z/2) - v2 = FreeCAD.Vector( x1/2 - dx1/2, -y1/2, -z/2) - v3 = FreeCAD.Vector( x2/2 + dx1/2, y1/2, -z/2) - v4 = FreeCAD.Vector(-x2/2 + dx1/2, y1/2, -z/2) - v5 = FreeCAD.Vector(-x3/2 - dx2/2, -y2/2, z/2) - v6 = FreeCAD.Vector( x3/2 - dx2/2, -y2/2, z/2) - v7 = FreeCAD.Vector( x4/2 + dx2/2, y2/2, z/2) - v8 = FreeCAD.Vector(-x4/2 + dx2/2, y2/2, z/2) + v1 = FreeCAD.Vector(-x1 / 2 - dx1 / 2, -y1 / 2, -z / 2) + v2 = FreeCAD.Vector(x1 / 2 - dx1 / 2, -y1 / 2, -z / 2) + v3 = FreeCAD.Vector(x2 / 2 + dx1 / 2, y1 / 2, -z / 2) + v4 = FreeCAD.Vector(-x2 / 2 + dx1 / 2, y1 / 2, -z / 2) + v5 = FreeCAD.Vector(-x3 / 2 - dx2 / 2, -y2 / 2, z / 2) + v6 = FreeCAD.Vector(x3 / 2 - dx2 / 2, -y2 / 2, z / 2) + v7 = FreeCAD.Vector(x4 / 2 + dx2 / 2, y2 / 2, z / 2) + v8 = FreeCAD.Vector(-x4 / 2 + dx2 / 2, y2 / 2, z / 2) # # xy faces # @@ -2796,14 +3725,14 @@ def createGeometry(self,fp): # # apply theta, phi distortions # - rho = z*math.tan(theta) - dx = rho*math.cos(phi) - dy = rho*math.sin(phi) + rho = z * math.tan(theta) + dx = rho * math.cos(phi) + dy = rho * math.sin(phi) for i in range(0, 4): - vxy1[i][0] -= dx/2 - vxy1[i][1] -= dy/2 - vxy2[i][0] += dx/2 - vxy2[i][1] += dy/2 + vxy1[i][0] -= dx / 2 + vxy1[i][1] -= dy / 2 + vxy2[i][0] += dx / 2 + vxy2[i][1] += dy / 2 fxy1 = Part.Face(Part.makePolygon(vxy1)) fxy2 = Part.Face(Part.makePolygon(vxy2)) @@ -2817,58 +3746,68 @@ def createGeometry(self,fp): # center is mid point of diagonal # - botCenter = ((v3+v4) + (v1+v2))/2 - topCenter = ((v7+v8) + (v5+v6))/2 - center = (topCenter+botCenter)/2 + botCenter = ((v3 + v4) + (v1 + v2)) / 2 + topCenter = ((v7 + v8) + (v5 + v6)) / 2 + center = (topCenter + botCenter) / 2 fp.Shape = translate(solid, -center) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTrd(GDMLsolid): - def __init__(self, obj, z, x1, x2, y1, y2, lunit, material, colour=None): + def __init__(self, obj, z, x1, x2, y1, y2, lunit, material, colour=None): super().__init__(obj) "3.4.15 : Trapezoid – x & y varying along z" - obj.addProperty("App::PropertyFloat", "z", "GDMLTrd", - "z").z = z - obj.addProperty("App::PropertyFloat", "x1", "GDMLTrd", - "Length x at face -z/2").x1 = x1 - obj.addProperty("App::PropertyFloat", "x2", "GDMLTrd", - "Length x at face +z/2").x2 = x2 - obj.addProperty("App::PropertyFloat", "y1", "GDMLTrd", - "Length y at face -z/2").y1 = y1 - obj.addProperty("App::PropertyFloat", "y2", "GDMLTrd", - "Length y at face +z/2").y2 = y2 - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTrd", - "lunit") + obj.addProperty("App::PropertyFloat", "z", "GDMLTrd", "z").z = z + obj.addProperty( + "App::PropertyFloat", "x1", "GDMLTrd", "Length x at face -z/2" + ).x1 = x1 + obj.addProperty( + "App::PropertyFloat", "x2", "GDMLTrd", "Length x at face +z/2" + ).x2 = x2 + obj.addProperty( + "App::PropertyFloat", "y1", "GDMLTrd", "Length y at face -z/2" + ).y1 = y1 + obj.addProperty( + "App::PropertyFloat", "y2", "GDMLTrd", "Length y at face +z/2" + ).y2 = y2 + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTrd", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTrd", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLTrd", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLTrd' + self.Type = "GDMLTrd" + obj.Proxy.Type = "GDMLTrd" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['z', 'x1', 'x2', 'y1', 'y2', 'lunit']: + if prop in ["z", "x1", "x2", "y1", "y2", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2878,20 +3817,20 @@ def createGeometry(self, fp): GDMLShared.trace("x2 : " + str(fp.x2)) mul = GDMLShared.getMult(fp) - x1 = (fp.x1 * mul)/2 - x2 = (fp.x2 * mul)/2 - y1 = (fp.y1 * mul)/2 - y2 = (fp.y2 * mul)/2 - z = (fp.z * mul)/2 + x1 = (fp.x1 * mul) / 2 + x2 = (fp.x2 * mul) / 2 + y1 = (fp.y1 * mul) / 2 + y2 = (fp.y2 * mul) / 2 + z = (fp.z * mul) / 2 v1 = FreeCAD.Vector(-x1, -y1, -z) v2 = FreeCAD.Vector(-x1, +y1, -z) - v3 = FreeCAD.Vector(x1, +y1, -z) - v4 = FreeCAD.Vector(x1, -y1, -z) + v3 = FreeCAD.Vector(x1, +y1, -z) + v4 = FreeCAD.Vector(x1, -y1, -z) - v5 = FreeCAD.Vector(-x2, -y2, z) - v6 = FreeCAD.Vector(-x2, +y2, z) - v7 = FreeCAD.Vector(x2, +y2, z) - v8 = FreeCAD.Vector(x2, -y2, z) + v5 = FreeCAD.Vector(-x2, -y2, z) + v6 = FreeCAD.Vector(-x2, +y2, z) + v7 = FreeCAD.Vector(x2, +y2, z) + v8 = FreeCAD.Vector(x2, -y2, z) # Make the wires/faces f1 = make_face4(v1, v2, v3, v4) f2 = make_face4(v1, v2, v6, v5) @@ -2905,59 +3844,89 @@ 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTube(GDMLsolid): - def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, - lunit, material, colour=None): + def __init__( + self, + obj, + rmin, + rmax, + z, + startphi, + deltaphi, + aunit, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat", "rmin", "GDMLTube", - "Inside Radius").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "GDMLTube", - "Outside Radius").rmax = rmax - obj.addProperty("App::PropertyFloat", "z", "GDMLTube", - "Length z").z = z - obj.addProperty("App::PropertyFloat", "startphi", "GDMLTube", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLTube", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLTube", - "aunit") - obj.aunit = ['rad', 'deg'] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTube", - "lunit") + """Add some custom properties to our Tube feature""" + obj.addProperty( + "App::PropertyFloat", "rmin", "GDMLTube", "Inside Radius" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "GDMLTube", "Outside Radius" + ).rmax = rmax + obj.addProperty( + "App::PropertyFloat", "z", "GDMLTube", "Length z" + ).z = z + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLTube", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLTube", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLTube", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTube", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLTube", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLTube", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLTube' + self.Type = "GDMLTube" + obj.Proxy.Type = "GDMLTube" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - - if prop in ['material']: + + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'z', 'startphi', 'deltaphi', - 'aunit', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "rmin", + "rmax", + "z", + "startphi", + "deltaphi", + "aunit", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -2974,8 +3943,9 @@ def createGeometry(self, fp): # print('rmax : '+str(rmax)) # print('z : '+str(z)) # print('deltaPhi : '+str(fp.deltaphi)) - tube = Part.makeCylinder(rmax, z, spos, sdir, - getAngleDeg(fp.aunit, fp.deltaphi)) + tube = Part.makeCylinder( + rmax, z, spos, sdir, getAngleDeg(fp.aunit, fp.deltaphi) + ) if fp.startphi != 0: tube.rotate(spos, sdir, getAngleDeg(fp.aunit, fp.startphi)) @@ -2983,49 +3953,79 @@ def createGeometry(self, fp): if rmin > 0: tube = tube.cut(Part.makeCylinder(rmin, z)) - base = FreeCAD.Vector(0, 0, -z/2) + base = FreeCAD.Vector(0, 0, -z / 2) fp.Shape = translate(tube, base) - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLcutTube(GDMLsolid): - def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, - lowX, lowY, lowZ, highX, highY, highZ, - lunit, material, colour=None): + def __init__( + self, + obj, + rmin, + rmax, + z, + startphi, + deltaphi, + aunit, + lowX, + lowY, + lowZ, + highX, + highY, + highZ, + lunit, + material, + colour=None, + ): super().__init__(obj) - '''Add some custom properties to our Tube feature''' - obj.addProperty("App::PropertyFloat", "rmin", "GDMLcutTube", - "Inside Radius").rmin = rmin - obj.addProperty("App::PropertyFloat", "rmax", "GDMLcutTube", - "Outside Radius").rmax = rmax - obj.addProperty("App::PropertyFloat", "z", "GDMLcutTube", - "Length z").z = z - obj.addProperty("App::PropertyFloat", "startphi", "GDMLcutTube", - "Start Angle").startphi = startphi - obj.addProperty("App::PropertyFloat", "deltaphi", "GDMLcutTube", - "Delta Angle").deltaphi = deltaphi - obj.addProperty("App::PropertyEnumeration", "aunit", "GDMLcutTube", - "aunit") - obj.aunit = ['rad', 'deg'] - obj.aunit = ['rad', 'deg'].index(aunit[0:3]) - obj.addProperty("App::PropertyFloat", "lowX", "GDMLcutTube", - "low X").lowX = lowX - obj.addProperty("App::PropertyFloat", "lowY", "GDMLcutTube", - "low Y").lowY = lowY - obj.addProperty("App::PropertyFloat", "lowZ", "GDMLcutTube", - "low Z").lowZ = lowZ - obj.addProperty("App::PropertyFloat", "highX", "GDMLcutTube", - "high X").highX = highX - obj.addProperty("App::PropertyFloat", "highY", "GDMLcutTube", - "high Y").highY = highY - obj.addProperty("App::PropertyFloat", "highZ", "GDMLcutTube", - "high Z").highZ = highZ - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLcutTube", - "lunit") + """Add some custom properties to our Tube feature""" + obj.addProperty( + "App::PropertyFloat", "rmin", "GDMLcutTube", "Inside Radius" + ).rmin = rmin + obj.addProperty( + "App::PropertyFloat", "rmax", "GDMLcutTube", "Outside Radius" + ).rmax = rmax + obj.addProperty( + "App::PropertyFloat", "z", "GDMLcutTube", "Length z" + ).z = z + obj.addProperty( + "App::PropertyFloat", "startphi", "GDMLcutTube", "Start Angle" + ).startphi = startphi + obj.addProperty( + "App::PropertyFloat", "deltaphi", "GDMLcutTube", "Delta Angle" + ).deltaphi = deltaphi + obj.addProperty( + "App::PropertyEnumeration", "aunit", "GDMLcutTube", "aunit" + ) + setAngleQuantity(obj, aunit) + obj.addProperty( + "App::PropertyFloat", "lowX", "GDMLcutTube", "low X" + ).lowX = lowX + obj.addProperty( + "App::PropertyFloat", "lowY", "GDMLcutTube", "low Y" + ).lowY = lowY + obj.addProperty( + "App::PropertyFloat", "lowZ", "GDMLcutTube", "low Z" + ).lowZ = lowZ + obj.addProperty( + "App::PropertyFloat", "highX", "GDMLcutTube", "high X" + ).highX = highX + obj.addProperty( + "App::PropertyFloat", "highY", "GDMLcutTube", "high Y" + ).highY = highY + obj.addProperty( + "App::PropertyFloat", "highZ", "GDMLcutTube", "high Z" + ).highZ = highZ + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLcutTube", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", "GDMLcutTube", - "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLcutTube", "Material" + ) # print('Add material') # print(material) setMaterial(obj, material) @@ -3035,26 +4035,42 @@ def __init__(self, obj, rmin, rmax, z, startphi, deltaphi, aunit, # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc obj.Proxy = self - self.Type = 'GDMLcutTube' + self.Type = "GDMLcutTube" + obj.Proxy.Type = "GDMLcutTube" self.colour = colour def onChanged(self, fp, prop): # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) - - if prop in ['rmin', 'rmax', 'z', 'startphi', 'deltaphi', 'aunit', - 'lowX', 'lowY', 'lowZ', - 'highX', 'highY', 'highZ', 'lunit']: + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in [ + "rmin", + "rmax", + "z", + "startphi", + "deltaphi", + "aunit", + "lowX", + "lowY", + "lowZ", + "highX", + "highY", + "highZ", + "lunit", + ]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -3066,9 +4082,11 @@ def cutShapeWithPlane(self, shape, plane, depth): # so = plane.extrude(plane.*1e10) # so = plane.extrude(plane.normalAt(1,1)*1e10) # so = plane.extrude(plane.normalAt(1,1)*100) - so = plane.extrude(plane.normalAt(1, 1)*depth) + so = plane.extrude(plane.normalAt(1, 1) * depth) + print('Plane extruded') # print('Plane extruded') # print(plane.normalAt(1,1)) + print(f" Normal {plane.normalAt(1,1)}") # return so # print('Extrude made - Now Cut') cut = shape.cut(so) @@ -3076,6 +4094,7 @@ def cutShapeWithPlane(self, shape, plane, depth): return cut def createGeometry(self, fp): + # Munther improved version Sep 23 currPlacement = fp.Placement angle = getAngleDeg(fp.aunit, fp.deltaphi) pntC = FreeCAD.Vector(0, 0, 0) @@ -3086,20 +4105,64 @@ def createGeometry(self, fp): z = mul * fp.z depth = 2 * max(rmax, z) botDir = FreeCAD.Vector(fp.lowX, fp.lowY, fp.lowZ) + botDir.normalize() topDir = FreeCAD.Vector(fp.highX, fp.highY, fp.highZ) + topDir.normalize() + + k = FreeCAD.Vector(0, 0, 1) # vector along z -axis + u = k - (k.dot(topDir))*topDir # component of k vector along plane + u.normalize() # unit vector along major axis + + v = k.cross(u) # unit vector along minor axis + v.normalize() + # print(f'u={u}, v={v}') + + costhet = k.dot(topDir) + thet_top = math.acos(costhet) + + corner_top = u*rmax/costhet + v*rmax + z/2*k + a = rmax/costhet # semi-major axis + b = rmax # semi-minor axis + + # print(f'corner_top = {corner_top}, topDir = {topDir}') + topPlane = Part.makePlane( + 2*a, 2*b, corner_top, topDir, -u + ) + # Part.show(topPlane) + + k = -FreeCAD.Vector(0, 0, 1) # vector along -z -axis + u = k - (k.dot(botDir))*botDir # componentof k vector along plane + u.normalize() # unit vector along major axis + + v = k.cross(u) # unit vector along minor axis + v.normalize() + # print(f'u={u}, v={v}') + + costhet = k.dot(botDir) + thet_bot = math.acos(costhet) - tube1 = Part.makeCylinder(rmax, z, pntC, dirC, angle) - tube2 = Part.makeCylinder(rmin, z, pntC, dirC, angle) + corner_bot = u*rmax/costhet + v*rmax + z/2*k # remember this k points down + a = rmax/costhet # semi-major axis + b = rmax # semi-minor axis + # print(f'corner_bot = {corner_bot}, botDir = {botDir}') + + botPlane = Part.makePlane( + 2*a, 2*b, corner_bot, botDir, -u + ) + # Part.show(botPlane) + + tube_height = z + rmax*math.tan(thet_top) + rmax*math.tan(thet_bot) + + tube1 = Part.makeCylinder(rmax, tube_height, pntC, dirC, angle) + tube2 = Part.makeCylinder(rmin, tube_height, pntC, dirC, angle) tube = tube1.cut(tube2) - topPlane = Part.makePlane(depth, depth, - FreeCAD.Vector(-rmax, -rmax, z), topDir) + tube.translate(FreeCAD.Vector(0, 0, -z/2 - rmax*math.tan(thet_bot))) + cutTube1 = self.cutShapeWithPlane(tube, topPlane, depth) - botPlane = Part.makePlane(depth, depth, - FreeCAD.Vector(rmax, rmax, 0.0), botDir) 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.Shape = cutTube2 + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement def createGeometry_hardcoded(self, fp): @@ -3110,34 +4173,35 @@ def createGeometry_hardcoded(self, fp): tube1 = Part.makeCylinder(20, 60, pntC, dirC, angle) tube2 = Part.makeCylinder(12, 60, pntC, dirC, angle) tube = tube1.cut(tube2) - topPlane = Part.makePlane(100, 100, - FreeCAD.Vector(-20, -20, 60), - FreeCAD.Vector(0.7, 0, 0.71)) - cutTube1 = self.cutShapeWithPlane(tube, topPlane) - botPlane = Part.makePlane(100, 100, - FreeCAD.Vector(20, 20, 0), - FreeCAD.Vector(0, -0.7, -0.71)) + topPlane = Part.makePlane( + 100, + 100, + FreeCAD.Vector(-20, -20, 60), + FreeCAD.Vector(0.7, 0, 0.71), + ) + cutTube1 = self.cutShapeWithPlane(tube, topPlane, 120) + botPlane = Part.makePlane( + 100, 100, FreeCAD.Vector(20, 20, 0), FreeCAD.Vector(0, -0.7, -0.71) + ) Part.show(botPlane) - cutTube2 = self.cutShapeWithPlane(cutTube1, botPlane) - print('Return result') + cutTube2 = self.cutShapeWithPlane(cutTube1, botPlane, 120) + print("Return result") fp.Shape = cutTube2 class GDMLVertex(GDMLcommon): def __init__(self, obj, x, y, z, lunit): super().__init__(obj) - obj.addProperty("App::PropertyFloat", "x", "GDMLVertex", - "x").x = x - obj.addProperty("App::PropertyFloat", "y", "GDMLVertex", - "y").y = y - obj.addProperty("App::PropertyFloat", "z", "GDMLVertex", - "z").z = z - self.Type = 'GDMLVertex' + obj.addProperty("App::PropertyFloat", "x", "GDMLVertex", "x").x = x + obj.addProperty("App::PropertyFloat", "y", "GDMLVertex", "y").y = y + obj.addProperty("App::PropertyFloat", "z", "GDMLVertex", "z").z = z + self.Type = "GDMLVertex" self.Object = obj obj.Proxy = self + obj.Proxy.Type = "GDMLVertex" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) # if not ('Restore' in fp.State) : # if prop in ['x','y', 'z'] : @@ -3152,21 +4216,26 @@ def execute(self, fp): class GDMLTriangular(GDMLcommon): def __init__(self, obj, v1, v2, v3, vtype): super().__init__(obj) - obj.addProperty("App::PropertyVector", "v1", "Triangular", - "v1").v1 = v1 - obj.addProperty("App::PropertyVector", "v2", "Triangular", - "v1").v2 = v2 - obj.addProperty("App::PropertyVector", "v3", "Triangular", - "v1").v3 = v3 - obj.addProperty("App::PropertyEnumeration", "vtype", "Triangular", - "vtype") + obj.addProperty( + "App::PropertyVector", "v1", "Triangular", "v1" + ).v1 = v1 + obj.addProperty( + "App::PropertyVector", "v2", "Triangular", "v1" + ).v2 = v2 + obj.addProperty( + "App::PropertyVector", "v3", "Triangular", "v1" + ).v3 = v3 + obj.addProperty( + "App::PropertyEnumeration", "vtype", "Triangular", "vtype" + ) obj.vtype = ["ABSOLUTE", "RELATIVE"] obj.vtype = ["ABSOLUTE", "RELATIVE"].index(vtype) - self.Type = 'GDMLTriangular' + self.Type = "GDMLTriangular" obj.Proxy = self + obj.Proxy.Type = "GDMLTriangular" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" pass def execute(self, fp): @@ -3176,24 +4245,22 @@ def execute(self, fp): class GDMLQuadrangular(GDMLcommon): def __init__(self, obj, v1, v2, v3, v4, vtype): super().__init__(obj) - obj.addProperty("App::PropertyVector", "v1", "Quadrang", - "v1").v1 = v1 - obj.addProperty("App::PropertyVector", "v2", "Quadrang", - "v2").v2 = v2 - obj.addProperty("App::PropertyVector", "v3", "Quadrang", - "v3").v3 = v3 - obj.addProperty("App::PropertyVector", "v4", "Quadrang", - "v4").v4 = v4 - obj.addProperty("App::PropertyEnumeration", "vtype", - "Quadrang", "vtype") + obj.addProperty("App::PropertyVector", "v1", "Quadrang", "v1").v1 = v1 + obj.addProperty("App::PropertyVector", "v2", "Quadrang", "v2").v2 = v2 + obj.addProperty("App::PropertyVector", "v3", "Quadrang", "v3").v3 = v3 + obj.addProperty("App::PropertyVector", "v4", "Quadrang", "v4").v4 = v4 + obj.addProperty( + "App::PropertyEnumeration", "vtype", "Quadrang", "vtype" + ) obj.vtype = ["ABSOLUTE", "RELATIVE"] obj.vtype = 0 - self.Type = 'GDMLQuadrangular' + self.Type = "GDMLQuadrangular" obj.Proxy = self + obj.Proxy.Type = "GDMLQuadrangular" def onChanged(self, fp, prop): - '''Do something when a property has changed''' - if 'Restore' in fp.State: + """Do something when a property has changed""" + if "Restore" in fp.State: return pass @@ -3203,43 +4270,68 @@ def execute(self, fp): class GDMLGmshTessellated(GDMLsolid): - - def __init__(self, obj, sourceObj, meshLen, vertex, facets, lunit, - material, colour=None): + def __init__( + self, + obj, + sourceObj, + meshLen, + vertex, + facets, + lunit, + material, + colour=None, + ): super().__init__(obj) - obj.addProperty('App::PropertyInteger', 'facets', 'GDMLGmshTessellated', - 'Facets').facets = len(facets) - obj.setEditorMode('facets', 1) - obj.addProperty('App::PropertyInteger', 'vertex', 'GDMLGmshTessellated', - 'Vertex').vertex = len(vertex) - obj.setEditorMode('vertex', 1) - obj.addProperty('App::PropertyFloat', 'm_maxLength', - 'GDMLGmshTessellated', - 'Max Length').m_maxLength = meshLen - obj.addProperty('App::PropertyFloat', 'm_curveLen', - 'GDMLGmshTessellated', - 'Curve Length').m_curveLen = meshLen - obj.addProperty('App::PropertyFloat', 'm_pointLen', - 'GDMLGmshTessellated', - 'Point Length').m_pointLen = meshLen - obj.addProperty("App::PropertyEnumeration", "lunit", - "GDMLGmshTessellated", "lunit") + obj.addProperty( + "App::PropertyInteger", "facets", "GDMLGmshTessellated", "Facets" + ).facets = len(facets) + obj.setEditorMode("facets", 1) + obj.addProperty( + "App::PropertyInteger", "vertex", "GDMLGmshTessellated", "Vertex" + ).vertex = len(vertex) + obj.setEditorMode("vertex", 1) + # Properties NOT the same GmshTessellate GmshMinTessellate + #obj.addProperty( + # "App::PropertyFloat", + # "m_maxLength", + # "GDMLGmshTessellated", + # "Max Length", + #).m_maxLength = meshLen + #obj.addProperty( + # "App::PropertyFloat", + # "m_curveLen", + # "GDMLGmshTessellated", + # "Curve Length", + #).m_curveLen = meshLen + #obj.addProperty( + # "App::PropertyFloat", + # "m_pointLen", + # "GDMLGmshTessellated", + # "Point Length", + #).m_pointLen = meshLen + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLGmshTessellated", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLTessellated", "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTessellated", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) - self.Type = 'GDMLGmshTessellated' + self.Type = "GDMLGmshTessellated" self.SourceObj = sourceObj self.Vertex = vertex self.Facets = facets self.Object = obj self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLGmshTessellated" def updateParams(self, vertex, facets, flag): - self.Vertex = vertex self.Facets = facets self.facets = len(facets) @@ -3247,33 +4339,36 @@ def updateParams(self, vertex, facets, flag): print(f"Vertex : {self.vertex} Facets : {self.facets}") def onChanged(self, fp, prop): - '''Do something when a property has changed''' - if 'Restore' in fp.State: + """Do something when a property has changed""" + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['editable']: + if prop in ["editable"]: if fp.editable is True: self.addProperties() - if prop in ['m_Remesh']: + if prop in ["m_Remesh"]: if fp.m_Remesh is True: self.reMesh(fp) self.execute(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) def execute(self, fp): # Here for remesh? self.createGeometry(fp) def addProperties(self): - print('Add Properties') + print("Add Properties") def reMesh(self, fp): from .GmshUtils import initialize, meshObj, getVertex, getFacets @@ -3291,34 +4386,60 @@ def reMesh(self, fp): # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): + # breakpoint() currPlacement = fp.Placement mul = GDMLShared.getMult(fp) FCfaces = [] - i = 0 for f in self.Facets: if len(f) == 3: - FCfaces.append(GDMLShared.triangle(mul*self.Vertex[f[0]], - mul*self.Vertex[f[1]], - mul*self.Vertex[f[2]])) + face = GDMLShared.triangle( + mul * self.Vertex[f[0]], + mul * self.Vertex[f[1]], + mul * self.Vertex[f[2]] + ) + if face is not None: + FCfaces.append(face) else: # len should then be 4 - FCfaces.append(GDMLShared.quad(mul*self.Vertex[f[0]], - mul*self.Vertex[f[1]], - mul*self.Vertex[f[2]], - mul*self.Vertex[f[3]])) + quadFace = GDMLShared.quad( + mul * self.Vertex[f[0]], + mul * self.Vertex[f[1]], + mul * self.Vertex[f[2]], + mul * self.Vertex[f[3]] + ) + if quadFace is not None: + FCfaces.append(quadFace) + else: + print(f"Create Quad Failed {f[0]} {f[1]} {f[2]} {f[3]}") + print("Creating as two triangles") + face = GDMLShared.triangle( + mul * self.Vertex[f[0]], + mul * self.Vertex[f[1]], + mul * self.Vertex[f[2]] + ) + if face is not None: + FCfaces.append(face) + face = GDMLShared.triangle( + mul * self.Vertex[f[0]], + mul * self.Vertex[f[2]], + mul * self.Vertex[f[3]] + ) + if face is not None: + FCfaces.append(face) + shell = Part.makeShell(FCfaces) if shell.isValid is False: - FreeCAD.Console.PrintWarning('Not a valid Shell/n') + FreeCAD.Console.PrintWarning("Not a valid Shell/n") try: solid = Part.Solid(shell) except: # make compound rather than just barf # visually able to view at least - FreeCAD.Console.PrintWarning('Problem making Solid/n') + FreeCAD.Console.PrintWarning("Problem making Solid/n") solid = Part.makeCompound(FCfaces) # if solid.Volume < 0: # solid.reverse() - # print(dir(solid)) + # print(dir(solid)) # bbox = solid.BoundBox # base = FreeCAD.Vector(-(bbox.XMin+bbox.XMax)/2, \ # -(bbox.YMin+bbox.YMax)/2 \ @@ -3328,37 +4449,46 @@ 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTessellated(GDMLsolid): - - def __init__(self, obj, vertex, facets, flag, lunit, material, - colour=None): + def __init__( + self, obj, vertex, facets, flag, lunit, material, colour=None + ): super().__init__(obj) # ######################################## # if flag == True - facets is Mesh.Facets - with Normals # if flag == False - facets is Faces i.e. from import GDMLTessellated # ######################################## - obj.addProperty('App::PropertyInteger', 'facets', 'GDMLTessellated', - 'Facets').facets = len(facets) - obj.setEditorMode('facets', 1) - obj.addProperty('App::PropertyInteger', 'vertex', 'GDMLTessellated', - 'Vertex').vertex = len(vertex) - obj.setEditorMode('vertex', 1) - obj.addProperty("App::PropertyEnumeration", "lunit", - "GDMLTessellated", "lunit") + obj.addProperty( + "App::PropertyInteger", "facets", "GDMLTessellated", "Facets" + ).facets = len(facets) + obj.setEditorMode("facets", 1) + obj.addProperty( + "App::PropertyInteger", "vertex", "GDMLTessellated", "Vertex" + ).vertex = len(vertex) + obj.setEditorMode("vertex", 1) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTessellated", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLTessellated", "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTessellated", + "Material", + ) setMaterial(obj, material) self.updateParams(vertex, facets, flag) if FreeCAD.GuiUp: updateColour(obj, colour, material) - self.Type = 'GDMLTessellated' - self.colour = colour - obj.Proxy = self + self.Type = "GDMLTessellated" + self.colour = colour + obj.Proxy = self + obj.Proxy.Type = "GDMLTessellated" def updateParams(self, vertex, facets, flag): # print('Update Params & Shape') @@ -3369,38 +4499,42 @@ def updateParams(self, vertex, facets, flag): # print(f"Vertex : {self.vertex} Facets : {self.facets}") def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['editable']: + if prop in ["editable"]: if fp.editable is True: self.addProperties() - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) def addProperties(self): - print('Add Properties') + print("Add Properties") # def execute(self, fp): in GDMLsolid def createGeometry(self, fp): - if hasattr(self, 'pshape'): + if hasattr(self, "pshape"): # print('Update Shape') fp.Shape = self.pshape - if hasattr(fp, 'pshape'): + if hasattr(fp, "pshape"): fp.pshape = self.pshape fp.vertex = self.vertex fp.facets = self.facets - if hasattr(fp,'scale'): super().scale(fp) + if hasattr(fp, "scale"): + super().scale(fp) def createShape(self, vertex, facets, flag): # Viewing outside of face vertex must be counter clockwise @@ -3414,55 +4548,41 @@ def createShape(self, vertex, facets, flag): # print('Facet') # print(f) if flag is True: - #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) + FCfaces.append(GDMLShared.facet(f)) else: if len(f) == 3: - FCfaces.append(GDMLShared.triangle( - mul*vertex[f[0]], - mul*vertex[f[1]], - mul*vertex[f[2]])) + FCfaces.append( + GDMLShared.triangle( + mul * vertex[f[0]], + mul * vertex[f[1]], + mul * vertex[f[2]] + ) + ) else: # len should then be 4 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) + face = GDMLShared.quad( + mul * vertex[f[0]], + mul * vertex[f[1]], + mul * vertex[f[2]], + mul * vertex[f[3]] + ) + FCfaces.append(face) + except: + face = GDMLShared.triangle( + mul * vertex[f[0]], + mul * vertex[f[1]], + mul * vertex[f[2]] + ) + FCfaces.append(face) + face = GDMLShared.triangle( + mul * vertex[f[0]], + mul * vertex[f[2]], + mul * vertex[f[3]] + ) + FCfaces.append(face) shell = Part.makeShell(FCfaces) if shell.isValid is False: - FreeCAD.Console.PrintWarning('Not a valid Shell/n') + FreeCAD.Console.PrintWarning("Not a valid Shell/n") # shell.check() # solid=Part.Solid(shell).removeSplitter() @@ -3471,54 +4591,488 @@ def createShape(self, vertex, facets, flag): except: # make compound rather than just barf # visually able to view at least - FreeCAD.Console.PrintWarning('Problem making Solid/n') + FreeCAD.Console.PrintWarning("Problem making Solid/n") solid = Part.makeCompound(FCfaces) return solid -class GDMLTetra(GDMLsolid): # 4 point Tetrahedron +class GDMLSampledTessellated(GDMLsolid): + def __init__( + self, + obj, + vertex, + facets, + lunit, + material, + solidFlag, + sampledFraction, + colour=None, + flag=True, + ): + super().__init__(obj) + from random import random + # ######################################## + # if flag == True - facets is Mesh.Facets - with Normals + # if flag == False - facets is Faces i.e. from import GDMLTessellated + # ######################################## + obj.addProperty( + "App::PropertyInteger", + "facets", + "GDMLSampledTessellated", + "Facets", + ).facets = len(facets) + obj.setEditorMode("facets", 1) + obj.addProperty( + "App::PropertyInteger", + "vertex", + "GDMLSampledTessellated", + "Vertex", + ).vertex = len(vertex) + obj.setEditorMode("vertex", 1) + obj.addProperty( + "App::PropertyEnumeration", + "lunit", + "GDMLSampledTessellated", + "lunit", + ) + setLengthQuantity(obj, lunit) + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLSampledTessellated", + "Material", + ) + + if flag is True: + nList = [len(f.Points) for f in facets] + else: + nList = [len(f) for f in facets] + + obj.addProperty( + "App::PropertyIntegerList", + "vertsPerFacet", + "GDMLSampledTessellated", + "Number of vertexes in each facet", + ).vertsPerFacet = nList + obj.setEditorMode("vertsPerFacet", 2) + + obj.addProperty( + "App::PropertyBool", + "solidFlag", + "GDMLSampledTessellated", + "Facets", + ).solidFlag = solidFlag + + percentageList = [str(i) for i in range(0, 105, 5)] + obj.addProperty( + "App::PropertyEnumeration", + "sampledFraction", + "GDMLSampledTessellated", + "Sampled percentage", + ).sampledFraction = percentageList + obj.sampledFraction = str(sampledFraction) + + # we use a set first to get rid of duplicate points + vertsSet = set() + for f in facets: + if flag is True: + for p in f.Points: + vertsSet.add(p) + else: + vertsSet.add(vertex[f[0]]) + vertsSet.add(vertex[f[1]]) + vertsSet.add(vertex[f[2]]) + if len(f) == 4: + vertsSet.add(vertex[f[3]]) + + vertsList = list(vertsSet) + obj.addProperty( + "App::PropertyVectorList", + "vertsList", + "GDMLSampledTessellated", + "Vertex list", + ).vertsList = vertsList + obj.setEditorMode("vertsList", 2) + + # create list of indexes for each face + Dict = {} + for i, v in enumerate(vertsList): + Dict[v] = i + + # now create a list of vert number references for each face + # there is probably a way to have lists of lists as a property; + # I just don't know about it, so we list the indexs in order + # and rely on the nList to get the number of points + indexList = [] + for f in facets: + if flag is True: + for v in f.Points: + indexList.append(Dict[v]) + else: + indexList.append(Dict[vertex[f[0]]]) + indexList.append(Dict[vertex[f[1]]]) + indexList.append(Dict[vertex[f[2]]]) + if len(f) == 4: + indexList.append(Dict[vertex[f[3]]]) + + obj.addProperty( + "App::PropertyIntegerList", + "indexList", + "GDMLSampledTessellated", + "Index List", + ).indexList = indexList + obj.setEditorMode("indexList", 2) + + setMaterial(obj, material) + self.updateParams(vertex, facets, solidFlag, sampledFraction, flag) + if FreeCAD.GuiUp: + updateColour(obj, colour, material) + if sampledFraction == 0 and solidFlag is False: + ViewProvider(obj.ViewObject) + modes = obj.ViewObject.Proxy.getDisplayModes(obj) + if "Points" in modes: + obj.ViewObject.DisplayMode = "Points" + obj.ViewObject.PointColor = (random(), random(), random(), 0.0) + self.Type = "GDMLSampledTessellated" + self.colour = colour + obj.Proxy = self + obj.Proxy.Type = "GDMLSampledTessellated" + + def updateParams(self, vertex, facets, solidFlag, sampledFraction, flag): + # print('Update Params & Shape') + self.pshape = self.createShape( + vertex, facets, solidFlag, sampledFraction, flag + ) + # print(f"Pshape vertex {len(self.pshape.Vertexes)}") + self.facets = len(facets) + self.vertex = len(vertex) + # print(f"Vertex : {self.vertex} Facets : {self.facets}") + + def onChanged(self, fp, prop): + """Do something when a property has changed""" + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if "Restore" in fp.State: + return + + if prop in ["material"]: + if FreeCAD.GuiUp: + if hasattr(self, "colour"): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in ["editable"]: + if fp.editable is True: + self.addProperties() + + if prop in ["scale"]: + self.createGeometry(fp) + + def addProperties(self): + print("Add Properties") + + # def execute(self, fp): in GDMLsolid + + def createGeometry(self, fp): + if hasattr(self, "pshape"): + # print('Update Shape') + fp.Shape = self.pshape + if hasattr(fp, "pshape"): + fp.pshape = self.pshape + fp.vertex = self.vertex + fp.facets = self.facets + if hasattr(fp, "scale"): + super().scale(fp) + + def createShape(self, vertex, facets, solidFlag, sampledFraction, flag): + # Viewing outside of face vertex must be counter clockwise + # if flag == True - facets is Mesh.Facets + # if flag == False - factes is Faces i.e. from import GDMLTessellated + # mul = GDMLShared.getMult(fp) + mul = GDMLShared.getMult(self) + if sampledFraction == 0 and solidFlag is False: + shape = self.cloud(vertex, facets, flag) + return shape + # print('Create Shape') + if solidFlag is False: + NMax = sampledFraction * len(facets) / 100 + nskip = int(len(facets) / NMax) + if nskip < 1: + nskip = 1 + else: + nskip = 1 + + FCfaces = [] + for i in range(0, len(facets), nskip): + f = facets[i] + # print('Facet') + # print(f) + if flag is True: + FCfaces.append(GDMLShared.facet(f)) + else: + if len(f) == 3: + FCfaces.append( + GDMLShared.triangle( + mul * vertex[f[0]], + 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]] + ) + ) + if solidFlag is False: + solid = Part.makeCompound(FCfaces) + else: + shell = Part.makeShell(FCfaces) + if shell.isValid is False: + FreeCAD.Console.PrintWarning("Not a valid Shell/n") + + # shell.check() + # solid=Part.Solid(shell).removeSplitter() + try: + solid = Part.Solid(shell) + except: + # make compound rather than just barf + # visually able to view at least + FreeCAD.Console.PrintWarning("Problem making Solid/n") + solid = Part.makeCompound(FCfaces) + + return solid + + def toMesh(self, obj): + import Mesh + + mesh = Mesh.Mesh() + # Viewing outside of face vertex must be counter clockwise + # if flag == True - facets is Mesh.Facets + # if flag == False - factes is Faces i.e. from import GDMLTessellated + # mul = GDMLShared.getMult(fp) + mul = GDMLShared.getMult(self) + print(f"mul {mul}") + verts = obj.vertsList + indexList = obj.indexList + i = 0 + for nVerts in obj.vertsPerFacet: + # print(f'Normal at : {n} dot {dot} {clockWise}') + i0 = indexList[i] + i1 = indexList[i + 1] + i2 = indexList[i + 2] + if nVerts == 3: + mesh.addFacet( + mul * verts[i0], mul * verts[i1], mul * verts[i2] + ) + elif nVerts == 4: + i3 = indexList[i + 3] + mesh.addFacet( + mul * verts[i0], + mul * verts[i1], + mul * verts[i2], + mul * verts[i3], + ) + i += nVerts + + return mesh + + def cloud(self, vertex, facets, flag): + print("Cloud called") + import random + + mul = GDMLShared.getMult(self) + pts = [] + if flag is True: + frac = 0.01 + Npts = int(frac * (len(facets))) + while Npts < 1000 and frac < 1: + frac += 0.01 + Npts = int(frac * (len(facets))) + jmax = len(facets) + for i in range(Npts): + j = random.randrange(jmax) + f = facets[j] + v = Part.Vertex(f.Points[0]) + pts.append(v) + else: + frac = 0.01 + Npts = int(frac * len(vertex)) + while Npts < 1000 and frac < 1: + frac += 0.01 + Npts = int(frac * (len(vertex))) + jmax = len(vertex) + for i in range(Npts): + j = random.randrange(jmax) + v = vertex[j] + pts.append(Part.Vertex(mul * v[0], mul * v[1], mul * v[2])) + + ret = Part.makeCompound(pts) + return ret + + def onChanged0(self, fp, prop): + """Do something when a property has changed""" + # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) + if "Restore" in fp.State: + return + + if prop in ["material"]: + if FreeCAD.GuiUp: + if hasattr(self, "colour"): + if self.colour is None: + fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 + + if prop in ["scale", "solidFlag", "sampledFraction"]: + self.createGeometry(fp) + + def createGeometry0(self, fp): + import time + + currPlacement = fp.Placement + mul = GDMLShared.getMult(fp) + if int(fp.sampledFraction) == 0: + return + # print('Create Shape') + + # The vertex index list, is not uniform, because some facets + # could have four vertexes, instead of three: + # indexList = [i00, i01, i02, i10, i11, i12, i13, i20, i21, i22, ....] + # vertsPerFacet = [2, 3, , 2, ...] + # if one traverses the facets in order, as we do on export, there is no + # problem finding the starting index for each facet. Bit if skip facets, + # as we do below, then we must build a list of the starting indexes of + # each facet + """ + i0List = [] + i = 0 + for j, nVerts in enumerate(fp.vertsPerFacet): + i0List.append(i) + i += nVerts + """ + + FCfaces = [] + if fp.solidFlag is False: + NMax = int(fp.sampledFraction) * fp.facets / 100 + nskip = int(fp.facets / NMax) + if nskip < 1: + nskip = 1 + else: + nskip = 1 + + print(f"nskip {nskip}") + indexList = fp.indexList + start = time.perf_counter() + i = 0 + for j, nVerts in enumerate(fp.vertsPerFacet): + if nVerts == 3: + i0 = indexList[i] + i1 = indexList[i + 1] + i2 = indexList[i + 2] + if j % nskip == 0: + FCfaces.append( + GDMLShared.triangle( + mul * fp.vertsList[i0], + mul * fp.vertsList[i1], + mul * fp.vertsList[i2] + ) + ) + else: # len should then be 4 + i0 = indexList[i] + i1 = indexList[i + 1] + i2 = indexList[i + 2] + i3 = indexList[i + 3] + if j % nskip == 0: + FCfaces.append( + GDMLShared.quad( + mul * fp.vertsList[i0], + mul * fp.vertsList[i1], + mul * fp.vertsList[i2], + mul * fp.vertsList[i3], + ) + ) + i += nVerts + end = time.perf_counter() + print(f"time to generate faces {(end-start)}") + + start = time.perf_counter() + if fp.solidFlag is False: + solid = Part.makeCompound(FCfaces) + else: + shell = Part.makeShell(FCfaces) + if shell.isValid is False: + FreeCAD.Console.PrintWarning("Not a valid Shell/n") + + # shell.check() + # solid=Part.Solid(shell).removeSplitter() + try: + solid = Part.Solid(shell) + except: + # make compound rather than just barf + # visually able to view at least + FreeCAD.Console.PrintWarning("Problem making Solid/n") + solid = Part.makeCompound(FCfaces) + end = time.perf_counter() + print(f"time to make solid {(end-start)}") + + fp.Shape = solid + if hasattr(fp, "scale"): + super().scale(fp) + fp.Placement = currPlacement + + +class GDMLTetra(GDMLsolid): # 4 point Tetrahedron def __init__(self, obj, v1, v2, v3, v4, lunit, material, colour=None): super().__init__(obj) - obj.addProperty("App::PropertyVector", "v1", "GDMLTra", - "v1").v1 = v1 - obj.addProperty("App::PropertyVector", "v2", "GDMLTra", - "v2").v2 = v2 - obj.addProperty("App::PropertyVector", "v3", "GDMLTra", - "v3").v3 = v3 - obj.addProperty("App::PropertyVector", "v4", "GDMLTra", - "v4").v4 = v4 - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTra", - "lunit") + obj.addProperty("App::PropertyVector", "v1", "GDMLTra", "v1").v1 = v1 + obj.addProperty("App::PropertyVector", "v2", "GDMLTra", "v2").v2 = v2 + obj.addProperty("App::PropertyVector", "v3", "GDMLTra", "v3").v3 = v3 + obj.addProperty("App::PropertyVector", "v4", "GDMLTra", "v4").v4 = v4 + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTra", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLTra", "Material") + obj.addProperty( + "App::PropertyEnumeration", "material", "GDMLTra", "Material" + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) # Suppress Placement - position & Rotation via parent App::Part # this makes Placement via Phyvol easier and allows copies etc - self.Type = 'GDMLTetra' + self.Type = "GDMLTetra" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTetra" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['v1', 'v2', 'v3', 'v4', 'lunit']: + if prop in ["v1", "v2", "v3", "v4", "lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -3535,25 +5089,33 @@ 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLTetrahedron(GDMLsolid): - ''' Does not exist as a GDML solid, but export as an Assembly of G4Tet ''' - ''' See paper Poole at al - Fast Tessellated solid navigation in GEANT4 ''' + """Does not exist as a GDML solid, but export as an Assembly of G4Tet""" + + """ See paper Poole at al - Fast Tessellated solid navigation in GEANT4 """ def __init__(self, obj, tetra, lunit, material, colour=None): super().__init__(obj) - obj.addProperty('App::PropertyInteger', 'tetra', 'GDMLTetrahedron', - 'Tetra').tetra = len(tetra) - obj.setEditorMode('tetra', 1) - obj.addProperty("App::PropertyEnumeration", "lunit", "GDMLTetrahedron", - "lunit") + obj.addProperty( + "App::PropertyInteger", "tetra", "GDMLTetrahedron", "Tetra" + ).tetra = len(tetra) + obj.setEditorMode("tetra", 1) + obj.addProperty( + "App::PropertyEnumeration", "lunit", "GDMLTetrahedron", "lunit" + ) setLengthQuantity(obj, lunit) - obj.addProperty("App::PropertyEnumeration", "material", - "GDMLTetrahedron", "Material") + obj.addProperty( + "App::PropertyEnumeration", + "material", + "GDMLTetrahedron", + "Material", + ) setMaterial(obj, material) if FreeCAD.GuiUp: updateColour(obj, colour, material) @@ -3562,26 +5124,30 @@ def __init__(self, obj, tetra, lunit, material, colour=None): # obj.addExtension('App::GroupExtensionPython') self.Tetra = tetra self.Object = obj - self.Type = 'GDMLTetrahedron' + self.Type = "GDMLTetrahedron" self.colour = colour obj.Proxy = self + obj.Proxy.Type = "GDMLTetrahedron" def onChanged(self, fp, prop): - '''Do something when a property has changed''' + """Do something when a property has changed""" # print(fp.Label+" State : "+str(fp.State)+" prop : "+prop) - if 'Restore' in fp.State: + if "Restore" in fp.State: return - if prop in ['material']: + if prop in ["material"]: if FreeCAD.GuiUp: - if hasattr(self, 'colour'): + if hasattr(self, "colour"): if self.colour is None: fp.ViewObject.ShapeColor = colourMaterial(fp.material) + if fp.material == "G4_AIR": + print("Set Transparency") + fp.ViewObject.Transparency = 98 - if prop in ['lunit']: + if prop in ["lunit"]: self.createGeometry(fp) - if prop in ['scale']: + if prop in ["scale"]: self.createGeometry(fp) # def execute(self, fp): in GDMLsolid @@ -3591,7 +5157,7 @@ def makeTetra(self, pt1, pt2, pt3, pt4): face2 = Part.Face(Part.makePolygon([pt1, pt2, pt4, pt1])) face3 = Part.Face(Part.makePolygon([pt4, pt2, pt3, pt4])) face4 = Part.Face(Part.makePolygon([pt1, pt3, pt4, pt1])) - return(Part.makeShell([face1, face2, face3, face4])) + return Part.makeShell([face1, face2, face3, face4]) def createGeometry(self, fp): currPlacement = fp.Placement @@ -3606,31 +5172,44 @@ 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) + if hasattr(fp, "scale"): + super().scale(fp) fp.Placement = currPlacement class GDMLFiles(GDMLcommon): def __init__(self, obj, FilesEntity, sectionDict): super().__init__(obj) - '''Add some custom properties to our Cone feature''' + """Add some custom properties to our Cone feature""" GDMLShared.trace("GDML Files") GDMLShared.trace(FilesEntity) - obj.addProperty("App::PropertyBool", "active", "GDMLFiles", - "split option").active = FilesEntity - obj.addProperty("App::PropertyString", "define", "GDMLFiles", - "define section").define = sectionDict.get('define', "") - obj.addProperty("App::PropertyString", "materials", "GDMLFiles", - "materials section").materials = sectionDict.get('materials', "") - obj.addProperty("App::PropertyString", "solids", "GDMLFiles", - "solids section").solids = sectionDict.get('solids', "") - obj.addProperty("App::PropertyString", "structure", "GDMLFiles", - "structure section").structure = sectionDict.get('structure', "") - self.Type = 'GDMLFiles' + obj.addProperty( + "App::PropertyBool", "active", "GDMLFiles", "split option" + ).active = FilesEntity + obj.addProperty( + "App::PropertyString", "define", "GDMLFiles", "define section" + ).define = sectionDict.get("define", "") + obj.addProperty( + "App::PropertyString", + "materials", + "GDMLFiles", + "materials section", + ).materials = sectionDict.get("materials", "") + obj.addProperty( + "App::PropertyString", "solids", "GDMLFiles", "solids section" + ).solids = sectionDict.get("solids", "") + obj.addProperty( + "App::PropertyString", + "structure", + "GDMLFiles", + "structure section", + ).structure = sectionDict.get("structure", "") + self.Type = "GDMLFiles" obj.Proxy = self + obj.Proxy.Type = "GDMLFiles" def execute(self, fp): - '''Do something when doing a recomputation, this method is mandatory''' + """Do something when doing a recomputation, this method is mandatory""" pass def onChanged(self, fp, prop): @@ -3649,10 +5228,12 @@ def __init__(self, obj): class GDMLconstant(GDMLcommon): def __init__(self, obj, name, value): super().__init__(obj) - obj.addProperty("App::PropertyString", "name", 'GDMLconstant', - 'name').name = name - obj.addProperty("App::PropertyString", "value", 'GDMLconstant', - 'value').value = value + obj.addProperty( + "App::PropertyString", "name", "GDMLconstant", "name" + ).name = name + obj.addProperty( + "App::PropertyString", "value", "GDMLconstant", "value" + ).value = value obj.Proxy = self self.Object = obj @@ -3660,10 +5241,12 @@ def __init__(self, obj, name, value): class GDMLvariable(GDMLcommon): def __init__(self, obj, name, value): super().__init__(obj) - obj.addProperty("App::PropertyString", "name", 'GDMLvariable', - 'name').name = name - obj.addProperty("App::PropertyString", "value", 'GDMLvariable', - 'value').value = value + obj.addProperty( + "App::PropertyString", "name", "GDMLvariable", "name" + ).name = name + obj.addProperty( + "App::PropertyString", "value", "GDMLvariable", "value" + ).value = value obj.Proxy = self self.Object = obj @@ -3671,33 +5254,52 @@ def __init__(self, obj, name, value): class GDMLquantity(GDMLcommon): def __init__(self, obj, name, type, unit, value): super().__init__(obj) - obj.addProperty("App::PropertyString", "name", 'GDMLvariable', - 'name').name = name - obj.addProperty("App::PropertyString", "type", 'GDMLvariable', - 'type').type = type - obj.addProperty("App::PropertyString", "unit", 'GDMLvariable', - 'unit').unit = unit - obj.addProperty("App::PropertyString", "value", 'GDMLvariable', - 'value').value = value + obj.addProperty( + "App::PropertyString", "name", "GDMLvariable", "name" + ).name = name + obj.addProperty( + "App::PropertyString", "type", "GDMLvariable", "type" + ).type = type + obj.addProperty( + "App::PropertyString", "unit", "GDMLvariable", "unit" + ).unit = unit + obj.addProperty( + "App::PropertyString", "value", "GDMLvariable", "value" + ).value = value obj.Proxy = self self.Object = obj class GDMLmaterial(GDMLcommon): - def __init__(self, obj, name, density=1.0, conduct=2.0, expand=3.0, - specific=4.0): + def __init__( + self, obj, name, density=1.0, conduct=2.0, expand=3.0, specific=4.0 + ): super().__init__(obj) # Add most properties later - obj.addProperty("App::PropertyString", "name", 'GDMLmaterial', - 'name').name = name - obj.addProperty("App::PropertyFloat", "density", "GDMLmaterial", - "Density kg/m^3").density = density - obj.addProperty("App::PropertyFloat", "conduct", "GDMLmaterial", - "Thermal Conductivity W/m/K").conduct = conduct - obj.addProperty("App::PropertyFloat", "expand", "GDMLmaterial", - "Expansion Coefficient m/m/K").expand = expand - obj.addProperty("App::PropertyFloat", "specific", "GDMLmaterial", - "Specific Heat J/kg/K").specific = specific + obj.addProperty( + "App::PropertyString", "name", "GDMLmaterial", "name" + ).name = name + obj.addProperty( + "App::PropertyFloat", "density", "GDMLmaterial", "Density kg/m^3" + ).density = density + obj.addProperty( + "App::PropertyFloat", + "conduct", + "GDMLmaterial", + "Thermal Conductivity W/m/K", + ).conduct = conduct + obj.addProperty( + "App::PropertyFloat", + "expand", + "GDMLmaterial", + "Expansion Coefficient m/m/K", + ).expand = expand + obj.addProperty( + "App::PropertyFloat", + "specific", + "GDMLmaterial", + "Specific Heat J/kg/K", + ).specific = specific obj.Proxy = self self.Object = obj @@ -3706,7 +5308,7 @@ def __init__(self, obj, name, density=1.0, conduct=2.0, expand=3.0, class GDMLfraction(GDMLcommon): def __init__(self, obj, ref, n): super().__init__(obj) - obj.addProperty("App::PropertyFloat", 'n', ref).n = n + obj.addProperty("App::PropertyFloat", "n", ref).n = n obj.Proxy = self self.Object = obj @@ -3741,6 +5343,275 @@ def __init__(self, obj, name, N, Z): self.Object = obj +class GDMLmatrix(GDMLcommon): + def __init__(self, obj, name, coldim, values): + super().__init__(obj) + obj.addProperty( + "App::PropertyInteger", "coldim", "GDMLmatrix", "coldin" + ).coldim = coldim + obj.addProperty( + "App::PropertyString", "values", "GDMLmatrix", "values" + ).values = values + obj.Proxy = self + self.Object = obj + + +class GDMLopticalsurface(GDMLcommon): + def __init__(self, obj, name, model, finish, typeVal, value): + super().__init__(obj) + print(f"passed name {name} model {model} finish {finish} type {typeVal}") + obj.addProperty( + "App::PropertyEnumeration", "model", "GDMLoptical", "model" + ) + self.modelList = [ + "glisur", # 0 original GEANT3 model + "unified", # 1 UNIFIED model + "LUT", # 2 Look-Up-Table model (LBNL model) + "DAVIS", # 3 DAVIS model + "dichroic" # 4 dichroic filter + ] + obj.model = self.modelList + # Set passed value + if model.isnumeric(): + obj.model = self.modelList[int(model)] + else: + obj.model = model + + # finish + obj.addProperty( + "App::PropertyEnumeration", "finish", "GDMLoptical" "finish" + ) + self.finish = [ + "polished", # 0 smooth perfectly polished surface + "polishedfrontpainted", # 1 smooth top - layer(front) paint + "polishedbackpainted", # 2 same is 'polished' but with a back-paint + + "ground", # 3 rough surface + "groundfrontpainted", # 4 rough top-layer (front) paint + "groundbackpainted", # 5 same as 'ground' but with a back-paint + + # for LBNL LUT model + "polishedlumirrorair", # 6 mechanically polished surface, with lumirror + "polishedlumirrorglue", # 7 mechanically polished surface, with lumirror & meltmount + "polishedair", # 8 mechanically polished surface + "polishedteflonair", # 9 mechanically polished surface, with teflon + "polishedtioair", # 10 mechanically polished surface, with tio paint + "polishedtyvekair", # 11 mechanically polished surface, with tyvek + "polishedvm2000air", # 12 mechanically polished surface, with esr film + "polishedvm2000glue", # 13 mechanically polished surface, with esr film & meltmount + + "etchedlumirrorair", # 14 chemically etched surface, with lumirror + "etchedlumirrorglue", # 15 chemically etched surface, with lumirror & meltmount + "etchedair", # 16 chemically etched surface + "etchedteflonair", # 17 chemically etched surface, with teflon + "etchedtioair", # 18 chemically etched surface, with tio paint + "etchedtyvekair", # 19 chemically etched surface, with tyvek + "etchedvm2000air", # 20 chemically etched surface, with esr film + "etchedvm2000glue", # 21 chemically etched surface, with esr film & meltmount + + "groundlumirrorair", # 22 rough-cut surface, with lumirror + "groundlumirrorglue", # 23 rough-cut surface, with lumirror & meltmount + "groundair", # 24 rough-cut surface + "groundteflonair", # 25 rough-cut surface, with teflon + "groundtioair", # 26 rough-cut surface, with tio paint + "groundtyvekair", # 27 rough-cut surface, with tyvek + "groundvm2000air", # 28 rough-cut surface, with esr film + "groundvm2000glue", # 29 rough-cut surface, with esr film & meltmount + + # for DAVIS model + "Rough_LUT", # 30 rough surface + "RoughTeflon_LUT", # 31 rough surface wrapped in Teflon tape + "RoughESR_LUT", # 32 rough surface wrapped with ESR + "RoughESRGrease_LUT", # 33 rough surface wrapped with ESR + # and coupled with optical grease + "Polished_LUT", # 34 polished surface + "PolishedTeflon_LUT", # 35 polished surface wrapped in Teflon tape + "PolishedESR_LUT", # 36 polished surface wrapped with ESR + "PolishedESRGrease_LUT", # 37 polished surface wrapped with ESR + # and coupled with optical grease + "Detector_LUT" # 38 polished surface with optical grease + ] + obj.finish = self.finish + if finish.isnumeric(): + obj.finish = self.finish[int(finish)] + else: + obj.finish = finish + + obj.addProperty( + "App::PropertyEnumeration", "type", "GDMLoptical", "type" + ) + self.type = [ # enum G4SurfaceType + "dielectric_metal", # dielectric-metal interface + "dielectric_dielectric", # dielectric-dielectric interface + "dielectric_LUT", # dielectric-Look-Up-Table interface + "dielectric_LUTDAVIS", # dielectric-Look-Up-Table DAVIS interface + "dielectric_dichroic", # dichroic filter interface + "firsov", # for Firsov Process + "x_ray", # for x-ray mirror process + "coated", # coated_dielectric-dielectric interface + ] + obj.type = self.type + if typeVal.isnumeric(): + obj.type = self.type[int(typeVal)] + else: + obj.type = typeVal + + obj.addProperty( + "App::PropertyFloat", "value", "GDMLoptical" + ).value = value + obj.Proxy = self + + def onChanged(self, fp, prop): + if prop in ["finishNum", "typeNum", "modelNum"]: + print(f"property {prop} is no longer supported. Please adjust property {prop[:-3]}") + +class GDMLskinsurface(GDMLcommon): + def __init__(self, obj, name, prop): + super().__init__(obj) + obj.addProperty( + "App::PropertyString", "surface", "GDMLskin", "surface property" + ).surface = prop + obj.Proxy = self + self.Object = obj + + +# ??? need for GDMLcommon ??? +class GDMLbordersurface(GDMLcommon): + ''' + In gdml the bordersurface takes the name of a surface and the name of two physvols + that are the placement of two volumes that contain the solids whose surfaces have + a common border. In FreeCAD we do not have an object that corresponds directly + to a physvol. We have an App::Part object that results in the creation of two gdml + objects: (1) a * # * (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) * @@ -28,25 +31,38 @@ __author__ = "Keith Sloan " __url__ = ["https://github.com/KeithSloan/FreeCAD_Geant4"] +from sys import breakpointhook + import FreeCAD, os, Part, math +import Sketcher +import FreeCAD as App +import FreeCADGui +from PySide import QtGui + from FreeCAD import Vector + +import random from .GDMLObjects import GDMLcommon, GDMLBox, GDMLTube # modif add # from .GDMLObjects import getMult, convertionlisteCharToLunit import sys +from pathlib import Path + try: import lxml.etree as ET + FreeCAD.Console.PrintMessage("running with lxml.etree\n") - XML_IO_VERSION = 'lxml' + 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' + XML_IO_VERSION = "xml" except ImportError: - FreeCAD.Console.PrintMessage('pb xml lib not found\n') + FreeCAD.Console.PrintMessage("pb xml lib not found\n") sys.exit() # xml handling # import argparse @@ -55,28 +71,110 @@ global zOrder -from .GDMLObjects import GDMLQuadrangular, GDMLTriangular, \ - GDML2dVertex, GDMLSection, \ - GDMLmaterial, GDMLfraction, \ - GDMLcomposite, GDMLisotope, \ - GDMLelement, GDMLconstant, GDMLvariable, \ - GDMLquantity +from .GDMLObjects import ( + GDMLQuadrangular, + GDMLTriangular, + GDML2dVertex, + GDMLSection, + GDMLmaterial, + GDMLfraction, + GDMLcomposite, + GDMLisotope, + GDMLelement, + GDMLconstant, + GDMLvariable, + GDMLquantity, + GDMLbordersurface, +) from . import GDMLShared -from . import exportExtrusion + + +def get_active_branch_name(): + gdml_dir = FreeCAD.getUserAppDataDir() + "/Mod/GDML" + head_dir = Path(gdml_dir) / ".git" / "HEAD" + try: + with head_dir.open("r") as f: content = f.read().splitlines() + except: + return f"Can't locate .git directory in {gdml_dir}" + + for line in content: + if line[0:4] == "ref:": + return line.partition("refs/heads/")[2] + # *************************************************************************** # 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']: +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 +class NameManager: + _nameCountDict: dict[str, int] = {} + _solidsNamesDict = {} + _volumeNamesDict = {} + + @staticmethod + def init(): + NameManager._nameCountDict = {} + NameManager._solidsNamesDict = {} + NameManager._volumeNamesDict = {} + + @staticmethod + def getName(obj) -> str: + if obj in NameManager._solidsNamesDict: + return NameManager._solidsNamesDict[obj] + name = obj.Label + if len(name) > 4: + if name[0:4] == "GDML": + if "_" in name: + name = name.split("_", 1)[1] + + if name[0].isdigit(): + name = "S" + name + + if name in NameManager._nameCountDict: + count = 1 + NameManager._nameCountDict[name] + NameManager._nameCountDict[name] = count + name = name + str(count) + else: + NameManager._nameCountDict[name] = 0 + + NameManager._solidsNamesDict[obj] = name + + return name + + @staticmethod + def nameUsedFor(obj): + if obj in NameManager._solidsNamesDict: + return NameManager._solidsNamesDict[obj] + else: + return None + + @staticmethod + def getVolumeName(vol) -> str: + if vol in NameManager._volumeNamesDict: + return NameManager._volumeNamesDict[vol] + + if vol.TypeId == "App::Part": + return vol.Label + + elif vol.TypeId == "App::Link": + return NameManager.getVolumeName(vol.LinkedObject) + else: + name = NameManager.nameUsedFor(vol) + if name is None: + name = "LV_"+NameManager.getName(vol) + NameManager._volumeNamesDict[vol] = name + return name + + @staticmethod + def getPhysvolName(vol): + name = NameManager.getName(vol) + return "PV_" + name # ## end modifs lambda @@ -96,21 +194,116 @@ def __new__(class_, value): def case(*args): return any((arg == switch.value for arg in args)) + +class MultiPlacer: + def __init__(self, obj): + self.obj = obj + self._name = NameManager.getName(obj) + + def place(self, volRef): + print("Can't place base class MultiPlace") + + def xml(self): + print("Can't place base class MultiPlace") + + def name(self): + return self._name + + @staticmethod + def getPlacer(obj): + if obj.TypeId == "Part::Mirroring": + return MirrorPlacer(obj) + else: + print(f"{obj.Label} is not a placer") + return None + + +class MirrorPlacer(MultiPlacer): + def __init__(self, obj): + super().__init__(obj) + self.assembly = None # defined AFTER place() + + def xml(self): + return self.assembly + + def place(self, volRef): + global structure + name = self.name() + assembly = ET.Element("assembly", {"name": name}) + # structure.insert(0, assembly) + # insert just before worlVol, which should be last + worldIndex = len(structure) - 1 + structure.insert(worldIndex, assembly) + pos = self.obj.Source.Placement.Base + name = volRef + "_mirror" + # bordersurface might need physvol to have a name + physvolName = NameManager.getPhysvolName(self.obj) + pvol = ET.SubElement( + assembly, "physvol", {"name": physvolName} + ) + ET.SubElement(pvol, "volumeref", {"ref": volRef}) + normal = self.obj.Normal + # reflect the position about the reflection plane + unitVec = normal.normalize() + posAlongNormal = pos.dot(unitVec) * unitVec + posParalelToPlane = pos - posAlongNormal + newPos = posParalelToPlane - posAlongNormal + # first reflect about x-axis + # then rotate to bring x-axis to direction of normal + rotX = False + if normal.x == 1: + scl = Vector(-1, 1, 1) + newPos = Vector(-pos.x, pos.y, pos.z) + elif normal.y == 1: + scl = Vector(1, -1, 1) + newPos = Vector(pos.x, -pos.y, pos.z) + elif normal.z == 1: + scl = Vector(1, 1, 1 - 1) + newPos = Vector(pos.x, pos.y, -pos.z) + else: + scl = Vector(-1, 1, 1) + newPos = Vector(-pos.x, pos.y, pos.z) + rotX = True + + rot = FreeCAD.Rotation() + if rotX is True: + # rotation to bring normal to x-axis (might have to reverse) + rot = FreeCAD.Rotation(Vector(1, 0, 0), unitVec) + # The angle of rotation of the image twice the angle of rotation of the mirror + rot.Angle = 2 * rot.Angle + newPos = rot * newPos + sourcePlacement = FreeCAD.Placement(newPos, rot) + # placement = self.obj.Placement*sourcePlacement + placement = sourcePlacement + exportPosition(name, pvol, placement.Base) + if rotX is True: + exportRotation(name, pvol, placement.Rotation) + exportScaling(name, pvol, scl) + + self.assembly = assembly + + +class PhysVolPlacement: + def __init__(self, ref, placement): + self.ref = ref # name reference: a string + self.placement = placement # physvol placement + + ######################################################### # Pretty format GDML # ######################################################### def indent(elem, level=0): - i = "\n" + level*" " - j = "\n" + (level-1)*" " + 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: - indent(subelem, level+1) + indent(subelem, level + 1) if not elem.tail or not elem.tail.strip(): elem.tail = j else: @@ -118,24 +311,38 @@ def indent(elem, level=0): elem.tail = j return elem + ######################################### +def cleanGDMLname(name): + # Clean GDML name for Geant4 + # Replace space and special characters with '_' + return name.replace('\r','').replace('(','_').replace(')','_').replace(' ','_') + + def nameFromLabel(label): - if ' ' not in label: + if " " not in label: return label else: - return(label.split(' ')[0]) + return label.split(" ")[0] 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) + NS = "http://www.w3.org/2001/XMLSchema-instance" + location_attribute = "{%s}noNamespaceSchemaLocation" % NS + # For some reason on my system around Sep 30, 2024, the following url is unreachable, + # I think because http:// is no longer accepted, so use https:// instead. DID NOT WORK!, + # although wget of url works. I don't know what's going on + gdml = ET.Element( + "gdml", + attrib={ + location_attribute: "https://service-spi.web.cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd" + }, + ) return gdml + ################################# # Setup GDML environment ################################# @@ -146,23 +353,29 @@ def GDMLstructure(): ################################# # globals ################################ - global gdml, constants, variables, define, materials, solids, \ - structure, setup + global gdml, constants, variables, define, materials, solids, structure, setup global WorldVOL - global defineCnt, LVcount, PVcount, POScount, ROTcount + global defineCnt, LVcount, PVcount, POScount, ROTcount, SCLcount + global centerDefined + global identityDefined + global identityName global gxml - defineCnt = LVcount = PVcount = POScount = ROTcount = 1 + centerDefined = False + identityDefined = False + identityName = 'identity' - gdml = initGDML() - define = ET.SubElement(gdml, 'define') - materials = ET.SubElement(gdml, 'materials') - solids = ET.SubElement(gdml, 'solids') - structure = ET.SubElement(gdml, 'structure') - setup = ET.SubElement(gdml, 'setup', {'name': 'Default', 'version': '1.0'}) - gxml = ET.Element('gxml') + defineCnt = LVcount = PVcount = POScount = ROTcount = SCLcount = 1 - exportExtrusion.setGlobals(define, materials, solids) + gdml = initGDML() + define = ET.SubElement(gdml, "define") + materials = ET.SubElement(gdml, "materials") + solids = ET.SubElement(gdml, "solids") + solids.clear() + structure = ET.SubElement(gdml, "structure") + setup = ET.SubElement(gdml, "setup", {"name": "Default", "version": "1.0"}) + gxml = ET.Element("gxml") + _ = SurfaceManager() return structure @@ -175,14 +388,40 @@ def defineMaterials(): def exportDefine(name, v): global define - 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): global define ET.SubElement(define, 'position', {'name': name + str(index), 'unit': 'mm', 'x': str(v.X), 'y': str(v.Y), 'z': str(v.Z)}) +""" + + +def exportDefineVertex(name, v, index): + global define + 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): @@ -197,20 +436,26 @@ def defineWorldBox(bbox): 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'}) - return(name) + 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", + }, + ) + 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. @@ -227,118 +472,86 @@ def quaternion2XYZ(rot): [ 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)]] + Rederivation from the previous version. Geant processes the rotation from + the gdml as R = Rx Ry Rz, i.e, Rx applied last, not first, so now we have - 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) + R = Rx Ry Rz = + [cosb*cosg, -cosb*sing, sinb], + [cosa*sing+cosg*sina*sinb, cosa*cosg-sina*sinb*sing, -cosb*sina], + [sina*sing-cosa*cosg*sinb, cosa*sinb*sing+cosg*sina, cosa*cosb] - ==> 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)) + To get the angles a(lpha), b(eta) for rotations around x, y axes, transform the unit vector (0,0,1) + [x,y,z] = Q*(0,0,1) = R*(0,0,1) ==> + x = sin(b) + y = -sin(a)cos(b) + z = cos(a)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) + ==> a = atan2(-y, x) = atan2(sin(a)*cos(b), cos(a)*cos(b)) = atan2(sin(a), cos(a)) + then b = atan2(x*cos(a), z) = atan2(sin(b)*cos(a), cos(b)*cos(a)] = atan2(sin(b), cos(b)) + + Once a, b are found, g(amma) can be found by transforming (1, 0, 0), or (0,1,0) + Since now a, b are known, one can form the inverses of Rx, Ry: + Rx^-1 = Rx(-a) 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) + Now R*(1,0,0) = Rx*Ry*Rz(1,0,0) = (x, y, z) + multiply both sides by Ry^-1 Rx^-1: + Ry^-1 Rx^-1 Rx Ry Rz (1,0,0) = Rz (1,0,0) = Ry(-b) Rx(-a) (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) + xp = cos(g) + yp = sin(g) + zp = 0 + + and g = atan2(yp, zp) + """ + v = rot * Vector(0, 0, 1) + print(v) + # solution 1. + if v.x > 1.0: + v.x = 1.0 + if v.x < -1.0: + v.x = -1.0 + b = math.asin(v.x) + if math.cos(b) > 0: + a = math.atan2(-v.y, v.z) + else: + a = math.atan2(v.y, -v.z) + # sanity check 1 + ysolution = -math.sin(a) * math.cos(b) + zsolution = math.cos(a) * math.cos(b) + if v.y * ysolution < 0 or v.z * zsolution < 0: + print("Trying second solution") + b = math.pi - b + if math.cos(b) > 0: + a = math.atan2(-v.y, v.z) + else: + a = math.atan2(v.y, -v.z) + # sanity check 2 + ysolution = -math.sin(a) * math.cos(b) + zsolution = math.cos(a) * math.cos(b) + if v.y * ysolution < 0 or v.z * zsolution < 0: + print("Failed both solutions!") + print(v.y, ysolution) + print(v.z, zsolution) 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) + Rxinv = FreeCAD.Rotation(Vector(1, 0, 0), math.degrees(-a)) + vp = Ryinv * Rxinv * rot * Vector(1, 0, 0) + g = math.atan2(vp.y, vp.x) - 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 - # - # Need to update so that use export of Rotation & position - # rather than this as well i.e one Place - # - global PVcount, POScount, ROTcount - pvName = 'PV'+name+str(PVcount) - PVcount += 1 - 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}) - 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)}) - # Realthunders enhancement to toEuler ixyz is intrinsic - rot = obj.Placement.Rotation - if hasattr(rot, 'toEulerAngles'): - angles = rot.toEulerAngles('ixyz') - angles = (angles[2], angles[1], angles[0]) - else: - print('Export of rotation probably wrong') - print('Needs toEulerAngles function - Use LinkStage 3') - angles = rot.toEuler() - GDMLShared.trace("Angles") - GDMLShared.trace(angles) - angles = quaternion2XYZ(rot) - a0 = angles[0] - # print(a0) - a1 = angles[1] - # 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(-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() - createLVandPV(adjObj, name, solidName) - - def reportObject(obj): GDMLShared.trace("Report Object") GDMLShared.trace(obj) - GDMLShared.trace("Name : "+obj.Name) - GDMLShared.trace("Type : "+obj.TypeId) - if hasattr(obj, 'Placement'): + GDMLShared.trace("Name : " + obj.Label) + 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)) + print("Pos : " + str(obj.Placement.Base)) + print("axis : " + str(obj.Placement.Rotation.Axis)) + print("angle : " + str(obj.Placement.Rotation.Angle)) while switch(obj.TypeId): @@ -362,22 +575,39 @@ def reportObject(obj): # FreeCAD Parts # ########################################### if case("Part::Sphere"): - print("Sphere Radius : "+str(obj.Radius)) + print("Sphere Radius : " + str(obj.Radius)) break if case("Part::Box"): - print("cube : (" + str(obj.Length)+"," + str(obj.Width)+"," + - str(obj.Height) + ")") + 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)) + 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)) + print( + "cone : Height " + + str(obj.Height) + + " Radius1 " + + str(obj.Radius1) + + " Radius2 " + + str(obj.Radius2) + ) break if case("Part::Torus"): @@ -416,957 +646,806 @@ def reportObject(obj): 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() 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): - # obj needed for Volune names - # object maynot have Mesh as part of Obj - # Name - allows control over name - print("Create Tessellate Logical Volume") - createLVandPV(obj, Name, 'Tessellated') - 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): - # 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 - 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 - -# Dropped through to here -# Need to check has Shape - - # print('Check if All planar') - planar = checkShapeAllPlanar(shape) - # print(planar) - - 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)) - - -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) - return(boxName) - - -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) - return(cylName) - - -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) - 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 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 - 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) - 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}) + pvol = ET.SubElement(xmlVol, "physvol", {"name": "PV_" + volName}) + ET.SubElement(pvol, "volumeref", {"ref": volName}) return pvol -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) - return volName +def getIdentifier(obj): + ''' For objects created from a gdml file we need a unique identifier to locate + the entry in the gdmlInfo sheet. + ''' + return obj.Label + if hasattr(obj, "CopyNumber"): + append = '.' + str(obj.CopyNumber) + else: + append = '' + + print(f"{obj.Name}{append}") + if obj.TypeId == "App::Part": + name = obj.Name + append + return name + elif obj.TypeId == "App::Link": + return obj.LinkedObject.Name + append + else: + return obj.Name + + +def addPhysVolPlacement(obj, xmlVol, placement, pvName=None, refName=None) -> None: + # obj: App:Part to be placed. + # xmlVol: the xml that the 4: - if name[0:4] == 'GDML': - if '_' in name: - return(name.split('_', 1)[1]) - return name - - -def processGDMLArb8Object(obj): - # Needs unique Name - # Remove leading GDMLArb8 from name on export - arb8Name = nameOfGDMLobject(obj) - - 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): - # Needs unique Name - # 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): - # Needs unique Name - # Remove leading GDMLTube_ from name on export - coneName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # Remove leading GDML text from name - cTubeName = nameOfGDMLobject(obj) - 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): - GDMLShared.trace('Elliptical Cone') - elconeName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - ellipsoidName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - eltubeName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # 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.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.SubElement(solids, 'orb', {'name': orbName, - 'r': str(obj.r), - 'lunit': obj.lunit}) - return solid, orbName - - -def processGDMLParaObject(obj): - # Needs unique Name - # flag needed for boolean otherwise parse twice - paraName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - # polyconeName = 'Cone' + obj.Name - polyconeName = nameOfGDMLobject(obj) - 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): - polyconeName = nameOfGDMLobject(obj) - 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): - polyhedraName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - # polyconeName = 'Cone' + obj.Name - GDMLShared.trace('export Polyhedra') - polyhedraName = nameOfGDMLobject(obj) - 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): - 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): - # Needs unique Name - sphereName = nameOfGDMLobject(obj) - - 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): - # Needs unique Name - # Need to output unique define positions - # Need to create set of positions - - tessName = nameOfGDMLobject(obj) - # Use more readable version - tessVname = tessName + '_' - # print(dir(obj)) - vertexHashcodeDict = {} - - 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 - 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): - 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.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))) - count = 0 - 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]) - 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}) - count = 0 - 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') - count += 1 - - return assembly, tetrahedronName - - -def processGDMLTorusObject(obj): - torusName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - trapName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - trdName = nameOfGDMLobject(obj) - 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 processGDMLTubeObject(obj): - # Needs unique Name - # flag needed for boolean otherwise parse twice - tubeName = nameOfGDMLobject(obj) - 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.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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - solidName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - solidName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - # flag needed for boolean otherwise parse twice - solidName = nameOfGDMLobject(obj) - 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): - # Needs unique Name - xtruName = nameOfGDMLobject(obj) - - 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'): +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)) + item.set("Z", str(obj.Z)) - if hasattr(obj, 'N'): + if hasattr(obj, "N"): # print(dir(obj)) - item.set('N', str(obj.N)) + item.set("N", str(obj.N)) - if hasattr(obj, 'formula'): + if hasattr(obj, "formula"): # print(dir(obj)) - item.set('formula', str(obj.formula)) + item.set("formula", str(obj.formula)) + + 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, "value"): + atom.set("value", str(obj.value)) + + +def processMatrix(obj): + + global define + print("add matrix to define") + ET.SubElement( + define, + "matrix", + {"name": obj.Label, "coldim": str(obj.coldim), "values": obj.values}, + ) + + +class SurfaceManager: + ''' Class to isolate methods and globals dealing with surfaces + from the rest of the code + ''' + + skinSurfaces = [] + + def __init__(self): + SurfaceManager.skinSurfaces = [] + + @staticmethod + def addSurface(s): + SurfaceManager.skinSurfaces.append(s) + + @staticmethod + def cleanFinish(finish): + print(f"finish {finish}") + if finish == "polished | polished": + return "polished" + else: + ext = "extended | " + if ext not in finish: + # print('Does not contain') + return finish.replace(" | ", "") + else: + # print(f"Replace {finish.replace(ext,'')}") + return finish.replace(ext, "") + + @staticmethod + def cleanExtended(var): + ext = "extended | " + if ext not in var: + return var + else: + return var.replace(ext, "") + + + @staticmethod + def processOpticalSurface(obj): + global solids + # print(solids) + print("Add opticalsurface") + print(str(solids)) + finish = SurfaceManager.cleanFinish(obj.finish) + type = SurfaceManager.cleanExtended(obj.type) + op = ET.SubElement( + solids, + "opticalsurface", + { + "name": obj.Label, + "model": obj.model, + "finish": finish, + "type": type, + "value": str(obj.value), + }, + ) + for prop in obj.PropertiesList: + if obj.getGroupOfProperty(prop) == "Properties": + ET.SubElement( + op, "property", {"name": prop, "ref": getattr(obj, prop)} + ) + + + @staticmethod + def processSkinSurfaces(): + global structure + + for ss in SurfaceManager.skinSurfaces: + structure.append(ss) + return + + @staticmethod + def getPVobject(doc, name_or_obj): + ''' + In an older version of GDMLbordersurface the two properties + PV1 and PV2 where strings that are the names of the physvol gdml. + In the current version PV1 and PV2 and App::Parts (or App::Link) + that eventually give rise to a physvol export. This helper metjod + returns the App::part object, in case PVname is a name (a string) + ''' + print(f"getPVobject {type(name_or_obj)}") + if hasattr(name_or_obj, "TypeId"): + # name_or_obj is an object + obj = name_or_obj # not necessary, but to emphasize type or argument + print(f"{obj.Label} {obj.TypeId}") + if obj.TypeId == "App::Part": + return obj + elif obj.TypeId == "App::Link": + return obj + else: + print("Not handled") + else: + name = name_or_obj # not necessary, but to emphasize type or argument + print("Old type : string") + obj = doc.getObject(name) + print(f"Found Object {obj.Label}") + return obj + + @staticmethod + def getPVname(parentObj, obj, idx, dictKey) -> str: + ''' + Unfortunately, geant produces its own internal names for assemblies. The generated name + is a very complicated thing that depends on the volume being placed and the number of times + it is being placed. As part of exporting the the document as gdml, we first build + a dictionary in buildAssemblyDict, that associates with each physvol placement of an assembly + the same name that geant generates. Here we retrieve the physvol name from the dictionary + if the dictKey is a key in the Assembly dictionary, or the name physvol name associated + with the parentObj (an App::Part) + ''' + # Obj is the source used to create candidates + print(f"getPVname {obj.Label}") + if dictKey in AssemblyDict: + print(f"returning name of {dictKey} from Assembly dictionary") + entry = AssemblyDict[dictKey] + return entry.getPVname(obj, idx) + else: + print("No Parent") + + return NameManager.getPhysvolName(parentObj) + + @staticmethod + def exportSurfaceProperty(Name, Surface, ref1, ref2): + borderSurface = ET.SubElement( + structure, "bordersurface", {"name": Name, "surfaceproperty": Surface} + ) + ET.SubElement(borderSurface, "physvolref", {"ref": ref1}) + ET.SubElement(borderSurface, "physvolref", {"ref": ref2}) - 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)) + @staticmethod + def checkFaces(pair1, pair2): + def preCheck(shape1, shape2): + # + # Precheck common faces, by checking + # if bounding boxes separation is comparable + # to sum of half-lengths + # + b1 = shape1.BoundBox + b2 = shape2.BoundBox + vcc = b2.Center - b1.Center + if ( + abs(vcc.x) > (b1.XLength + b2.XLength) * 1.01 / 2 + or abs(vcc.y) > (b1.YLength + b2.YLength) * 1.01 / 2 + or abs(vcc.z) > (b1.ZLength + b2.ZLength) * 1.01 / 2 + ): + return False + else: + return True + + tolerence = 1e-6 + obj1 = pair1[0] + matrix1 = pair1[1].Matrix + obj2 = pair2[0] + matrix2 = pair2[1].Matrix + + if hasattr(obj1, "Shape") and hasattr(obj2, "Shape"): + obj1t = obj1.Shape.transformGeometry(matrix1) + obj2t = obj2.Shape.transformGeometry(matrix2) + if not preCheck(obj1t, obj2t): + print("Fails precheck") + return False + + faces1 = obj1t.Faces + faces2 = obj2t.Faces + # faces1 = obj1.Shape.Faces + # faces2 = obj2.Shape.Faces + for f1 in faces1: + comShape = f1.common(faces2, tolerence) + if len(comShape.Faces) > 0: + print("Common") + return True + else: + print("Not common") + return False - if hasattr(obj, 'type'): - atom.set('unit', str(obj.type)) - if hasattr(obj, 'atom_value'): - atom.set('value', str(obj.atom_value)) + @staticmethod + def processSurface(name, cnt, surface, + Obj1, obj1, idx1, dictKey1, + Obj2, obj2, idx2, dictKey2): + print(f"processSurface {name} {surface}") + print(f" {Obj1.Label} {obj1.Label} {Obj2.Label} {obj2.Label}") + ref1 = SurfaceManager.getPVname(Obj1, obj1, idx1, dictKey1) + ref2 = SurfaceManager.getPVname(Obj2, obj2, idx2, dictKey2) + SurfaceManager.exportSurfaceProperty(name + str(cnt), surface, ref1, ref2) + return cnt + 1 + + @staticmethod + def processCandidates(name, surface, check, Obj1, dict1, Obj2, dict2): + cnt = 1 + for assem1, set1 in dict1.items(): + print(f"process Candidates {assem1} {check} {len(set1)}") + for assem2, set2 in dict2.items(): + print(f"process Candidates {assem2} {check} {len(set2)}") + for idx1, items1 in enumerate(set1): + obj1 = items1[0] + for idx2, items2 in enumerate(set2): + obj2 = items2[0] + if items1 != items2: + if check: + pairStr = f"{obj1.Label} : {obj2.Label} " + if SurfaceManager.checkFaces(items1, items2): + cnt = SurfaceManager.processSurface(name, cnt, surface, + Obj1, obj1, idx1, assem1, + Obj2, obj2, idx2, assem2) + print(f"<<< Common face : {pairStr} >>>") + cnt += 1 + else: + print(f"<<< No common face : {pairStr} >>>") + else: + cnt = SurfaceManager.processSurface( + name, cnt, surface, + Obj1, obj1, idx1, assem1, + Obj2, obj2, idx2, assem2 + ) + + @staticmethod + def _getSubVols(vol, placement, volLabel): + global childObjects + + """return a flattened list of terminal solids that fall + under this vol. By flattened we mean something like: + vol + subVol1 + subVol2 + solid1 + solid2 + ... + subVol3 + solid3 + solid4 + .... + + The returned list is a list of triples: + ((solid1, placement1, subvol2.Label), (solid2, placement2, subvol2.Label), (solid3, placement3, subVol3.Label), ... + """ + print(f"getSubVols {vol.Label} {volLabel} {placement} ") + volsList = [] + print(f"_getSubVols: isContainer({vol.Label}) = {isContainer(vol)}") + if len(childObjects[vol]) == 0: + return [(vol, placement, volLabel)] + + # we assume that the user meant to select ONLY the top volume of a container as the solid with + # the optical surface property. + if isContainer(vol): + obj = childObjects[vol][0] + return [(obj, placement * obj.Placement, volLabel)] + + # vol must be an assembly, recurse + for obj in childObjects[vol]: + typeId = obj.TypeId + tObj = obj + # print(obj.Label) + if hasattr(obj, "LinkedObject"): + typeId = obj.LinkedObject.TypeId + if len(childObjects[obj]) != 0: + tObj = childObjects[obj][0] + + if typeId == "App::Part": + volsList += SurfaceManager._getSubVols(tObj, placement * obj.Placement, obj.Label) + else: + if typeId == "Part::FeaturePython": + volsList.append((obj, placement, volLabel)) + + return volsList + + @staticmethod + def getSubVols(vol, placement): + """ + given a structure of the form + vol + subVol1 + subVol2 + solid1 + solid2 + ... + subVol3 + solid3 + solid4 + .... + + return a dictionary: + {subVol2.Label: ((solid1, placement1), (solid2, placement2)), + subVol3.Label: ((solid1, placement1), (solid2, placement2))} + """ + + flattenedList = SurfaceManager._getSubVols(vol, placement, vol.Label) + solidsDict = {} + for item in flattenedList: + vol = item[0] + parentLabel = item[2] + if parentLabel in solidsDict: + solidsDict[parentLabel].append((item[0], item[1])) + else: + solidsDict[parentLabel] = [(item[0], item[1])] + + return solidsDict + + @staticmethod + def processBorderSurfaces(): + print("==============================================") + print(f"Export Border Surfaces - Assemblies {len(AssemblyDict)}") + print("==============================================") + # print(AssemblyDict) + doc = FreeCAD.ActiveDocument + + for obj in doc.Objects: + if obj.TypeId == "App::FeaturePython": + print(f"TypeId {obj.TypeId} Name {obj.Label}") + # print(dir(obj)) + # print(obj.Proxy) + if isinstance(obj.Proxy, GDMLbordersurface): + print("Border Surface") + obj1 = SurfaceManager.getPVobject(doc, obj.PV1) + candSet1 = SurfaceManager.getSubVols(obj1, obj1.Placement) + print(f"Candidates 1 : {obj1.Label} {len(candSet1)}") + printSet("Candidate1", candSet1) + obj2 = SurfaceManager.getPVobject(doc, obj.PV2) + candSet2 = SurfaceManager.getSubVols(obj2, obj2.Placement) + print(f"Candidates 2 : {obj2.Label} {len(candSet2)}") + printSet("Candidate2", candSet2) + # default for old borderSurface Objects + check = False + if hasattr(obj, "CheckCommonFaces"): + check = obj.CheckCommonFaces + SurfaceManager.processCandidates( + obj.Label, + obj.Surface, + check, + obj1, + candSet1, + obj2, + candSet2, + ) + + +def printListObj(name, listArg): + print(f"<=== Object {name} list ===>") + for obj in listArg: + print(obj.Label) + print("<===============================") + + +def printSet(name, dictArg): + print(f"<=== Object Set {name} len {len(dictArg)} ===>") + for k, v in dictArg.items(): + print(k) + for obj in v: + print(f"\t {obj[0].Label}") + print("<===============================") + + + +def processSpreadsheetMatrix(sheet): + # Stupid way of finding how many rows. columns: + # increase col, row until we get an exception for that cell + # You would think the API would provide a simple function + def ncols(): + n = 0 + try: + # TODO: deal with case n > 26 + while n < 26 * 26: + sheet.get(chr(ord("A") + n) + "1") + n += 1 + except: + pass + return n + + def nrows(): + n = 0 + try: + while n < 256 * 256: + sheet.get("A" + str(n + 1)) + n += 1 + except: + pass + return n - if hasattr(obj, 'value'): - atom.set('value', str(obj.value)) + global define + print("add matrix to define") + + coldim = ncols() + rows = nrows() + + s = "" + for row in range(0, rows): + for col in range(0, coldim): + cell = chr(ord("A") + col) + str(row + 1) + s += str(sheet.get(cell)) + " " + + ET.SubElement( + define, + "matrix", + {"name": sheet.Label, "coldim": str(coldim), "values": s[:-1]}, + ) + + +def processOpticals(): + print("Process Opticals") + Grp = FreeCAD.ActiveDocument.getObject("Opticals") + if hasattr(Grp, "Group"): + for obj in Grp.Group: + print(f"Name : {obj.Label}") + while switch(obj.Label): + if case("Matrix"): + print("Matrix") + for m in obj.Group: + if m.TypeId == "Spreadsheet::Sheet": + processSpreadsheetMatrix(m) + else: + processMatrix(m) + break + + if case("Surfaces"): + print("Surfaces") + print(obj.Group) + for s in obj.Group: + SurfaceManager.processOpticalSurface(s) + break + + if case("SkinSurfaces"): + print("SkinSurfaces") + for s in obj.Group: + SurfaceManager.processSkinSurfaces(s) + break def processMaterials(): print("\nProcess Materials") global materials - for GName in ['Constants', 'Variables', - 'Isotopes', 'Elements', 'Materials']: + for GName in [ + "Define", + "Isotopes", + "Elements", + "Materials", + ]: Grp = FreeCAD.ActiveDocument.getObject(GName) if Grp is not None: - # print(Grp.TypeId+" : "+Grp.Name) + # print(Grp.TypeId+" : "+Grp.Label) print(Grp.Label) if processGroup(Grp) is False: break @@ -1377,61 +1456,153 @@ def processFractionsComposites(obj, item): 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)}) + ET.SubElement( + item, + "composite", + {"n": str(obj.n), "ref": nameFromLabel(obj.Label)}, + ) def createMaterials(group): global materials 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'): + if obj.Label != "Geant4": + if not hasattr(obj, 'Group'): + continue + item = ET.SubElement( + materials, "material", {"name": nameFromLabel(obj.Label)} + ) + + # property must be first + for prop in obj.PropertiesList: + if obj.getGroupOfProperty(prop) == "Properties": + ET.SubElement( + item, + "property", + {"name": prop, "ref": getattr(obj, prop)}, + ) + + 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)}, + ) + + 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)) + 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, "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) + for o in obj.Group: + processFractionsComposites(o, item) -def createElements(group): +def postCreateGeantMaterials(): + ''' + Geant4 materials are not added or created in createMaterials. + Here we add those materials to the materials group + ''' global materials - for obj in group: - # print(f'Element : {obj.Label}') - item = ET.SubElement(materials, 'element', { - 'name': nameFromLabel(obj.Label)}) - # Common code IsoTope and Elements1 + global usedGeant4Materials + + usedElements = set() + + # collect the used elements in all the used materials + for mat in usedGeant4Materials: + obj = FreeCAD.ActiveDocument.getObjectsByLabel(mat)[0] + for grpItem in obj.Group: + words = grpItem.Label.split(':') + elemName = words[0][:-1] + usedElements.add(elemName) + # create for them + for elemName in usedElements: + obj = FreeCAD.ActiveDocument.getObject(elemName) + createElement(obj) + + for mat in usedGeant4Materials: + obj = FreeCAD.ActiveDocument.getObjectsByLabel(mat)[0] + item = ET.SubElement( + materials, "material", {"name": str(mat)} + ) + # property must be first + for prop in obj.PropertiesList: + if obj.getGroupOfProperty(prop) == "Properties": + ET.SubElement( + item, + "property", + {"name": prop, "ref": getattr(obj, prop)}, + ) + + 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: + createElement(obj) + + +def createElement(obj): + global materials + 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) + + def createConstants(group): global define for obj in group: @@ -1439,8 +1610,78 @@ def createConstants(group): # print("GDML constant") # print(dir(obj)) - ET.SubElement(define, 'constant', {'name': obj.Label, - 'value': obj.value}) + ET.SubElement( + define, "constant", {"name": obj.Label, "value": obj.value} + ) + +def createDefine(group): + global define + + from .GDMLShared import definesColumn + # should come up with a test that relies on version number of the WB to test for this + sheet = FreeCAD.ActiveDocument.getObject("defines") + if sheet is None: # Older docs not having a define spreadsheet + createConstants(group) + createVariables(group) + return + + numRows = GDMLShared.lastRow(sheet) + for row in range(1, numRows+1): + entryType = sheet.get(definesColumn['type']+str(row)) + entryName = sheet.get(definesColumn['name']+str(row)) + + if entryType == 'constant' or entryType == 'variable': + value = sheet.getContents(definesColumn['value'] + str(row)) + if len(value) > 0: + if value[0] == "=": + value = GDMLShared.SheetHandler.FC_expression_to_gdml(value[1:]) + + ET.SubElement( + define, str(entryType), {"name": str(entryName), "value": str(value)} + ) + + elif entryType == "quantity": + quantityType = sheet.get(definesColumn['quantity_type'] + str(row)) + quantityUnit = sheet.get(definesColumn['quantity_unit'] + str(row)) + quantityValue = sheet.getContents(definesColumn['quantity_value'] + str(row)) + if quantityValue[0] == '=': + quantityValue = GDMLShared.SheetHandler.FC_expression_to_gdml(quantityValue) + + ET.SubElement( + define, str(entryType), {"name": str(entryName), "type": str(quantityType), + "value": str(quantityValue),"unit": quantityUnit} + ) + + + elif entryType == "position": + attrib = {} + attrib["name"] = str(entryName) + for prop in ["x", "y", "z", "unit"]: + cell = definesColumn["pos_" + prop] + str(row) + value = sheet.getContents(cell) + if len(value) > 0: + if value[0] == "=": + value = GDMLShared.SheetHandler.FC_expression_to_gdml(value[1:]) + else: + value = sheet.get(cell) + attrib[prop] = str(value) + + ET.SubElement( define, str(entryType), attrib) + + elif entryType == "rotation": + attrib = {} + attrib["name"] = str(entryName) + for prop in ["x", "y", "z", "unit"]: + cell = definesColumn["rot_" + prop] + str(row) + value = sheet.getContents(cell) + if len(value) > 0: + if value[0] == "=": + value = GDMLShared.SheetHandler.FC_expression_to_gdml(value[1:]) + else: + value = sheet.get(cell) + attrib[prop] = str(value) + + ET.SubElement( define, str(entryType), attrib) def createVariables(group): @@ -1450,8 +1691,9 @@ def createVariables(group): # print("GDML variable") # print(dir(obj)) - ET.SubElement(define, 'variable', {'name': obj.Label, - 'value': obj.value}) + ET.SubElement( + define, "variable", {"name": obj.Label, "value": obj.value} + ) def createQuantities(group): @@ -1461,10 +1703,16 @@ def createQuantities(group): # print("GDML quantity") # print(dir(obj)) - 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): @@ -1477,28 +1725,23 @@ def createIsotopes(group): # 'name' : obj.Label}) # ET.SubElement(item,'atom',{'unit': obj.unit, \ # 'value': str(obj.value)}) - item = ET.SubElement(materials, 'isotope', {'name': obj.Label}) + item = ET.SubElement(materials, "isotope", {"name": obj.Label}) processIsotope(obj, item) def processGroup(obj): - print('Process Group '+obj.Label) + print("Process Group " + obj.Label) # print(obj.TypeId) # print(obj.Group) # if hasattr(obj,'Group') : # return - if hasattr(obj, 'Group'): + if hasattr(obj, "Group"): # print(" Object List : "+obj.Label) # print(obj) - while switch(obj.Name): - if case("Constants"): + while switch(obj.Label): + if case("Define"): # print("Constants") - createConstants(obj.Group) - break - - if case("Variables"): - # print("Variables") - createVariables(obj.Group) + createDefine(obj.Group) break if case("Quantities"): @@ -1527,219 +1770,6 @@ def processGroup(obj): break -def processGDMLSolid(obj): - # Deal with GDML Solids first - # Deal with FC Objects that convert - # print(dir(obj)) - # print(dir(obj.Proxy)) - print(obj.Proxy.Type) - 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 @@ -1747,584 +1777,857 @@ def consume(iterator): next(islice(iterator, 2, 2), None) -def getXmlVolume(volObj): - global structure - if volObj is None: - return None - xmlvol = structure.find("volume[@name='%s']" % volObj.Label) - if xmlvol is None: - print(volObj.Label+' Not Found') - return xmlvol - - -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 getDefaultMaterial(): + # should get this from GDML settings under "settings" + # for now this does not exist, so simply put steel + return "G4_STAINLESS-STEEL" 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 + # Temporary fix until the SetMaterials works + # Somehow (now Feb 20) if a new gdml object is added + # the default material is Geant4, and SetMaterials fails to change it + from .GDMLMaterials import getMaterialsList + global usedGeant4Materials + + GDMLShared.trace("get Material : " + obj.Label) + if hasattr(obj, "material"): + material = obj.material + elif hasattr(obj, "Tool"): + GDMLShared.trace("Has tool - check Base") + try: + material = getMaterial(obj.Base) + except Exception as e: + print(e) + print("Using default material") + material = getDefaultMaterial() + + elif hasattr(obj, "Base"): + GDMLShared.trace("Has Base - check Base") + try: + material = getMaterial(obj.Base) + except Exception as e: + print(e) + print("Using default material") + material = getDefaultMaterial() + + elif hasattr(obj, "Objects"): + GDMLShared.trace("Has Objects - check Objects") + try: + material = getMaterial(obj.Objects[0]) + except Exception as e: + print(e) + print("Using default material") + material = getDefaultMaterial() + else: - return None + material = getDefaultMaterial() + if material[0:3] == "G4_": + print(f"Found Geant material {material}") + usedGeant4Materials.add(material) -''' + return material + + +""" def printObjectInfo(xmlVol, volName, xmlParent, parentName): print("Process Object : "+obj.Label+' Type '+obj.TypeId) if xmlVol is not None : - xmlstr = ET.tostring(xmlVol) + xmlstr = ET.tostring(xmlVol) else : xmlstr = 'None' print('Volume : '+volName+' : '+str(xmlstr)) if xmlParent is not None : - xmlstr = ET.tostring(xmlParent) + xmlstr = ET.tostring(xmlParent) else : xmlstr = 'None' print('Parent : '+str(parentName)+' : '+str(xmlstr)) -''' - - -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 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 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 +def buildAssemblyTree(worldVol): + from .AssemblyHelper import AssemblyHelper + + global AssemblyDict + + def processContainer(vol): + objects = assemblyHeads(vol) + imprNum = 1 + for obj in objects[1:]: + print( + f" buildAssemblyTree::processContainer {obj.Label} {obj.TypeId} " + ) + processVolAssem(obj, imprNum) + + def processVolAssem(vol, imprNum): + # vol - Volume Object + # xmlParent - xml of this volume's Parent + if vol.Label[:12] != "NOT_Expanded": + if isContainer(vol): + processContainer(vol) + elif isAssembly(vol): + processAssembly(vol, imprNum) + elif vol.TypeId == "App::Link": + processLink(vol, imprNum) + else: + print( + f"{vol.Label} is neither a link, nor an assembly nor a container" + ) + + def processLink(vol, imprNum): + linkedObj = vol.getLinkedObject() + if linkedObj.Label in AssemblyDict: + entry = AssemblyDict[linkedObj.Label] + instCnt = entry.www + imprNum = entry.xxx + 1 + entry = AssemblyHelper(vol, instCnt, imprNum) + AssemblyDict[vol.Label] = entry 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 + processVolAssem(linkedObj, imprNum) + + def processAssembly(vol, imprNum): + print(f"{vol.Label} typeId= {vol.TypeId}") + if hasattr(vol, "LinkedObject"): + print(f"{vol.Label} has a LinkedObject") + linkedObj = vol.getLinkedObject() + if linkedObj.Label in AssemblyDict: + entry = AssemblyDict[linkedObj.Label] + instCnt = entry.www + imprNum = entry.xxx + 1 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 + instCnt = AssemblyHelper.maxWww + 1 + entry = AssemblyHelper(vol, instCnt, imprNum) + AssemblyDict[vol.Label] = entry + assemObjs = assemblyHeads(vol) + imprNum += 1 + for obj in assemObjs: + print( + f" buildAssemblyTree::processAssembly {obj.Label} {obj.TypeId} " + ) + if obj.TypeId == "App::Part": + processVolAssem(obj, imprNum) + elif obj.TypeId == "App::Link": + processLink(obj, imprNum) + else: + if SolidExporter.isSolid(obj): + entry.addSolid(obj) + processContainer(worldVol) -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 + for k, v in AssemblyDict.items(): + print(f"Assembly: {k} av_{v.www}_impr_{v.xxx}") -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 - GDMLShared.trace('Process Object : ' + obj.Label) - while switch(obj.TypeId): +def createXMLvolume(name): + GDMLShared.trace("create xml volume : " + name) + elem = ET.Element("volume", {"name": name}) + return elem - 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)) - processVolAssem(obj, xmlVol, volName, True) - 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 +def createXMLassembly(name): + GDMLShared.trace("create xml assembly : " + name) + elem = ET.Element("assembly", {"name": name}) + return elem - 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) +def invPlacement(placement): + inv = placement.inverse() + return inv + # tra = inv.Base + # rot = inv.Rotation + # T = FreeCAD.Placement(tra, FreeCAD.Rotation()) + # R = FreeCAD.Placement(FreeCAD.Vector(), rot) + # return R*T + + +def isArrayType(obj): + obj1 = obj + if obj.TypeId == "App::Link": + obj1 = obj.LinkedObject + if obj1.TypeId == "Part::FeaturePython": + if not hasattr(obj1.Proxy, 'Type'): + return False # discovered that InvoluteGears don't have a 'Type' + typeId = obj1.Proxy.Type + if typeId == "Array": + if obj1.ArrayType == "ortho": + return True + elif obj1.ArrayType == "polar": + return True + elif typeId == "PathArray": + return True + elif typeId == "PointArray": + return True + elif typeId == "Clone": + clonedObj = obj1.Objects[0] + return isArrayType(clonedObj) - 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 + else: + return False + else: + return False - 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 - - # Following not needed as handled bu Outlist on Xtru - - # if isinstance(obj.Proxy, GDML2dVertex) : - # return(processGDML2dVertObject(obj, addVolsFlag)) - # break - - # 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 +def isArrayOfPart(obj): + ''' test if the obj (an array) is an array of App::Part ''' + obj1 = obj + if obj.TypeId == "App::Link": + obj1 = obj.LinkedObject + typeId = obj1.Proxy.Type + if typeId == "Clone": + clonedObj = obj1.Objects[0] + return isArrayOfPart(clonedObj) + else: + return obj1.Base.TypeId == "App::Part" + +def processArrayPart(array, xmlVol): + # vol: array object + global physVolStack + from . import arrayUtils + # vol: Array item + # new approach 2024-08-07: + # Create an assembly for the array. The assembly has the name of the array Part + # place the repeated array elements in this new assembly and place + # the array assembly in the xmlVol with the position and rotation of the array + # xmlVol: xml item into which the array elements are placed/exported + + arrayRef = NameManager.getName(array) + arrayXML = createXMLassembly(arrayRef) + print(f"Process Array Part {array.Label} Base {array.Base} {xmlVol}") + processVolAssem(array.Base, None, array.Base.Label) + basePhysVol = physVolStack.pop() + baseRotation = basePhysVol.placement.Rotation + baseTranslation = basePhysVol.placement.Base + + arrayPos = array.Placement.Base + arrayRot = array.Placement.Rotation + + parent = array.InList[0] + print(f"parent {parent}") + arrayType = arrayUtils.typeOfArray(array) + while switch(arrayType): + if case("ortho"): + pos = basePhysVol.placement.Base + print(f"basePhysVol: {basePhysVol.ref} position: {arrayPos}") + placements = arrayUtils.placementList(array, offsetVector=pos, rot=baseRotation) + print(f'Number of placements = {len(placements)}') + for i, placement in enumerate(placements): + ix, iy, iz = arrayUtils.orthoIndexes(i, array) + baseName = array.Base.Label + '-' + str(ix) + '-' + str(iy) + \ + '-' + str(iz) + print(f"Base Name {baseName}") + # print(f"Add Placement to {parent.Label} volref {vol.Base.Label}") + addPhysVolPlacement(array.Base, arrayXML, + placement, pvName=str(baseName)) + break - if case("Part::Cylinder"): - print(" Cylinder") - # return(processCylinderObject(obj, addVolsFlag)) - processCylinderObject(obj, True) - # testAddPhysVol(obj, xmlParent, parentName) - return idx + 1 + if case("polar"): + positionVector = baseRotation.inverted()*baseTranslation # + vol.Placement.Base + placements = arrayUtils.placementList(array, offsetVector=positionVector, rot=baseRotation) + print(f'Number of placements = {len(placements)}') + for i, placement in enumerate(placements): + baseName = array.Base.Label + '-' + str(i) + addPhysVolPlacement(array.Base, arrayXML, + placement, pvName=str(baseName)) + break - if case("Part::Cone"): - print(" Cone") - # return(processConeObject(obj, addVolsFlag)) - processConeObject(obj, True) - # testAddPhysVol(obj, xmlParent, parentName) - return idx + 1 + if case("PathArray") or case("PointArray"): + pos = basePhysVol.placement.Base + print(f"basePhysVol: {basePhysVol.ref} position: {arrayPos}") + placements = arrayUtils.placementList(array, offsetVector=pos, rot=baseRotation) + for i, placement in enumerate(placements): + baseName = array.Base.Label + '-' + str(i) + addPhysVolPlacement(array.Base, arrayXML, + placement, pvName=str(baseName)) + break - 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 + placement = array.Placement + # if psPlacement is not None: + # placement = invPlacement(psPlacement) * placement + addPhysVolPlacement(array, xmlVol, placement) + physVolStack.append(PhysVolPlacement(array, placement)) - if hasattr(obj, 'Shape'): - if obj.Shape.isValid(): - # return(processObjectShape(obj)) - processObjectShape(obj) - # testAddPhysVol(obj, xmlParent, parentName) - return idx + 1 + structure.append(arrayXML) + # structure.append(xmlVol) -def insertXMLvolume(name): - # Insert at beginning for sub volumes - GDMLShared.trace('insert xml volume : ' + name) - elem = ET.Element('volume', {'name': name}) +def processAssembly(vol, xmlVol, xmlParent, parentName, psPlacement): global structure - structure.insert(0, elem) - return elem - - -def insertXMLvolObj(obj): - # name = cleanVolName(obj, obj.Label) - name = obj.Label - return insertXMLvolume(name) + global physVolStack + # vol - Volume Object + # xmlVol - xml of this assembly + # xmlParent - xml of this volume's Parent + # psPlacement: parent solid placement, may be None + # App::Part will have Booleans & Multifuse objects also in the list + # So for s in list is not so good + # xmlVol could be created dummy volume -def insertXMLassembly(name): - # Insert at beginning for sub volumes - GDMLShared.trace('insert xml assembly : ' + name) - elem = ET.Element('assembly', {'name': name}) - global structure - structure.insert(0, elem) - return elem + # GDMLShared.setTrace(True) + volName = NameManager.getVolumeName(vol) + # GDMLShared.trace("Process Assembly : " + volName) + # if GDMLShared.getTrace() == True : + # printVolumeInfo(vol, xmlVol, xmlParent, parentName) + assemObjs = assemblyHeads(vol) + # print(f"ProcessAssembly: vol.TypeId {vol.TypeId}") + print(f"ProcessAssembly: {vol.Name} Label {vol.Label}") + print(f"Assem Objs {assemObjs}") + # + # Note that the assembly object is under an App::Part, not + # a solid, so there is no need to adjust for a "parent solid" + # placement. + # + for obj in assemObjs: + if obj.TypeId == "App::Part": + processVolAssem(obj, xmlVol, volName, None) + elif obj.TypeId == "App::Link": + print("Process Link") + # PhysVol needs to be unique + if hasattr(obj, "LinkedObject"): + volRef = NameManager.getVolumeName(obj.LinkedObject) + elif hasattr(obj, "VolRef"): + volRef = obj.VolRef + print(f"VolRef {volRef}") + addPhysVolPlacement(obj, xmlVol, obj.Placement, refName=volRef) + physVolStack.append(PhysVolPlacement(volName, obj.Placement)) + elif isArrayType(obj): + processArrayPart(obj, xmlVol) + else: + _ = processVolume(obj, xmlVol, None) + # the assembly could be placed in a container; adjust + # for its placement, if any, given in the argument + placement = vol.Placement + if psPlacement is not None: + placement = invPlacement(psPlacement) * placement + addPhysVolPlacement(vol, xmlParent, placement) + physVolStack.append(PhysVolPlacement(volName, placement)) -def insertXMLassemObj(obj): - # name = cleanVolName(obj, obj.Label) - name = obj.Label - return insertXMLassembly(name) + structure.append(xmlVol) -def createXMLvol(name): - return ET.SubElement(structure, 'volume', {'name': name}) +def processVolume(vol, xmlParent, psPlacement): + global structure + global physVolStack -def processAssembly(vol, xmlVol, xmlParent, parentName, addVolsFlag): # vol - Volume Object - # xmlVol - xml of this volume - # xmlParent - xml of this volumes Paretnt + # xmlParent - xml of this volume's Parent # App::Part will have Booleans & Multifuse objects also in the list # 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) - volName = 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'): - 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) - - 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) + if vol.TypeId == "App::Link": + print("Volume is Link") + placement = vol.Placement + if psPlacement is not None: + placement = invPlacement(psPlacement) * placement + + addPhysVolPlacement( + vol, + xmlParent, + placement) + return + volName = NameManager.getVolumeName(vol) -def printVolumeInfo(vol, xmlVol, xmlParent, parentName): - if xmlVol is not None: - xmlstr = ET.tostring(xmlVol) + if vol.TypeId == "App::Part": + topObject = topObj(vol) else: - xmlstr = 'None' - print(xmlstr) - GDMLShared.trace(' '+vol.Label + ' - ' + str(xmlstr)) - if xmlParent is not None: - xmlstr = ET.tostring(xmlParent) + topObject = vol + if topObject is None: + return + + if isMultiPlacement(topObject): + xmlVol, volName = processMultiPlacement(topObject, xmlParent) + partPlacement = topObject.Placement + if psPlacement is not None: + partPlacement = invPlacement(psPlacement) * partPlacement else: - xmlstr = 'None' - GDMLShared.trace(' Parent : ' + str(parentName) + ' : ' + str(xmlstr)) + solidExporter = SolidExporter.getExporter(topObject) + if solidExporter is None: + return + solidExporter.export() + print(f"Process Volume - solids count {len(list(solids))}") + # 1- adds a maxCount: - maxCount = boolCount - rootBool = obj - - if rootBool is not None: - processObject(cnt, idx, rootBool, - xmlVol, volName, xmlParent, parentName) + +def processContainer(vol, xmlParent, psPlacement): + # vol: a container: a volume that has a solid that contains other volume + # psPlacement: placement of parent solid. Could be None. + # + print(f"Process Container {vol.Label}") + global structure + global physVolStack + + volName = NameManager.getVolumeName(vol) + objects = assemblyHeads(vol) + newXmlVol = createXMLvolume(volName) + solidExporter = SolidExporter.getExporter(objects[0]) + solidExporter.export() + addVolRef(newXmlVol, volName, objects[0], solidExporter.name()) + + solidPlacement = solidExporter.placement() + + # for reasons I don't understand, perhaps, just floating point precision issue + # I am getting partPlacement != vol.Placement even when solidPlacement is identity placement + # So I have to test by hand. MMH + if solidPlacement == FreeCAD.Placement(): + partPlacement = vol.Placement + else: + partPlacement = vol.Placement * solidPlacement + + # + # Note that instead of testing for None, I could have + # just used an identity placement which has an identity inverse + # + if psPlacement is not None: + partPlacement = invPlacement(psPlacement) * partPlacement + + addPhysVolPlacement(vol, xmlParent, partPlacement) + # N.B. the parent solid placement (psPlacement) only directly + # affects vol, the container volume. All the daughters are placed + # relative to that, so do not need the extra shift of psPlacement + # directly. + # However, if the container solid has a non-dentity placement + # then the daughters need adjustment by that + # The solid containing the daughter volumes has been exported above + # so start at the next object. + if solidPlacement == FreeCAD.Placement(): + # No adjustment of daughters needed + myPlacement = None + else: + # adjust by our solids non-zero placement + myPlacement = solidPlacement + + for obj in objects[1:]: + if obj.TypeId == "App::Link": + print("Process Link") + if solidPlacement == FreeCAD.Placement(): + addPhysVolPlacement(obj, newXmlVol, obj.Placement) + else: + addPhysVolPlacement( + obj, newXmlVol, + invPlacement(solidPlacement) * obj.Placement) + elif obj.TypeId == "App::Part": + processVolAssem(obj, newXmlVol, volName, myPlacement) 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) + _ = processVolume(obj, newXmlVol, myPlacement) + + # Note that the placement of the container itself should be last + # so it can be popped first if we are being called from an array + physVolStack.append(PhysVolPlacement(volName, partPlacement)) + structure.append(newXmlVol) + +def processVolAssem(vol, xmlParent, parentName, psPlacement=None): -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) + # xmlParent - xml of this volume's Parent + # psPlacement = parent solid placement. + # If the vol is placed inside a solid + # and that solid has a non-zero placement + # we need to shift vol by inverse of the psPlacement + if vol.Label[:12] != "NOT_Expanded": + print(f"process VolAsm Name {vol.Name} Label {vol.Label}") + volName = NameManager.getName(vol) + if isContainer(vol): + processContainer(vol, xmlParent, psPlacement) + elif isAssembly(vol): + newXmlVol = createXMLassembly(volName) + processAssembly(vol, newXmlVol, xmlParent, parentName, + psPlacement) else: - newXmlVol = insertXMLassembly(volName) - processAssembly(vol, newXmlVol, xmlParent, parentName, addVolsFlag) + processVolume(vol, xmlParent, psPlacement) + else: + print("skipping " + vol.Label) + + +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)) + + +def processMultiPlacement(obj, xmlParent): + global childObjects + print(f"procesMultiPlacement {obj.Label}") + + def getChildren(obj): + children = [] + for o in childObjects[obj]: + children.append(o) + children += getChildren(o) + + return children + + children = [obj] + getChildren(obj) + # export first solid in solids (booleans etc) + for i, s in enumerate(children): + if SolidExporter.isSolid(s): + exporter = SolidExporter.getExporter(s) + exporter.export() + solidName = exporter.name() + volName = "LV_" + solidName + volXML = createXMLvolume(volName) + structure.append(volXML) + addVolRef(volXML, obj.Label, s, solidName) + addPhysVolPlacement(s, xmlParent, exporter.placement(), refName=volName) + break + placers = children[:i] # placers without the solids + j = len(placers) + for pl in reversed(placers): + j -= 1 + placer = MultiPlacer.getPlacer(pl) + placer.place(volName) + volName = placer.name() + volXML = placer.xml() + structure.append(volXML) + if j != 0: + addPhysVolPlacement(pl, xmlParent, pl.Placement, refName=volName) - # addPhysVolPlacement(vol,xmlParent,volName) - # elif obj.TypeId == 'App::Link' : - # addPhysVolPlacement(obj,xmlVol,objName) + return volXML, volName # name of last placer (an assembly) 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}) - 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'}) + worldVol = ET.SubElement(structure, "volume", {"name": volName}) + ET.SubElement(worldVol, "materialref", {"ref": "G4_AIR"}) + ET.SubElement(worldVol, "solidref", {"ref": boxName}) + ET.SubElement(gxml, "volume", {"name": volName, "material": "G4_AIR"}) return worldVol +def buildDocTree(): + from PySide import QtWidgets + + global childObjects + childObjects = {} # dictionary of list of child objects for each object + # TypeIds that should not go in to the tree + skippedTypes = ["App::Origin", "Sketcher::SketchObject", "Part::Compound"] + + def addDaughters(item: QtWidgets.QTreeWidgetItem): + print (f"--------addDaughters {item.text(0)}") + objectLabel = item.text(0) + object = App.ActiveDocument.getObjectsByLabel(objectLabel)[0] + if object not in childObjects: + childObjects[object] = [] + for i in range(item.childCount()): + childItem = item.child(i) + treeLabel = childItem.text(0) + try: + childObject = App.ActiveDocument.getObjectsByLabel(treeLabel)[0] + objType = childObject.TypeId + if objType not in skippedTypes: + childObjects[object].append(childObject) + addDaughters(childItem) + except Exception as e: + print(e) + return + + # Get world volume from document tree widget + worldObj = FreeCADGui.Selection.getSelection()[0] + # tree = FreeCADGui.getMainWindow().findChildren(QtGui.QTreeWidget)[0] + # it = QtGui.QTreeWidgetItemIterator(tree) + + mw1 = FreeCADGui.getMainWindow() + print (f"---------Number of trees {len(mw1.findChildren(QtGui.QTreeWidget))}") + treesSel = mw1.findChildren(QtGui.QTreeWidget) + print (f"---------Number of trees {len(treesSel)}") + # breakpoint() + + doc = FreeCAD.ActiveDocument + found = False + + for tree in treesSel: + print(f"--------Tree {tree.objectName()}") + items = tree.selectedItems() + for item in items: + treeLabel = item.text(0) + print(f"--------Item {treeLabel}") + print(f"--------Doc.Label {doc.Label}") + # if not found: + # if treeLabel != doc.Label: + # continue + # found = True + try: + objs = doc.getObjectsByLabel(treeLabel) + print(f"--------Objects {objs}") + if len(objs) == 0: + continue + + obj = objs[0] + if obj == worldObj: + print(f"--------World Object {obj.Label}") + # we presume first app part is world volume + addDaughters(item) + break + except Exception as e: + print(e) + FreeCADobject = None + + +def isContainer(obj): + # return True if The App::Part is of the form: + # App::Part + # -solid (Part:xxx) + # -App::Part + # -App::Part + # .... + # So a container satisfies the current isAssembly requirements + # plus the siblings must have the above form + # obj that satisfy isContainer get exported as + # + # + # + # + # + # This is in contract to assembly, which is exported as + # + # + # .... + # + # Must be assembly first + global childObjects + + if not isAssembly(obj): + return False + heads = assemblyHeads(obj) + if heads is None: + return False + if len(heads) < 2: + return False + + # first must be solid + if not SolidExporter.isSolid(heads[0]): + return False + + # we cannot have an array as the containing solid of a container + if isArrayType(heads[0]): + return False + + # rest must not be solids, but only second is tested here + if SolidExporter.isSolid(heads[1]): + return False + + return True + + +# is the object something we export as a Volume, or Assembly or a solid? +def isGeometryExport(obj): + return obj.TypeId == "App::Part" or SolidExporter.isSolid(obj) + + +def isAssembly(obj): + # return True if obj is an assembly. + # To be an assembly the obj must be: + # (1) an App::Part or an App::Link and + # (2) it has either (1) At least one App::Part as a subpart or + # (2) more than one "terminal" object + # A terminal object is one that has associated with it ONE volume + # A volume refers to ONE solid + # A terminal item CAN be a boolean, or an extrusion (and in the future + # a chamfer or a fillet. So a terminal element need NOT have an empty + # child list + # N.B. App::Link is treated as a non-assembly, even though it might be linked + # to an assembly, because all we need to place it is the volref of its link + + global childObjects -def countGDMLObj(objList): - # Return counts GDML objects exportables - # #rint('countGDMLObj') - GDMLShared.trace('countGDMLObj') - gcount = 0 - # print(range(len(objList))) - for idx in range(len(objList)): - # print('idx : '+str(idx)) - obj = objList[idx] - if obj.TypeId == 'Part::FeaturePython': - gcount += 1 - if obj.TypeId == 'Part::Cut' \ - or obj.TypeId == 'Part::Fuse' \ - or obj.TypeId == 'Part::Common': - gcount -= 1 - # print('countGDMLObj - Count : '+str(gcount)) - GDMLShared.trace('countGDMLObj - gdml : ' + str(gcount)) - return gcount - - -def checkGDMLstructure(objList): + print(f"testing isAsembly for: {obj.Label}") + if obj.TypeId != "App::Part": + return False + + for ob in childObjects[obj]: + if ob.TypeId == "App::Part" or ob.TypeId == "App::Link": + print(True) + return True # Yes, even if ONE App::Part is under this, we treat it as an assembly + + if len(childObjects[obj]) > 1: + print("Yes, it is an Assembly") + return True + else: + # need to check for arrays. Arrays of App::Part are treated as an assembly + if len(childObjects[obj]) == 1: + topObject = childObjects[obj][0] + if isArrayType(topObject) and isArrayOfPart(topObject): + return True + else: + return False + else: + return False + + +def assemblyHeads(obj): + # return a list of subassembly heads for this object + # Subassembly heads are themselves either assemblies + # or terminal objects (those that produce a 1: # More than one GDML Object need to insert Dummy + global childObjects + + GDMLShared.trace("check GDML structure") + GDMLShared.trace(obj) + print("check GDML structure") + vCount, lCount, gCount = countGDMLObj(obj) + print(f"GDML Counts : {vCount} {gCount}") + if gCount > 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 + if ( + gCount == 1 and len(obj.OutList) == 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' : - # return False - # return True def locateXMLvol(vol): @@ -2334,36 +2637,54 @@ def locateXMLvol(vol): def exportWorldVol(vol, fileExt): + global childObjects 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) is False: - GDMLShared.trace('Insert Dummy Volume') - xmlVol = createXMLvol('dummy') + 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) is False: + GDMLShared.trace("Insert Dummy Volume") + createXMLvolume("dummy") xmlParent = createWorldVol(vol.Label) parentName = vol.Label - addPhysVol(xmlParent, 'dummy') + addPhysVol(xmlParent, "dummy") else: - GDMLShared.trace('Valid Structure') + 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) + + # print(vol.OutList) + vCount, lCount, gCount = countGDMLObj(vol) + print(f"Root GDML Counts {vCount} {gCount}") + + # Munther Please check/correct + # if gCount > 0: # one GDML defining world volume + # if isAssembly(vol): + # heads = assemblyHeads(vol) + # worlSolid = heads[0] + # xmlVol = processVolume(worlSolid, xmlParent, volName=WorldVOL) + # for obj in heads[1:]: # skip first volume (done above) + # processVolAssem(obj, xmlVol, WorldVOL) + # else: + # xmlVol = processVolume(vol, xmlParent) + # else: # no volume defining world + # xmlVol = createXMLassembly(vol.Label) + # processAssembly(vol, xmlVol, xmlParent, parentName) + + # The world volume does not have a parent + + buildAssemblyTree(vol) + processVolAssem(vol, xmlParent, WorldVOL) + + SurfaceManager.processSkinSurfaces() + SurfaceManager.processBorderSurfaces() def exportElementAsXML(dirPath, fileName, flag, elemName, elem): @@ -2374,12 +2695,12 @@ def exportElementAsXML(dirPath, fileName, flag, elemName, elem): # xmlElem.append(elem) # indent(xmlElem) if flag is True: - filename = fileName+'-' + elemName + '.xml' + filename = fileName + "-" + elemName + ".xml" else: - filename = elemName + '.xml' + filename = elemName + ".xml" # ET.ElementTree(xmlElem).write(os.path.join(dirPath,filename)) ET.ElementTree(elem).write(os.path.join(dirPath, filename)) - docString += '\n' + docString += "\n' gdml.append(ET.Entity(elemName)) @@ -2387,67 +2708,98 @@ def exportGDMLstructure(dirPath, fileName): global gdml, docString, importStr print("Write GDML structure to Directory") gdml = initGDML() - docString = '\n\n' + docString += "]>\n" # 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): from . import GDMLShared + from sys import platform + from .AssemblyHelper import AssemblyHelper + global zOrder + global AssemblyDict + AssemblyDict = {} + AssemblyHelper.maxWww = 0 + + global usedGeant4Materials + usedGeant4Materials = set() + + global physVolStack + physVolStack = [] # GDMLShared.setTrace(True) - GDMLShared.trace('exportGDML') - print("====> Start GDML Export 1.6") - print('File extension : '+fileExt) + GDMLShared.trace("exportGDML") + print("====> Start GDML Export 2.0") + branch = get_active_branch_name() + print(f"branch: {branch}") + print("File extension : " + fileExt) GDMLstructure() zOrder = 1 processMaterials() exportWorldVol(first, fileExt) + params = FreeCAD.ParamGet( + "User parameter:BaseApp/Preferences/Mod/GDML" + ) + exportG4Materials = params.GetBool('exportG4Materials', False) + if exportG4Materials: + postCreateGeantMaterials() + processOpticals() # format & write GDML file # xmlstr = ET.tostring(structure) # print('Structure : '+str(xmlstr)) - if fileExt == '.gdml': - indent(gdml) + if fileExt == ".gdml": + # indent(gdml) + print(len(list(solids))) 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) + # ET.ElementTree(gdml).write(filepath, xml_declaration=True) + # Problem with pretty Print on Windows ? + if platform == "win32": + indent(gdml) + ET.ElementTree(gdml).write(filepath, xml_declaration=True, encoding='UTF-8') + else: + ET.ElementTree(gdml).write( + filepath, pretty_print=True, xml_declaration=True, + encoding='UTF-8' + ) print("GDML file written") - if fileExt == '.GDML': + if fileExt == ".GDML": filePath = os.path.split(filepath) - print('Input File Path : '+filepath) + print("Input File Path : " + filepath) fileName = os.path.splitext(filePath[1])[0] - print('File Name : '+fileName) + print("File Name : " + fileName) dirPath = os.path.join(filePath[0], fileName) - print('Directory Path : '+dirPath) + 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') + print("Invalid Path") # change to Qt Warning - if fileExt == '.xml': - xmlElem = ET.Element('xml') + if fileExt == ".xml": + xmlElem = ET.Element("xml") xmlElem.append(solids) xmlElem.append(structure) indent(xmlElem) @@ -2456,39 +2808,51 @@ def exportGDML(first, filepath, fileExt): def exportGDMLworld(first, filepath, fileExt): - if filepath.lower().endswith('.gdml'): + global childObjects + + buildDocTree() # creates global childObjects + NameManager.init() + SolidExporter.init() + + # for debugging doc tree + for obj in childObjects: + s = "" + for child in childObjects[obj]: + s += child.Label + ", " + print(f"{obj.Label} [{s}]") + + if filepath.lower().endswith(".gdml"): # GDML Export - print('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) + vCount, lcount, gCount = countGDMLObj(first) + if gCount > 1: + from .GDMLQtDialogs import showInvalidWorldVol + + showInvalidWorldVol() + else: + exportGDML(first, filepath, fileExt) def hexInt(f): - return hex(int(f*255))[2:].zfill(2) + 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 + global childObjects # if flag == True ignore Parts that convert - print('scanForStl') - print(first.Name+' : '+first.Label+' : '+first.TypeId) + print("scanForStl") + print(first.Name + " : " + first.Label + " : " + first.TypeId) while switch(first.TypeId): if case("App::Origin"): @@ -2536,151 +2900,3116 @@ def scanForStl(first, gxml, path, flag): break # Deal with Booleans which will have Tool - if hasattr(first, '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) + for obj in childObjects[first]: + 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'): + 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) + print("===> Colour " + str(col) + " " + colHex) mat = lookupColour(col) - print('Material : '+mat) - if hasattr(first, 'Placement'): + 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}) + ET.SubElement( + gxml, + "volume", + { + "name": first.Label, + "color": colHex, + "material": mat, + "position": pos, + }, + ) def exportGXML(first, path, flag): - print('Path : '+path) + print("Path : " + path) # basename = 'target_'+os.path.basename(path) - gxml = ET.Element('gxml') - print('ScanForStl') + gxml = ET.Element("gxml") + print("ScanForStl") scanForStl(first, gxml, path, flag) # 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, "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') + if filename.lower().endswith(".xml"): + print("Export Materials to XML file : " + filename) + xml = ET.Element("xml") global define - define = ET.SubElement(xml, 'define') + define = ET.SubElement(xml, "define") global materials - materials = ET.SubElement(xml, 'materials') + materials = ET.SubElement(xml, "materials") processMaterials() indent(xml) ET.ElementTree(xml).write(filename) else: - print('File extension must be xml') + print("File extension must be xml") + + +def exportOpticals(first, filename): + if filename.lower().endswith(".xml"): + print("Export Opticals to XML file : " + filename) + xml = ET.Element("xml") + global define + define = ET.SubElement(xml, "define") + global solids + solids = ET.SubElement(xml, "solids") + processOpticals() + 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'}) + print("Create gcard : " + basename) + print("Path : " + path) + gcard = ET.Element("gcard") + ET.SubElement(gcard, "detector", {"name": "target_cad", "factory": "CAD"}) if flag is True: - ET.SubElement(gcard, 'detector', { - 'name': 'target_gdml', 'factory': 'GDML'}) + 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): if not os.path.exists(path): - print('Creating Directory : ' + path) + print("Creating Directory : " + path) os.mkdir(path) - def exportGEMC(first, path, flag): # flag = True GEMC - GDML # flag = False just CAD global gxml - print('Export GEMC') + print("Export GEMC") # 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 is True: - print('Create GDML directory') - gdmlPath = os.path.join(path, 'gdml') + 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') + 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') + 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" + global refPlacement + + refPlacement = {} # a dictionary of name as key, and placement as value + # the name could that of + # S' = R^-1 * S * R + # + # s = FreeCAD.Matrix() + # s.scale(self.obj.Scale.x, self.obj.Scale.y, self.obj.Scale.z) + # rot = FreeCAD.Rotation(self.rotation()) + # rot.Angle = -rot.Angle + # sprime = rot.inverted()*s*rot + # return FreeCAD.Vector(sprime.A11, sprime.A22, sprime.A33) + # + # For scaling then rotating + return self.obj.Scale + else: + return FreeCAD.Vector(1, 1, 1) + + def export(self): + if len(self.obj.Objects) == 1: + self._export1(self.obj.Objects[0]) + return + + # Here deal with scaling multi objects: + # Make a multiunion of all the objects, then scale that + exporters = [] + for obj in self.obj.Objects: + exporter = SolidExporter.getExporter(obj) + exporter.export() + exporters.append(exporter) + + unionXML = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + for i, exporter in enumerate(exporters): + volRef = exporter.name() + nodeName = f"{self.name()}_{i}" + nodeXML = ET.SubElement( + unionXML, "multiUnionNode", {"name": nodeName} + ) + ET.SubElement(nodeXML, "solid", {"ref": volRef}) + exportPosition(nodeName, nodeXML, exporter.position()) + rot = FreeCAD.Rotation(exporter.rotation()) + # for reasons that I don't understand, in booleans and multiunions + # angle is NOT reversed, so undo reversal of exportRotation + exportRotation(nodeName, nodeXML, rot, invertRotation=False) + + self._exportScaled() + + def _export1(self, clonedObj): + # for now we only deal with one cloned object + clonedObj = self.obj.Objects[0] + exporter = SolidExporter.getExporter(clonedObj) + exporter.export() + # The scaling of the position turns out to be more complicated + # than I first thought (MMH). Draft->scale scales the position of the + # the cloned object, i.e., the clone has a placement that already + # includes the scaling of the placement of the cloned object, so it is + # not necessary to repeat the scaling. HOWEVER, for several of the + # objects we deal with, the position that is + # exported to the gdml IS NOT obj.Placement. For example, a regular box + # as its origin at corner, whereas a gdml box has its origin at the + # center, so we take that into account on export by adding a shift by + # half of each dimension. Draft/scale will scale the + # cube.Placement, which is (0,0,0), so nothing happens. The solution: + # get the clone position, unscale it, then get the exporter.position(), + # and then scale THAT. Note that once an object has been cloned, the + # clone no longer keeps track of the objects POSITION, but it does + # keep track of its dimensions. So if the object is doubles in size, + # the (scaled) double will change, but if the object is MOVED, the + # clone will not change its position! So the following algorithm, would + # fail. There is no way to know if the difference between the scaled + # position and the clone's position is due to the clone moving or the + # object moving. + clonedPlacement = FreeCAD.Placement( + exporter.placement() + ) # copy the placement + m = clonedPlacement.Matrix + invRotation = FreeCAD.Placement(m.inverse()).Rotation + clonedPosition = clonedPlacement.Base + clonedRotation = clonedPlacement.Rotation + # unrotate original position + r1 = invRotation * clonedPosition + objPosition = FreeCAD.Vector(clonedObj.Placement.Base) + if self.hasScale(): + scale = self.obj.Scale + r1.scale(scale.x, scale.y, scale.z) + delta = self.obj.Placement.Base - objPosition.scale( + scale.x, scale.y, scale.z + ) + else: + delta = self.obj.Placement.Base - objPosition + objRotation = FreeCAD.Rotation(clonedObj.Placement.Rotation) + myRotation = self.obj.Placement.Rotation + # additional rotation of clone + objRotation.invert() + additionalRotation = myRotation * objRotation + desiredRotation = additionalRotation * clonedRotation + r2 = desiredRotation * r1 + # if neither clone not original have moved, delta should be zero + # If it is not, there is no way to know which moved, but we are working + # on the assumption only clone moved + # same consideration for rotations. Draft scale copies the obj.Placement.Rotation + # But that is not necessarily the exporters rotation (e.g. extruded ellipses + # rotation depends on orientation of ellipse). Further, the clone itself + # could have an extra rotation. + print(r2, delta) + clonedPosition = r2 + delta + placement = FreeCAD.Placement(clonedPosition, desiredRotation) + + self._position = placement.Base + self._rotation = placement.Rotation + self._name = exporter.name() + self._exportScaled() + + +class BoxExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def export(self): + if self.exported(): + return + super().export() + + ET.SubElement( + solids, + "box", + { + "name": self.name(), + "x": str(self.obj.Length.Value), + "y": str(self.obj.Width.Value), + "z": str(self.obj.Height.Value), + "lunit": "mm", + }, + ) + self._exportScaled() + + def position(self): + delta = FreeCAD.Vector( + self.obj.Length.Value / 2, + self.obj.Width.Value / 2, + self.obj.Height.Value / 2, + ) + # Part::Box has its origin at the corner + # gdml box has its origin at the center + # In FC, rotations are about corner. In GDML about + # center. The following gives correct position of center + # of exported cube + pos = self.obj.Placement.Base + self.obj.Placement.Rotation * delta + return pos + + +class CylinderExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def export(self): + if self.exported(): + return + super().export() + + # Needs unique Name + # This is for non GDML cylinder/tube + ET.SubElement( + solids, + "tube", + { + "name": self.name(), + "rmax": str(self.obj.Radius.Value), + "deltaphi": str(float(self.obj.Angle.Value)), + "aunit": "deg", + "z": str(self.obj.Height.Value), + "lunit": "mm", + }, + ) + self._exportScaled() + + def position(self): + delta = FreeCAD.Vector(0, 0, self.obj.Height.Value / 2) + # see comments in BoxExporter + pos = self.obj.Placement.Base + self.obj.Placement.Rotation * delta + return pos + + +class ConeExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def export(self): + if self.exported(): + return + super().export() + + ET.SubElement( + solids, + "cone", + { + "name": self.name(), + "rmax1": str(self.obj.Radius1.Value), + "rmax2": str(self.obj.Radius2.Value), + "deltaphi": str(float(self.obj.Angle.Value)), + "aunit": "deg", + "z": str(self.obj.Height.Value), + "lunit": "mm", + }, + ) + self._exportScaled() + + def position(self): + # Adjustment for position in GDML + delta = FreeCAD.Vector(0, 0, self.obj.Height.Value / 2) + # see comments in BoxExporter + pos = self.obj.Placement.Base + self.obj.Placement.Rotation * delta + return pos + + +class SphereExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def export(self): + if self.exported(): + return + super().export() + + ET.SubElement( + solids, + "sphere", + { + "name": self.name(), + "rmax": str(self.obj.Radius.Value), + "starttheta": str(90.0 - float(self.obj.Angle2.Value)), + "deltatheta": str( + float(self.obj.Angle2.Value - self.obj.Angle1.Value) + ), + "deltaphi": str(float(self.obj.Angle3.Value)), + "aunit": "deg", + "lunit": "mm", + }, + ) + self._exportScaled() + + def position(self): + # see comments in processBoxObject + unrotatedpos = self.obj.Placement.Base + pos = self.obj.Placement.Rotation * unrotatedpos + return pos + + +class BooleanExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + baseExporter = SolidExporter.getExporter(self.obj.Base) + basePlacement = baseExporter.placement() + self._placement = self.obj.Placement * basePlacement + + def isBoolean(self, obj): + id = obj.TypeId + return id == "Part::Cut" or id == "Part::Fuse" or id == "Part::Common" + + def boolOperation(self, 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.TypeId} not handled yet") + return None + + def position(self): + return self._placement.Base + + def rotation(self): + return self._placement.Rotation + + def placement(self): + return self._placement + + def export(self): + """ + 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") + if self.exported(): + return + super().export() + + + obj = self.obj + boolsList = [obj] # list of booleans that are part of obj + # dynamic list that is used to figure out when we've iterated over all + # subobjects that are booleans + tmpList = [obj] + ref1 = {} # first solid exporter + ref2 = {} # second solid exporter + while len(tmpList) > 0: + boolobj = tmpList.pop() + solidExporter = SolidExporter.getExporter(boolobj.Base) + ref1[boolobj] = solidExporter + if self.isBoolean(boolobj.Base): + tmpList.append(boolobj.Base) + boolsList.append(boolobj.Base) + else: + solidExporter.export() + + solidExporter = SolidExporter.getExporter(boolobj.Tool) + ref2[boolobj] = solidExporter + if self.isBoolean(boolobj.Tool): + tmpList.append(boolobj.Tool) + boolsList.append(boolobj.Tool) + else: + solidExporter.export() + + # Now tmpList is empty and boolsList has list of all booleans + for boolobj in reversed(boolsList): + operation = self.boolOperation(boolobj) + if operation is None: + continue + solidName = boolobj.Label + boolXML = ET.SubElement( + solids, str(operation), {"name": solidName} + ) + ET.SubElement(boolXML, "first", {"ref": ref1[boolobj].name()}) + ET.SubElement(boolXML, "second", {"ref": ref2[boolobj].name()}) + # process position & rotation + # Note that only the second item in the boolean (the Tool in FC parlance) + # gets a position and a rotation. But these are relative to the + # first. So convolve placement of second with inverse placement of first + placementFirst = ref1[boolobj].placement() + placementSecond = invPlacement(placementFirst) * ref2[boolobj].placement() + rot = placementSecond.Rotation + pos = placementSecond.Base # must also rotate position + toolObj = ref2[boolobj].obj # the tool object of the boolean + if placementFirst == FreeCAD.Placement(): # we give up on expressions, unless 1st object (Base( has no placement + xexpr = GDMLShared.getPropertyExpression(toolObj, '.Placement.Base.x') + yexpr = GDMLShared.getPropertyExpression(toolObj, '.Placement.Base.y') + zexpr = GDMLShared.getPropertyExpression(toolObj, '.Placement.Base.z') + pos = (xexpr, yexpr, zexpr) + + exportPosition(toolObj.Name, boolXML, pos) + # For booleans, gdml want actual rotation, not reverse + # processRotation export negative of rotation angle(s) + exportRotation(toolObj.Name, boolXML, rot, invertRotation=False) + self._exportScaled() + + +class GDMLSolidExporter(SolidExporter): + def __init__(self, obj, tag, propertyList=None): + super().__init__(obj) + self._name = NameManager.getName(obj) + self.propertyList = propertyList + self.tag = tag + + def name(self): + return self._name + + def export(self): + if self.exported(): + return + super().export() + + if self.propertyList is None: + return # presumably the child will do its own export in that case + + attrib = {} + attrib["name"] = self.name() + for prop in self.propertyList: + attrib[prop] = str(GDMLShared.getPropertyExpression(self.obj, prop)) # get expression or value + + ET.SubElement(solids, str(self.tag), attrib) + self._exportScaled() + + +class GDMLArb8Exporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'arb8', ['v1x', 'v1y', 'v2x', 'v2y', 'v3x', 'v3y', 'v4x', 'v4y', + 'v5x', 'v5y', 'v6x', 'v6y', 'v7x', 'v7y', 'v8x', 'v8y', 'dz', 'lunit']) + + def export(self): + if self.exported(): + return + super().export() + + ET.SubElement( + solids, + "arb8", + { + "name": self.name(), + "v1x": str(self.obj.v1x), + "v1y": str(self.obj.v1y), + "v2x": str(self.obj.v2x), + "v2y": str(self.obj.v2y), + "v3x": str(self.obj.v3x), + "v3y": str(self.obj.v3y), + "v4x": str(self.obj.v4x), + "v4y": str(self.obj.v4y), + "v5x": str(self.obj.v5x), + "v5y": str(self.obj.v5y), + "v6x": str(self.obj.v6x), + "v6y": str(self.obj.v6y), + "v7x": str(self.obj.v7x), + "v7y": str(self.obj.v7y), + "v8x": str(self.obj.v8x), + "v8y": str(self.obj.v8y), + "dz": str(self.obj.dz), + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + + +class GDMLBoxExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, "box", ['x', 'y', 'z', 'lunit']) + + +class GDMLConeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, "cone", ['rmin1', 'rmin2', 'rmax1', 'rmax2', 'startphi', 'deltaphi', 'aunit', 'z', 'lunit']) + + +class GDMLcutTubeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, "cutTube", ['rmin', 'rmax', 'startphi', 'deltaphi', 'aunit', 'z', + 'highX', 'highY', 'highZ', 'lowX', 'lowY', 'lowZ', 'lunit'] ) + + +class GDMLElConeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'elcone', ['dx', 'dy', 'zcut', 'zmax', 'lunit']) + + +class GDMLEllipsoidExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'ellipsoid', ['ax', 'by', 'cz', 'zcut1', 'zcut2', 'lunit']) + + +class GDMLElTubeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'eltube', ['dx', 'dy', 'dz', 'lunit']) + + +class GDMLHypeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'hype', ['rmin', 'rmax', 'z', 'inst', 'outst', 'aunit', 'lunit']) + + +class GDMLParaboloidExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'paraboloid', ['rlo', 'rhi', 'dz', 'lunit']) + + +class GDMLOrbExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'orb', ['r', 'lunit']) + + +class GDMLParaExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'para', ['x', 'y', 'z', 'alpha', 'theta', 'phi', 'aunit', 'lunit']) + + +class GDMLPolyconeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'polycone') + + def export(self): + if self.exported(): + return + super().export() + + cone = ET.SubElement( + solids, + "polycone", + { + "name": self.name(), + "startphi": str(self.obj.startphi), + "deltaphi": str(self.obj.deltaphi), + "aunit": self.obj.aunit, + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + + for zplane in self.obj.OutList: + ET.SubElement( + cone, + "zplane", + { + "rmin": str(zplane.rmin), + "rmax": str(zplane.rmax), + "z": str(zplane.z), + }, + ) + + +class GDMLGenericPolyconeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'genericPolycone') + + def export(self): + if self.exported(): + return + super().export() + + cone = ET.SubElement( + solids, + "genericPolycone", + { + "name": self.name(), + "startphi": str(self.obj.startphi), + "deltaphi": str(self.obj.deltaphi), + "aunit": self.obj.aunit, + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + for rzpoint in self.obj.OutList: + ET.SubElement( + cone, "rzpoint", {"r": str(rzpoint.r), "z": str(rzpoint.z)} + ) + + +class GDMLGenericPolyhedraExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'genericPolyhedra') + + def export(self): + if self.exported(): + return + super().export() + + polyhedra = ET.SubElement( + solids, + "genericPolyhedra", + { + "name": self.name(), + "startphi": str(self.obj.startphi), + "deltaphi": str(self.obj.deltaphi), + "numsides": str(self.obj.numsides), + "aunit": self.obj.aunit, + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + for rzpoint in self.obj.OutList: + ET.SubElement( + polyhedra, + "rzpoint", + {"r": str(rzpoint.r), "z": str(rzpoint.z)}, + ) + + +class GDMLPolyhedraExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'polyhedra') + + def export(self): + if self.exported(): + return + super().export() + + poly = ET.SubElement( + solids, + "polyhedra", + { + "name": self.name(), + "startphi": str(self.obj.startphi), + "deltaphi": str(self.obj.deltaphi), + "numsides": str(self.obj.numsides), + "aunit": self.obj.aunit, + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + + for zplane in self.obj.OutList: + ET.SubElement( + poly, + "zplane", + { + "rmin": str(zplane.rmin), + "rmax": str(zplane.rmax), + "z": str(zplane.z), + }, + ) + + +class GDMLSphereExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, "sphere", ['rmin', 'rmax', 'startphi', 'deltaphi', + 'starttheta', 'deltatheta', 'aunit', 'lunit']) + + +class GDMLSampledTessellatedExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'tesselated') + + def export(self): + if self.exported(): + return + super().export() + + tessName = self.name() + print(f"tessname: {tessName}") + # Use more readable version + tessVname = tessName + "_" + # print(dir(obj)) + + verts = self.obj.vertsList + tess = ET.SubElement(solids, "tessellated", {"name": tessName}) + for i, v in enumerate(verts): + exportDefineVertex(tessVname, v, i) + + i = 0 + indexList = self.obj.indexList + for nVerts in self.obj.vertsPerFacet: + # print(f'Normal at : {n} dot {dot} {clockWise}') + if nVerts == 3: + i0 = indexList[i] + i1 = indexList[i + 1] + i2 = indexList[i + 2] + ET.SubElement( + tess, + "triangular", + { + "vertex1": tessVname + str(i0), + "vertex2": tessVname + str(i1), + "vertex3": tessVname + str(i2), + "type": "ABSOLUTE", + }, + ) + elif nVerts == 4: + i0 = indexList[i] + i1 = indexList[i + 1] + i2 = indexList[i + 2] + i3 = indexList[i + 3] + ET.SubElement( + tess, + "quadrangular", + { + "vertex1": tessVname + str(i0), + "vertex2": tessVname + str(i1), + "vertex3": tessVname + str(i2), + "vertex4": tessVname + str(i3), + "type": "ABSOLUTE", + }, + ) + i += nVerts + self._exportScaled() + + +class GDMLTessellatedExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'tesselated') + + def export(self): + if self.exported(): + return + super().export() + + tessName = self.name() + # Use more readable version + tessVname = tessName + "_" + # print(dir(obj)) + vertexHashcodeDict = {} + + """ + tess = ET.SubElement(solids, 'tessellated', {'name': tessName}) + + #for i, v in enumerate(self.obj.Shape.Vertexes): + for i, v in enumerate(self.obj.Shape.Vertexes): + vertexHashcodeDict[v.hashCode()] = i + exportDefineVertex(tessVname, self.obj.Vertexes[i], i) + + for f in self.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: + i0 = vertexHashcodeDict[vertexes[0].hashCode()] + i1 = vertexHashcodeDict[vertexes[1].hashCode()] + i2 = vertexHashcodeDict[vertexes[2].hashCode()] + 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'}) + """ + tess = ET.SubElement(solids, "tessellated", {"name": tessName}) + placementCorrection = self.obj.Placement.inverse() + for i, v in enumerate(self.obj.Shape.Vertexes): + vertexHashcodeDict[v.hashCode()] = i + exportDefineVertex(tessVname, placementCorrection * v.Point, i) + + for f in self.obj.Shape.Faces: + # print(f'len(f.Edges) {len(f.Edges)}') + # 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: + i0 = vertexHashcodeDict[vertexes[0].hashCode()] + i1 = vertexHashcodeDict[vertexes[1].hashCode()] + i2 = vertexHashcodeDict[vertexes[2].hashCode()] + 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", + }, + ) + self._exportScaled() + + +class GDMLTetraExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'tet') + + def export(self): + if self.exported(): + return + super().export() + + tetraName = self.name() + v1Name = tetraName + "v1" + v2Name = tetraName + "v2" + v3Name = tetraName + "v3" + v4Name = tetraName + "v4" + exportDefine(v1Name, self.obj.v1) + exportDefine(v2Name, self.obj.v2) + exportDefine(v3Name, self.obj.v3) + exportDefine(v4Name, self.obj.v4) + + ET.SubElement( + solids, + "tet", + { + "name": tetraName, + "vertex1": v1Name, + "vertex2": v2Name, + "vertex3": v3Name, + "vertex4": v4Name, + }, + ) + self._exportScaled() + + +class GDMLTetrahedronExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'tet') + + def export(self): + if self.exported(): + return + super().export() + + global structure + global solids + tetrahedronName = self.name() + print("Len Tet" + str(len(self.obj.Proxy.Tetra))) + count = 0 + for t in self.obj.Proxy.Tetra: + tetraName = tetrahedronName + "_" + 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]) + 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": self.obj.material}) + ET.SubElement(lvol, "solidref", {"ref": tetraName}) + count += 1 + + # Now put out Assembly + assembly = ET.SubElement( + structure, "assembly", {"name": tetrahedronName} + ) + count = 0 + for t in self.obj.Proxy.Tetra: + lvName = "Tetra" + str(count) + physvol = ET.SubElement( + assembly, "physvol", {"name": "PV_Tetra" + str(count)} + ) + ET.SubElement(physvol, "volumeref", {"ref": lvName}) + # ET.SubElement(physvol, 'position') + # ET.SubElement(physvol, 'rotation') + count += 1 + self._exportScaled() + + +class GDMLTorusExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'torus', ['rmin', 'rmax', 'rtor', + 'startphi', 'deltaphi', 'aunit', 'lunit']) + + +class GDMLTrapExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'trap', ['z', 'theta', 'phi', + 'x1', 'x2', 'x3', 'x4', 'y1', 'y2']) + + def export(self): + if self.exported(): + return + super().export() + + ET.SubElement( + solids, + "trap", + { + "name": self.name(), + "z": str(self.obj.z), + "theta": str(self.obj.theta), + "phi": str(self.obj.phi), + "x1": str(self.obj.x1), + "x2": str(self.obj.x2), + "x3": str(self.obj.x3), + "x4": str(self.obj.x4), + "y1": str(self.obj.y1), + "y2": str(self.obj.y2), + "alpha1": str(self.obj.alpha), + "alpha2": str(self.obj.alpha), + "aunit": self.obj.aunit, + "lunit": self.obj.lunit, + }, + ) + self._exportScaled() + + +class GDMLTrdExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'trd', ['z', 'x1', 'x2', 'y1', 'y2', 'lunit']) + + +class GDMLTubeExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'tube', ['rmin', 'rmax', 'startphi', 'deltaphi', 'aunit', 'z', 'lunit']) + + +class GDMLTwistedboxExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'twistedbox', ['PhiTwist', 'x', 'y', 'z', 'aunit', 'lunit']) + + +class GDMLTwistedtrdExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'twistedtrd', ['PhiTwist', 'x1', 'x2', 'y1', 'y2', 'z', 'aunit', 'lunit']) + + +class GDMLTwistedtrapExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'twistedtrap', ['PhiTwist', 'x1', 'x2', 'y1', 'y2', 'x3', 'x4', 'z', + 'Theta', 'Phi', 'Alph', 'aunit', 'lunit']) + + +class GDMLTwistedtubsExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'twistedtubs', ['twistedangle', 'endinnerrad', 'endouterrad', 'zlen', + 'phi', 'aunit', 'lunit']) + + +class GDMLXtruExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'xtru') + + def export(self): + if self.exported(): + return + super().export() + + xtru = ET.SubElement( + solids, "xtru", {"name": self.name(), "lunit": self.obj.lunit} + ) + for items in self.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), + }, + ) + self._exportScaled() + + +class GDML2dVertexExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'twoDimVertex') + + def export(self): + ET.SubElement( + solids, "twoDimVertex", {"x": self.obj.x, "y": self.obj.y} + ) + + +class GDMLborderSurfaceExporter(GDMLSolidExporter): + def __init__(self, obj): + super().__init__(obj, 'bordersurface') + + def export(self): + borderSurface = ET.SubElement( + structure, + "bordersurface", + {"name": self.obj.Name, "surfaceproperty": self.obj.surface}, + ) + + print(self.obj.pv1) + if self.obj.pv1[:3] == "av_": + # for assembly auto generated names (starting with 'av_' we do not + # include the 'PV_' in the name + ET.SubElement(borderSurface, "physvolref", {"ref": self.obj.pv1}) + else: + ET.SubElement( + borderSurface, "physvolref", {"ref": "PV_" + self.obj.pv1} + ) + print(self.obj.pv1) + if self.obj.pv2[:3] == "av_": + ET.SubElement(borderSurface, "physvolref", {"ref": self.obj.pv2}) + else: + ET.SubElement( + borderSurface, "physvolref", {"ref": "PV_" + self.obj.pv2} + ) + + +class MultiFuseExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def name(self): + solidName = "MultiFuse" + self.obj.Label + return solidName + + def export(self): + if self.exported(): + return + super().export() + + GDMLShared.trace("Multifuse - multiunion") + # test and fix + # First add solids in list before reference + print("Output Solids") + exporters = [] + for sub in self.obj.OutList: + exporter = SolidExporter.getExporter(sub) + if exporter is not None: + exporter.export() + exporters.append(exporter) + + GDMLShared.trace("Output Solids Complete") + multUnion = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + + num = 1 + for exp in exporters: + GDMLShared.trace(exp.name()) + node = ET.SubElement( + multUnion, "multiUnionNode", {"name": "node-" + str(num)} + ) + ET.SubElement(node, "solid", {"ref": exp.name()}) + processPlacement(exp.name(), node, exp.placement()) + num += 1 + + GDMLShared.trace("Return MultiFuse") + self._exportScaled() + + +class OrthoArrayExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + self._name = "MultiUnion-" + self.obj.Label + + def export(self): + if self.exported(): + return + super().export() + + from . import arrayUtils + base = self.obj.Base + print(f"Base {base.Label}") + if hasattr(base, "TypeId") and base.TypeId == "App::Part": + print( + f"**** Arrays of {base.TypeId} ({base.Label}) currently not supported ***" + ) + return + baseExporter = SolidExporter.getExporter(base) + if baseExporter is None: + print(f"Cannot export {base.Label}") + return + baseExporter.export() + volRef = baseExporter.name() + unionXML = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + basePos = baseExporter.position() + baseRotation = FreeCAD.Rotation(baseExporter.rotation()) + for i, placement in enumerate(arrayUtils.placementList(self.obj, offsetVector=basePos)): + ix, iy, iz = arrayUtils.orthoIndexes(i, self.obj) + nodeName = f"{self.name()}_{ix}_{iy}_{iz}" + translate = placement.Base + nodeXML = ET.SubElement( + unionXML, "multiUnionNode", {"name": nodeName} + ) + ET.SubElement(nodeXML, "solid", {"ref": volRef}) + ET.SubElement( + nodeXML, + "position", + { + "name": f"{self.name()}_pos_{ix}_{iy}_{iz}", + "x": str(translate.x), + "y": str(translate.y), + "z": str(translate.z), + "unit": "mm", + }, + ) + if baseRotation.Angle != 0: + exportRotation(self.name(), nodeXML, baseRotation, invertRotation=False) + + self._exportScaled() + + +class PolarArrayExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def name(self): + solidName = "MultiUnion-" + self.obj.Label + return solidName + + def export(self): + if self.exported(): + return + super().export() + + from . import arrayUtils + base = self.obj.Base + print(base.Label) + if hasattr(base, "TypeId") and base.TypeId == "App::Part": + print( + f"**** Arrays of {base.TypeId} ({base.Label}) currently not supported ***" + ) + return + baseExporter = SolidExporter.getExporter(base) + baseExporter.export() + baseRotation = baseExporter.rotation() + volRef = baseExporter.name() + unionXML = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + positionVector = baseExporter.position() + for i, placement in enumerate(arrayUtils.placementList(self.obj, + offsetVector=positionVector)): + rot = placement.Rotation + pos = placement.Base + rot = rot * baseRotation + rot.Angle = -rot.Angle # undo angle reversal by exportRotation + nodeName = f"{self.name()}_{i}" + nodeXML = ET.SubElement( + unionXML, "multiUnionNode", {"name": nodeName} + ) + ET.SubElement(nodeXML, "solid", {"ref": volRef}) + exportPosition(nodeName, nodeXML, pos) + exportRotation(nodeName, nodeXML, rot) + self._exportScaled() + + +class PathArrayExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def name(self): + solidName = "MultiUnion-" + self.obj.Label + return solidName + + def export(self): + if self.exported(): + return + super().export() + + base = self.obj.Base + print(base.Label) + if hasattr(base, "TypeId") and base.TypeId == "App::Part": + print( + f"**** Arrays of {base.TypeId} ({base.Label}) currently not supported ***" + ) + return + baseExporter = SolidExporter.getExporter(base) + baseExporter.export() + volRef = baseExporter.name() + unionXML = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + count = self.obj.Count + positionVector = baseExporter.position() + rot = base.Placement.Rotation + extraTranslation = self.obj.ExtraTranslation + pathObj = self.obj.PathObject + path = pathObj.Shape.Edges[0] + points = path.discretize(Number=count) + for i, point in enumerate(points): + pos = point + positionVector + extraTranslation + nodeName = f"{self.name()}_{i}" + nodeXML = ET.SubElement( + unionXML, "multiUnionNode", {"name": nodeName} + ) + ET.SubElement(nodeXML, "solid", {"ref": volRef}) + exportPosition(nodeName, nodeXML, pos) + exportRotation(nodeName, nodeXML, rot) + self._exportScaled() + + +class PointArrayExporter(SolidExporter): + def __init__(self, obj): + super().__init__(obj) + + def name(self): + solidName = "MultiUnion-" + self.obj.Label + return solidName + + def export(self): + if self.exported(): + return + super().export() + + base = self.obj.Base + print(base.Label) + if hasattr(base, "TypeId") and base.TypeId == "App::Part": + print( + f"**** Arrays of {base.TypeId} ({base.Label}) currently not supported ***" + ) + return + baseExporter = SolidExporter.getExporter(base) + baseExporter.export() + volRef = baseExporter.name() + unionXML = ET.SubElement(solids, "multiUnion", {"name": self.name()}) + positionVector = baseExporter.position() + rotBase = base.Placement.Rotation + extraTranslation = self.obj.ExtraPlacement.Base + extraRotation = self.obj.ExtraPlacement.Rotation + extraRotation.Angle = -extraRotation.Angle + rot = extraRotation * rotBase + pointObj = self.obj.PointObject + points = pointObj.Links + for i, point in enumerate(points): + pos = point.Placement.Base + positionVector + extraTranslation + nodeName = f"{self.name()}_{i}" + nodeXML = ET.SubElement( + unionXML, "multiUnionNode", {"name": nodeName} + ) + ET.SubElement(nodeXML, "solid", {"ref": volRef}) + exportPosition(nodeName, nodeXML, pos) + exportRotation(nodeName, nodeXML, rot) + self._exportScaled() + + +# +# ------------------------------revolutionExporter ---------------------------- +# +global Deviation # Fractional deviation of revolve object +############################################# +# Helper functions for Revolve 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 + ) + + @staticmethod + def isCircle(arc1, arc2): + ''' + test if two arcs can be joined into a single circle + return true if so, false if not + TODO: make tests in terms of some small fraction epsilon + ''' + # Both must be circular arcs + c1 = arc1.Curve + c2 = arc2.Curve + if (c1.TypeId != "Part::GeomCircle" or + c2.TypeId != "Part::GeomCircle"): + print("Not Arc") + return False + # They must have same radius + if c1.Radius != c2.Radius: + print("Not same radius") + return False + # They must have the same center + if c1.Center != c2.Center: + print("not same center") + return False + # They must join end to end + # for reasons I don't understand, both arcs + # have the same first and last parameters and the + # last parameter is 2*pi. The sort edges must + # not be calculating edges correctly + ''' + if (c1.FirstParameter != c2.LastParameter or + c1.LastParameter != c2.FirstParameter): + print("dont match ends") + print(f'c1.0 {c1.FirstParameter} c1.1 {c1.LastParameter}') + print(f'c2.0 {c2.FirstParameter} c2.1 {c2.LastParameter}') + return False + ''' + if (arc1.Vertexes[0].Point != arc2.Vertexes[1].Point or + arc1.Vertexes[1].Point != arc2.Vertexes[0].Point): + print("dont match ends") + print(f'c1.0 {arc1.Vertexes[0].Point} c1.1 {arc1.Vertexes[1].Point}') + print(f'c2.0 {arc2.Vertexes[0].Point} c2.1 {arc2.Vertexes[1].Point}') + return False + + # They must be in the same plane + if c1.Axis != c2.Axis: + print("not same axis") + return False + + return True + + @staticmethod + def arcs2circle(arc1, arc2): + ''' + combine two arc edges into a single circle edge + ''' + circle = None + if ClosedCurve.isCircle(arc1, arc2): + curve = arc1.Curve + circle = Part.makeCircle(curve.Radius, curve.Center, curve.Axis) + return circle + + +class RevolvedClosedCurve(ClosedCurve): + def __init__(self, name, edgelist, angle, axis): + super().__init__(name, edgelist) + self.angle = angle + self.axis = axis + self.position = Vector(0, 0, 0) + self.rotation = [0, 0, 0] # TBD + self.deflectionFraction = 0.001 + + def export(self): + verts = self.discretize() + exportPolycone(self.name, verts, self.angle) + + def discretize(self): + deflection = Deviation * radialExtent(self.edgeList) + print(f"Deflection = {deflection}") + edge = self.edgeList[0] + return edge.discretize(Deflection=deflection) + + +class RevolvedCircle(RevolvedClosedCurve): + def __init__(self, name, edgelist, angle, axis): + super().__init__(name, edgelist, angle, axis) + z = edgelist[0].Curve.Center.z + self.position = FreeCAD.Vector(0, 0, z) + + def export(self): + edge = self.edgeList[0] + rmax = edge.Curve.Radius + x = edge.Curve.Center.x + y = edge.Curve.Center.y + startphi = math.degrees(math.atan2(y, x)) + rtor = math.sqrt(x * x + y * y) + exportTorus(self.name, rmax, rtor, startphi, self.angle) + + +class RevolvedNEdges(RevolvedClosedCurve): + def __init__(self, name, edgelist, angle, axis): + super().__init__(name, edgelist, angle, axis) + + def export(self): + global solids + + # maxdev = self.deflectionFraction*radialExtent(self.edgeList) + verts = [] + for i, e in enumerate(self.edgeList): + + while switch(e.Curve.TypeId): + if case("Part::GeomLineSegment"): + print("Part::GeomLineSegment") + verts.append(e.Vertexes[0].Point) + break + + if case("Part::GeomLine"): + print("Part::GeomLine") + verts.append(e.Vertexes[0].Point) + break + + else: + curveName = self.name + "_c" + str(i) + curveSection = RevolvedClosedCurve( + curveName, [e], self.angle, self.axis + ) + verts += curveSection.discretize() + break + + xtruName = self.name + exportPolycone(xtruName, verts, self.angle) + + +# arrange a list of edges in the x-y plane in Counter Clockwise 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 same side of normal or opposite. Return true if v points along normal + + +def pointInsideEdge(v0, v1, normal): + v = v1 - v0 + if v.dot(normal) < 0: + return False + else: + return True + + +def edgelistArea(edgelist: list[Part.Edge]) -> float: + face = Part.Face(Part.Wire(edgelist)) + return face.Area + + +def sortEdgelistsByFaceArea(listoflists): + listoflists.sort(reverse=True, key=edgelistArea) + + +# return maxRadialdistance - minRadialDistance +def radialExtent(edges, axis=Vector(0, 0, 1)): + rmin = sys.float_info.max + rmax = -sys.float_info.max + for e in edges: + b = e.BoundBox + for i in range(0, 8): # loop over box boundaries + v = b.getPoint(i) + radialVector = v - v.dot(axis) * axis + r = radialVector.Length + if r < rmin: + rmin = r + elif r > rmax: + rmax = r + + return rmax - rmin + + +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 exportTorus(name, rmax, rtor, startphi, angle): + global solids + + ET.SubElement( + solids, + "torus", + { + "name": name, + "rmin": "0", + "rmax": str(rmax), + "rtor": str(rtor), + "startphi": str(startphi), + "deltaphi": str(angle), + "aunit": "deg", + "lunit": "mm", + }, + ) + + +def exportPolycone(name, vlist, angle): + global solids + + # if x > 0 stratphi = 0 + # if x < 0 startphi = 180 + # FreeCAD says can't recompute of both x < 0 and x> are used in a revolve + startphi = 0 + for v in vlist: + if v.x != 0 or v.y != 0: + startphi = math.degrees(math.atan2(v.y, v.x)) + break + + cone = ET.SubElement( + solids, + "genericPolycone", + { + "name": name, + "startphi": str(startphi), + "deltaphi": str(angle), + "aunit": "deg", + "lunit": "mm", + }, + ) + for v in vlist: + r = math.sqrt(v.x * v.x + v.y * v.y) + ET.SubElement(cone, "rzpoint", {"r": str(r), "z": str(v.z)}) + return + + +def getRevolvedCurve(name, edges, angle, axis): + # Return an RevolvedClosedCurve 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::GeomCircle"): + if closed is True: + print("Circle") + return RevolvedCircle(name, edges, angle, axis) + else: + print("Revolve Arc of Circle") + return RevolvedClosedCurve(name, edges, angle, axis) + # return RevolvedArcSection(name, edges, height) + + else: + print(f"revolve {e.Curve.TypeId}") + return RevolvedClosedCurve(name, edges, angle, axis) + + else: # three or more edges + return RevolvedNEdges(name, edges, angle, axis) + + +# scale up a solid that will be subtracted so it punches through 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 position vector + # + pos = closedCurve.position + if isinstance(closedCurve, ExtrudedCircle) or isinstance( + closedCurve, ExtrudedEllipse + ): + pos = rot * closedCurve.position + + return pos + + +class RevolutionExporter(SolidExporter): + def __init__(self, revolveObj): + super().__init__(revolveObj) + self.sketchObj = revolveObj.Source + self.lastName = self.obj.Label # initial name: might be modified later + # generate the positions that get computed during export + self.export(doExport=False) + + def name(self): + # override default name in SolidExporter + prefix = "" + if self.lastName[0].isdigit(): + prefix = "R" + return prefix + self.lastName + + def position(self): + # This presumes export has been called before position() + # Things will be screwed up, otherwise + return self._position + + def rotation(self): + # This presumes export has been called before position() + # Things will be screwed up, other wise + return self._rotation + + def export(self, doExport=True): + # The placement of the revolved item gets calculated here, during export + # but boolean exporter tries to get the position BEFORE the export here happens + # so to generate the position before the export happens, the doExport flag is used + # to run this code to generate the position, WITHOUT actually doing the export + # + global Deviation + revolveObj = self.obj + axis = revolveObj.Axis + angle = revolveObj.Angle + revolveCenter = revolveObj.Base + + # Fractional deviation + Deviation = revolveObj.ViewObject.Deviation / 100.0 + + # rotation to take revolve direction to z -axis + rot_dir_to_z = FreeCAD.Rotation(axis, Vector(0, 0, 1)) + edges = [edge.rotated(Vector(0, 0, 0), rot_dir_to_z.Axis, math.degrees(rot_dir_to_z.Angle)) + for edge in revolveObj.Source.Shape.Edges] + + # adjustment of Symmetric revolves + if revolveObj.Symmetric: + edges = [edge.rotated(Vector(0, 0, 0), Vector(0, 0, 1), -angle/2) + for edge in edges] + + # adjustment for off-center revolve axis + if revolveCenter != Vector(0, 0, 0): + edges = [edge.translated(-revolveCenter) for edge in edges] + + + sortededges = Part.sortEdges(edges) + + # sort by largest area to smallest area + sortEdgelistsByFaceArea(sortededges) + # getClosedCurve returns one of the sub classes of ClosedCurve that + # knows how to export the specific closed edges + # Make names based on Revolve name + eName = revolveObj.Label + # get a list of curves (instances of class ClosedCurve) + # for each set of closed edges + curves = [] + for i, edges in enumerate(sortededges): + curve = getRevolvedCurve(eName + str(i), edges, angle, axis) + if curve is not None: + curves.append(curve) + if len(curves) == 0: + print("No edges that can be revolved were found") + return + + # 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 + if doExport: + 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 + if doExport: + curve.export() + if parity == 0: + boolType = "union" + secondName = curve.name + secondPos = curve.position + else: + boolType = "subtraction" + secondName = curve.name + secondPos = curve.position + + booleanName = curve.name + "_bool" + if doExport: + 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" + # position of second relative to first + if doExport: + exportDefine(posName, relativePosition) + 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 + + self.lastName = booleanName + # 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 Revolution placement + + revolvePosition = revolveObj.Placement.Base + zoffset = Vector(0, 0, 0) + angles = quaternion2XYZ(revolveObj.Placement.Rotation) + # need to add rotations of elliptical tubes. Assume extrusion is on z-axis + # Probably will not work in general + zAngle = angles[2] + rootRot[2] + print(rootPos) + print(rootCurve.name) + print(rootCurve.position) + rootPos = rotatedPos(rootCurve, revolveObj.Placement.Rotation) + print(rootPos) + Base = revolvePosition + rootPos - zoffset + + # add back the off-center revolve axis + if revolveCenter != Vector(0, 0, 0): + Base += revolveCenter + + rotX = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), angles[0]) + rotY = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), angles[1]) + rotZ = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), zAngle) + + rot = rotX * rotY * rotZ + + # rotate back to revolve direction + rot = rot_dir_to_z.inverted() * rot + + placement = FreeCAD.Placement(Base, FreeCAD.Rotation(rot)) + self._position = placement.Base + self._rotation = placement.Rotation + + +# +# -----------------------------------------------extrusionExporter----------------------------------------------------- +# +############################################# +# Helper functions for extrude construction + +# One of the closed curves (list of edges) representing a part +# of the sketch + +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): + verts = self.discretize() + exportXtru(self.name, verts, self.height) + + def midPoint(self): + edge = self.edgeList[0] + verts = edge.discretize(Number=51) + return verts[int(len(verts) / 2)] + + def discretize(self): + global Deviation + edge = self.edgeList[0] + deflection = Deviation * edge.BoundBox.DiagonalLength + print(f"Deflection = {deflection}") + return edge.discretize(Deflection=deflection) + + +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 + + def midPoint(self): + edge = self.edgeList[0] + verts = edge.discretize(Number=3) + return verts[1] + + def area(self): + edge = self.edgeList[0] + v0 = edge.Vertexes[0].Point + v1 = edge.Vertexes[1].Point + L1 = Part.LineSegment(v0, v1) + chordEdge = Part.Edge(L1) + face = Part.Face(Part.Wire([edge, chordEdge])) + + return face.Area + + def export(self): + global solids + + 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 + + # TODO must deal with case where cutting chord is along major axis + # u_vc_vcenter = vc_vcenter.normalize() + # unit vector from 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 rotated 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 beginning angle of unrotated 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 + + return vmid + """ + + def export(self): + global solids + + 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 rotated 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 beginning angle of unrotated 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 ExtrudedBSpline(ExtrudedClosedCurve): + def __init__(self, name, edgelist, height): + super().__init__(name, edgelist, height) + + +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 + + else: + print(f"Arc of {e.Curve.TypeId}") + arcXtruName = self.name + "_bs" + str(i) + arcSection = ExtrudedClosedCurve( + arcXtruName, [e], self.height + ) + arcSection.export() + + midpnt = arcSection.midPoint() + inside = pointInsideEdge(midpnt, v0, normal) + edgeCurves.append([arcXtruName, inside]) + 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 = edgelistArea([self.edgeList[0]]) + area1 = edgelistArea([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 isSubtraction(self, edge): + # Does the given edge increase or decrease the area + # of the polygon formed by verts + ftot = Part.Face(Part.Wire(self.edgeList)) + # form face from edge and its chord + v0 = edge.Vertexes[0].Point + v1 = edge.Vertexes[1].Point + L1 = Part.LineSegment(v0, v1) + E1 = Part.Edge(L1) + fEdge = Part.Face(Part.Wire([edge, E1])) + + # form face from other edges without edge being tested + edgesWithout = [] + for e in self.edgeList: + if e != edge: + edgesWithout.append(e) + else: + v0 = edge.Vertexes[0].Point + v1 = edge.Vertexes[1].Point + L1 = Part.LineSegment(v0, v1) + edgesWithout.append(Part.Edge(L1)) + fwithout = Part.Face(Part.Wire(edgesWithout)) + + totArea = ftot.Area + edgeArea = fEdge.Area + withoutArea = fwithout.Area + print( + f"totArea {totArea}, edgeArea {edgeArea}, withoutArea {withoutArea}" + ) + + if totArea < 0.999 * ( + edgeArea + withoutArea + ): # 0.99 safety margin for totArea = edgeArea+withoutArea + if totArea > edgeArea: + return True + else: + # we need to reverse order of subtraction + return None # poor way of signaling need to swap subtraction order + else: + return False + + def export(self): + global solids + + verts = [] + + for i, e in enumerate(self.edgeList): + while switch(e.Curve.TypeId): + if case("Part::GeomLineSegment"): + verts.append(e.Vertexes[0].Point) + break + + if case("Part::GeomLine"): + verts.append(e.Vertexes[0].Point) + break + + else: + print(f"Curve {e.Curve.TypeId}") + arcXtruName = self.name + "_g" + str(i) + arcSection = ExtrudedClosedCurve( + arcXtruName, [e], self.height + ) + bsplineVerts = arcSection.discretize() + verts = verts + bsplineVerts + break + + # verts.append(verts[0]) + xtruName = self.name + exportXtru(xtruName, verts, self.height) + + +# 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 + + +def discretizeMinusOne(edgeList, iSkip): + # return discretized edge list except for iSkip + verts = [] + for i in range(len(edgeList)): + edge = edgeList[i] + if ( + i == iSkip + or edge.Curve.TypeId == "Part::GeomLine" + or edge.Curve.TypeId == "Part::GeomLineSegment" + ): + verts.append(edge.Vertexes[0].Point) + verts.append(edge.Vertexes[1].Point) + else: + verts += edge.discretize(24) + return verts + + +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): + global solids + + # We are assuming that the points to that form the + # the edges to be extruded are al coplanar and in the x-y plane + # with a possible zoffset, which is taken as the common + # z-coordinate of the first vertex + if len(vlist) < 3: + return + zoffset = vlist[0].z + xtru = ET.SubElement(solids, "xtru", {"name": name, "lunit": "mm"}) + + # prune verts: Closed curves get stitched from adjacent edges + # sometimes the beginning vertex f the next edge is the same as the end vertex + # of the previous edge. Geant gives a warning about that. To remove the warning + # we will remove vertices closer to each other than 0.1 nm - 1e-7 mm. If someone is trying + # to model objects with smaller dimensions, then they should take a lesson in Quantum Mechanics + # Because only adjacent edges could be the same, we just compare each edge to the preceding edge + vpruned = vlist[:] + for i in range(len(vlist)): + i1 = (i+1) % len(vlist) + if ((vlist[i1] - vlist[i]).Length < 1e-7): + vpruned.remove(vlist[i1]) + + for v in vpruned: + 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) + + else: + print(" B spline") + return ExtrudedClosedCurve(name, edges, height) + + elif len(edges) == 2: # exactly two edges + # if the two edges are tow arcs that make a circle, extrude resulting circle + edge = ClosedCurve.arcs2circle(edges[0], edges[1]) + if edge is not None: + return ExtrudedCircle(name, [edge], height) + else: + return ExtrudedNEdges(name, edges, height) + else: # three or more edges + return ExtrudedNEdges(name, edges, height) + + +class ExtrusionExporter(SolidExporter): + def __init__(self, extrudeObj): + global Deviation + super().__init__(extrudeObj) + self.sketchObj = extrudeObj.Base + self.lastName = self.obj.Label # initial name: might be modified later + Deviation = self.obj.ViewObject.Deviation / 100.0 + # generate the positions that get computed during export + self.export(doExport=False) + + def position(self): + # This presumes export has been called before position() + # Things will be screwed up, otherwise + return self._position + + def rotation(self): + # This presumes export has been called before position() + # Things will be screwed up, otherwise + return self._rotation + + def name(self): + # override default name in SolidExporter + prefix = "" + if self.lastName[0].isdigit(): + prefix = "X" + return prefix + self.lastName + + def export(self, doExport=True): + # The placement of the extruded item gets calculated here, during export + # but boolean exporter tries to get the position BEFORE the export here happens + # so to generate the position before the export happens, the doExport flag is used + # to run this code to generate the position, WITHOUT actually doing the export + # + + sketchObj: Sketcher.SketchObject = self.sketchObj + extrudeObj: Part.Feature = self.obj + eName = self.name() + + extrudeDirection = extrudeObj.Dir + + # rotation to take extrude direction to z -axis + rot_dir_to_z = FreeCAD.Rotation(extrudeDirection, Vector(0, 0, 1)) + edges = [edge.rotated(Vector(0, 0, 0), rot_dir_to_z.Axis, math.degrees(rot_dir_to_z.Angle)) for edge in sketchObj.Shape.Edges] + sortededges = Part.sortEdges(edges) + # sort by largest area to smallest area + sortEdgelistsByFaceArea(sortededges) + # getCurve returns one of the sub classes of ClosedCurve that + # knows how to export the specific 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 + # 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 + if doExport: + 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 + if doExport: + 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 + if doExport: + scaleUp(secondName, curve.name, 1.10) + secondPos = curve.position - Vector(0, 0, 0.01 * height) + + booleanName = curve.name + "_bool" + if doExport: + 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" + if doExport: + 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 + + self.lastName = ( + booleanName # our name should the name f the last solid created + ) + + # 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 + extrudeObj = self.obj + 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 will not work in general + zAngle = angles[2] + rootRot[2] + rootPos = rotatedPos(rootCurve, extrudeObj.Placement.Rotation) + print(rootPos) + Base = extrudePosition + rootPos - zoffset + + rotX = FreeCAD.Rotation(FreeCAD.Vector(1, 0, 0), angles[0]) + rotY = FreeCAD.Rotation(FreeCAD.Vector(0, 1, 0), angles[1]) + rotZ = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), zAngle) + + rot = rotZ * rotY * rotX + + # rotate back to extrude direction + rot = rot_dir_to_z.inverted() * rot + + placement = FreeCAD.Placement(Base, FreeCAD.Rotation(rot)) + self._position = placement.Base + self._rotation = placement.Rotation + + +class GDMLMeshExporter(GDMLSolidExporter): + # FreeCAD Mesh only supports triangular Facets + def __init__(self, obj): + super().__init__(obj, 'tessellated') + + def export(self): + if self.exported(): + return + super().export() + + tessName = self.name().replace('\r','').replace('(','_').replace(')','_') + # Use more readable version + tessVname = tessName + "_" + tess = ET.SubElement(solids, "tessellated", {"name": tessName}) + placementCorrection = self.obj.Placement.inverse() + for i, v in enumerate(self.obj.Mesh.Points): + v = FreeCAD.Vector(v.x, v.y, v.z) + exportDefineVertex(tessVname, placementCorrection * v, i) + for f in self.obj.Mesh.Facets: + indices = f.PointIndices + i0 = indices[0] + i1 = indices[1] + i2 = indices[2] + ET.SubElement( + tess, + "triangular", + { + "vertex1": tessVname + str(i0), + "vertex2": tessVname + str(i1), + "vertex3": tessVname + str(i2), + "type": "ABSOLUTE", + }, + ) + self._exportScaled() + + +class AutoTessellateExporter(SolidExporter): + shapesDict = {} # a dictionary of exported shapes and their names + + def __init__(self, obj): + super().__init__(obj) + + def export(self): + import MeshPart + + shape = self.obj.Shape.copy(False) + shape.Placement = FreeCAD.Placement() # remove object's placement + alreadyExportedName = AutoTessellateExporter.alreadyExported(shape) + if alreadyExportedName is not None: + self._name = alreadyExportedName + return + + else: + AutoTessellateExporter.shapesDict[shape] = self.name() + + viewObject = self.obj.ViewObject + deflection = viewObject.Deviation + angularDeflection = math.radians(viewObject.AngularDeflection) + mesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=deflection, + AngularDeflection=angularDeflection, Relative=False) + + tessName = self.name() + # Use more readable version + tess = ET.SubElement(solids, "tessellated", {"name": tessName}) + tessVname = tessName + "_" + placementCorrection = self.obj.Placement.inverse() + for i, v in enumerate(mesh.Points): + v = FreeCAD.Vector(v.x, v.y, v.z) + exportDefineVertex(tessVname, v, i) + for f in mesh.Facets: + indices = f.PointIndices + i0 = indices[0] + i1 = indices[1] + i2 = indices[2] + ET.SubElement( + tess, + "triangular", + { + "vertex1": tessVname + str(i0), + "vertex2": tessVname + str(i1), + "vertex3": tessVname + str(i2), + "type": "ABSOLUTE", + }, + ) + self._exportScaled() + + + @staticmethod + def centerOfMass(pts: [Vector]) -> Vector: + cm = Vector(0, 0, 0) + for pt in pts: + cm += pt + + return cm + + @staticmethod + def principalMoments(pts: [Vector]) -> tuple: + Ixx = 0 + Iyy = 0 + Izz = 0 + for pt in pts: + Ixx += pt.y * pt.y + pt.z * pt.z + Iyy += pt.x * pt.x + pt.z * pt.z + Izz += pt.x * pt.x + pt.y * pt.y + + return Ixx, Iyy, Izz + + + @staticmethod + def identicalShapes(shp1, shp2) -> bool: + # return True if shapes are the same + verts1 = shp1.Vertexes + verts2 = shp2.Vertexes + + # Test 1, same number of vertexes + if len(verts1) != len(verts2): + return False + + # Test 2, Center of mass + pts1 = [v.Point for v in shp1.Vertexes] + pts2 = [v.Point for v in shp2.Vertexes] + cm1 = AutoTessellateExporter.centerOfMass(pts1) + cm2 = AutoTessellateExporter.centerOfMass(pts2) + if (cm1 - cm2).Length > 1e-04: + return False + + # Test 3, compare volumes. + # I am not sure which is faster, moment of inertial calculation or volume calculation + # I Shape.Volume is calculated in C it is probably faster than CM and should be done first + # But I don't know for sure + if (shp1.Volume - shp2.Volume) > 1e-6: + return False + + # Test 4, same moments of inertia + II1 = AutoTessellateExporter.principalMoments(pts1) + II2 = AutoTessellateExporter.principalMoments(pts2) + + for i, II in enumerate(II1): + if abs(II1[i] - II2[i]) > 1e-08: + return False + + # Well, ChatGPT says in principle one can have all moments of inertia to be the same for all axes + # and the shapes be different. Dr. Omar Hijab also convinced me of this. + # If all of the above is true, it is likely that the shapes are the same. But to be on + # the safe side, we sample a few of the points and assume they are ordered the same. + # If the shapes are really the same but because the ordering of the points is different then + # at worst we export the same shape twice. This is much better than exporting one shape, when in fact + # they are two different shapes. The likelihood that two different shapes match in order is extremely + # small + + # Test 4 + nsamples = int(len(verts1)/10) + 1 + sampled = set() # to avoid sampling same point twice we keep track of already sampled points + if nsamples >= len(pts1): + nsamples = len(pts1) + + while len(sampled) < nsamples: + index = random.randint(0, nsamples-1) + if (pts2[index]-pts1[index]).Length > 1e-04: + return False + sampled.add(index) + + return True + + @staticmethod + def alreadyExported(shape) -> str | None: + for shp in AutoTessellateExporter.shapesDict: + if AutoTessellateExporter.identicalShapes(shape, shp): + return AutoTessellateExporter.shapesDict[shp] # return name of shape + + return None # shape not already exported +