mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36:20 +00:00
First steps towards a PyQt4 application
This commit is contained in:
@@ -92,6 +92,7 @@ class entity_instance(object):
|
|||||||
def is_a(self, *args): return self.wrapped_data.is_a(*args)
|
def is_a(self, *args): return self.wrapped_data.is_a(*args)
|
||||||
def id(self): return self.wrapped_data.id()
|
def id(self): return self.wrapped_data.id()
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
if type(self) != type(other): return False
|
||||||
return self.wrapped_data == other.wrapped_data
|
return self.wrapped_data == other.wrapped_data
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.id(), self.wrapped_data.file_pointer()))
|
return hash((self.id(), self.wrapped_data.file_pointer()))
|
||||||
@@ -151,3 +152,4 @@ def create_entity(type,*args,**kwargs):
|
|||||||
|
|
||||||
version = ifcopenshell_wrapper.version()
|
version = ifcopenshell_wrapper.version()
|
||||||
schema_identifier = ifcopenshell_wrapper.schema_identifier()
|
schema_identifier = ifcopenshell_wrapper.schema_identifier()
|
||||||
|
get_supertype = ifcopenshell_wrapper.get_supertype
|
||||||
|
|||||||
@@ -1,88 +1,2 @@
|
|||||||
###############################################################################
|
from . import occ_utils as utils
|
||||||
# #
|
from .main import *
|
||||||
# This file is part of IfcOpenShell. #
|
|
||||||
# #
|
|
||||||
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
|
||||||
# it under the terms of the Lesser GNU General Public License as published by #
|
|
||||||
# the Free Software Foundation, either version 3.0 of the License, or #
|
|
||||||
# (at your option) any later version. #
|
|
||||||
# #
|
|
||||||
# IfcOpenShell 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 #
|
|
||||||
# Lesser GNU General Public License for more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the Lesser GNU General Public License #
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
|
||||||
# #
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from .. import ifcopenshell_wrapper
|
|
||||||
|
|
||||||
def has_occ():
|
|
||||||
try: import OCC.BRepTools
|
|
||||||
except: return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
has_occ = has_occ()
|
|
||||||
wrap_shape_creation = lambda settings, shape: shape
|
|
||||||
if has_occ:
|
|
||||||
from . import occ_utils as utils
|
|
||||||
wrap_shape_creation = lambda settings, shape: utils.create_shape_from_serialization(shape) if getattr(settings, 'use_python_opencascade', False) else shape
|
|
||||||
|
|
||||||
|
|
||||||
# Subclass the settings module to provide an additional
|
|
||||||
# setting to enable pythonOCC when available
|
|
||||||
class settings(ifcopenshell_wrapper.settings):
|
|
||||||
if has_occ:
|
|
||||||
USE_PYTHON_OPENCASCADE = -1
|
|
||||||
def set(self, *args):
|
|
||||||
setting, value = args
|
|
||||||
if setting == settings.USE_PYTHON_OPENCASCADE:
|
|
||||||
self.set(settings.USE_BREP_DATA, value)
|
|
||||||
self.set(settings.USE_WORLD_COORDS, value)
|
|
||||||
self.set(settings.DISABLE_TRIANGULATION, value)
|
|
||||||
self.use_python_opencascade = value
|
|
||||||
else:
|
|
||||||
ifcopenshell_wrapper.settings.set(self, *args)
|
|
||||||
|
|
||||||
|
|
||||||
# Hide templating precision to the user by choosing based on Python's
|
|
||||||
# internal float type. This is probably always going to be a double.
|
|
||||||
for ty in (ifcopenshell_wrapper.iterator_single_precision, ifcopenshell_wrapper.iterator_double_precision):
|
|
||||||
if ty.mantissa_size() == sys.float_info.mant_dig:
|
|
||||||
_iterator = ty
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure people are able to use python's platform agnostic paths
|
|
||||||
class iterator(_iterator):
|
|
||||||
def __init__(self, settings, filename):
|
|
||||||
self.settings = settings
|
|
||||||
_iterator.__init__(self, settings, os.path.abspath(filename))
|
|
||||||
if has_occ:
|
|
||||||
def get(self):
|
|
||||||
return wrap_shape_creation(self.settings, _iterator.get(self))
|
|
||||||
|
|
||||||
|
|
||||||
def create_shape(settings, inst, repr=None):
|
|
||||||
return wrap_shape_creation(
|
|
||||||
settings,
|
|
||||||
ifcopenshell_wrapper.create_shape(
|
|
||||||
settings,
|
|
||||||
inst.wrapped_data,
|
|
||||||
repr.wrapped_data if repr is not None else None
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
def iterate(settings, filename):
|
|
||||||
it = iterator(settings, filename)
|
|
||||||
if it.initialize():
|
|
||||||
while True:
|
|
||||||
yield it.get()
|
|
||||||
if not it.next(): break
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,370 @@
|
|||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import operator
|
||||||
|
import functools
|
||||||
|
|
||||||
|
import OCC.AIS
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
|
from collections import defaultdict, Iterable
|
||||||
|
|
||||||
|
from PyQt4 import QtGui, QtCore
|
||||||
|
|
||||||
|
try: from OCC.Display.pyqt4Display import qtViewer3d
|
||||||
|
except:
|
||||||
|
import OCC.Display
|
||||||
|
OCC.Display.backend.get_backend("qt-pyqt4")
|
||||||
|
from OCC.Display.qtDisplay import qtViewer3d
|
||||||
|
|
||||||
|
from .main import create_shape, settings
|
||||||
|
from .occ_utils import display_shape
|
||||||
|
|
||||||
|
# Depending on Python version and what not there may or may not be a QString
|
||||||
|
try:
|
||||||
|
from PyQt4.QtCore import QString
|
||||||
|
except ImportError:
|
||||||
|
QString = str
|
||||||
|
|
||||||
|
class application(QtGui.QApplication):
|
||||||
|
|
||||||
|
"""A pythonOCC, PyQt based IfcOpenShell application
|
||||||
|
with two tree views and a graphical 3d view"""
|
||||||
|
|
||||||
|
class abstract_treeview(QtGui.QTreeWidget):
|
||||||
|
|
||||||
|
"""Base class for the two treeview controls"""
|
||||||
|
|
||||||
|
instanceSelected = QtCore.pyqtSignal([object])
|
||||||
|
instanceVisibilityChanged = QtCore.pyqtSignal([object, int])
|
||||||
|
instanceDisplayModeChanged = QtCore.pyqtSignal([object, int])
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QtGui.QTreeView.__init__(self)
|
||||||
|
self.setColumnCount(len(self.ATTRIBUTES))
|
||||||
|
self.setHeaderLabels(self.ATTRIBUTES)
|
||||||
|
self.children = defaultdict(list)
|
||||||
|
|
||||||
|
def get_children(self, inst):
|
||||||
|
c = [inst]
|
||||||
|
i = 0
|
||||||
|
while i < len(c):
|
||||||
|
c.extend(self.children[c[i]])
|
||||||
|
i += 1
|
||||||
|
return c
|
||||||
|
|
||||||
|
def contextMenuEvent(self, event):
|
||||||
|
menu = QtGui.QMenu(self)
|
||||||
|
visibility = [menu.addAction("Show"), menu.addAction("Hide")]
|
||||||
|
displaymode = [menu.addAction("Solid"), menu.addAction("Wireframe")]
|
||||||
|
action = menu.exec_(self.mapToGlobal(event.pos()))
|
||||||
|
index = self.selectionModel().currentIndex()
|
||||||
|
inst = index.data(QtCore.Qt.UserRole)
|
||||||
|
if hasattr(inst, 'toPyObject'):
|
||||||
|
inst = inst.toPyObject()
|
||||||
|
if action in visibility:
|
||||||
|
self.instanceVisibilityChanged.emit(inst, visibility.index(action))
|
||||||
|
elif action in displaymode:
|
||||||
|
self.instanceDisplayModeChanged.emit(inst, displaymode.index(action))
|
||||||
|
|
||||||
|
def clicked(self, index):
|
||||||
|
inst = index.data(QtCore.Qt.UserRole)
|
||||||
|
if hasattr(inst, 'toPyObject'):
|
||||||
|
inst = inst.toPyObject()
|
||||||
|
if inst:
|
||||||
|
self.instanceSelected.emit(inst)
|
||||||
|
|
||||||
|
def select(self, product):
|
||||||
|
itm = self.product_to_item.get(product)
|
||||||
|
if itm is None: return
|
||||||
|
self.selectionModel().setCurrentIndex(itm, QtGui.QItemSelectionModel.SelectCurrent | QtGui.QItemSelectionModel.Rows);
|
||||||
|
|
||||||
|
class decomposition_treeview(abstract_treeview):
|
||||||
|
|
||||||
|
"""Treeview with typical IFC decomposition relationships"""
|
||||||
|
|
||||||
|
ATTRIBUTES = ['Entity', 'GlobalId', 'Name']
|
||||||
|
|
||||||
|
def parent(self, instance):
|
||||||
|
if instance.is_a("IfcOpeningElement"):
|
||||||
|
return instance.VoidsElements[0].RelatingBuildingElement
|
||||||
|
if instance.is_a("IfcElement"):
|
||||||
|
fills = instance.FillsVoids
|
||||||
|
if len(fills):
|
||||||
|
return fills[0].RelatingOpeningElement
|
||||||
|
containments = instance.ContainedInStructure
|
||||||
|
if len(containments):
|
||||||
|
return containments[0].RelatingStructure
|
||||||
|
if instance.is_a("IfcObjectDefinition"):
|
||||||
|
decompositions = instance.Decomposes
|
||||||
|
if len(decompositions):
|
||||||
|
return decompositions[0].RelatingObject
|
||||||
|
|
||||||
|
def load_file(self, f):
|
||||||
|
products = list(f.by_type("IfcProduct")) + list(f.by_type("IfcProject"))
|
||||||
|
parents = list(map(self.parent, products))
|
||||||
|
items = {}
|
||||||
|
skipped = 0
|
||||||
|
ATTRS = self.ATTRIBUTES
|
||||||
|
while len(items) + skipped < len(products):
|
||||||
|
for product, parent in zip(products, parents):
|
||||||
|
if parent is None and not product.is_a("IfcProject"):
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
if (parent is None or parent in items) and product not in items:
|
||||||
|
sl = []
|
||||||
|
for attr in ATTRS:
|
||||||
|
if attr == 'Entity':
|
||||||
|
sl.append(product.is_a())
|
||||||
|
else:
|
||||||
|
sl.append(getattr(product, attr) or '')
|
||||||
|
itm = items[product] = QtGui.QTreeWidgetItem(items.get(parent, self), sl)
|
||||||
|
itm.setData(0, QtCore.Qt.UserRole, product)
|
||||||
|
self.children[parent].append(product)
|
||||||
|
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
|
||||||
|
self.connect(self, QtCore.SIGNAL("clicked(const QModelIndex &)"), self.clicked)
|
||||||
|
self.expandAll()
|
||||||
|
|
||||||
|
class type_treeview(abstract_treeview):
|
||||||
|
|
||||||
|
"""Treeview with typical IFC decomposition relationships"""
|
||||||
|
|
||||||
|
ATTRIBUTES = ['Name']
|
||||||
|
|
||||||
|
def load_file(self, f):
|
||||||
|
products = list(f.by_type("IfcProduct"))
|
||||||
|
types = set(map(lambda i: i.is_a(), products))
|
||||||
|
items = {}
|
||||||
|
for t in types:
|
||||||
|
def add(t):
|
||||||
|
s = ifcopenshell.get_supertype(t)
|
||||||
|
if s: add(s)
|
||||||
|
s2, t2 = map(QString, (s,t))
|
||||||
|
if t2 not in items:
|
||||||
|
itm = items[t2] = QtGui.QTreeWidgetItem(items.get(s2, self), [t2])
|
||||||
|
itm.setData(0, QtCore.Qt.UserRole, t2)
|
||||||
|
self.children[s2].append(t2)
|
||||||
|
add(t)
|
||||||
|
|
||||||
|
for p in products:
|
||||||
|
t = QString(p.is_a())
|
||||||
|
itm = items[p] = QtGui.QTreeWidgetItem(items.get(t, self), [p.Name or '<no name>'])
|
||||||
|
itm.setData(0, QtCore.Qt.UserRole, t)
|
||||||
|
self.children[t].append(p)
|
||||||
|
|
||||||
|
self.product_to_item = dict(zip(items.keys(), map(self.indexFromItem, items.values())))
|
||||||
|
self.connect(self, QtCore.SIGNAL("clicked(const QModelIndex &)"), self.clicked)
|
||||||
|
self.expandAll()
|
||||||
|
|
||||||
|
class viewer(qtViewer3d):
|
||||||
|
|
||||||
|
instanceSelected = QtCore.pyqtSignal([object])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ais_to_key(ais_handle):
|
||||||
|
def yield_shapes():
|
||||||
|
ais = ais_handle.GetObject()
|
||||||
|
if hasattr(ais, 'Shape'):
|
||||||
|
yield ais.Shape()
|
||||||
|
return
|
||||||
|
shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle)
|
||||||
|
if not shp.IsNull(): yield shp.Shape()
|
||||||
|
return
|
||||||
|
mult = ais_handle
|
||||||
|
if mult.IsNull():
|
||||||
|
shp = OCC.AIS.Handle_AIS_Shape.DownCast(ais_handle)
|
||||||
|
if not shp.IsNull(): yield shp
|
||||||
|
else:
|
||||||
|
li = mult.GetObject().ConnectedTo()
|
||||||
|
for i in range(li.Length()):
|
||||||
|
shp = OCC.AIS.Handle_AIS_Shape.DownCast(li.Value(i+1))
|
||||||
|
if not shp.IsNull(): yield shp
|
||||||
|
return tuple(shp.HashCode(1 << 24) for shp in yield_shapes())
|
||||||
|
|
||||||
|
def __init__(self, widget):
|
||||||
|
qtViewer3d.__init__(self, widget)
|
||||||
|
self.ais_to_product = {}
|
||||||
|
self.product_to_ais = {}
|
||||||
|
self.counter = 0
|
||||||
|
self.window = widget
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.InitDriver()
|
||||||
|
self._display.Select = self.HandleSelection
|
||||||
|
|
||||||
|
def load_file(self, f):
|
||||||
|
|
||||||
|
s = settings()
|
||||||
|
s.set(s.USE_PYTHON_OPENCASCADE, True)
|
||||||
|
|
||||||
|
v = self._display
|
||||||
|
|
||||||
|
t = {0: time.time()}
|
||||||
|
def update(dt = None):
|
||||||
|
t1 = time.time()
|
||||||
|
if t1 - t[0] > (dt or -1):
|
||||||
|
v.FitAll()
|
||||||
|
v.Repaint()
|
||||||
|
t[0] = t1
|
||||||
|
|
||||||
|
terminate = [False]
|
||||||
|
self.window.window_closed.connect(lambda *args: operator.setitem(terminate, 0, True))
|
||||||
|
|
||||||
|
for p in f.by_type("IfcProduct"):
|
||||||
|
if terminate[0]: break
|
||||||
|
if p.Representation is None: continue
|
||||||
|
shape = create_shape(s, p)
|
||||||
|
ais = display_shape(shape, viewer_handle=v)
|
||||||
|
ais.GetObject().SetSelectionPriority(self.counter)
|
||||||
|
self.ais_to_product[self.counter] = p
|
||||||
|
self.product_to_ais[p] = ais
|
||||||
|
self.counter += 1
|
||||||
|
QtGui.QApplication.processEvents()
|
||||||
|
if p.is_a() in {'IfcSpace', 'IfcOpeningElement'}:
|
||||||
|
v.Context.Erase(ais, True)
|
||||||
|
update(0.1)
|
||||||
|
update()
|
||||||
|
|
||||||
|
def select(self, product):
|
||||||
|
ais = self.product_to_ais.get(product)
|
||||||
|
if ais is None: return
|
||||||
|
v = self._display.Context
|
||||||
|
v.ClearSelected(False)
|
||||||
|
v.SetSelected(ais, True)
|
||||||
|
|
||||||
|
def toggle(self, product_or_products, fn):
|
||||||
|
if not isinstance(product_or_products, Iterable):
|
||||||
|
product_or_products = [product_or_products]
|
||||||
|
aiss = list(filter(None, map(self.product_to_ais.get, product_or_products)))
|
||||||
|
last = len(aiss) - 1
|
||||||
|
for i, ais in enumerate(aiss):
|
||||||
|
fn(ais, i == last)
|
||||||
|
|
||||||
|
def toggle_visibility(self, product_or_products, flag):
|
||||||
|
v = self._display.Context
|
||||||
|
if flag:
|
||||||
|
def visibility(ais, last):
|
||||||
|
v.Erase(ais, last)
|
||||||
|
else:
|
||||||
|
def visibility(ais, last):
|
||||||
|
v.Display(ais, last)
|
||||||
|
self.toggle(product_or_products, visibility)
|
||||||
|
|
||||||
|
def toggle_wireframe(self, product_or_products, flag):
|
||||||
|
v = self._display.Context
|
||||||
|
if flag:
|
||||||
|
def wireframe(ais, last):
|
||||||
|
if v.IsDisplayed(ais):
|
||||||
|
v.SetDisplayMode(ais, 0, last)
|
||||||
|
else:
|
||||||
|
def wireframe(ais, last):
|
||||||
|
if v.IsDisplayed(ais):
|
||||||
|
v.SetDisplayMode(ais, 1, last)
|
||||||
|
self.toggle(product_or_products, wireframe)
|
||||||
|
|
||||||
|
def HandleSelection(self, X, Y):
|
||||||
|
v = self._display.Context
|
||||||
|
v.Select()
|
||||||
|
v.InitSelected()
|
||||||
|
if v.MoreSelected():
|
||||||
|
ais = v.SelectedInteractive()
|
||||||
|
inst = self.ais_to_product[ais.GetObject().SelectionPriority()]
|
||||||
|
self.instanceSelected.emit(inst)
|
||||||
|
|
||||||
|
class window(QtGui.QMainWindow):
|
||||||
|
|
||||||
|
TITLE = "IfcOpenShell IFC viewer"
|
||||||
|
|
||||||
|
window_closed = QtCore.pyqtSignal([])
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QtGui.QMainWindow.__init__(self)
|
||||||
|
self.setWindowTitle(self.TITLE)
|
||||||
|
self.menu = self.menuBar()
|
||||||
|
self.menus = {}
|
||||||
|
|
||||||
|
def closeEvent(self, *args):
|
||||||
|
self.window_closed.emit()
|
||||||
|
|
||||||
|
def add_menu_item(self, menu, label, callback, icon=None, shortcut=None):
|
||||||
|
m = self.menus.get(menu)
|
||||||
|
if m is None:
|
||||||
|
m = self.menu.addMenu(menu)
|
||||||
|
self.menus[menu] = m
|
||||||
|
|
||||||
|
if icon:
|
||||||
|
a = QtGui.QAction(QtGui.QIcon(icon), label, self)
|
||||||
|
else:
|
||||||
|
a = QtGui.QAction(label, self)
|
||||||
|
|
||||||
|
if shortcut:
|
||||||
|
a.setShortcut(shortcut)
|
||||||
|
|
||||||
|
a.triggered.connect(callback)
|
||||||
|
m.addAction(a)
|
||||||
|
|
||||||
|
|
||||||
|
def makeSelectionHandler(self, component):
|
||||||
|
def handler(inst):
|
||||||
|
for c in self.components:
|
||||||
|
if c != component:
|
||||||
|
c.select(inst)
|
||||||
|
return handler
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QtGui.QApplication.__init__(self, sys.argv)
|
||||||
|
self.window = application.window()
|
||||||
|
self.tree = application.decomposition_treeview()
|
||||||
|
self.tree2 = application.type_treeview()
|
||||||
|
self.canvas = application.viewer(self.window)
|
||||||
|
self.tabs = QtGui.QTabWidget()
|
||||||
|
self.window.resize(800, 600)
|
||||||
|
splitter = QtGui.QSplitter(QtCore.Qt.Horizontal)
|
||||||
|
splitter.addWidget(self.tabs)
|
||||||
|
self.tabs.addTab(self.tree, 'Decomposition')
|
||||||
|
self.tabs.addTab(self.tree2, 'Types')
|
||||||
|
splitter.addWidget(self.canvas)
|
||||||
|
splitter.setSizes([200,600])
|
||||||
|
self.window.setCentralWidget(splitter)
|
||||||
|
self.canvas.initialize()
|
||||||
|
self.components = [self.tree, self.tree2, self.canvas]
|
||||||
|
self.files = {}
|
||||||
|
|
||||||
|
self.window.add_menu_item('File', '&Open', self.browse, shortcut='CTRL+O')
|
||||||
|
self.window.add_menu_item('File', '&Close', self.clear, shortcut='CTRL+W')
|
||||||
|
self.window.add_menu_item('File', '&Exit', self.window.close, shortcut='ALT+F4')
|
||||||
|
|
||||||
|
self.tree.instanceSelected.connect(self.makeSelectionHandler(self.tree))
|
||||||
|
self.tree2.instanceSelected.connect(self.makeSelectionHandler(self.tree2))
|
||||||
|
self.canvas.instanceSelected.connect(self.makeSelectionHandler(self.canvas))
|
||||||
|
for t in [self.tree, self.tree2]:
|
||||||
|
t.instanceVisibilityChanged.connect(functools.partial(self.change_visibility, t))
|
||||||
|
t.instanceDisplayModeChanged.connect(functools.partial(self.change_displaymode, t))
|
||||||
|
|
||||||
|
def change_visibility(self, tree, inst, flag):
|
||||||
|
insts = tree.get_children(inst)
|
||||||
|
self.canvas.toggle_visibility(insts, flag)
|
||||||
|
|
||||||
|
def change_displaymode(self, tree, inst, flag):
|
||||||
|
insts = tree.get_children(inst)
|
||||||
|
self.canvas.toggle_wireframe(insts, flag)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.window.show()
|
||||||
|
sys.exit(self.exec_())
|
||||||
|
|
||||||
|
def browse(self):
|
||||||
|
filename = QtGui.QFileDialog.getOpenFileName(self.window, 'Open file',".","Industry Foundation Classes (*.ifc)")
|
||||||
|
self.load(filename)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.canvas._display.Context.RemoveAll()
|
||||||
|
self.tree.clear()
|
||||||
|
self.files.clear()
|
||||||
|
|
||||||
|
def load(self, fn):
|
||||||
|
if fn in self.files: return
|
||||||
|
f = ifcopenshell.open(fn)
|
||||||
|
self.files[fn] = f
|
||||||
|
for c in self.components:
|
||||||
|
c.load_file(f)
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
###############################################################################
|
||||||
|
# #
|
||||||
|
# This file is part of IfcOpenShell. #
|
||||||
|
# #
|
||||||
|
# IfcOpenShell is free software: you can redistribute it and/or modify #
|
||||||
|
# it under the terms of the Lesser GNU General Public License as published by #
|
||||||
|
# the Free Software Foundation, either version 3.0 of the License, or #
|
||||||
|
# (at your option) any later version. #
|
||||||
|
# #
|
||||||
|
# IfcOpenShell 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 #
|
||||||
|
# Lesser GNU General Public License for more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the Lesser GNU General Public License #
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||||
|
# #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from .. import ifcopenshell_wrapper
|
||||||
|
|
||||||
|
def has_occ():
|
||||||
|
try: import OCC.BRepTools
|
||||||
|
except: return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
has_occ = has_occ()
|
||||||
|
wrap_shape_creation = lambda settings, shape: shape
|
||||||
|
if has_occ:
|
||||||
|
from . import occ_utils as utils
|
||||||
|
wrap_shape_creation = lambda settings, shape: utils.create_shape_from_serialization(shape) if getattr(settings, 'use_python_opencascade', False) else shape
|
||||||
|
|
||||||
|
# Subclass the settings module to provide an additional
|
||||||
|
# setting to enable pythonOCC when available
|
||||||
|
class settings(ifcopenshell_wrapper.settings):
|
||||||
|
if has_occ:
|
||||||
|
USE_PYTHON_OPENCASCADE = -1
|
||||||
|
def set(self, *args):
|
||||||
|
setting, value = args
|
||||||
|
if setting == settings.USE_PYTHON_OPENCASCADE:
|
||||||
|
self.set(settings.USE_BREP_DATA, value)
|
||||||
|
self.set(settings.USE_WORLD_COORDS, value)
|
||||||
|
self.set(settings.DISABLE_TRIANGULATION, value)
|
||||||
|
self.use_python_opencascade = value
|
||||||
|
else:
|
||||||
|
ifcopenshell_wrapper.settings.set(self, *args)
|
||||||
|
|
||||||
|
# Hide templating precision to the user by choosing based on Python's
|
||||||
|
# internal float type. This is probably always going to be a double.
|
||||||
|
for ty in (ifcopenshell_wrapper.iterator_single_precision, ifcopenshell_wrapper.iterator_double_precision):
|
||||||
|
if ty.mantissa_size() == sys.float_info.mant_dig:
|
||||||
|
_iterator = ty
|
||||||
|
|
||||||
|
|
||||||
|
# Make sure people are able to use python's platform agnostic paths
|
||||||
|
class iterator(_iterator):
|
||||||
|
def __init__(self, settings, filename):
|
||||||
|
self.settings = settings
|
||||||
|
_iterator.__init__(self, settings, os.path.abspath(filename))
|
||||||
|
if has_occ:
|
||||||
|
def get(self):
|
||||||
|
return wrap_shape_creation(self.settings, _iterator.get(self))
|
||||||
|
|
||||||
|
|
||||||
|
def create_shape(settings, inst, repr=None):
|
||||||
|
return wrap_shape_creation(
|
||||||
|
settings,
|
||||||
|
ifcopenshell_wrapper.create_shape(
|
||||||
|
settings,
|
||||||
|
inst.wrapped_data,
|
||||||
|
repr.wrapped_data if repr is not None else None
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def iterate(settings, filename):
|
||||||
|
it = iterator(settings, filename)
|
||||||
|
if it.initialize():
|
||||||
|
while True:
|
||||||
|
yield it.get()
|
||||||
|
if not it.next(): break
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +81,9 @@ def yield_subshapes(shape):
|
|||||||
yield it.Value()
|
yield it.Value()
|
||||||
it.Next()
|
it.Next()
|
||||||
|
|
||||||
def display_shape(shape, clr=None):
|
def display_shape(shape, clr=None, viewer_handle=None):
|
||||||
|
if viewer_handle is None: viewer_handle = handle
|
||||||
|
|
||||||
if isinstance(shape, shape_tuple):
|
if isinstance(shape, shape_tuple):
|
||||||
shape, representation = shape.geometry, shape
|
shape, representation = shape.geometry, shape
|
||||||
else: representation is None
|
else: representation is None
|
||||||
@@ -145,9 +147,11 @@ def display_shape(shape, clr=None):
|
|||||||
if len(default_style_applied) == 3: default_style_applied += (1.,)
|
if len(default_style_applied) == 3: default_style_applied += (1.,)
|
||||||
applied_styles += (default_style_applied,)
|
applied_styles += (default_style_applied,)
|
||||||
|
|
||||||
min_transp = min(map(operator.itemgetter(3), applied_styles))
|
if len(applied_styles):
|
||||||
if min_transp < 1.:
|
# The only way for this not to be true if is the entire shape is NULL
|
||||||
ais.SetTransparency(1.)
|
min_transp = min(map(operator.itemgetter(3), applied_styles))
|
||||||
|
if min_transp < 1.:
|
||||||
|
ais.SetTransparency(1.)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ais = OCC.AIS.AIS_Shape(shape)
|
ais = OCC.AIS.AIS_Shape(shape)
|
||||||
@@ -158,7 +162,7 @@ def display_shape(shape, clr=None):
|
|||||||
ais.SetColor(clr)
|
ais.SetColor(clr)
|
||||||
|
|
||||||
ais_handle = ais.GetHandle()
|
ais_handle = ais.GetHandle()
|
||||||
handle.Context.Display(ais_handle, False)
|
viewer_handle.Context.Display(ais_handle, False)
|
||||||
|
|
||||||
return ais_handle
|
return ais_handle
|
||||||
|
|
||||||
|
|||||||
@@ -220,4 +220,15 @@ namespace IfcUtil {
|
|||||||
const char* const version() {
|
const char* const version() {
|
||||||
return IFCOPENSHELL_VERSION;
|
return IFCOPENSHELL_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_supertype(std::string n) {
|
||||||
|
boost::to_upper(n);
|
||||||
|
/// @todo: Redo without copy once Parent() function from performance_improvements branch is in.
|
||||||
|
IfcSchema::Type::Enum t = IfcSchema::Type::FromString(n);
|
||||||
|
if (IfcSchema::Type::Parent(t) != -1) {
|
||||||
|
return IfcSchema::Type::ToString(IfcSchema::Type::Parent(t));
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
%}
|
%}
|
||||||
Reference in New Issue
Block a user