├── icons ├── abcl.gif ├── acl.gif ├── cc3p.gif ├── circ.gif ├── hcl.gif ├── hvcl.gif ├── join.gif ├── lbcl.gif ├── line.gif ├── poly.gif ├── rect.gif ├── sep.gif ├── slot.gif ├── tpcl.gif ├── vcl.gif ├── arc3p.gif ├── arcc2p.gif ├── array.gif ├── cccirc.gif ├── ccirc.gif ├── cctan2.gif ├── cctan3.gif ├── cltan1.gif ├── cltan2.gif ├── del_c.gif ├── del_el.gif ├── del_g.gif ├── fillet.gif ├── parcl.gif ├── perpcl.gif ├── rotate.gif ├── split.gif ├── refangcl.gif ├── stretch.gif └── translate.gif ├── OCCUtils ├── __init__.py ├── wire.py ├── solid.py ├── Image.py ├── shell.py ├── vertex.py ├── Iteration.py ├── base.py ├── types_lut.py ├── face.py ├── edge.py └── Topology.py ├── treelib ├── exceptions.py ├── plugins.py ├── __init__.py └── node.py ├── misc ├── circleexample.py ├── myqtDisplay.py ├── example.py ├── example1.py ├── buildFaceBottomUp.py ├── tkrpncalc.py ├── core_topology_local_ops.py └── bottle.py ├── waysToMoveShape.txt ├── display.txt ├── README.md ├── sew.py ├── myStepXcafReader.py ├── step └── tiltedCup.stp └── unusedDynamic.py /icons/abcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/abcl.gif -------------------------------------------------------------------------------- /icons/acl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/acl.gif -------------------------------------------------------------------------------- /icons/cc3p.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cc3p.gif -------------------------------------------------------------------------------- /icons/circ.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/circ.gif -------------------------------------------------------------------------------- /icons/hcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/hcl.gif -------------------------------------------------------------------------------- /icons/hvcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/hvcl.gif -------------------------------------------------------------------------------- /icons/join.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/join.gif -------------------------------------------------------------------------------- /icons/lbcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/lbcl.gif -------------------------------------------------------------------------------- /icons/line.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/line.gif -------------------------------------------------------------------------------- /icons/poly.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/poly.gif -------------------------------------------------------------------------------- /icons/rect.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/rect.gif -------------------------------------------------------------------------------- /icons/sep.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/sep.gif -------------------------------------------------------------------------------- /icons/slot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/slot.gif -------------------------------------------------------------------------------- /icons/tpcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/tpcl.gif -------------------------------------------------------------------------------- /icons/vcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/vcl.gif -------------------------------------------------------------------------------- /icons/arc3p.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/arc3p.gif -------------------------------------------------------------------------------- /icons/arcc2p.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/arcc2p.gif -------------------------------------------------------------------------------- /icons/array.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/array.gif -------------------------------------------------------------------------------- /icons/cccirc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cccirc.gif -------------------------------------------------------------------------------- /icons/ccirc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/ccirc.gif -------------------------------------------------------------------------------- /icons/cctan2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cctan2.gif -------------------------------------------------------------------------------- /icons/cctan3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cctan3.gif -------------------------------------------------------------------------------- /icons/cltan1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cltan1.gif -------------------------------------------------------------------------------- /icons/cltan2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/cltan2.gif -------------------------------------------------------------------------------- /icons/del_c.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/del_c.gif -------------------------------------------------------------------------------- /icons/del_el.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/del_el.gif -------------------------------------------------------------------------------- /icons/del_g.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/del_g.gif -------------------------------------------------------------------------------- /icons/fillet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/fillet.gif -------------------------------------------------------------------------------- /icons/parcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/parcl.gif -------------------------------------------------------------------------------- /icons/perpcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/perpcl.gif -------------------------------------------------------------------------------- /icons/rotate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/rotate.gif -------------------------------------------------------------------------------- /icons/split.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/split.gif -------------------------------------------------------------------------------- /icons/refangcl.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/refangcl.gif -------------------------------------------------------------------------------- /icons/stretch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/stretch.gif -------------------------------------------------------------------------------- /icons/translate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tpaviot/cadviewer/master/icons/translate.gif -------------------------------------------------------------------------------- /OCCUtils/__init__.py: -------------------------------------------------------------------------------- 1 | from OCCUtils.Common import get_boundingbox 2 | from OCCUtils.Topology import Topo 3 | -------------------------------------------------------------------------------- /treelib/exceptions.py: -------------------------------------------------------------------------------- 1 | class NodePropertyError(Exception): 2 | """Basic Node attribute error""" 3 | pass 4 | 5 | 6 | class NodeIDAbsentError(NodePropertyError): 7 | """Exception throwed if a node's identifier is unknown""" 8 | pass 9 | 10 | 11 | class NodePropertyAbsentError(NodePropertyError): 12 | """Exception throwed if a node's data property is not specified""" 13 | pass 14 | 15 | 16 | class MultipleRootError(Exception): 17 | """Exception throwed if more than one root exists in a tree.""" 18 | pass 19 | 20 | 21 | class DuplicatedNodeIdError(Exception): 22 | """Exception throwed if an identifier already exists in a tree.""" 23 | pass 24 | 25 | 26 | class LinkPastRootNodeError(Exception): 27 | """ 28 | Exception throwed in Tree.link_past_node() if one attempts 29 | to "link past" the root node of a tree. 30 | """ 31 | pass 32 | 33 | 34 | class InvalidLevelNumber(Exception): 35 | pass 36 | 37 | 38 | class LoopError(Exception): 39 | """ 40 | Exception thrown if trying to move node B to node A's position 41 | while A is B's ancestor. 42 | """ 43 | pass 44 | 45 | 46 | -------------------------------------------------------------------------------- /misc/circleexample.py: -------------------------------------------------------------------------------- 1 | from OCC.gp import gp_Pnt, gp_Pnt2d, gp_OX2d 2 | from OCC.Geom2d import Geom2d_Circle 3 | from OCC.Geom2dAdaptor import Geom2dAdaptor_Curve 4 | from OCC.GCPnts import GCPnts_UniformAbscissa 5 | 6 | from OCC.Display.SimpleGui import init_display 7 | display, start_display, add_menu, add_function_to_menu = init_display() 8 | 9 | 10 | def points_from_curve(): 11 | radius = 5. 12 | abscissa = 3. 13 | circle = Geom2d_Circle(gp_OX2d(), radius, True) 14 | gac = Geom2dAdaptor_Curve(circle.GetHandle()) 15 | ua = GCPnts_UniformAbscissa(gac, abscissa) 16 | a_sequence = [] 17 | if ua.IsDone(): 18 | n = ua.NbPoints() 19 | for count in range(1, n + 1): 20 | p = gp_Pnt2d() 21 | circle.D0(ua.Parameter(count), p) 22 | a_sequence.append(p) 23 | # convert analytic to bspline 24 | display.DisplayShape(circle, update=True) 25 | i = 0 26 | for p in a_sequence: 27 | i = i + 1 28 | pstring = 'P%i : parameter %f' % (i, ua.Parameter(i)) 29 | pnt = gp_Pnt(p.X(), p.Y(), 0) 30 | # display points 31 | display.DisplayShape(pnt, update=True) 32 | display.DisplayMessage(pnt, pstring) 33 | 34 | if __name__ == '__main__': 35 | points_from_curve() 36 | start_display() 37 | -------------------------------------------------------------------------------- /treelib/plugins.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # Copyright (C) 2011 4 | # Brett Alistair Kromkamp - brettkromkamp@gmail.com 5 | # Copyright (C) 2012-2017 6 | # Xiaming Chen - chenxm35@gmail.com 7 | # and other contributors. 8 | # All rights reserved. 9 | # 10 | # Licensed under the Apache License, Version 2.0 (the "License"); 11 | # you may not use this file except in compliance with the License. 12 | # You may obtain a copy of the License at 13 | # 14 | # http://www.apache.org/licenses/LICENSE-2.0 15 | # 16 | # Unless required by applicable law or agreed to in writing, software 17 | # distributed under the License is distributed on an "AS IS" BASIS, 18 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | # See the License for the specific language governing permissions and 20 | # limitations under the License. 21 | """ 22 | This is a public location to maintain contributed 23 | utilities to extend the basic Tree class. 24 | 25 | Deprecated! We prefer a unified processing of Tree object. 26 | """ 27 | from __future__ import unicode_literals 28 | 29 | import codecs 30 | 31 | 32 | def export_to_dot(tree, filename=None, shape='circle', graph='digraph'): 33 | """Exports the tree in the dot format of the graphviz software""" 34 | print('Deprecated module. Use `tree.to_graphviz()` instead.') 35 | tree.to_graphviz(filename=filename, shape=shape, graph=graph) 36 | -------------------------------------------------------------------------------- /waysToMoveShape.txt: -------------------------------------------------------------------------------- 1 | Ways to move a TopoDS_shape: 2 | 3 | # TopLoc_Location 4 | # as shown in 5 | tFace = BRepBuilderAPI_MakeFace(mFace).Face() 6 | faceNormal = Construct.face_normal(mFace) 7 | vctr = gp_Vec(faceNormal).Multiplied(value) 8 | trsf = gp_Trsf() 9 | trsf.SetTranslation(vctr) 10 | tFace.Move(TopLoc_Location(trsf)) 11 | 12 | # BRepBuilderAPI_Transform 13 | # as shown in core_topology_boolean.py 14 | 15 | def translate_topods_from_vector(brep_or_iterable, vec, copy=False): 16 | ''' 17 | translate a brep over a vector 18 | @param brep: the Topo_DS to translate 19 | @param vec: the vector defining the translation 20 | @param copy: copies to brep if True 21 | ''' 22 | trns = gp_Trsf() 23 | trns.SetTranslation(vec) 24 | brep_trns = BRepBuilderAPI_Transform(brep_or_iterable, trns, copy) 25 | brep_trns.Build() 26 | return brep_trns.Shape() 27 | 28 | 29 | # as shown in core_classic_occ_bottle.py 30 | # Create a wire out of the edges 31 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), aEdge2.Edge(), aEdge3.Edge()) 32 | 33 | # Quick way to specify the X axis 34 | xAxis = gp_OX() 35 | 36 | # Set up the mirror 37 | aTrsf = gp_Trsf() 38 | aTrsf.SetMirror(xAxis) 39 | 40 | # Apply the mirror transformation 41 | aBRespTrsf = BRepBuilderAPI_Transform(aWire.Wire(), aTrsf) 42 | 43 | # Get the mirrored shape back out of the transformation and convert back to a wire 44 | aMirroredShape = aBRespTrsf.Shape() 45 | 46 | # A wire instead of a generic shape now 47 | aMirroredWire = topods.Wire(aMirroredShape) 48 | 49 | -------------------------------------------------------------------------------- /OCCUtils/wire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ##Copyright 2008-2013 Jelle Feringa (jelleferinga@gmail.com) 4 | ## 5 | ##This file is part of pythonOCC. 6 | ## 7 | ##pythonOCC is free software: you can redistribute it and/or modify 8 | ##it under the terms of the GNU Lesser General Public License as published by 9 | ##the Free Software Foundation, either version 3 of the License, or 10 | ##(at your option) any later version. 11 | ## 12 | ##pythonOCC is distributed in the hope that it will be useful, 13 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ##GNU Lesser General Public License for more details. 16 | ## 17 | ##You should have received a copy of the GNU Lesser General Public License 18 | ##along with pythonOCC. If not, see 19 | 20 | from OCC.Core.TopoDS import TopoDS_Wire 21 | 22 | from OCCUtils.base import BaseObject 23 | 24 | 25 | class Wire(TopoDS_Wire, BaseObject): 26 | def __init__(self, wire): 27 | ''' 28 | ''' 29 | assert isinstance(wire, TopoDS_Wire), 'need a TopoDS_Wire, got a %s' % wire.__class__ 30 | assert not wire.IsNull() 31 | super(Wire, self).__init__() 32 | BaseObject.__init__(self, 'wire') 33 | # we need to copy the base shape using the following three 34 | # lines 35 | assert self.IsNull() 36 | self.TShape(wire.TShape()) 37 | self.Location(wire.Location()) 38 | self.Orientation(wire.Orientation()) 39 | assert not self.IsNull() 40 | -------------------------------------------------------------------------------- /display.txt: -------------------------------------------------------------------------------- 1 | Context 2 | Context_handle 3 | Create 4 | DisableAntiAliasing 5 | DisplayColoredShape 6 | DisplayMessage 7 | DisplayShape 8 | DisplayVector 9 | DynamicZoom 10 | EnableAntiAliasing 11 | EraseAll 12 | ExportToImage 13 | FitAll 14 | GetContext 15 | GetSelectedShape 16 | GetSelectedShapes 17 | GetView 18 | GetViewer 19 | Init 20 | MoveTo 21 | OnResize 22 | Pan 23 | Repaint 24 | ResetView 25 | Rotation 26 | Select 27 | SelectArea 28 | SetBackgroundImage 29 | SetDoubleBuffer 30 | SetModeHLR 31 | SetModeShaded 32 | SetModeWireFrame 33 | SetOrthographic 34 | SetSelectionMode 35 | SetSelectionModeEdge 36 | SetSelectionModeFace 37 | SetSelectionModeNeutral 38 | SetSelectionModeShape 39 | SetSelectionModeVertex 40 | ShiftSelect 41 | StartRotation 42 | Test 43 | Tumble 44 | View 45 | View_Bottom 46 | View_Front 47 | View_Iso 48 | View_Left 49 | View_Rear 50 | View_Right 51 | View_Top 52 | View_handle 53 | Viewer 54 | Viewer_handle 55 | Zoom 56 | ZoomArea 57 | ZoomFactor 58 | __class__ 59 | __delattr__ 60 | __dict__ 61 | __doc__ 62 | __format__ 63 | __getattribute__ 64 | __hash__ 65 | __init__ 66 | __module__ 67 | __new__ 68 | __reduce__ 69 | __reduce_ex__ 70 | __repr__ 71 | __setattr__ 72 | __sizeof__ 73 | __str__ 74 | __subclasshook__ 75 | __swig_destroy__ 76 | __weakref__ 77 | _inited 78 | _local_context_opened 79 | _select_callbacks 80 | _struc_mgr 81 | _window_handle 82 | display_graduated_trihedron 83 | display_trihedron 84 | register_select_callback 85 | selected_shape 86 | selected_shapes 87 | set_bg_gradient_color 88 | set_raytracing_mode 89 | this 90 | thisown 91 | unregister_callback 92 | -------------------------------------------------------------------------------- /treelib/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 2 | # Brett Alistair Kromkamp - brettkromkamp@gmail.com 3 | # Copyright (C) 2012-2017 4 | # Xiaming Chen - chenxm35@gmail.com 5 | # and other contributors. 6 | # All rights reserved. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | """ 20 | treelib - Python 2/3 Tree Implementation 21 | 22 | `treelib` is a Python module with two primary classes: Node and Tree. 23 | Tree is a self-contained structure with some nodes and connected by branches. 24 | A tree owns merely a root, while a node (except root) has some children and one parent. 25 | 26 | Note: To solve string compatibility between Python 2.x and 3.x, treelib follows 27 | the way of porting Python 3.x to 2/3. That means, all strings are manipulated as 28 | unicode and you do not need u'' prefix anymore. The impacted functions include `str()`, 29 | `show()` and `save2file()` routines. 30 | But if your data contains non-ascii characters and Python 2.x is used, 31 | you have to trigger the compatibility by declaring `unicode_literals` in the code: 32 | 33 | .. code-block:: python 34 | 35 | >>> from __future__ import unicode_literals 36 | """ 37 | __version__ = '1.5.5' 38 | 39 | from .tree import Tree 40 | from .node import Node 41 | -------------------------------------------------------------------------------- /OCCUtils/solid.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2013 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.Core.TopoDS import TopoDS_Solid 19 | 20 | from OCCUtils.Topology import Topo 21 | from OCCUtils.base import GlobalProperties, BaseObject 22 | from OCCUtils.shell import Shell 23 | 24 | 25 | class Solid(TopoDS_Solid, BaseObject): 26 | def __init__(self, solid): 27 | assert isinstance(solid, TopoDS_Solid), 'need a TopoDS_Solid, got a %s' % solid.__class__ 28 | assert not solid.IsNull() 29 | super(Solid, self).__init__() 30 | BaseObject.__init__(self, 'solid') 31 | # we need to copy the base shape using the following three 32 | # lines 33 | assert self.IsNull() 34 | self.TShape(solid.TShape()) 35 | self.Location(solid.Location()) 36 | self.Orientation(solid.Orientation()) 37 | assert not self.IsNull() 38 | 39 | self.GlobalProperties = GlobalProperties(self) 40 | 41 | def shells(self): 42 | return (Shell(sh) for sh in Topo(self)) 43 | -------------------------------------------------------------------------------- /misc/myqtDisplay.py: -------------------------------------------------------------------------------- 1 | 2 | from OCC.Display import qtDisplay 3 | from OCC.Display.backend import get_qt_modules 4 | 5 | QtCore, QtGui, QtWidgets, QtOpenGL = get_qt_modules() 6 | 7 | class point(object): 8 | def __init__(self, obj=None): 9 | self.x = 0 10 | self.y = 0 11 | if obj is not None: 12 | self.set(obj) 13 | 14 | def set(self, obj): 15 | self.x = obj.x() 16 | self.y = obj.y() 17 | 18 | class MyqtViewer3d(qtDisplay.qtViewer3d): 19 | " Modify qtViewer3d to emit signals" 20 | 21 | def mousePressEvent(self, event): 22 | self.emit(QtCore.SIGNAL("LMBPressed")) 23 | #print 'LMBPressed signal emitted.' 24 | self.setFocus() 25 | self.dragStartPos = point(event.pos()) 26 | self._display.StartRotation(self.dragStartPos.x, self.dragStartPos.y) 27 | 28 | def mouseReleaseEvent(self, event): 29 | self.emit(QtCore.SIGNAL("LMBReleased")) 30 | #print 'LMBReleased signal emitted.' 31 | pt = point(event.pos()) 32 | modifiers = event.modifiers() 33 | 34 | if event.button() == QtCore.Qt.LeftButton: 35 | pt = point(event.pos()) 36 | if self._select_area: 37 | [Xmin, Ymin, dx, dy] = self._drawbox 38 | self._display.SelectArea(Xmin, Ymin, Xmin + dx, Ymin + dy) 39 | self._select_area = False 40 | else: 41 | # multiple select if shift is pressed 42 | if modifiers == QtCore.Qt.ShiftModifier: 43 | self._display.ShiftSelect(pt.x, pt.y) 44 | else: 45 | # single select otherwise 46 | self._display.Select(pt.x, pt.y) 47 | elif event.button() == QtCore.Qt.RightButton: 48 | if self._zoom_area: 49 | [Xmin, Ymin, dx, dy] = self._drawbox 50 | self._display.ZoomArea(Xmin, Ymin, Xmin + dx, Ymin + dy) 51 | self._zoom_area = False 52 | -------------------------------------------------------------------------------- /OCCUtils/Image.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ##Copyright 2008-2015 Thomas Paviot (tpaviot@gmail.com) 4 | ## 5 | ##This file is part of pythonOCC. 6 | ## 7 | ##pythonOCC is free software: you can redistribute it and/or modify 8 | ##it under the terms of the GNU Lesser General Public License as published by 9 | ##the Free Software Foundation, either version 3 of the License, or 10 | ##(at your option) any later version. 11 | ## 12 | ##pythonOCC is distributed in the hope that it will be useful, 13 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ##GNU Lesser General Public License for more details. 16 | ## 17 | ##You should have received a copy of the GNU Lesser General Public License 18 | ##along with pythonOCC. If not, see . 19 | 20 | import os 21 | import os.path 22 | 23 | 24 | class Texture(object): 25 | """ 26 | This class encapsulates the necessary texture properties: 27 | Filename, toScaleU, etc. 28 | """ 29 | def __init__(self, filename): 30 | if not os.path.isfile(filename): 31 | raise IOError("File %s not found.\n" % filename) 32 | self._filename = filename 33 | self._toScaleU = 1.0 34 | self._toScaleV = 1.0 35 | self._toRepeatU = 1.0 36 | self._toRepeatV = 1.0 37 | self._originU = 0.0 38 | self._originV = 0.0 39 | 40 | def TextureScale(self, toScaleU, toScaleV): 41 | self._toScaleU = toScaleU 42 | self._toScaleV = toScaleV 43 | 44 | def TextureRepeat(self, toRepeatU, toRepeatV): 45 | self._toRepeatU = toRepeatU 46 | self._toRepeatV = toRepeatV 47 | 48 | def TextureOrigin(self, originU, originV): 49 | self._originU = originU 50 | self._originV = originV 51 | 52 | def GetProperties(self): 53 | return (self._filename, 54 | self._toScaleU, self._toScaleV, 55 | self._toRepeatU, self._toRepeatV, 56 | self._originU, self._originV) 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cadviewer 2 | Simple 3D CAD app using PythonOCC and PyQt5 3 | 4 | This repo is like an old attic in a sense. It contains various code that I have 5 | written as I have experimented with what and how I might go about writing 6 | a CAD application built on PythonOCC. I decided to post it on GitHub. 7 | 8 | I stumbled across some work I did a few years ago, trying to build a simple CAD app 9 | using PythonOCC. Having not looked at it in over 3 years, I wasn't sure it would be 10 | worth the trouble to get it running on the latest version of PyhonOCC and Python 3. 11 | A screenshot from some old code posted online: 12 | https://sites.google.com/site/pythonocc/cadviewer 13 | reminds me that I was using PythonOCC version 0.16.3-dev at the time. 14 | With the recent release of PyOCC version7.4.0-beta, I decided to give it a go. 15 | I asked Thomas Paviot for useful resources to help me understand the changes 16 | in the API from version 0.16 to the current version. His advice was very helpful: 17 | 18 | """ 19 | pythonocc-0.16.3 is 4 years old, in the meantime code has changed because of : 20 | 21 | * API changes from opencascade. Have a look at the release notes for each release. Most changes occurred when switching to occt7x series (see https://www.opencascade.com/content/previous-releases for an history of opencascascade releases) ; 22 | 23 | * changes in pythonocc itself. There have been two major changes that you have to know about while porting your old code to the new release : 24 | 25 | 1. The package structure has changed. You have to move all 'from OCC.xxx import xxx' to 'from OCC.Core.xxx import xxx'. That's not a big deal. 26 | 27 | 2. There is not Handle anymore. GetHandle and GetObject methods have disappeared. Just pass the object itself, the wrapper decides wether it has to pass the Handle or the Object to the C++ layer. You can check this commit (https://github.com/tpaviot/pythonocc-demos/commit/e59acdce5720d84ce76134789b48c268e36446d6#diff-68b70730ce65eb74e098809766ab3d0d), where we ported the old 'occ bottle example'. 28 | """ 29 | 30 | Here is my progress (so far) on getting it running again: 31 | 32 | cadViewer.py produces the main GUI (but still crashes when the buttons are clicked). 33 | 34 | got import STEP kind of working, but part names are still broken. 35 | 36 | -------------------------------------------------------------------------------- /OCCUtils/shell.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2015 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.Core.TopoDS import TopoDS_Shell 19 | from OCC.ShapeAnalysis import ShapeAnalysis_Shell 20 | 21 | from OCCUtils.Topology import Topo 22 | from OCCUtils.base import BaseObject, GlobalProperties 23 | 24 | 25 | class Shell(TopoDS_Shell, BaseObject): 26 | _n = 0 27 | 28 | def __init__(self, shell): 29 | assert isinstance(shell, TopoDS_Shell), 'need a TopoDS_Shell, got a %s' % shell.__class__ 30 | assert not shell.IsNull() 31 | super(Shell, self).__init__() 32 | BaseObject.__init__(self, 'shell') 33 | # we need to copy the base shape using the following three 34 | # lines 35 | assert self.IsNull() 36 | self.TShape(shell.TShape()) 37 | self.Location(shell.Location()) 38 | self.Orientation(shell.Orientation()) 39 | assert not self.IsNull() 40 | 41 | self.GlobalProperties = GlobalProperties(self) 42 | self._n += 1 43 | 44 | def analyse(self): 45 | """ 46 | 47 | :return: 48 | """ 49 | ss = ShapeAnalysis_Shell(self) 50 | if ss.HasFreeEdges(): 51 | bad_edges = [e for e in Topo(ss.BadEdges()).edges()] 52 | return bad_edges 53 | 54 | def Faces(self): 55 | """ 56 | 57 | :return: 58 | """ 59 | return Topo(self, True).faces() 60 | 61 | def Wires(self): 62 | """ 63 | :return: 64 | """ 65 | return Topo(self, True).wires() 66 | 67 | def Edges(self): 68 | return Topo(self, True).edges() 69 | -------------------------------------------------------------------------------- /OCCUtils/vertex.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2013 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.gp import gp_Pnt, gp_Vec, gp_Dir, gp_XYZ, gp_Pnt2d 19 | from OCC.Core.TopoDS import TopoDS_Vertex 20 | from OCC.ShapeBuild import ShapeBuild_ReShape 21 | 22 | from OCCUtils.base import BaseObject 23 | from OCCUtils.Construct import make_vertex 24 | 25 | 26 | class Vertex(TopoDS_Vertex, BaseObject): 27 | """ 28 | wraps gp_Pnt 29 | """ 30 | _n = 0 31 | 32 | def __init__(self, x, y, z): 33 | super(Vertex, self).__init__() 34 | """Constructor for KbeVertex""" 35 | BaseObject.__init__(self, name='Vertex #{0}'.format(self._n)) 36 | 37 | self._n += 1 # should be a property of KbeObject 38 | self._pnt = gp_Pnt(x, y, z) 39 | self._vertex = make_vertex(self._pnt) 40 | TopoDS_Vertex.__init__(self, self._vertex) 41 | 42 | def _update(self): 43 | """ 44 | 45 | """ 46 | # TODO: perhaps should take an argument until which topological level 47 | # topological entities bound to the vertex should be updated too... 48 | reshape = ShapeBuild_ReShape() 49 | reshape.Replace(self._vertex, make_vertex(self._pnt)) 50 | 51 | @staticmethod 52 | def from_pnt(cls, pnt): 53 | x, y, z = pnt.X(), pnt.Y(), pnt.Z() 54 | return cls(x, y, z) 55 | 56 | @property 57 | def x(self): 58 | return self._pnt.X() 59 | 60 | @x.setter 61 | def x(self, val): 62 | self._pnt.SetX(val) 63 | self._update() 64 | 65 | @property 66 | def y(self): 67 | return self._pnt.Y() 68 | 69 | @y.setter 70 | def y(self, val): 71 | self._pnt.SetY(val) 72 | self._update() 73 | 74 | @property 75 | def z(self): 76 | return self._pnt.Z() 77 | 78 | @z.setter 79 | def z(self, val): 80 | self._pnt.SetZ(val) 81 | self._update() 82 | 83 | @property 84 | def xyz(self): 85 | return self._pnt.Coord() 86 | 87 | @xyz.setter 88 | def xyz(self, *val): 89 | self._pnt.SetXYZ(*val) 90 | self._update() 91 | 92 | def __repr__(self): 93 | return self.name 94 | 95 | @property 96 | def as_vec(self): 97 | '''returns a gp_Vec version of self''' 98 | return gp_Vec(*self._pnt.Coord()) 99 | 100 | @property 101 | def as_dir(self): 102 | '''returns a gp_Dir version of self''' 103 | return gp_Dir(*self._pnt.Coord()) 104 | 105 | @property 106 | def as_xyz(self): 107 | '''returns a gp_XYZ version of self''' 108 | return gp_XYZ(*self._pnt.Coord()) 109 | 110 | @property 111 | def as_pnt(self): 112 | return self._pnt 113 | 114 | @property 115 | def as_2d(self): 116 | '''returns a gp_Pnt2d version of self''' 117 | return gp_Pnt2d(*self._pnt.Coord()[:2]) 118 | -------------------------------------------------------------------------------- /misc/example.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from math import pi 3 | 4 | from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox 5 | from OCC.AIS import * 6 | from OCC.Quantity import * 7 | from OCC.Display.SimpleGui import init_display 8 | from OCC.TopoDS import * 9 | from OCC.gp import * 10 | from OCC.TopLoc import * 11 | from OCC.Geom import * 12 | from OCC.BRep import BRep_Tool_Surface 13 | from OCC.GCE2d import * 14 | from OCC.BRepBuilderAPI import * 15 | 16 | display, start_display, add_menu, add_function_to_menu = init_display() 17 | 18 | 19 | def geom_plane_from_face(aFace): 20 | """ 21 | Returns the geometric plane entity from a planar surface 22 | """ 23 | return Handle_Geom_Plane.DownCast(OCC.BRep.BRep_Tool_Surface(aFace)).GetObject() 24 | 25 | def redraw(event=None): 26 | # display with crisp edges and transpaarency 27 | context = display.Context 28 | context.RemoveAll() 29 | context.SetAutoActivateSelection(False) 30 | aisShape = AIS_Shape(Box) 31 | h_aisShape = aisShape.GetHandle() 32 | context.Display(h_aisShape) 33 | context.SetTransparency(h_aisShape, .6) 34 | context.HilightWithColor(h_aisShape, OCC.Quantity.Quantity_NOC_BLACK) 35 | display.FitAll() 36 | 37 | def makeBox(event=None): 38 | global Box 39 | # Make a box 40 | Box = BRepPrimAPI_MakeBox(60, 60, 50).Shape() 41 | redraw() 42 | 43 | def rotateBox(): 44 | aisShape = AIS_Shape(Box) 45 | ax1 = gp_Ax1(gp_Pnt(0., 0., 0.), gp_Dir(1., 0., 0.)) 46 | aRotTrsf = gp_Trsf() 47 | angle = pi/6 48 | aRotTrsf.SetRotation(ax1, angle) 49 | aTopLoc = TopLoc_Location(aRotTrsf) 50 | Box.Move(aTopLoc) 51 | redraw() 52 | 53 | def enableFaceSelect(event=None): 54 | display.selected_shape = None 55 | display.SetSelectionModeFace() 56 | 57 | def makeSqProfile(size, surface): 58 | # points and segments need to be in CW sequence to get W pointing along Z 59 | aPnt1 = gp_Pnt2d(-size, size) 60 | aPnt2 = gp_Pnt2d(size, size) 61 | aPnt3 = gp_Pnt2d(size, -size) 62 | aPnt4 = gp_Pnt2d(-size, -size) 63 | aSegment1 = GCE2d_MakeSegment(aPnt1, aPnt2) 64 | aSegment2 = GCE2d_MakeSegment(aPnt2, aPnt3) 65 | aSegment3 = GCE2d_MakeSegment(aPnt3, aPnt4) 66 | aSegment4 = GCE2d_MakeSegment(aPnt4, aPnt1) 67 | print 'Next is where something crashes' 68 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value(), 69 | Handle_Geom_Surface(surface)) 70 | aEdge2 = BRepBuilderAPI_MakeEdge(aSegment2.Value(), 71 | Handle_Geom_Surface(surface)) 72 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment3.Value(), 73 | Handle_Geom_Surface(surface)) 74 | aEdge4 = BRepBuilderAPI_MakeEdge(aSegment4.Value(), 75 | Handle_Geom_Surface(surface)) 76 | print "Doesn't get here (with rotated box)" 77 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), 78 | aEdge2.Edge(), 79 | aEdge3.Edge(), 80 | aEdge4.Edge()) 81 | 82 | myWireProfile = aWire.Wire() 83 | return myWireProfile # TopoDS_Wire 84 | 85 | def wireProfileOnFace(event=None): 86 | aShape = display.GetSelectedShape() 87 | shapes = display.GetSelectedShapes() 88 | face = None 89 | if aShape: 90 | face = topods_Face(aShape) 91 | print "A shape found:" 92 | elif shapes: 93 | aShape = shapes[0] 94 | face = topods_Face(aShape) 95 | print len(shapes), "Shapes found" 96 | if face: 97 | surface = geom_plane_from_face(face) 98 | wireProfile = makeSqProfile(50, surface) 99 | display.DisplayShape(wireProfile) 100 | else: 101 | print 'no face' 102 | 103 | def exit(event=None): 104 | sys.exit() 105 | 106 | if __name__ == '__main__': 107 | add_menu('operations') 108 | add_function_to_menu('operations', makeBox) 109 | add_function_to_menu('operations', rotateBox) 110 | add_function_to_menu('operations', enableFaceSelect) 111 | add_function_to_menu('operations', wireProfileOnFace) 112 | add_function_to_menu('operations', exit) 113 | start_display() 114 | -------------------------------------------------------------------------------- /OCCUtils/Iteration.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2015 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see . 17 | 18 | ''' 19 | This module helps looping through topology 20 | ''' 21 | from OCC.BRep import BRep_Tool 22 | 23 | from OCCUtils.Topology import WireExplorer, Topo 24 | from OCCUtils.edge import Edge 25 | 26 | 27 | class EdgePairsFromWire(object): 28 | ''' 29 | helper class to loop through a wire and return ordered pairs of edges 30 | ''' 31 | def __init__(self, wire): 32 | self.wire = wire 33 | self.edge_pairs = [] 34 | self.prev_edge = None 35 | self.we = WireExplorer(self.wire).ordered_edges() 36 | self.number_of_edges = self.we.__length_hint__() 37 | self.previous_edge = None 38 | self.current_edge = None 39 | self.first_edge = None 40 | self.index = 0 41 | 42 | def next(self): 43 | if self.index == 0: 44 | # first edge, need to set self.previous_edge 45 | self.previous_edge = next(self.we) 46 | self.current_edge = next(self.we) 47 | self.first_edge = self.previous_edge # for the last iteration 48 | self.index += 1 49 | return [self.previous_edge, self.current_edge] 50 | elif self.index == self.number_of_edges-1: 51 | # no next edge 52 | self.index += 1 53 | return [self.current_edge, self.first_edge] 54 | else: 55 | self.previous_edge = self.current_edge 56 | self.current_edge = next(self.we) 57 | self.index += 1 58 | return [self.previous_edge, self.current_edge] 59 | 60 | def __iter__(self): 61 | return self 62 | 63 | 64 | class LoopWirePairs(object): 65 | ''' 66 | for looping through consequtive wires 67 | assures that the returned edge pairs are ordered 68 | ''' 69 | def __init__(self, wireA, wireB): 70 | self.wireA = wireA 71 | self.wireB = wireB 72 | self.we_A = WireExplorer(self.wireA) 73 | self.we_B = WireExplorer(self.wireB) 74 | self.tp_A = Topo(self.wireA) 75 | self.tp_B = Topo(self.wireB) 76 | self.bt = BRep_Tool() 77 | self.vertsA = [v for v in self.we_A.ordered_vertices()] 78 | self.vertsB = [v for v in self.we_B.ordered_vertices()] 79 | 80 | self.edgesA = [v for v in WireExplorer(wireA).ordered_edges()] 81 | self.edgesB = [v for v in WireExplorer(wireB).ordered_edges()] 82 | 83 | self.pntsB = [self.bt.Pnt(v) for v in self.vertsB] 84 | self.number_of_vertices = len(self.vertsA) 85 | self.index = 0 86 | 87 | def closest_point(self, vertexFromWireA): 88 | pt = self.bt.Pnt(vertexFromWireA) 89 | distances = [pt.Distance(i) for i in self.pntsB] 90 | indx_max_dist = distances.index(min(distances)) 91 | return self.vertsB[indx_max_dist] 92 | 93 | def next(self): 94 | if self.index == self.number_of_vertices: 95 | raise StopIteration 96 | 97 | vert = self.vertsA[self.index] 98 | closest = self.closest_point(vert) 99 | edges_a = self.tp_A.edges_from_vertex(vert) 100 | edges_b = self.tp_B.edges_from_vertex(closest) 101 | a1, a2 = Edge(next(edges_a)), Edge(next(edges_a)) 102 | b1, b2 = Edge(next(edges_b)), Edge(next(edges_b)) 103 | mpA = a1.mid_point() 104 | self.index += 1 105 | 106 | if mpA.Distance(b1.mid_point()) < mpA.Distance(b2.mid_point()): 107 | return iter([a1, a2]), iter([b1, b2]) 108 | else: 109 | return iter([a1, a2]), iter([b2, b1]) 110 | 111 | def __iter__(self): 112 | return self 113 | -------------------------------------------------------------------------------- /misc/example1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Figured out what the problem was, after studying 3 | pythonocc-core/examples/core_topology_local_ops.py 4 | 5 | BRepBuilderAPI_MakeEdge(aSegment1.Value(), Handle_Geom_Surface(surface)) 6 | needs a 'surface' and I was feeding it a Geom_Plane. 7 | """ 8 | import sys 9 | from math import pi 10 | 11 | from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox 12 | from OCC.AIS import * 13 | from OCC.Quantity import * 14 | from OCC.Display.SimpleGui import init_display 15 | from OCC.TopoDS import * 16 | from OCC.gp import * 17 | from OCC.TopLoc import * 18 | from OCC.Geom import * 19 | from OCC.BRep import BRep_Tool_Surface 20 | from OCC.GCE2d import * 21 | from OCC.BRepBuilderAPI import * 22 | 23 | display, start_display, add_menu, add_function_to_menu = init_display() 24 | context = display.Context 25 | 26 | def geom_plane_from_face(aFace): 27 | """ 28 | Returns the geometric plane entity from a planar surface 29 | """ 30 | return Handle_Geom_Plane_DownCast(OCC.BRep.BRep_Tool_Surface(aFace)).GetObject() 31 | 32 | def surface_from_face(aFace): 33 | """ 34 | Returns a surface entity from a face 35 | """ 36 | return OCC.BRep.BRep_Tool_Surface(aFace) 37 | 38 | 39 | def redraw(event=None): 40 | # display with crisp edges and transparency 41 | context.RemoveAll() 42 | context.SetAutoActivateSelection(False) 43 | aisShape = AIS_Shape(Box) 44 | h_aisShape = aisShape.GetHandle() 45 | context.Display(h_aisShape) 46 | context.SetTransparency(h_aisShape, .6) 47 | context.HilightWithColor(h_aisShape, OCC.Quantity.Quantity_NOC_BLACK) 48 | display.FitAll() 49 | 50 | def makeBox(event=None): 51 | global Box 52 | # Make a box 53 | Box = BRepPrimAPI_MakeBox(60, 60, 50).Shape() 54 | redraw() 55 | 56 | def rotateBox(): 57 | aisShape = AIS_Shape(Box) 58 | ax1 = gp_Ax1(gp_Pnt(0., 0., 0.), gp_Dir(1., 0., 0.)) 59 | aRotTrsf = gp_Trsf() 60 | angle = pi/6 61 | aRotTrsf.SetRotation(ax1, angle) 62 | aTopLoc = TopLoc_Location(aRotTrsf) 63 | Box.Move(aTopLoc) 64 | redraw() 65 | 66 | def enableFaceSelect(event=None): 67 | display.selected_shape = None 68 | display.SetSelectionModeFace() 69 | 70 | def makeSqProfile(size, surface): 71 | # points and segments need to be in CW sequence to get W pointing along Z 72 | aPnt1 = gp_Pnt2d(-size, size) 73 | aPnt2 = gp_Pnt2d(size, size) 74 | aPnt3 = gp_Pnt2d(size, -size) 75 | aPnt4 = gp_Pnt2d(-size, -size) 76 | aSegment1 = GCE2d_MakeSegment(aPnt1, aPnt2) 77 | aSegment2 = GCE2d_MakeSegment(aPnt2, aPnt3) 78 | aSegment3 = GCE2d_MakeSegment(aPnt3, aPnt4) 79 | aSegment4 = GCE2d_MakeSegment(aPnt4, aPnt1) 80 | print 'Next is where something crashes' 81 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value(), 82 | Handle_Geom_Surface(surface)) 83 | aEdge2 = BRepBuilderAPI_MakeEdge(aSegment2.Value(), 84 | Handle_Geom_Surface(surface)) 85 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment3.Value(), 86 | Handle_Geom_Surface(surface)) 87 | aEdge4 = BRepBuilderAPI_MakeEdge(aSegment4.Value(), 88 | Handle_Geom_Surface(surface)) 89 | print "Doesn't get here (with rotated box)" 90 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), 91 | aEdge2.Edge(), 92 | aEdge3.Edge(), 93 | aEdge4.Edge()) 94 | 95 | myWireProfile = aWire.Wire() 96 | return myWireProfile # TopoDS_Wire 97 | 98 | def wireProfileOnFace(event=None): 99 | aShape = display.GetSelectedShape() 100 | shapes = display.GetSelectedShapes() 101 | face = None 102 | if aShape: 103 | face = topods_Face(aShape) 104 | print "A shape found:" 105 | elif shapes: 106 | aShape = shapes[0] 107 | face = topods_Face(aShape) 108 | print len(shapes), "Shapes found" 109 | if face: 110 | surface = surface_from_face(face) 111 | wireProfile = makeSqProfile(50, surface) 112 | display.DisplayShape(wireProfile) 113 | else: 114 | print 'no face' 115 | 116 | def exit(event=None): 117 | sys.exit() 118 | 119 | if __name__ == '__main__': 120 | add_menu('operations') 121 | add_function_to_menu('operations', makeBox) 122 | add_function_to_menu('operations', rotateBox) 123 | add_function_to_menu('operations', enableFaceSelect) 124 | add_function_to_menu('operations', wireProfileOnFace) 125 | add_function_to_menu('operations', exit) 126 | start_display() 127 | -------------------------------------------------------------------------------- /sew.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from math import pi 3 | 4 | from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox 5 | from OCC.AIS import * 6 | from OCC.Quantity import * 7 | from OCC.Display.SimpleGui import init_display 8 | from OCC.TopoDS import * 9 | from OCC.gp import * 10 | from OCC.TopLoc import * 11 | from OCC.Geom import * 12 | from OCC.BRep import BRep_Tool_Surface 13 | from OCC.GCE2d import * 14 | from OCC.BRepBuilderAPI import * 15 | from OCC.GC import * 16 | import aocutils.brep.solid_make 17 | 18 | display, start_display, add_menu, add_function_to_menu = init_display() 19 | 20 | 21 | def geom_plane_from_face(aFace): 22 | """ 23 | Returns the geometric plane entity from a planar surface 24 | """ 25 | return Handle_Geom_Plane.DownCast(OCC.BRep.BRep_Tool_Surface(aFace)).GetObject() 26 | 27 | def redraw(shape, event=None): 28 | # display with crisp edges and transpaarency 29 | context = display.Context 30 | context.RemoveAll() 31 | context.SetAutoActivateSelection(False) 32 | aisShape = AIS_Shape(shape) 33 | h_aisShape = aisShape.GetHandle() 34 | context.Display(h_aisShape) 35 | context.SetTransparency(h_aisShape, .1) 36 | context.HilightWithColor(h_aisShape, OCC.Quantity.Quantity_NOC_BLACK) 37 | display.FitAll() 38 | 39 | def makeBox(event=None): 40 | # Make a box 41 | Box = BRepPrimAPI_MakeBox(60, 60, 50).Shape() 42 | redraw() 43 | 44 | def rotateBox(): 45 | aisShape = AIS_Shape(Box) 46 | ax1 = gp_Ax1(gp_Pnt(0., 0., 0.), gp_Dir(1., 0., 0.)) 47 | aRotTrsf = gp_Trsf() 48 | angle = pi/6 49 | aRotTrsf.SetRotation(ax1, angle) 50 | aTopLoc = TopLoc_Location(aRotTrsf) 51 | Box.Move(aTopLoc) 52 | redraw() 53 | 54 | def enableFaceSelect(event=None): 55 | display.selected_shape = None 56 | display.SetSelectionModeFace() 57 | 58 | def makeSqProfile(size, surface): 59 | # points and segments need to be in CW sequence to get W pointing along Z 60 | aPnt1 = gp_Pnt2d(-size, size) 61 | aPnt2 = gp_Pnt2d(size, size) 62 | aPnt3 = gp_Pnt2d(size, -size) 63 | aPnt4 = gp_Pnt2d(-size, -size) 64 | aSegment1 = GCE2d_MakeSegment(aPnt1, aPnt2) 65 | aSegment2 = GCE2d_MakeSegment(aPnt2, aPnt3) 66 | aSegment3 = GCE2d_MakeSegment(aPnt3, aPnt4) 67 | aSegment4 = GCE2d_MakeSegment(aPnt4, aPnt1) 68 | print 'Next is where something crashes' 69 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value(), 70 | Handle_Geom_Surface(surface)) 71 | aEdge2 = BRepBuilderAPI_MakeEdge(aSegment2.Value(), 72 | Handle_Geom_Surface(surface)) 73 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment3.Value(), 74 | Handle_Geom_Surface(surface)) 75 | aEdge4 = BRepBuilderAPI_MakeEdge(aSegment4.Value(), 76 | Handle_Geom_Surface(surface)) 77 | print "Doesn't get here (with rotated box)" 78 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), 79 | aEdge2.Edge(), 80 | aEdge3.Edge(), 81 | aEdge4.Edge()) 82 | 83 | myWireProfile = aWire.Wire() 84 | return myWireProfile # TopoDS_Wire 85 | 86 | def wireProfileOnFace(event=None): 87 | aShape = display.GetSelectedShape() 88 | shapes = display.GetSelectedShapes() 89 | face = None 90 | if aShape: 91 | face = topods_Face(aShape) 92 | print "A shape found:" 93 | elif shapes: 94 | aShape = shapes[0] 95 | face = topods_Face(aShape) 96 | print len(shapes), "Shapes found" 97 | if face: 98 | surface = geom_plane_from_face(face) 99 | wireProfile = makeSqProfile(50, surface) 100 | display.DisplayShape(wireProfile) 101 | else: 102 | print 'no face' 103 | 104 | def translatePnt(p1, vec): 105 | p2 = gp_Pnt() 106 | p2 = p1.Translated(vec) 107 | return p2 108 | 109 | def pointsToWire(p1, p2, p3, p4): 110 | seg1 = GC_MakeSegment(p1, p2) 111 | seg2 = GC_MakeSegment(p2, p3) 112 | seg3 = GC_MakeSegment(p3, p4) 113 | seg4 = GC_MakeSegment(p4, p1) 114 | edge1 = BRepBuilderAPI_MakeEdge(seg1.Value()) 115 | edge2 = BRepBuilderAPI_MakeEdge(seg2.Value()) 116 | edge3 = BRepBuilderAPI_MakeEdge(seg3.Value()) 117 | edge4 = BRepBuilderAPI_MakeEdge(seg4.Value()) 118 | wire = BRepBuilderAPI_MakeWire(edge1.Edge(), edge2.Edge(), 119 | edge3.Edge(), edge4.Edge()) 120 | return wire.Wire() 121 | 122 | def sewBox(): 123 | # Length of shape (spine) 124 | Vec = gp_Vec(0, 0, 10) 125 | # starting with bot vertices, make bot wire & face 126 | p1 = gp_Pnt(0, 0, 0) 127 | p2 = gp_Pnt(20, 0, 0) 128 | p3 = gp_Pnt(20, 20, 0) 129 | p4 = gp_Pnt(0, 20, 0) 130 | botWire = pointsToWire(p1, p2, p3, p4) 131 | botFace = BRepBuilderAPI_MakeFace(botWire).Face() 132 | # starting with topvertices, make top face 133 | p5 = translatePnt(p1, Vec) 134 | p6 = translatePnt(p2, Vec) 135 | p7 = translatePnt(p3, Vec) 136 | p8 = translatePnt(p4, Vec) 137 | topWire = pointsToWire(p5, p6, p7, p8) 138 | topFace = BRepBuilderAPI_MakeFace(topWire).Face() 139 | # Make spine (wire) to make 'pipe' 140 | spineSeg = GC_MakeSegment(p1, p5) 141 | spineEdge = BRepBuilderAPI_MakeEdge(spineSeg.Value()) 142 | spineWire = BRepBuilderAPI_MakeWire(spineEdge.Edge()).Wire() 143 | pipe = OCC.BRepOffsetAPI.BRepOffsetAPI_MakePipe(botWire, spineWire).Shape() 144 | # Sew together botFace, pipe, and topFace to get solid 145 | tolerance = 1e-6 146 | sew = OCC.BRepBuilderAPI.BRepBuilderAPI_Sewing(tolerance) 147 | sew.Add(botFace) 148 | sew.Add(pipe) 149 | sew.Add(topFace) 150 | sew.Perform() 151 | res = sew.SewedShape() 152 | print type(res) 153 | redraw(res) 154 | 155 | def exit(event=None): 156 | sys.exit() 157 | 158 | if __name__ == '__main__': 159 | add_menu('operations') 160 | add_function_to_menu('operations', makeBox) 161 | add_function_to_menu('operations', rotateBox) 162 | add_function_to_menu('operations', enableFaceSelect) 163 | add_function_to_menu('operations', wireProfileOnFace) 164 | add_function_to_menu('operations', exit) 165 | add_menu('Experimental') 166 | add_function_to_menu('Experimental', sewBox) 167 | 168 | start_display() 169 | -------------------------------------------------------------------------------- /treelib/node.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (C) 2011 3 | # Brett Alistair Kromkamp - brettkromkamp@gmail.com 4 | # Copyright (C) 2012-2017 5 | # Xiaming Chen - chenxm35@gmail.com 6 | # and other contributors. 7 | # All rights reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | """ 21 | Node structure in treelib. 22 | 23 | A :class:`Node` object contains basic properties such as node identifier, 24 | node tag, parent node, children nodes etc., and some operations for a node. 25 | """ 26 | import uuid 27 | 28 | from .exceptions import NodePropertyError 29 | 30 | 31 | class Node(object): 32 | """ 33 | Nodes are elementary objects that are stored in the `_nodes` dictionary of a Tree. 34 | Use `data` attribute to store node-specific data. 35 | """ 36 | 37 | #: Mode constants for routine `update_fpointer()`. 38 | (ADD, DELETE, INSERT, REPLACE) = list(range(4)) 39 | 40 | def __init__(self, tag=None, identifier=None, expanded=True, data=None): 41 | """Create a new Node object to be placed inside a Tree object""" 42 | 43 | #: if given as a parameter, must be unique 44 | self._identifier = None 45 | self._set_identifier(identifier) 46 | 47 | #: None or something else 48 | #: if None, self._identifier will be set to the identifier's value. 49 | if tag is None: 50 | self._tag = self._identifier 51 | else: 52 | self._tag = tag 53 | 54 | #: boolean 55 | self.expanded = expanded 56 | 57 | #: identifier of the parent's node : 58 | self._bpointer = None 59 | #: identifier(s) of the soons' node(s) : 60 | self._fpointer = list() 61 | 62 | #: User payload associated with this node. 63 | self.data = data 64 | 65 | def __lt__(self, other): 66 | return self.tag < other.tag 67 | 68 | def _set_identifier(self, nid): 69 | """Initialize self._set_identifier""" 70 | if nid is None: 71 | self._identifier = str(uuid.uuid1()) 72 | else: 73 | self._identifier = nid 74 | 75 | @property 76 | def bpointer(self): 77 | """ 78 | The parent ID of a node. This attribute can be 79 | accessed and modified with ``.`` and ``=`` operator respectively. 80 | """ 81 | return self._bpointer 82 | 83 | @bpointer.setter 84 | def bpointer(self, nid): 85 | """Set the value of `_bpointer`.""" 86 | if nid is not None: 87 | self._bpointer = nid 88 | else: 89 | # print("WARNING: the bpointer of node %s " \ 90 | # "is set to None" % self._identifier) 91 | self._bpointer = None 92 | 93 | @property 94 | def fpointer(self): 95 | """ 96 | With a getting operator, a list of IDs of node's children is obtained. With 97 | a setting operator, the value can be list, set, or dict. For list or set, 98 | it is converted to a list type by the package; for dict, the keys are 99 | treated as the node IDs. 100 | """ 101 | return self._fpointer 102 | 103 | @fpointer.setter 104 | def fpointer(self, value): 105 | """Set the value of `_fpointer`.""" 106 | if value is None: 107 | self._fpointer = list() 108 | elif isinstance(value, list): 109 | self._fpointer = value 110 | elif isinstance(value, dict): 111 | self._fpointer = list(value.keys()) 112 | elif isinstance(value, set): 113 | self._fpointer = list(value) 114 | else: # TODO: add deprecated routine 115 | pass 116 | 117 | @property 118 | def identifier(self): 119 | """ 120 | The unique ID of a node within the scope of a tree. This attribute can be 121 | accessed and modified with ``.`` and ``=`` operator respectively. 122 | """ 123 | return self._identifier 124 | 125 | @identifier.setter 126 | def identifier(self, value): 127 | """Set the value of `_identifier`.""" 128 | if value is None: 129 | print("WARNING: node ID can not be None") 130 | else: 131 | self._set_identifier(value) 132 | 133 | def is_leaf(self): 134 | """Return true if current node has no children.""" 135 | if len(self.fpointer) == 0: 136 | return True 137 | else: 138 | return False 139 | 140 | def is_root(self): 141 | """Return true if self has no parent, i.e. as root.""" 142 | return self._bpointer is None 143 | 144 | @property 145 | def tag(self): 146 | """ 147 | The readable node name for human. This attribute can be accessed and 148 | modified with ``.`` and ``=`` operator respectively. 149 | """ 150 | return self._tag 151 | 152 | @tag.setter 153 | def tag(self, value): 154 | """Set the value of `_tag`.""" 155 | self._tag = value if value is not None else None 156 | 157 | def update_bpointer(self, nid): 158 | """Set the parent (indicated by the ``nid`` parameter) of a node.""" 159 | self.bpointer = nid 160 | 161 | def update_fpointer(self, nid, mode=ADD, replace=None): 162 | """ 163 | Update the children list with different modes: addition (Node.ADD or 164 | Node.INSERT) and deletion (Node.DELETE). 165 | """ 166 | if nid is None: 167 | return 168 | 169 | if mode is self.ADD: 170 | self._fpointer.append(nid) 171 | 172 | elif mode is self.DELETE: 173 | if nid in self._fpointer: 174 | self._fpointer.remove(nid) 175 | 176 | elif mode is self.INSERT: # deprecate to ADD mode 177 | print("WARNING: INSERT is deprecated to ADD mode") 178 | self.update_fpointer(nid) 179 | 180 | elif mode is self.REPLACE: 181 | if replace is None: 182 | raise NodePropertyError( 183 | 'Argument "repalce" should be provided when mode is {}'.format(mode) 184 | ) 185 | 186 | ind = self._fpointer.index(nid) 187 | self._fpointer[ind] = replace 188 | 189 | def __repr__(self): 190 | name = self.__class__.__name__ 191 | kwargs = [ 192 | "tag={0}".format(self.tag), 193 | "identifier={0}".format(self.identifier), 194 | "data={0}".format(self.data), 195 | ] 196 | return "%s(%s)" % (name, ", ".join(kwargs)) 197 | -------------------------------------------------------------------------------- /OCCUtils/base.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2013 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | ''' 19 | Please note the following; 20 | @readonly 21 | means that the decorated method is a readonly descriptor 22 | @property 23 | means that the decorated method can be set / get using the descriptor 24 | ( irony is that just using @property *would* 25 | result in a readonly descriptor :") 26 | 27 | Sometimes a set of methods should be contained in another module or class, 28 | or simply grouped. 29 | For instance the set of methods after: 30 | #=========================================================================== 31 | # Curve.local_properties 32 | #=========================================================================== 33 | 34 | Can be a module, class or namespace. 35 | 36 | ''' 37 | 38 | import functools 39 | 40 | from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_Copy 41 | from OCC.Core.BRepGProp import (brepgprop_VolumeProperties, 42 | brepgprop_LinearProperties, 43 | brepgprop_SurfaceProperties) 44 | from OCC.Core.BRepCheck import (BRepCheck_Vertex, BRepCheck_Edge, BRepCheck_Wire, 45 | BRepCheck_Face, BRepCheck_Shell, BRepCheck_Analyzer) 46 | from OCC.GProp import GProp_GProps 47 | from OCC.Display.SimpleGui import init_display 48 | 49 | from OCCUtils.Common import get_boundingbox 50 | from OCCUtils.Construct import (make_vertex, TOLERANCE) 51 | from OCCUtils.types_lut import shape_lut, topo_lut, curve_lut, surface_lut 52 | 53 | #=========================================================================== 54 | # DISPLAY 55 | #=========================================================================== 56 | global display 57 | 58 | 59 | class singleton(object): 60 | def __init__(self, cls): 61 | self.cls = cls 62 | self.instance_container = [] 63 | 64 | def __call__(self, *args, **kwargs): 65 | if not len(self.instance_container): 66 | cls = functools.partial(self.cls, *args, **kwargs) 67 | self.instance_container.append(cls()) 68 | return self.instance_container[0] 69 | 70 | 71 | @singleton 72 | class Display(object): 73 | def __init__(self): 74 | self.display, self.start_display, self.add_menu, self.add_function_to_menu = init_display() 75 | 76 | def __call__(self, *args, **kwargs): 77 | return self.display.DisplayShape(*args, **kwargs) 78 | 79 | #============ 80 | # base class 81 | #============ 82 | 83 | 84 | class BaseObject(object): 85 | """base class for all objects""" 86 | def __init__(self, name=None, tolerance=TOLERANCE): 87 | self.GlobalProperties = GlobalProperties(self) 88 | self.name = name 89 | self._dirty = False 90 | self.tolerance = tolerance 91 | self.display_set = False 92 | 93 | @property 94 | def is_dirty(self): 95 | '''when an object is dirty, its topology will be 96 | rebuild when update is called''' 97 | return self._dirty 98 | 99 | @is_dirty.setter 100 | def is_dirty(self, _bool): 101 | self._dirty = bool(_bool) 102 | 103 | @property 104 | def topo_type(self): 105 | return topo_lut[self.ShapeType()] 106 | 107 | @property 108 | def geom_type(self): 109 | if self.topo_type == 'edge': 110 | return curve_lut[self.ShapeType()] 111 | if self.topo_type == 'face': 112 | return surface_lut[self.adaptor.GetType()] 113 | else: 114 | raise ValueError('geom_type works only for edges and faces...') 115 | 116 | def set_display(self, display): 117 | if hasattr(display, 'DisplayShape'): 118 | self.display_set = True 119 | self.display = display 120 | else: 121 | raise ValueError('not a display') 122 | 123 | def check(self): 124 | """ 125 | """ 126 | _check = dict(vertex=BRepCheck_Vertex, edge=BRepCheck_Edge, 127 | wire=BRepCheck_Wire, face=BRepCheck_Face, 128 | shell=BRepCheck_Shell) 129 | _check[self.topo_type] 130 | # TODO: BRepCheck will be able to inform *what* actually is the matter, 131 | # though implementing this still is a bit of work... 132 | raise NotImplementedError 133 | 134 | def is_valid(self): 135 | analyse = BRepCheck_Analyzer(self) 136 | ok = analyse.IsValid() 137 | if ok: 138 | return True 139 | else: 140 | return False 141 | 142 | def copy(self): 143 | """ 144 | 145 | :return: 146 | """ 147 | cp = BRepBuilderAPI_Copy(self) 148 | cp.Perform(self) 149 | # get the class, construct a new instance 150 | # cast the cp.Shape() to its specific TopoDS topology 151 | _copy = self.__class__(shape_lut(cp.Shape())) 152 | return _copy 153 | 154 | def distance(self, other): 155 | ''' 156 | return the minimum distance 157 | 158 | :return: minimum distance, 159 | minimum distance points on shp1 160 | minimum distance points on shp2 161 | ''' 162 | return minimum_distance(self, other) 163 | 164 | def show(self, *args, **kwargs): 165 | """ 166 | renders the topological entity in the viewer 167 | 168 | :param update: redraw the scene or not 169 | """ 170 | if not self.display_set: 171 | Display()(self, *args, **kwargs) 172 | else: 173 | self.disp.DisplayShape(*args, **kwargs) 174 | 175 | def build(self): 176 | if self.name.startswith('Vertex'): 177 | self = make_vertex(self) 178 | 179 | def __eq__(self, other): 180 | return self.IsEqual(other) 181 | 182 | def __ne__(self, other): 183 | return not self.__eq__(other) 184 | 185 | 186 | class GlobalProperties(object): 187 | ''' 188 | global properties for all topologies 189 | ''' 190 | def __init__(self, instance): 191 | self.instance = instance 192 | 193 | @property 194 | def system(self): 195 | self._system = GProp_GProps() 196 | # todo, type should be abstracted with TopoDS... 197 | _topo_type = self.instance.topo_type 198 | if _topo_type == 'face' or _topo_type == 'shell': 199 | brepgprop_SurfaceProperties(self.instance, self._system) 200 | elif _topo_type == 'edge': 201 | brepgprop_LinearProperties(self.instance, self._system) 202 | elif _topo_type == 'solid': 203 | brepgprop_VolumeProperties(self.instance, self._system) 204 | return self._system 205 | 206 | def centre(self): 207 | """ 208 | :return: centre of the entity 209 | """ 210 | return self.system.CentreOfMass() 211 | 212 | def inertia(self): 213 | '''returns the inertia matrix''' 214 | return self.system.MatrixOfInertia(), self.system.MomentOfInertia() 215 | 216 | def area(self): 217 | '''returns the area of the surface''' 218 | return self.system.Mass() 219 | 220 | def bbox(self): 221 | ''' 222 | returns the bounding box of the face 223 | ''' 224 | return get_boundingbox(self.instance) 225 | -------------------------------------------------------------------------------- /OCCUtils/types_lut.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2015 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.Core.BRepCheck import * 19 | from OCC.Core.GeomAbs import * 20 | from OCC.Core.TopoDS import topods, TopoDS_Shape 21 | from OCC.Core.BRep import BRep_Tool_Surface 22 | from OCC.Core.TopAbs import * 23 | #from OCC.Core.Geom import Handle_Geom_Plane, Handle_Geom_CylindricalSurface 24 | 25 | 26 | class ShapeToTopology(object): 27 | ''' 28 | looks up the topology type and returns the corresponding topological entity 29 | ''' 30 | def __init__(self): 31 | self.topoTypes = {TopAbs_VERTEX: topods.Vertex, 32 | TopAbs_EDGE: topods.Edge, 33 | TopAbs_FACE: topods.Face, 34 | TopAbs_WIRE: topods.Wire, 35 | TopAbs_SHELL: topods.Shell, 36 | TopAbs_SOLID: topods.Solid, 37 | TopAbs_COMPOUND: topods.Compound, 38 | TopAbs_COMPSOLID: topods.CompSolid, 39 | } 40 | 41 | def __call__(self, shape): 42 | if isinstance(shape, TopoDS_Shape): 43 | return self.topoTypes[shape.ShapeType()](shape) 44 | else: 45 | raise AttributeError('shape has not method `ShapeType`') 46 | 47 | def __getitem__(self, item): 48 | return self(item) 49 | 50 | 51 | class EnumLookup(object): 52 | """ 53 | perform bi-directional lookup of Enums'... 54 | """ 55 | def __init__(self, li_in, li_out): 56 | self.d = {} 57 | for a, b in zip(li_in, li_out): 58 | self.d[a] = b 59 | self.d[b] = a 60 | 61 | def __getitem__(self, item): 62 | return self.d[item] 63 | 64 | 65 | _curve_typesA = (GeomAbs_Line, GeomAbs_Circle, GeomAbs_Ellipse, 66 | GeomAbs_Hyperbola, GeomAbs_Parabola, 67 | GeomAbs_BezierCurve, GeomAbs_BSplineCurve, GeomAbs_OtherCurve) 68 | _curve_typesB = ('line', 'circle', 'ellipse', 'hyperbola', 'parabola', 69 | 'bezier', 'spline', 'other') 70 | _surface_typesA = (GeomAbs_Plane, GeomAbs_Cylinder, GeomAbs_Cone, 71 | GeomAbs_Sphere, GeomAbs_Torus, GeomAbs_BezierSurface, 72 | GeomAbs_BSplineSurface, GeomAbs_SurfaceOfRevolution, 73 | GeomAbs_SurfaceOfExtrusion, 74 | GeomAbs_OffsetSurface, GeomAbs_OtherSurface) 75 | _surface_typesB = ('plane', 'cylinder', 'cone', 'sphere', 'torus', 'bezier', 76 | 'spline', 'revolution', 'extrusion', 'offset', 'other') 77 | 78 | 79 | _stateA = ('in', 'out', 'on', 'unknown') 80 | _stateB = (TopAbs_IN, TopAbs_OUT, TopAbs_ON, TopAbs_UNKNOWN) 81 | 82 | 83 | _orientA = ['TopAbs_FORWARD', 'TopAbs_REVERSED', 'TopAbs_INTERNAL', 84 | 'TopAbs_EXTERNAL'] 85 | _orientB = [TopAbs_FORWARD, TopAbs_REVERSED, TopAbs_INTERNAL, 86 | TopAbs_EXTERNAL] 87 | 88 | 89 | _topoTypesA = ['vertex', 'edge', 'wire', 'face', 'shell', 90 | 'solid', 'compsolid', 'compound', 'shape'] 91 | _topoTypesB = [TopAbs_VERTEX, TopAbs_EDGE, TopAbs_WIRE, TopAbs_FACE, 92 | TopAbs_SHELL, TopAbs_SOLID, 93 | TopAbs_COMPSOLID, TopAbs_COMPOUND, TopAbs_SHAPE] 94 | 95 | 96 | _geom_types_a = ['line', 'circle', 'ellipse', 'hyperbola', 'parabola', 97 | 'beziercurve', 'bsplinecurve', 'othercurve'] 98 | _geom_types_b = [GeomAbs_Line, GeomAbs_Circle, GeomAbs_Ellipse, 99 | GeomAbs_Hyperbola, GeomAbs_Parabola, GeomAbs_BezierCurve, 100 | GeomAbs_BSplineCurve, GeomAbs_OtherCurve] 101 | 102 | 103 | # TODO: make a function that generalizes this, there is absolutely 104 | # no need for 2 lists to define an EnumLookup 105 | 106 | def fix_formatting(_str): 107 | return [i.strip() for i in _str.split(',')] 108 | 109 | _brep_check_a = fix_formatting("NoError, InvalidPointOnCurve,\ 110 | InvalidPointOnCurveOnSurface, InvalidPointOnSurface,\ 111 | No3DCurve, Multiple3DCurve, Invalid3DCurve, NoCurveOnSurface,\ 112 | InvalidCurveOnSurface, InvalidCurveOnClosedSurface, InvalidSameRangeFlag,\ 113 | InvalidSameParameterFlag,\ 114 | InvalidDegeneratedFlag, FreeEdge, InvalidMultiConnexity, InvalidRange,\ 115 | EmptyWire, RedundantEdge, SelfIntersectingWire, NoSurface,\ 116 | InvalidWire, RedundantWire, IntersectingWires, InvalidImbricationOfWires,\ 117 | EmptyShell, RedundantFace, UnorientableShape, NotClosed,\ 118 | NotConnected, SubshapeNotInShape, BadOrientation, BadOrientationOfSubshape,\ 119 | InvalidToleranceValue, CheckFail") 120 | 121 | _brep_check_b = [BRepCheck_NoError, BRepCheck_InvalidPointOnCurve, 122 | BRepCheck_InvalidPointOnCurveOnSurface, 123 | BRepCheck_InvalidPointOnSurface, 124 | BRepCheck_No3DCurve, BRepCheck_Multiple3DCurve, 125 | BRepCheck_Invalid3DCurve, BRepCheck_NoCurveOnSurface, 126 | BRepCheck_InvalidCurveOnSurface, 127 | BRepCheck_InvalidCurveOnClosedSurface, 128 | BRepCheck_InvalidSameRangeFlag, 129 | BRepCheck_InvalidSameParameterFlag, 130 | BRepCheck_InvalidDegeneratedFlag, BRepCheck_FreeEdge, 131 | BRepCheck_InvalidMultiConnexity, BRepCheck_InvalidRange, 132 | BRepCheck_EmptyWire, BRepCheck_RedundantEdge, 133 | BRepCheck_SelfIntersectingWire, BRepCheck_NoSurface, 134 | BRepCheck_InvalidWire, BRepCheck_RedundantWire, 135 | BRepCheck_IntersectingWires, 136 | BRepCheck_InvalidImbricationOfWires, 137 | BRepCheck_EmptyShell, BRepCheck_RedundantFace, 138 | BRepCheck_UnorientableShape, BRepCheck_NotClosed, 139 | BRepCheck_NotConnected, BRepCheck_SubshapeNotInShape, 140 | BRepCheck_BadOrientation, BRepCheck_BadOrientationOfSubshape, 141 | BRepCheck_InvalidToleranceValue, BRepCheck_CheckFail] 142 | 143 | brepcheck_lut = EnumLookup(_brep_check_a, _brep_check_b) 144 | curve_lut = EnumLookup(_curve_typesA, _curve_typesB) 145 | surface_lut = EnumLookup(_surface_typesA, _surface_typesB) 146 | state_lut = EnumLookup(_stateA, _stateB) 147 | orient_lut = EnumLookup(_orientA, _orientB) 148 | topo_lut = EnumLookup(_topoTypesA, _topoTypesB) 149 | shape_lut = ShapeToTopology() 150 | geom_lut = EnumLookup(_geom_types_a, _geom_types_b) 151 | 152 | # todo: refactor, these classes have been moved from the "Topology" directory 153 | # which had too many overlapping methods & classes, that are 154 | # now part of the KBE module... 155 | # still need to think what to do with these... 156 | # what_is_face should surely become a lut [ geom_lut? ] 157 | # i'm not sure whether casting to a gp_* is useful... 158 | 159 | classes = dir() 160 | geom_classes = [] 161 | for elem in classes: 162 | if elem.startswith('Geom') and not 'swig' in elem: 163 | geom_classes.append(elem) 164 | 165 | 166 | def what_is_face(face): 167 | ''' Returns all class names for which this class can be downcasted 168 | ''' 169 | if not face.ShapeType() == TopAbs_FACE: 170 | print('%s is not a TopAbs_FACE. Conversion impossible') 171 | return None 172 | hs = BRep_Tool_Surface(face) 173 | obj = hs.GetObject() 174 | result = [] 175 | for elem in classes: 176 | if (elem.startswith('Geom') and not 'swig' in elem): 177 | geom_classes.append(elem) 178 | # Run the test for each class 179 | for geom_class in geom_classes: 180 | if obj.IsKind(geom_class) and not geom_class in result: 181 | result.append(geom_class) 182 | return result 183 | 184 | 185 | def face_is_plane(face): 186 | ''' Returns True if the TopoDS_Shape is a plane, False otherwise 187 | ''' 188 | hs = BRep_Tool_Surface(face) 189 | downcast_result = Handle_Geom_Plane().DownCast(hs) 190 | # the handle is null if downcast failed or is not possible, 191 | # that is to say the face is not a plane 192 | if downcast_result.IsNull(): 193 | return False 194 | else: 195 | return True 196 | 197 | 198 | def shape_is_cylinder(face): 199 | ''' Returns True is the TopoDS_Shape is a cylinder, False otherwise 200 | ''' 201 | hs = BRep_Tool_Surface(face) 202 | downcast_result = Handle_Geom_CylindricalSurface().DownCast(hs) 203 | if downcast_result.IsNull(): 204 | return False 205 | else: 206 | return True 207 | -------------------------------------------------------------------------------- /misc/buildFaceBottomUp.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from math import pi 3 | 4 | from OCC.TopAbs import * 5 | from OCC.TopExp import * 6 | from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox 7 | from OCC.AIS import * 8 | from OCC.Quantity import * 9 | from OCC.Display.SimpleGui import init_display 10 | from OCC.TopoDS import * 11 | from OCC.gp import * 12 | from OCC.TopLoc import * 13 | from OCC.Geom import * 14 | from OCC.BRep import * 15 | from OCC.GCE2d import * 16 | from OCC.BRepBuilderAPI import * 17 | from OCC.GC import * 18 | from OCCUtils import Topology 19 | import aocutils.brep.solid_make 20 | 21 | display, start_display, add_menu, add_function_to_menu = init_display() 22 | 23 | 24 | def geom_plane_from_face(aFace): 25 | """ 26 | Returns the geometric plane entity from a planar surface 27 | """ 28 | return Handle_Geom_Plane.DownCast(OCC.BRep.BRep_Tool_Surface(aFace)).GetObject() 29 | 30 | def redraw(shape, event=None): 31 | # display with crisp edges and transpaarency 32 | context = display.Context 33 | context.RemoveAll() 34 | context.SetAutoActivateSelection(False) 35 | aisShape = AIS_Shape(shape) 36 | h_aisShape = aisShape.GetHandle() 37 | context.Display(h_aisShape) 38 | context.SetTransparency(h_aisShape, .1) 39 | context.HilightWithColor(h_aisShape, OCC.Quantity.Quantity_NOC_BLACK) 40 | display.FitAll() 41 | 42 | def makeBox(event=None): 43 | # Make a box 44 | Box = BRepPrimAPI_MakeBox(60, 60, 50).Shape() 45 | redraw() 46 | 47 | def rotateBox(): 48 | aisShape = AIS_Shape(Box) 49 | ax1 = gp_Ax1(gp_Pnt(0., 0., 0.), gp_Dir(1., 0., 0.)) 50 | aRotTrsf = gp_Trsf() 51 | angle = pi/6 52 | aRotTrsf.SetRotation(ax1, angle) 53 | aTopLoc = TopLoc_Location(aRotTrsf) 54 | Box.Move(aTopLoc) 55 | redraw() 56 | 57 | def enableFaceSelect(event=None): 58 | display.selected_shape = None 59 | display.SetSelectionModeFace() 60 | 61 | def makeSqProfile(size, surface): 62 | # points and segments need to be in CW sequence to get W pointing along Z 63 | aPnt1 = gp_Pnt2d(-size, size) 64 | aPnt2 = gp_Pnt2d(size, size) 65 | aPnt3 = gp_Pnt2d(size, -size) 66 | aPnt4 = gp_Pnt2d(-size, -size) 67 | aSegment1 = GCE2d_MakeSegment(aPnt1, aPnt2) 68 | aSegment2 = GCE2d_MakeSegment(aPnt2, aPnt3) 69 | aSegment3 = GCE2d_MakeSegment(aPnt3, aPnt4) 70 | aSegment4 = GCE2d_MakeSegment(aPnt4, aPnt1) 71 | print 'Next is where something crashes' 72 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value(), 73 | Handle_Geom_Surface(surface)) 74 | aEdge2 = BRepBuilderAPI_MakeEdge(aSegment2.Value(), 75 | Handle_Geom_Surface(surface)) 76 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment3.Value(), 77 | Handle_Geom_Surface(surface)) 78 | aEdge4 = BRepBuilderAPI_MakeEdge(aSegment4.Value(), 79 | Handle_Geom_Surface(surface)) 80 | print "Doesn't get here (with rotated box)" 81 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), 82 | aEdge2.Edge(), 83 | aEdge3.Edge(), 84 | aEdge4.Edge()) 85 | 86 | myWireProfile = aWire.Wire() 87 | return myWireProfile # TopoDS_Wire 88 | 89 | def wireProfileOnFace(event=None): 90 | aShape = display.GetSelectedShape() 91 | shapes = display.GetSelectedShapes() 92 | face = None 93 | if aShape: 94 | face = topods_Face(aShape) 95 | print "A shape found:" 96 | elif shapes: 97 | aShape = shapes[0] 98 | face = topods_Face(aShape) 99 | print len(shapes), "Shapes found" 100 | if face: 101 | surface = geom_plane_from_face(face) 102 | wireProfile = makeSqProfile(50, surface) 103 | display.DisplayShape(wireProfile) 104 | else: 105 | print 'no face' 106 | 107 | def translatePnt(p1, vec): 108 | p2 = gp_Pnt() 109 | p2 = p1.Translated(vec) 110 | return p2 111 | 112 | def pointsToWire(p1, p2, p3, p4): 113 | seg1 = GC_MakeSegment(p1, p2) 114 | seg2 = GC_MakeSegment(p2, p3) 115 | seg3 = GC_MakeSegment(p3, p4) 116 | seg4 = GC_MakeSegment(p4, p1) 117 | edge1 = BRepBuilderAPI_MakeEdge(seg1.Value()) 118 | edge2 = BRepBuilderAPI_MakeEdge(seg2.Value()) 119 | edge3 = BRepBuilderAPI_MakeEdge(seg3.Value()) 120 | edge4 = BRepBuilderAPI_MakeEdge(seg4.Value()) 121 | wire = BRepBuilderAPI_MakeWire(edge1.Edge(), edge2.Edge(), 122 | edge3.Edge(), edge4.Edge()) 123 | return wire.Wire() 124 | 125 | def sewBox(): 126 | # Length of shape (spine) 127 | Vec = gp_Vec(0, 0, 10) 128 | # starting with bot vertices, make bot wire & face 129 | p1 = gp_Pnt(0, 0, 0) 130 | p2 = gp_Pnt(20, 0, 0) 131 | p3 = gp_Pnt(20, 20, 0) 132 | p4 = gp_Pnt(0, 20, 0) 133 | botWire = pointsToWire(p1, p2, p3, p4) 134 | botFace = BRepBuilderAPI_MakeFace(botWire).Face() 135 | # starting with topvertices, make top face 136 | p5 = translatePnt(p1, Vec) 137 | p6 = translatePnt(p2, Vec) 138 | p7 = translatePnt(p3, Vec) 139 | p8 = translatePnt(p4, Vec) 140 | topWire = pointsToWire(p5, p6, p7, p8) 141 | topFace = BRepBuilderAPI_MakeFace(topWire).Face() 142 | # Make spine (wire) to make 'pipe' 143 | spineSeg = GC_MakeSegment(p1, p5) 144 | spineEdge = BRepBuilderAPI_MakeEdge(spineSeg.Value()) 145 | spineWire = BRepBuilderAPI_MakeWire(spineEdge.Edge()).Wire() 146 | pipe = OCC.BRepOffsetAPI.BRepOffsetAPI_MakePipe(botWire, spineWire).Shape() 147 | # Sew together botFace, pipe, and topFace to get solid 148 | tolerance = 1e-6 149 | sew = OCC.BRepBuilderAPI.BRepBuilderAPI_Sewing(tolerance) 150 | sew.Add(botFace) 151 | sew.Add(pipe) 152 | sew.Add(topFace) 153 | sew.Perform() 154 | res = sew.SewedShape() 155 | redraw(res) 156 | 157 | def buildFaceBotUp(): 158 | """Following procedure in Roman Lygen's blog 159 | """ 160 | origin = gp_Pnt(0,0,0) 161 | wDir = gp_Dir(0,0,1) 162 | uDir = gp_Dir(1,0,0) 163 | vDir = gp_Dir(0,1,0) 164 | xyzAx3 = gp_Ax3(origin, wDir, uDir) 165 | gpPlane = gp_Pln(xyzAx3) # type OCC.gp.gp_Pln 166 | # gpPlane = gp_XOY() # type gp_Ax2 167 | plane = Geom_Plane(gpPlane) # type: OCC.Geom.Geom_Plane 168 | aSurf = Handle_Geom_Surface(plane) # type: Handle_Geom_Surface 169 | anExtC = Geom_Circle(gp_XOY(), 10.0) # type: OCC.Geom.Geom_Circle 170 | anIntC = Geom_Circle(gp_XOY(), 5.0) # type: OCC.Geom.Geom_Circle 171 | anExtE = BRepBuilderAPI_MakeEdge(anExtC.GetHandle()) 172 | anIntE = BRepBuilderAPI_MakeEdge(anIntC.GetHandle()) 173 | anExtWire = BRepBuilderAPI_MakeWire(anExtE.Edge()) 174 | anIntWire = BRepBuilderAPI_MakeWire(anIntE.Edge()) 175 | aFace = BRepBuilderAPI_MakeFace(anExtWire.Wire()) 176 | aFace.Add(anIntWire.Wire()) # adds wire to the face as a hole 177 | display.DisplayShape(aFace.Face()) 178 | edgeList = [] 179 | anEdgeExplorer = TopExp_Explorer(aFace.Face(), TopAbs_EDGE) 180 | while anEdgeExplorer.More(): 181 | anEdge = topods.Edge(anEdgeExplorer.Current()) 182 | anEdgeExplorer.Next() 183 | edgeList.append(anEdge) 184 | print 'Number of edges: ', len(edgeList) 185 | Topology.dumpTopology(aFace.Face()) 186 | topo = Topology.Topo(aFace.Face()) 187 | print 'Number of wires: ', topo.number_of_wires_from_face(aFace.Face()) 188 | wires = topo.wires_from_face(aFace.Face()) 189 | for wire in wires: 190 | display.DisplayShape(wire) 191 | 192 | def testEdge(): 193 | origin = gp_Pnt(0,0,0) 194 | wDir = gp_Dir(0,0,1) 195 | uDir = gp_Dir(1,0,0) 196 | vDir = gp_Dir(0,1,0) 197 | xyzAx3 = gp_Ax3(origin, wDir, uDir) 198 | gpPlane = gp_Pln(xyzAx3) # type OCC.gp.gp_Pln 199 | # gpPlane = gp_XOY() # type gp_Ax2 200 | plane = Geom_Plane(gpPlane) # type: OCC.Geom.Geom_Plane 201 | aSurf = Handle_Geom_Surface(plane) # type: Handle_Geom_Surface 202 | anExtC = Geom_Circle(gp_XOY(), 10.0) # type: OCC.Geom.Geom_Circle 203 | anIntC = Geom_Circle(gp_XOY(), 5.0) # type: OCC.Geom.Geom_Circle 204 | anExtE = BRepBuilderAPI_MakeEdge(anExtC.GetHandle()) 205 | anIntE = BRepBuilderAPI_MakeEdge(anIntC.GetHandle()) 206 | anExtWire = BRepBuilderAPI_MakeWire(anExtE.Edge()) 207 | anIntWire = BRepBuilderAPI_MakeWire(anIntE.Edge()) 208 | aFace = BRepBuilderAPI_MakeFace(anExtWire.Wire()) 209 | aFace.Add(anIntWire.Wire()) # adds wire to the face as a hole 210 | display.DisplayShape(aFace.Face()) 211 | edgeList = [] 212 | anEdgeExplorer = TopExp_Explorer(aFace.Face(), TopAbs_EDGE) 213 | while anEdgeExplorer.More(): 214 | anEdge = topods.Edge(anEdgeExplorer.Current()) 215 | 216 | anEdgeExplorer.Next() 217 | edgeList.append(anEdge) 218 | print 'Number of edges: ', len(edgeList) 219 | for edge in edgeList: 220 | hCurve, umin, umax = BRep_Tool.Curve(edge) 221 | curve = hCurve.GetObject() # type: Geom_Curve 222 | print 'umin = ', umin 223 | print 'umax = ', umax 224 | print 'Is Periodic? ', curve.IsPeriodic() 225 | print 'Shape: ', curve.Continuity() 226 | print 'first: ', curve.FirstParameter() 227 | print 'last: ', curve.LastParameter() 228 | vectr = curve.DN(0.0, 2) 229 | print 'curvature at u=0: ', vectr.Magnitude() 230 | 231 | seg = GC_MakeSegment(gp_Pnt(0,0,0), gp_Pnt(2,2,2)) 232 | edg = BRepBuilderAPI_MakeEdge(seg.Value()).Edge() 233 | print type(edg) 234 | hCurve, umin, umax = BRep_Tool.Curve(edg) 235 | curve = hCurve.GetObject() 236 | print type(curve) # type: Geom_Curve 237 | print 'Is Periodic? ', curve.IsPeriodic() 238 | print 'Shape: ', curve.Continuity() 239 | print 'first: ', curve.FirstParameter() 240 | print 'last: ', curve.LastParameter() 241 | vectr = curve.DN(0.0, 2) 242 | print 'curvature at u=0: ', vectr.Magnitude() 243 | 244 | 245 | def exit(event=None): 246 | sys.exit() 247 | 248 | if __name__ == '__main__': 249 | add_menu('operations') 250 | add_function_to_menu('operations', makeBox) 251 | add_function_to_menu('operations', rotateBox) 252 | add_function_to_menu('operations', enableFaceSelect) 253 | add_function_to_menu('operations', wireProfileOnFace) 254 | add_function_to_menu('operations', exit) 255 | add_menu('Experimental') 256 | add_function_to_menu('Experimental', sewBox) 257 | add_function_to_menu('Experimental', buildFaceBotUp) 258 | add_function_to_menu('Experimental', testEdge) 259 | 260 | start_display() 261 | -------------------------------------------------------------------------------- /misc/tkrpncalc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # CADvas 4 | # A 2D CAD application written in Python and based on the Tkinter canvas. 5 | # The latest version of this file can be found at: 6 | # http://members.localnet.com/~blanding/cadvas 7 | # 8 | # Author: Doug Blanding 9 | # 10 | # CADvas is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2 of the License, or 13 | # (at your option) any later version. 14 | # 15 | # CADvas is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with CADvas; if not, write to the Free Software 22 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 | # 24 | 25 | from __future__ import division 26 | import sys 27 | from Tkinter import * 28 | import math 29 | 30 | def but(root, text, row, col, com=None, span=2, clr='darkslateblue', pad=1): 31 | w = Button(root, text=text, command=com, bg=clr, fg='white', padx=pad) 32 | w.grid(row=row, column=col, columnspan=span, sticky=E+W) 33 | 34 | def ent(root, var, row, col=2, span=10): 35 | e = Entry(root, textvariable=var, relief=SUNKEN) 36 | e.grid(row=row, column=col, columnspan=span) 37 | 38 | def f2s(f): 39 | """Convert float to string with 12 significant figures.""" 40 | return '%1.12f' % f 41 | 42 | class Calculator(Toplevel): 43 | """RPN (Reverse Polish Notation) calculator styled after the one 44 | used in CoCreate SolidDesigner CAD.""" 45 | mem = '' 46 | keip = False # Flag set when keyboard entry is in progress 47 | needrup = False # Flag signaling need to rotate up with next keyboard entry 48 | def __init__(self, caller=None): 49 | Toplevel.__init__(self) 50 | self.caller = caller # ref to Draw instance 51 | self.title('RPN Calc') 52 | self.protocol("WM_DELETE_WINDOW", self.quit) 53 | #self.resizable(width=0, height=0) 54 | if caller: 55 | self.transient(caller) 56 | 57 | but(self, 't', 0, 0, lambda r='t': self.pr(r), clr='dimgray') 58 | but(self, 'z', 1, 0, lambda r='z': self.pr(r), clr='dimgray') 59 | but(self, 'y', 2, 0, lambda r='y': self.pr(r), clr='dimgray') 60 | but(self, 'x', 3, 0, lambda r='x': self.pr(r), clr='dimgray') 61 | 62 | self.tdisplay = StringVar() 63 | self.zdisplay = StringVar() 64 | self.ydisplay = StringVar() 65 | self.xdisplay = StringVar() 66 | ent(self, self.tdisplay, 0) 67 | ent(self, self.zdisplay, 1) 68 | ent(self, self.ydisplay, 2) 69 | ent(self, self.xdisplay, 3) 70 | 71 | but(self, 'mm->in', 4, 0, self.mm2in, span=4, clr='dimgray') 72 | but(self, 'in->mm', 4, 4, self.in2mm, span=4, clr='dimgray') 73 | but(self, 'Sto', 4, 8, self.storex, clr='darkgreen') 74 | but(self, 'Rcl', 4, 10, self.recallx, clr='darkgreen') 75 | but(self, '7', 5, 0, lambda c='7': self.keyin(c), clr='steelblue') 76 | but(self, '8', 5, 2, lambda c='8': self.keyin(c), clr='steelblue') 77 | but(self, '9', 5, 4, lambda c='9': self.keyin(c), clr='steelblue') 78 | but(self, '+', 5, 6, lambda op='+': self.calc(op)) 79 | but(self, 'Rup', 5, 8, self.rotateup, clr='darkgreen') 80 | but(self, 'Rdn', 5, 10, self.rotatedn, clr='darkgreen') 81 | but(self, '4', 6, 0, lambda c='4': self.keyin(c), clr='steelblue') 82 | but(self, '5', 6, 2, lambda c='5': self.keyin(c), clr='steelblue') 83 | but(self, '6', 6, 4, lambda c='6': self.keyin(c), clr='steelblue') 84 | but(self, '-', 6, 6, lambda op='-': self.calc(op)) 85 | but(self, '<-', 6, 8, self.trimx, clr='darkred') 86 | but(self, 'x<>y', 6, 10, self.swapxy, clr='darkgreen', pad=0) 87 | but(self, '1', 7, 0, lambda c='1': self.keyin(c), clr='steelblue') 88 | but(self, '2', 7, 2, lambda c='2': self.keyin(c), clr='steelblue') 89 | but(self, '3', 7, 4, lambda c='3': self.keyin(c), clr='steelblue') 90 | but(self, '*', 7, 6, lambda op='*': self.calc(op)) 91 | but(self, 'Clx', 7, 8, self.clearx, clr='darkred') 92 | but(self, 'Clr', 7, 10, self.clearall, clr='darkred') 93 | but(self, '0', 8, 0, lambda c='0': self.keyin(c), clr='steelblue', pad=3) 94 | but(self, '.', 8, 2, lambda c='.': self.keyin(c)) 95 | but(self, '+/-', 8, 4, lambda op='+/-': self.calc(op)) 96 | but(self, ' / ', 8, 6, lambda c='/': self.calc(c), pad=3) 97 | but(self, 'ENTER', 8, 8, self.enter, span=4, clr='darkgoldenrod') 98 | but(self, 'Sin', 9, 0, lambda op='math.sin(x)': self.func(op, in_cnvrt=1), 99 | span=3, clr='darkgoldenrod') 100 | but(self, 'Cos', 9, 3, lambda op='math.cos(x)': self.func(op, in_cnvrt=1), 101 | span=3, clr='darkgoldenrod') 102 | but(self, 'Tan', 9, 6, lambda op='math.tan(x)': self.func(op, in_cnvrt=1), 103 | span=3, clr='darkgoldenrod') 104 | but(self, 'Pi', 9, 9, lambda op='math.pi': self.func(op), span=3, clr='darkgoldenrod') 105 | but(self, 'ASin', 10, 0, lambda op='math.asin(x)': self.func(op, out_cnvrt=1), 106 | span=3, clr='darkgoldenrod') 107 | but(self, 'ACos', 10, 3, lambda op='math.acos(x)': self.func(op, out_cnvrt=1), 108 | span=3, clr='darkgoldenrod') 109 | but(self, 'ATan', 10, 6, lambda op='math.atan(x)': self.func(op, out_cnvrt=1), 110 | span=3, clr='darkgoldenrod') 111 | but(self, '', 10, 9, span=3, clr='darkgoldenrod') 112 | but(self, 'x^2', 11, 0, lambda op='x**2': self.func(op), span=3, clr='darkgreen') 113 | but(self, '1/x', 11, 3, lambda op='1/x': self.func(op), span=3, clr='darkgreen') 114 | but(self, 'e^x', 11, 6, lambda op='math.e**x': self.func(op), span=3, clr='darkgreen') 115 | but(self, '10^x', 11, 9, lambda op='10**x': self.func(op), span=3, clr='darkgreen') 116 | but(self, 'Sqrt', 12, 0, lambda op='math.sqrt(x)': self.func(op), span=3, clr='darkgreen') 117 | but(self, 'y^x', 12, 3, lambda op='y**x': self.func(op), span=3, clr='darkgreen') 118 | but(self, 'ln', 12, 6, lambda op='math.log(x)': self.func(op), span=3, clr='darkgreen') 119 | but(self, 'log', 12, 9, lambda op='math.log10(x)': self.func(op), span=3, clr='darkgreen') 120 | 121 | 122 | def quit(self): 123 | if self.caller: 124 | self.caller.calculator = None 125 | self.destroy() 126 | 127 | def pr(self, val): 128 | """Send value in register to caller.""" 129 | # There must be a better way to get this value 130 | str_value = `eval('self.'+val+'display.get()')`.strip("'") 131 | self.caller.enterfloat(str_value) 132 | self.keip = False 133 | self.needrup = True 134 | 135 | def keyin(self, c): 136 | if self.keip: 137 | self.xdisplay.set(self.xdisplay.get()+c) 138 | else: 139 | self.keip = True 140 | if self.needrup: 141 | self.rotateup(loop=0) 142 | self.clearx() 143 | self.keyin(c) 144 | 145 | def enter(self): 146 | self.tdisplay.set(self.zdisplay.get()) 147 | self.zdisplay.set(self.ydisplay.get()) 148 | self.ydisplay.set(self.xdisplay.get()) 149 | self.keip = False 150 | self.needrup = False 151 | 152 | def calc(self, op): 153 | """Arithmetic calculations between x and y registers, then rotate down.""" 154 | try: 155 | if op == '+/-': 156 | self.xdisplay.set(`eval('-'+self.xdisplay.get())`) 157 | else: 158 | x = `eval(self.ydisplay.get()+op+self.xdisplay.get())` 159 | self.xdisplay.set(x) 160 | self.ydisplay.set(self.zdisplay.get()) 161 | self.zdisplay.set(self.tdisplay.get()) 162 | self.keip = False 163 | self.needrup = True 164 | except: 165 | self.xdisplay.set("ERROR") 166 | 167 | 168 | def func(self, op, in_cnvrt=0, out_cnvrt=0): 169 | """Evaluate function op then put result in x-register, don't rotate stack. 170 | if in_cnvrt: convert input value of x-register from degrees to radians. 171 | if out_cnvrt: convert output value of x-register from radians to degrees.""" 172 | try: 173 | x = float(self.xdisplay.get()) 174 | except: 175 | x = 0 176 | try: 177 | y = float(self.ydisplay.get()) 178 | except: 179 | y = 0 180 | if in_cnvrt: 181 | x = x * math.pi / 180 182 | result = eval(op) 183 | if out_cnvrt: 184 | result = result * 180 / math.pi 185 | self.xdisplay.set(f2s(result)) 186 | self.keip = False 187 | self.needrup = True 188 | 189 | def mm2in(self): 190 | if self.xdisplay.get(): 191 | self.xdisplay.set(`eval(self.xdisplay.get()+'/25.4')`) 192 | self.keip = False 193 | self.needrup = True 194 | 195 | def in2mm(self): 196 | if self.xdisplay.get(): 197 | self.xdisplay.set(`eval(self.xdisplay.get()+'*25.4')`) 198 | self.keip = False 199 | self.needrup = True 200 | 201 | def storex(self): 202 | self.mem = self.xdisplay.get() 203 | self.keip = False 204 | self.needrup = True 205 | 206 | def recallx(self): 207 | self.rotateup() 208 | self.xdisplay.set(self.mem) 209 | self.keip = False 210 | self.needrup = True 211 | 212 | def rotateup(self, loop=1): 213 | x = self.tdisplay.get() 214 | self.tdisplay.set(self.zdisplay.get()) 215 | self.zdisplay.set(self.ydisplay.get()) 216 | self.ydisplay.set(self.xdisplay.get()) 217 | if loop: 218 | self.xdisplay.set(x) 219 | 220 | def rotatedn(self): 221 | x = self.xdisplay.get() 222 | self.xdisplay.set(self.ydisplay.get()) 223 | self.ydisplay.set(self.zdisplay.get()) 224 | self.zdisplay.set(self.tdisplay.get()) 225 | self.tdisplay.set(x) 226 | 227 | def trimx(self): 228 | self.xdisplay.set(self.xdisplay.get()[:-1]) 229 | 230 | def swapxy(self): 231 | x = self.xdisplay.get() 232 | y = self.ydisplay.get() 233 | self.xdisplay.set(y) 234 | self.ydisplay.set(x) 235 | 236 | def clearx(self): 237 | self.xdisplay.set('') 238 | 239 | def clearall(self): 240 | self.xdisplay.set('') 241 | self.ydisplay.set('') 242 | self.zdisplay.set('') 243 | self.tdisplay.set('') 244 | 245 | def putx(self, value): 246 | if self.needrup: 247 | self.rotateup(loop=0) 248 | self.xdisplay.set(`value`) 249 | self.keip = False 250 | self.needrup = True 251 | 252 | if __name__ == '__main__': 253 | Calculator().mainloop() 254 | -------------------------------------------------------------------------------- /misc/core_topology_local_ops.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2009-2016 Thomas Paviot (tpaviot@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see . 17 | import sys 18 | from math import pi 19 | 20 | from OCC.BRep import BRep_Tool_Surface 21 | from OCC.BRepAlgoAPI import BRepAlgoAPI_Section, BRepAlgoAPI_Fuse 22 | from OCC.BRepBuilderAPI import BRepBuilderAPI_MakeWire, BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace, \ 23 | BRepBuilderAPI_GTransform 24 | from OCC.BRepFeat import BRepFeat_MakePrism, BRepFeat_MakeDPrism, BRepFeat_SplitShape, \ 25 | BRepFeat_MakeLinearForm, BRepFeat_MakeRevol 26 | from OCC.BRepLib import breplib_BuildCurves3d 27 | from OCC.BRepOffset import BRepOffset_Skin 28 | from OCC.BRepOffsetAPI import BRepOffsetAPI_MakeThickSolid, BRepOffsetAPI_MakeOffsetShape 29 | from OCC.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakePrism 30 | from OCC.Display.SimpleGui import init_display 31 | from OCC.GCE2d import GCE2d_MakeLine 32 | from OCC.Geom import Handle_Geom_Plane_DownCast, Geom_Plane 33 | from OCC.Geom2d import Geom2d_Circle 34 | from OCC.GeomAbs import GeomAbs_Arc 35 | from OCC.TopTools import TopTools_ListOfShape 36 | from OCC.TopoDS import TopoDS_Face 37 | from OCC.gp import gp_Pnt2d, gp_Circ2d, gp_Ax2d, gp_Dir2d, gp_Pnt, gp_Pln, gp_Vec, gp_OX, gp_Trsf, gp_GTrsf 38 | 39 | from core_topology_traverse import Topo 40 | 41 | display, start_display, add_menu, add_function_to_menu = init_display() 42 | 43 | 44 | def extrusion(event=None): 45 | # Make a box 46 | Box = BRepPrimAPI_MakeBox(400., 250., 300.) 47 | S = Box.Shape() 48 | 49 | # Choose the first Face of the box 50 | F = next(Topo(S).faces()) 51 | surf = BRep_Tool_Surface(F) 52 | 53 | # Make a plane from this face 54 | Pl = Handle_Geom_Plane_DownCast(surf) 55 | Pln = Pl.GetObject() 56 | 57 | # Get the normal of this plane. This will be the direction of extrusion. 58 | D = Pln.Axis().Direction() 59 | 60 | # Inverse normal 61 | #D.Reverse() 62 | 63 | # Create the 2D planar sketch 64 | MW = BRepBuilderAPI_MakeWire() 65 | p1 = gp_Pnt2d(200., -100.) 66 | p2 = gp_Pnt2d(100., -100.) 67 | aline = GCE2d_MakeLine(p1, p2).Value() 68 | Edge1 = BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)) 69 | MW.Add(Edge1.Edge()) 70 | p1 = p2 71 | p2 = gp_Pnt2d(100., -200.) 72 | aline = GCE2d_MakeLine(p1, p2).Value() 73 | Edge2 = BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)) 74 | MW.Add(Edge2.Edge()) 75 | p1 = p2 76 | p2 = gp_Pnt2d(200., -200.) 77 | aline = GCE2d_MakeLine(p1, p2).Value() 78 | Edge3 = BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)) 79 | MW.Add(Edge3.Edge()) 80 | p1 = p2 81 | p2 = gp_Pnt2d(200., -100.) 82 | aline = GCE2d_MakeLine(p1, p2).Value() 83 | Edge4 = BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)) 84 | MW.Add(Edge4.Edge()) 85 | 86 | # Build Face from Wire. NB: a face is required to generate a solid. 87 | MKF = BRepBuilderAPI_MakeFace() 88 | MKF.Init(surf, False, 1e-6) 89 | MKF.Add(MW.Wire()) 90 | FP = MKF.Face() 91 | breplib_BuildCurves3d(FP) 92 | 93 | MKP = BRepFeat_MakePrism(S, FP, F, D, False, True) 94 | MKP.Perform(200.) 95 | # TODO MKP completes, seeing a split operation but no extrusion 96 | assert MKP.IsDone() 97 | res1 = MKP.Shape() 98 | 99 | display.EraseAll() 100 | display.DisplayColoredShape(res1, 'BLUE') 101 | display.DisplayColoredShape(FP, 'YELLOW') 102 | display.FitAll() 103 | 104 | 105 | def brepfeat_prism(event=None): 106 | box = BRepPrimAPI_MakeBox(400, 250, 300).Shape() 107 | faces = Topo(box).faces() 108 | 109 | for i in range(3): 110 | face = next(faces) 111 | 112 | srf = BRep_Tool_Surface(face) 113 | 114 | c = gp_Circ2d(gp_Ax2d(gp_Pnt2d(200, 130), 115 | gp_Dir2d(1, 0)), 75) 116 | 117 | circle = Geom2d_Circle(c).GetHandle() 118 | 119 | wire = BRepBuilderAPI_MakeWire() 120 | wire.Add(BRepBuilderAPI_MakeEdge(circle, srf, 0., pi).Edge()) 121 | wire.Add(BRepBuilderAPI_MakeEdge(circle, srf, pi, 2. * pi).Edge()) 122 | wire.Build() 123 | 124 | display.DisplayShape(wire.Wire()) 125 | 126 | mkf = BRepBuilderAPI_MakeFace() 127 | mkf.Init(srf, False, 1e-6) 128 | mkf.Add(wire.Wire()) 129 | mkf.Build() 130 | 131 | new_face = mkf.Face() 132 | breplib_BuildCurves3d(new_face) 133 | 134 | display.DisplayColoredShape(box, 'GREEN') 135 | display.DisplayShape(new_face) 136 | """ 137 | prism = BRepFeat_MakeDPrism(box, mkf.Face(), face, 100, True, True) 138 | 139 | prism.Perform(400) 140 | assert prism.IsDone() 141 | display.EraseAll() 142 | display.DisplayShape(prism.Shape()) 143 | """ 144 | #display.DisplayColoredShape(wire.Wire(), 'RED') 145 | display.FitAll() 146 | 147 | 148 | def thick_solid(event=None): 149 | S = BRepPrimAPI_MakeBox(150, 200, 110).Shape() 150 | 151 | topo = Topo(S) 152 | vert = next(topo.vertices()) 153 | 154 | shapes = TopTools_ListOfShape() 155 | for f in topo.faces_from_vertex(vert): 156 | shapes.Append(f) 157 | 158 | _thick_solid = BRepOffsetAPI_MakeThickSolid(S, shapes, 15, 0.01) 159 | display.EraseAll() 160 | display.DisplayShape(_thick_solid.Shape()) 161 | display.FitAll() 162 | 163 | 164 | def offset_cube(event=None): 165 | S2 = BRepPrimAPI_MakeBox(gp_Pnt(300, 0, 0), 220, 140, 180).Shape() 166 | offsetB = BRepOffsetAPI_MakeOffsetShape(S2, -20, 0.01, BRepOffset_Skin, False, False, GeomAbs_Arc) 167 | offB = display.DisplayColoredShape(S2, 'BLUE') 168 | display.Context.SetTransparency(offB, 0.3) 169 | display.DisplayColoredShape(offsetB.Shape(), 'GREEN') 170 | display.FitAll() 171 | 172 | 173 | def split_shape(event=None): 174 | S = BRepPrimAPI_MakeBox(gp_Pnt(-100, -60, -80), 150, 200, 170).Shape() 175 | asect = BRepAlgoAPI_Section(S, gp_Pln(1, 2, 1, -15), False) 176 | asect.ComputePCurveOn1(True) 177 | asect.Approximation(True) 178 | asect.Build() 179 | R = asect.Shape() 180 | 181 | asplit = BRepFeat_SplitShape(S) 182 | 183 | for edg in Topo(R).edges(): 184 | face = TopoDS_Face() 185 | if asect.HasAncestorFaceOn1(edg, face): 186 | asplit.Add(edg, face) 187 | 188 | asplit.Build() 189 | display.EraseAll() 190 | display.DisplayShape(asplit.Shape()) 191 | display.FitAll() 192 | 193 | 194 | def brep_feat_rib(event=None): 195 | mkw = BRepBuilderAPI_MakeWire() 196 | 197 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(0., 0., 0.), gp_Pnt(200., 0., 0.)).Edge()) 198 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(200., 0., 0.), gp_Pnt(200., 0., 50.)).Edge()) 199 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(200., 0., 50.), gp_Pnt(50., 0., 50.)).Edge()) 200 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(50., 0., 50.), gp_Pnt(50., 0., 200.)).Edge()) 201 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(50., 0., 200.), gp_Pnt(0., 0., 200.)).Edge()) 202 | mkw.Add(BRepBuilderAPI_MakeEdge(gp_Pnt(0., 0., 200.), gp_Pnt(0., 0., 0.)).Edge()) 203 | 204 | S = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(mkw.Wire()).Face(), 205 | gp_Vec(gp_Pnt(0., 0., 0.), 206 | gp_Pnt(0., 100., 0.))) 207 | display.EraseAll() 208 | # display.DisplayShape(S.Shape()) 209 | 210 | W = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(gp_Pnt(50., 45., 100.), 211 | gp_Pnt(100., 45., 50.)).Edge()) 212 | 213 | aplane = Geom_Plane(0., 1., 0., -45.) 214 | 215 | aform = BRepFeat_MakeLinearForm(S.Shape(), W.Wire(), aplane.GetHandle(), 216 | gp_Vec(0., 10., 0.), gp_Vec(0., 0., 0.), 217 | 1, True) 218 | aform.Perform() 219 | display.DisplayShape(aform.Shape()) 220 | display.FitAll() 221 | 222 | 223 | def brep_feat_local_revolution(event=None): 224 | S = BRepPrimAPI_MakeBox(400., 250., 300.).Shape() 225 | faces = list(Topo(S).faces()) 226 | F1 = faces[2] 227 | surf = BRep_Tool_Surface(F1) 228 | 229 | D = gp_OX() 230 | 231 | MW1 = BRepBuilderAPI_MakeWire() 232 | p1 = gp_Pnt2d(100., 100.) 233 | p2 = gp_Pnt2d(200., 100.) 234 | aline = GCE2d_MakeLine(p1, p2).Value() 235 | MW1.Add(BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)).Edge()) 236 | 237 | p1 = gp_Pnt2d(200., 100.) 238 | p2 = gp_Pnt2d(150., 200.) 239 | aline = GCE2d_MakeLine(p1, p2).Value() 240 | MW1.Add(BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)).Edge()) 241 | 242 | p1 = gp_Pnt2d(150., 200.) 243 | p2 = gp_Pnt2d(100., 100.) 244 | aline = GCE2d_MakeLine(p1, p2).Value() 245 | MW1.Add(BRepBuilderAPI_MakeEdge(aline, surf, 0., p1.Distance(p2)).Edge()) 246 | 247 | MKF1 = BRepBuilderAPI_MakeFace() 248 | MKF1.Init(surf, False, 1e-6) 249 | MKF1.Add(MW1.Wire()) 250 | FP = MKF1.Face() 251 | breplib_BuildCurves3d(FP) 252 | MKrev = BRepFeat_MakeRevol(S, FP, F1, D, 1, True) 253 | F2 = faces[4] 254 | MKrev.Perform(F2) 255 | display.EraseAll() 256 | display.DisplayShape(MKrev.Shape()) 257 | display.FitAll() 258 | 259 | 260 | def brep_feat_extrusion_protrusion(event=None): 261 | # Extrusion 262 | S = BRepPrimAPI_MakeBox(400., 250., 300.).Shape() 263 | faces = Topo(S).faces() 264 | F = next(faces) 265 | surf1 = BRep_Tool_Surface(F) 266 | 267 | Pl1 = Handle_Geom_Plane_DownCast(surf1).GetObject() 268 | 269 | D1 = Pl1.Pln().Axis().Direction().Reversed() 270 | MW = BRepBuilderAPI_MakeWire() 271 | p1, p2 = gp_Pnt2d(200., -100.), gp_Pnt2d(100., -100.) 272 | aline = GCE2d_MakeLine(p1, p2).Value() 273 | MW.Add(BRepBuilderAPI_MakeEdge(aline, surf1, 0., p1.Distance(p2)).Edge()) 274 | 275 | p1, p2 = gp_Pnt2d(100., -100.), gp_Pnt2d(100., -200.) 276 | aline = GCE2d_MakeLine(p1, p2).Value() 277 | MW.Add(BRepBuilderAPI_MakeEdge(aline, surf1, 0., p1.Distance(p2)).Edge()) 278 | 279 | p1, p2 = gp_Pnt2d(100., -200.), gp_Pnt2d(200., -200.) 280 | aline = GCE2d_MakeLine(p1, p2).Value() 281 | MW.Add(BRepBuilderAPI_MakeEdge(aline, surf1, 0., p1.Distance(p2)).Edge()) 282 | 283 | p1, p2 = gp_Pnt2d(200., -200.), gp_Pnt2d(200., -100.) 284 | aline = GCE2d_MakeLine(p1, p2).Value() 285 | MW.Add(BRepBuilderAPI_MakeEdge(aline, surf1, 0., p1.Distance(p2)).Edge()) 286 | 287 | MKF = BRepBuilderAPI_MakeFace() 288 | MKF.Init(surf1, False, 1e-6) 289 | MKF.Add(MW.Wire()) 290 | FP = MKF.Face() 291 | breplib_BuildCurves3d(FP) 292 | 293 | display.EraseAll() 294 | MKP = BRepFeat_MakePrism(S, FP, F, D1, 0, True) 295 | MKP.PerformThruAll() 296 | 297 | res1 = MKP.Shape() 298 | display.DisplayShape(res1) 299 | 300 | # Protrusion 301 | next(faces) 302 | F2 = next(faces) 303 | surf2 = BRep_Tool_Surface(F2) 304 | Pl2 = Handle_Geom_Plane_DownCast(surf2).GetObject() 305 | D2 = Pl2.Pln().Axis().Direction().Reversed() 306 | MW2 = BRepBuilderAPI_MakeWire() 307 | p1, p2 = gp_Pnt2d(100., 100.), gp_Pnt2d(200., 100.) 308 | aline = GCE2d_MakeLine(p1, p2).Value() 309 | MW2.Add(BRepBuilderAPI_MakeEdge(aline, surf2, 0., p1.Distance(p2)).Edge()) 310 | 311 | p1, p2 = gp_Pnt2d(200., 100.), gp_Pnt2d(150., 200.) 312 | aline = GCE2d_MakeLine(p1, p2).Value() 313 | MW2.Add(BRepBuilderAPI_MakeEdge(aline, surf2, 0., p1.Distance(p2)).Edge()) 314 | 315 | p1, p2 = gp_Pnt2d(150., 200.), gp_Pnt2d(100., 100.) 316 | aline = GCE2d_MakeLine(p1, p2).Value() 317 | MW2.Add(BRepBuilderAPI_MakeEdge(aline, surf2, 0., p1.Distance(p2)).Edge()) 318 | 319 | MKF2 = BRepBuilderAPI_MakeFace() 320 | MKF2.Init(surf2, False, 1e-6) 321 | MKF2.Add(MW2.Wire()) 322 | MKF2.Build() 323 | 324 | FP = MKF2.Face() 325 | breplib_BuildCurves3d(FP) 326 | MKP2 = BRepFeat_MakePrism(res1, FP, F2, D2, 0, True) 327 | MKP2.PerformThruAll() 328 | display.EraseAll() 329 | 330 | trf = gp_Trsf() 331 | trf.SetTranslation(gp_Vec(0, 0, 300)) 332 | gtrf = gp_GTrsf() 333 | gtrf.SetTrsf(trf) 334 | tr = BRepBuilderAPI_GTransform(MKP2.Shape(), gtrf, True) 335 | 336 | fused = BRepAlgoAPI_Fuse(tr.Shape(), MKP2.Shape()) 337 | fused.RefineEdges() 338 | fused.Build() 339 | print('Boolean operation error status:', fused.ErrorStatus()) 340 | display.DisplayShape(fused.Shape()) 341 | display.FitAll() 342 | 343 | def exit(event=None): 344 | sys.exit() 345 | 346 | 347 | if __name__ == '__main__': 348 | add_menu('topology local operations') 349 | add_function_to_menu('topology local operations', brepfeat_prism) 350 | add_function_to_menu('topology local operations', extrusion) 351 | add_function_to_menu('topology local operations', thick_solid) 352 | add_function_to_menu('topology local operations', offset_cube) 353 | add_function_to_menu('topology local operations', split_shape) 354 | add_function_to_menu('topology local operations', brep_feat_rib) 355 | add_function_to_menu('topology local operations', brep_feat_local_revolution) 356 | add_function_to_menu('topology local operations', brep_feat_extrusion_protrusion) 357 | add_function_to_menu('topology local operations', exit) 358 | start_display() 359 | -------------------------------------------------------------------------------- /myStepXcafReader.py: -------------------------------------------------------------------------------- 1 | # 2 | # myStepXcafReader.py 3 | # The goal of this module is to be able to read (and write) step files with complete 4 | # Assembly / Part structure, including the names of parts and assemblies, colors 5 | # of parts, and with all components shown in their correct positions. 6 | # The latest version of this file can be found at: 7 | # //https://github.com/dblanding/cadviewer 8 | # 9 | # Author: Doug Blanding 10 | # 11 | # myStepXcafReader is free software; you can redistribute it and/or modify 12 | # it under the terms of the GNU General Public License as published by 13 | # the Free Software Foundation; either version 2 of the License, or 14 | # (at your option) any later version. 15 | # 16 | # myStepXcafReader is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # if not, write to the Free Software Foundation, Inc. 23 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 24 | # 25 | 26 | 27 | from __future__ import print_function 28 | 29 | import logging 30 | import os.path 31 | import OCC.Core.BRep 32 | from OCC.Core.IFSelect import IFSelect_RetDone 33 | import OCC.Core.Interface 34 | import OCC.Core.Quantity 35 | from OCC.Core.STEPCAFControl import STEPCAFControl_Reader 36 | import OCC.Core.STEPControl 37 | from OCC.Core.TDataStd import TDataStd_Name, TDataStd_Name_GetID 38 | from OCC.Core.TCollection import TCollection_ExtendedString 39 | import OCC.Core.TColStd 40 | from OCC.Core.TDF import TDF_LabelSequence 41 | from OCC.Core.TDocStd import TDocStd_Document 42 | import OCC.Core.TopAbs 43 | import OCC.Core.TopoDS 44 | from OCC.Core.XCAFApp import XCAFApp_Application_GetApplication 45 | from OCC.Core.XCAFDoc import (XCAFDoc_DocumentTool_ShapeTool, 46 | XCAFDoc_DocumentTool_ColorTool, 47 | XCAFDoc_DocumentTool_LayerTool, 48 | XCAFDoc_DocumentTool_MaterialTool) 49 | 50 | import OCC.Core.XSControl 51 | #import aocutils.topology 52 | import treelib 53 | 54 | logger = logging.getLogger(__name__) 55 | logger.setLevel(10) # 10 = debug; 20 = info; 40 = error 56 | 57 | class StepXcafImporter(object): 58 | """ 59 | Read a step file with the goal of collecting a complete and accurate 60 | Assembly/Part structure, including the names of parts and assemblies, 61 | part color, and with all components shown in their correct positions. 62 | Data stored in self.tree 63 | """ 64 | def __init__(self, filename, nextUID=0): 65 | 66 | self.filename = filename 67 | print(filename) 68 | self.tree = treelib.tree.Tree() # to hold assembly structure 69 | self._currentUID = nextUID 70 | self.assyUidStack = [0] 71 | self.assyLocStack = [] 72 | 73 | self.read_file() 74 | 75 | def getNewUID(self): 76 | uid = self._currentUID + 1 77 | self._currentUID = uid 78 | return uid 79 | 80 | def getName(self, label): 81 | '''Get the part name from its label.''' 82 | N = TDataStd_Name() 83 | label.FindAttribute(TDataStd_Name_GetID(), N) 84 | strdump = N.DumpToString() 85 | #name = strdump.split('|')[-2] 86 | return strdump 87 | 88 | def getColor(self, shape): 89 | # Get the part color 90 | #string_seq = self.layer_tool.GetObject().GetLayers(shape) 91 | color = OCC.Core.Quantity.Quantity_Color() 92 | self.color_tool.GetColor(shape, 93 | OCC.Core.XCAFDoc.XCAFDoc_ColorSurf, color) 94 | logger.debug("color: {0}, {1}, {2}".format(color.Red(), 95 | color.Green(), 96 | color.Blue())) 97 | return color 98 | 99 | def findComponents(self, label, comps): # Discover Components of an Assembly 100 | logger.debug("") 101 | logger.debug("Finding components of label (entry = %s)" % label.EntryDumpToString()) 102 | for j in range(comps.Length()): 103 | logger.debug("loop %i of %i" % (j+1, comps.Length())) 104 | cLabel = comps.Value(j+1) 105 | cShape = self.shape_tool.GetShape(cLabel) 106 | logger.debug("Label %i - type : %s" % (j+1, type(cLabel))) 107 | logger.debug("Entry: %s" % cLabel.EntryDumpToString()) 108 | name = self.getName(cLabel) 109 | logger.debug("Part name: %s" % name) 110 | logger.debug("Is Assembly? %s" % self.shape_tool.IsAssembly(cLabel)) 111 | logger.debug("Is Component? %s" % self.shape_tool.IsComponent(cLabel)) 112 | logger.debug("Is Simple Shape? %s" % self.shape_tool.IsSimpleShape(cLabel)) 113 | logger.debug("Is Reference? %s" % self.shape_tool.IsReference(cLabel)) 114 | refLabel = OCC.Core.TDF.TDF_Label() 115 | isRef = self.shape_tool.GetReferredShape(cLabel, refLabel) 116 | if isRef: 117 | refShape = self.shape_tool.GetShape(refLabel) 118 | refLabelEntry = refLabel.EntryDumpToString() 119 | logger.debug("Entry of referred shape: %s" % refLabelEntry) 120 | refName = self.getName(refLabel) 121 | logger.debug("Name of referred shape: %s" % refName) 122 | logger.debug("Is Assembly? %s" % self.shape_tool.IsAssembly(refLabel)) 123 | logger.debug("Is Component? %s" % self.shape_tool.IsComponent(refLabel)) 124 | logger.debug("Is Simple Shape? %s" % self.shape_tool.IsSimpleShape(refLabel)) 125 | logger.debug("Is Reference? %s" % self.shape_tool.IsReference(refLabel)) 126 | if self.shape_tool.IsSimpleShape(refLabel): 127 | tempAssyLocStack = list(self.assyLocStack) 128 | tempAssyLocStack.reverse() 129 | 130 | for loc in tempAssyLocStack: 131 | cShape.Move(loc) 132 | 133 | color = self.getColor(refShape) 134 | self.tree.create_node(name, 135 | self.getNewUID(), 136 | self.assyUidStack[-1], 137 | {'a': False, 'l': None, 'c': color, 's': cShape}) 138 | elif self.shape_tool.IsAssembly(refLabel): 139 | name = self.getName(cLabel) # Instance name 140 | aLoc = OCC.Core.TopLoc.TopLoc_Location() 141 | aLoc = self.shape_tool.GetLocation(cLabel) 142 | self.assyLocStack.append(aLoc) 143 | newAssyUID = self.getNewUID() 144 | self.tree.create_node(name, 145 | newAssyUID, 146 | self.assyUidStack[-1], 147 | {'a': True, 'l': aLoc, 'c': None, 's': None}) 148 | self.assyUidStack.append(newAssyUID) 149 | rComps = OCC.Core.TDF.TDF_LabelSequence() # Components of Assy 150 | subchilds = False 151 | isAssy = self.shape_tool.GetComponents(refLabel, rComps, subchilds) 152 | logger.debug("Assy name: %s" % name) 153 | logger.debug("Is Assembly? %s" % isAssy) 154 | logger.debug("Number of components: %s" % rComps.Length()) 155 | if rComps.Length(): 156 | self.findComponents(refLabel, rComps) 157 | self.assyUidStack.pop() 158 | self.assyLocStack.pop() 159 | return 160 | 161 | def read_file(self): 162 | """ 163 | Build self.tree (treelib.Tree()) containing the CAD data read from a step file. 164 | Each node of self.tree contains the following: 165 | (Name, UID, ParentUID, {Data}) where the Data keys are: 166 | 'a' (isAssy?), 'l' (TopLoc_Location), 'c' (Quantity_Color), 's' (TopoDS_Shape) 167 | """ 168 | logger.info("Reading STEP file") 169 | doc = TDocStd_Document(TCollection_ExtendedString("STEP")) 170 | 171 | # Create the application 172 | app = XCAFApp_Application_GetApplication() 173 | app.NewDocument(TCollection_ExtendedString("MDTV-CAF"), doc) 174 | 175 | # Get root shapes 176 | shape_tool = XCAFDoc_DocumentTool_ShapeTool(doc.Main()) 177 | shape_tool.SetAutoNaming(True) 178 | self.color_tool = XCAFDoc_DocumentTool_ColorTool(doc.Main()) 179 | layer_tool = XCAFDoc_DocumentTool_LayerTool(doc.Main()) 180 | l_materials = XCAFDoc_DocumentTool_MaterialTool(doc.Main()) 181 | 182 | step_reader = STEPCAFControl_Reader() 183 | step_reader.SetColorMode(True) 184 | step_reader.SetLayerMode(True) 185 | step_reader.SetNameMode(True) 186 | step_reader.SetMatMode(True) 187 | 188 | status = step_reader.ReadFile(self.filename) 189 | if status == IFSelect_RetDone: 190 | logger.info("Transfer doc to STEPCAFControl_Reader") 191 | step_reader.Transfer(doc) 192 | 193 | labels = TDF_LabelSequence() 194 | color_labels = TDF_LabelSequence() 195 | 196 | shape_tool.GetShapes(labels) 197 | self.shape_tool = shape_tool 198 | #self.shape_tool = shape_tool 199 | logger.info('Number of labels at root : %i' % labels.Length()) 200 | label = labels.Value(1) # First label at root 201 | name = self.getName(label) 202 | logger.info('Name of root label: %s' % name) 203 | isAssy = shape_tool.IsAssembly(label) 204 | logger.info("First label at root holds an assembly? %s" % isAssy) 205 | if isAssy: 206 | # If first label at root holds an assembly, it is the Top Assembly. 207 | # Through this label, the entire assembly is accessible. 208 | # No need to examine other labels at root explicitly. 209 | topLoc = OCC.Core.TopLoc.TopLoc_Location() 210 | topLoc = shape_tool.GetLocation(label) 211 | self.assyLocStack.append(topLoc) 212 | entry = label.EntryDumpToString() 213 | logger.debug("Entry: %s" % entry) 214 | logger.debug("Top assy name: %s" % name) 215 | # Create root node for top assy 216 | newAssyUID = self.getNewUID() 217 | self.tree.create_node(name, 218 | newAssyUID, 219 | None, 220 | {'a': True, 'l': None, 'c': None, 's': None}) 221 | self.assyUidStack.append(newAssyUID) 222 | topComps = OCC.Core.TDF.TDF_LabelSequence() # Components of Top Assy 223 | subchilds = False 224 | isAssy = shape_tool.GetComponents(label, topComps, subchilds) 225 | logger.debug("Is Assembly? %s" % isAssy) 226 | logger.debug("Number of components: %s" % topComps.Length()) 227 | logger.debug("Is Reference? %s" % shape_tool.IsReference(label)) 228 | if topComps.Length(): 229 | self.findComponents(label, topComps) 230 | else: 231 | # Labels at root can hold solids or compounds (which are 'crude' assemblies) 232 | # Either way, we will need to create a root node in self.tree 233 | newAssyUID = self.getNewUID() 234 | self.tree.create_node(os.path.basename(self.filename), 235 | newAssyUID, 236 | None, 237 | {'a': True, 'l': None, 'c': None, 's': None}) 238 | self.assyUidStack = [newAssyUID] 239 | for j in range(labels.Length()): 240 | label = labels.Value(j+1) 241 | name = self.getName(label) 242 | isAssy = shape_tool.IsAssembly(label) 243 | logger.debug("Label %i is assembly?: %s" % (j+1, isAssy)) 244 | shape = shape_tool.GetShape(label) 245 | color = self.getColor(shape) 246 | isSimpleShape = self.shape_tool.IsSimpleShape(label) 247 | logger.debug("Is Simple Shape? %s" % isSimpleShape) 248 | shapeType = shape.ShapeType() 249 | logger.debug("The shape type is: %i" % shapeType) 250 | if shapeType == 0: 251 | logger.debug("The shape type is OCC.Core.TopAbs.TopAbs_COMPOUND") 252 | topo = aocutils.topology.Topo(shape) 253 | logger.debug("Nb of compounds : %i" % topo.number_of_compounds) 254 | logger.debug("Nb of solids : %i" % topo.number_of_solids) 255 | logger.debug("Nb of shells : %i" % topo.number_of_shells) 256 | newAssyUID = self.getNewUID() 257 | for i, solid in enumerate(topo.solids): 258 | name = "P%s" % str(i+1) 259 | self.tree.create_node(name, 260 | self.getNewUID(), 261 | self.assyUidStack[-1], 262 | {'a': False, 'l': None, 'c': color, 's': solid}) 263 | elif shapeType == 2: 264 | logger.debug("The shape type is OCC.Core.TopAbs.TopAbs_SOLID") 265 | self.tree.create_node(name, 266 | self.getNewUID(), 267 | self.assyUidStack[-1], 268 | {'a': False, 'l': None, 'c': color, 's': shape}) 269 | elif shapeType == 3: 270 | logger.debug("The shape type is OCC.Core.TopAbs.TopAbs_SHELL") 271 | self.tree.create_node(name, 272 | self.getNewUID(), 273 | self.assyUidStack[-1], 274 | {'a': False, 'l': None, 'c': color, 's': shape}) 275 | 276 | return True 277 | 278 | -------------------------------------------------------------------------------- /OCCUtils/face.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2013 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.Core.BRep import BRep_Tool_Surface, BRep_Tool 19 | from OCC.Core.BRepTopAdaptor import BRepTopAdaptor_FClass2d 20 | from OCC.Geom import Geom_Curve 21 | from OCC.GeomAPI import GeomAPI_ProjectPointOnSurf 22 | from OCC.GeomLib import GeomLib_IsPlanarSurface 23 | from OCC.Core.TopAbs import TopAbs_IN 24 | from OCC.Core.TopExp import topexp 25 | from OCC.Core.TopoDS import TopoDS_Vertex, TopoDS_Face, TopoDS_Edge 26 | from OCC.GeomLProp import GeomLProp_SLProps 27 | from OCC.Core.BRepTools import breptools_UVBounds 28 | from OCC.Core.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_HSurface 29 | from OCC.ShapeAnalysis import ShapeAnalysis_Surface 30 | from OCC.GeomProjLib import geomprojlib 31 | from OCC.Adaptor3d import Adaptor3d_IsoCurve 32 | from OCC.gp import gp_Pnt2d, gp_Dir 33 | 34 | from OCCUtils.base import BaseObject 35 | from OCCUtils.edge import Edge 36 | from OCCUtils.Construct import TOLERANCE, to_adaptor_3d 37 | from OCCUtils.Topology import Topo, WireExplorer 38 | 39 | 40 | class DiffGeomSurface(object): 41 | def __init__(self, instance): 42 | self.instance = instance 43 | self._curvature = None 44 | self._curvature_initiated = False 45 | 46 | def curvature(self, u, v): 47 | '''returns the curvature at the u parameter 48 | the curvature object can be returned too using 49 | curvatureType == curvatureType 50 | curvatureTypes are: 51 | gaussian 52 | minimum 53 | maximum 54 | mean 55 | curvatureType 56 | ''' 57 | if not self._curvature_initiated: 58 | self._curvature = GeomLProp_SLProps(self.instance.surface_handle, u, v, 2, 1e-7) 59 | 60 | _domain = self.instance.domain() 61 | if u in _domain or v in _domain: 62 | print('<<>>') 63 | div = 1000 64 | delta_u, delta_v = (_domain[0] - _domain[1])/div, (_domain[2] - _domain[3])/div 65 | 66 | if u in _domain: 67 | low, hi = u-_domain[0], u-_domain[1] 68 | if low < hi: 69 | u = u - delta_u 70 | else: 71 | u = u + delta_u 72 | 73 | if v in _domain: 74 | low, hi = v-_domain[2], v-_domain[3] 75 | if low < hi: 76 | v = v - delta_v 77 | else: 78 | v = v + delta_v 79 | 80 | self._curvature.SetParameters(u, v) 81 | self._curvature_initiated = True 82 | 83 | return self._curvature 84 | 85 | def gaussian_curvature(self, u, v): 86 | return self.curvature(u, v).GaussianCurvature() 87 | 88 | def min_curvature(self, u, v): 89 | return self.curvature(u, v).MinCurvature() 90 | 91 | def mean_curvature(self, u, v): 92 | return self.curvature(u, v).MeanCurvature() 93 | 94 | def max_curvature(self, u, v): 95 | return self.curvature(u, v).MaxCurvature() 96 | 97 | def normal(self, u, v): 98 | # TODO: should make this return a gp_Vec 99 | curv = self.curvature(u, v) 100 | if curv.IsNormalDefined(): 101 | return curv.Normal() 102 | else: 103 | raise ValueError('normal is not defined at u,v: {0}, {1}'.format(u, v)) 104 | 105 | def tangent(self, u, v): 106 | dU, dV = gp_Dir(), gp_Dir() 107 | curv = self.curvature(u, v) 108 | if curv.IsTangentUDefined() and curv.IsTangentVDefined(): 109 | curv.TangentU(dU), curv.TangentV(dV) 110 | return dU, dV 111 | else: 112 | return None, None 113 | 114 | def radius(self, u, v): 115 | '''returns the radius at u 116 | ''' 117 | # TODO: SHOULD WE RETURN A SIGNED RADIUS? ( get rid of abs() )? 118 | try: 119 | _crv_min = 1./self.min_curvature(u, v) 120 | except ZeroDivisionError: 121 | _crv_min = 0. 122 | 123 | try: 124 | _crv_max = 1./self.max_curvature(u, v) 125 | except ZeroDivisionError: 126 | _crv_max = 0. 127 | return abs((_crv_min+_crv_max)/2.) 128 | 129 | 130 | class Face(TopoDS_Face, BaseObject): 131 | """high level surface API 132 | object is a Face if part of a Solid 133 | otherwise the same methods do apply, apart from the topology obviously 134 | """ 135 | def __init__(self, face): 136 | ''' 137 | ''' 138 | assert isinstance(face, TopoDS_Face), 'need a TopoDS_Face, got a %s' % face.__class__ 139 | assert not face.IsNull() 140 | super(Face, self).__init__() 141 | BaseObject.__init__(self, 'face') 142 | # we need to copy the base shape using the following three 143 | # lines 144 | assert self.IsNull() 145 | self.TShape(face.TShape()) 146 | self.Location(face.Location()) 147 | self.Orientation(face.Orientation()) 148 | assert not self.IsNull() 149 | 150 | # cooperative classes 151 | self.DiffGeom = DiffGeomSurface(self) 152 | 153 | # STATE; whether cooperative classes are yet initialized 154 | self._curvature_initiated = False 155 | self._geometry_lookup_init = False 156 | 157 | #=================================================================== 158 | # properties 159 | #=================================================================== 160 | self._h_srf = None 161 | self._srf = None 162 | self._adaptor = None 163 | self._adaptor_handle = None 164 | self._classify_uv = None # cache the u,v classifier, no need to rebuild for every sample 165 | self._topo = None 166 | 167 | # aliasing of useful methods 168 | def is_u_periodic(self): 169 | return self.adaptor.IsUPeriodic() 170 | 171 | def is_v_periodic(self): 172 | return self.adaptor.IsVPeriodic() 173 | 174 | def is_u_closed(self): 175 | return self.adaptor.IsUClosed() 176 | 177 | def is_v_closed(self): 178 | return self.adaptor.IsVClosed() 179 | 180 | def is_u_rational(self): 181 | return self.adaptor.IsURational() 182 | 183 | def is_v_rational(self): 184 | return self.adaptor.IsVRational() 185 | 186 | def u_degree(self): 187 | return self.adaptor.UDegree() 188 | 189 | def v_degree(self): 190 | return self.adaptor.VDegree() 191 | 192 | def u_continuity(self): 193 | return self.adaptor.UContinuity() 194 | 195 | def v_continuity(self): 196 | return self.adaptor.VContinuity() 197 | 198 | def domain(self): 199 | '''the u,v domain of the curve 200 | :return: UMin, UMax, VMin, VMax 201 | ''' 202 | return breptools_UVBounds(self) 203 | 204 | def mid_point(self): 205 | """ 206 | :return: the parameter at the mid point of the face, 207 | and its corresponding gp_Pnt 208 | """ 209 | u_min, u_max, v_min, v_max = self.domain() 210 | u_mid = (u_min + u_max) / 2. 211 | v_mid = (v_min + v_max) / 2. 212 | return ((u_mid, v_mid), self.adaptor.Value(u_mid, v_mid)) 213 | 214 | @property 215 | def topo(self): 216 | if self._topo is not None: 217 | return self._topo 218 | else: 219 | self._topo = Topo(self) 220 | return self._topo 221 | 222 | @property 223 | def surface(self): 224 | if self._srf is None or self.is_dirty: 225 | self._h_srf = BRep_Tool_Surface(self) 226 | self._srf = self._h_srf.GetObject() 227 | return self._srf 228 | 229 | @property 230 | def surface_handle(self): 231 | if self._h_srf is None or self.is_dirty: 232 | self.surface # force building handle 233 | return self._h_srf 234 | 235 | @property 236 | def adaptor(self): 237 | if self._adaptor is not None and not self.is_dirty: 238 | pass 239 | else: 240 | self._adaptor = BRepAdaptor_Surface(self) 241 | self._adaptor_handle = BRepAdaptor_HSurface() 242 | self._adaptor_handle.Set(self._adaptor) 243 | return self._adaptor 244 | 245 | @property 246 | def adaptor_handle(self): 247 | if self._adaptor_handle is not None and not self.is_dirty: 248 | pass 249 | else: 250 | self.adaptor 251 | return self._adaptor_handle 252 | 253 | def is_closed(self): 254 | sa = ShapeAnalysis_Surface(self.surface_handle) 255 | # sa.GetBoxUF() 256 | return sa.IsUClosed(), sa.IsVClosed() 257 | 258 | def is_planar(self, tol=TOLERANCE): 259 | '''checks if the surface is planar within a tolerance 260 | :return: bool, gp_Pln 261 | ''' 262 | print(self.surface_handle) 263 | is_planar_surface = GeomLib_IsPlanarSurface(self.surface_handle, tol) 264 | return is_planar_surface.IsPlanar() 265 | 266 | def is_trimmed(self): 267 | """ 268 | :return: True if the Wire delimiting the Face lies on the bounds 269 | of the surface 270 | if this is not the case, the wire represents a contour that delimits 271 | the face [ think cookie cutter ] 272 | and implies that the surface is trimmed 273 | """ 274 | _round = lambda x: round(x, 3) 275 | a = map(_round, breptools_UVBounds(self)) 276 | b = map(_round, self.adaptor.Surface().Surface().GetObject().Bounds()) 277 | if a != b: 278 | print('a,b', a, b) 279 | return True 280 | return False 281 | 282 | def on_trimmed(self, u, v): 283 | '''tests whether the surface at the u,v parameter has been trimmed 284 | ''' 285 | if self._classify_uv is None: 286 | self._classify_uv = BRepTopAdaptor_FClass2d(self, 1e-9) 287 | uv = gp_Pnt2d(u, v) 288 | if self._classify_uv.Perform(uv) == TopAbs_IN: 289 | return True 290 | else: 291 | return False 292 | 293 | def parameter_to_point(self, u, v): 294 | '''returns the coordinate at u,v 295 | ''' 296 | return self.surface.Value(u, v) 297 | 298 | def point_to_parameter(self, pt): 299 | ''' 300 | returns the uv value of a point on a surface 301 | @param pt: 302 | ''' 303 | sas = ShapeAnalysis_Surface(self.surface_handle) 304 | uv = sas.ValueOfUV(pt, self.tolerance) 305 | return uv.Coord() 306 | 307 | def continuity_edge_face(self, edge, face): 308 | """ 309 | compute the continuity between two faces at :edge: 310 | 311 | :param edge: an Edge or TopoDS_Edge from :face: 312 | :param face: a Face or TopoDS_Face 313 | :return: bool, GeomAbs_Shape if it has continuity, otherwise 314 | False, None 315 | """ 316 | bt = BRep_Tool() 317 | if bt.HasContinuity(edge, self, face): 318 | continuity = bt.Continuity(edge, self, face) 319 | return True, continuity 320 | else: 321 | return False, None 322 | 323 | #=========================================================================== 324 | # Surface.project 325 | # project curve, point on face 326 | #=========================================================================== 327 | 328 | def project_vertex(self, pnt, tol=TOLERANCE): 329 | '''projects self with a point, curve, edge, face, solid 330 | method wraps dealing with the various topologies 331 | 332 | if other is a point: 333 | returns uv, point 334 | 335 | ''' 336 | if isinstance(pnt, TopoDS_Vertex): 337 | pnt = BRep_Tool.Pnt(pnt) 338 | 339 | proj = GeomAPI_ProjectPointOnSurf(pnt, self.surface_handle, tol) 340 | uv = proj.LowerDistanceParameters() 341 | proj_pnt = proj.NearestPoint() 342 | 343 | return uv, proj_pnt 344 | 345 | def project_curve(self, other): 346 | # this way Geom_Circle and alike are valid too 347 | if (isinstance(other, TopoDS_Edge) or 348 | isinstance(other, Geom_Curve) or 349 | issubclass(other, Geom_Curve)): 350 | # convert edge to curve 351 | first, last = topexp.FirstVertex(other), topexp.LastVertex(other) 352 | lbound, ubound = BRep_Tool().Parameter(first, other), BRep_Tool().Parameter(last, other) 353 | other = BRep_Tool.Curve(other, lbound, ubound).GetObject() 354 | return geomprojlib.Project(other, self.surface_handle) 355 | 356 | def project_edge(self, edg): 357 | if hasattr(edg, 'adaptor'): 358 | return self.project_curve(self, self.adaptor) 359 | return self.project_curve(self, to_adaptor_3d(edg)) 360 | 361 | def iso_curve(self, u_or_v, param): 362 | """ 363 | get the iso curve from a u,v + parameter 364 | :param u_or_v: 365 | :param param: 366 | :return: 367 | """ 368 | uv = 0 if u_or_v == 'u' else 1 369 | iso = Adaptor3d_IsoCurve(self.adaptor_handle.GetHandle(), uv, param) 370 | return iso 371 | 372 | def edges(self): 373 | return [Edge(i) for i in WireExplorer(next(self.topo.wires())).ordered_edges()] 374 | 375 | def __repr__(self): 376 | return self.name 377 | 378 | def __str__(self): 379 | return self.__repr__() 380 | 381 | if __name__ == "__main__": 382 | from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeSphere 383 | sph = BRepPrimAPI_MakeSphere(1, 1).Face() 384 | fc = Face(sph) 385 | print(fc.is_trimmed()) 386 | print(fc.is_planar()) 387 | -------------------------------------------------------------------------------- /misc/bottle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # cadViewer.py 4 | # An embryonic python 3D CAD application with very little functionality. 5 | # Perhaps it could be a starting point for a more elaborate program. 6 | # It may be only useful to facilitate the exploration of pythonOCC syntax. 7 | # The latest version of this file can be found at: 8 | # https://sites.google.com/site/pythonocc/ 9 | # 10 | # Author: Doug Blanding 11 | # 12 | # cadViewer is free software; you can redistribute it and/or modify 13 | # it under the terms of the GNU General Public License as published by 14 | # the Free Software Foundation; either version 2 of the License, or 15 | # (at your option) any later version. 16 | # 17 | # cadViewer is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU General Public License 23 | # if not, write to the Free Software Foundation, Inc. 24 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25 | # 26 | 27 | 28 | from __future__ import absolute_import 29 | 30 | from PyQt4.QtGui import QApplication 31 | from guiApp import MainWindow 32 | import sys 33 | import math 34 | from OCC.gp import * 35 | from OCC.GC import * 36 | from OCC.BRepBuilderAPI import * 37 | from OCC.TopoDS import * 38 | from OCC.TopExp import * 39 | from OCC.TopAbs import * 40 | from OCC.BRepAlgoAPI import * 41 | from OCC.BRepFilletAPI import * 42 | from OCC.BRepPrimAPI import * 43 | from OCC.Geom import * 44 | from OCC.Geom2d import * 45 | from OCC.GCE2d import * 46 | from OCC.BRepOffsetAPI import * 47 | from OCC.IGESControl import * 48 | from OCC.TopTools import * 49 | from OCC.Standard import * 50 | from OCC.TopLoc import * 51 | from OCC.Quantity import * 52 | from OCC.AIS import AIS_Shape 53 | import OCC.BRepLib as BRepLib 54 | import OCC.BRep as BRep 55 | import workplane 56 | 57 | # Make Bottle Stuff... 58 | 59 | def face_is_plane(face): 60 | """ 61 | Returns True if the TopoDS_Shape is a plane, False otherwise 62 | """ 63 | hs = OCC.BRep.BRep_Tool_Surface(face) 64 | downcast_result = Handle_Geom_Plane.DownCast(hs) 65 | # The handle is null if downcast failed or is not possible, that is to say the face is not a plane 66 | if downcast_result.IsNull(): 67 | return False 68 | else: 69 | return True 70 | 71 | def geom_plane_from_face(aFace): 72 | """ 73 | Returns the geometric plane entity from a planar surface 74 | """ 75 | return Handle_Geom_Plane.DownCast(OCC.BRep.BRep_Tool_Surface(aFace)).GetObject() 76 | 77 | # Bottle Dimensions... 78 | width = 50 79 | height = 70 80 | thickness = 30 81 | 82 | def makeBottle(): # complete bottle 83 | startBottle(startOnly=False) 84 | 85 | def startBottle(startOnly=True): # minus the neck fillet, shelling & threads 86 | partName = "Bottle-start" 87 | # The points we'll use to create the profile of the bottle's body 88 | aPnt1 = gp_Pnt(-width / 2.0, 0, 0) 89 | aPnt2 = gp_Pnt(-width / 2.0, -thickness / 4.0, 0) 90 | aPnt3 = gp_Pnt(0, -thickness / 2.0, 0) 91 | aPnt4 = gp_Pnt(width / 2.0, -thickness / 4.0, 0) 92 | aPnt5 = gp_Pnt(width / 2.0, 0, 0) 93 | 94 | aArcOfCircle = GC_MakeArcOfCircle(aPnt2, aPnt3, aPnt4) 95 | aSegment1 = GC_MakeSegment(aPnt1, aPnt2) 96 | aSegment2 = GC_MakeSegment(aPnt4, aPnt5) 97 | 98 | # Could also construct the line edges directly using the points instead of the resulting line 99 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value()) 100 | aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value()) 101 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value()) 102 | 103 | # Create a wire out of the edges 104 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), aEdge2.Edge(), aEdge3.Edge()) 105 | 106 | # Quick way to specify the X axis 107 | xAxis = gp_OX() 108 | 109 | # Set up the mirror 110 | aTrsf = gp_Trsf() 111 | aTrsf.SetMirror(xAxis) 112 | 113 | # Apply the mirror transformation 114 | aBRespTrsf = BRepBuilderAPI_Transform(aWire.Wire(), aTrsf) 115 | 116 | # Get the mirrored shape back out of the transformation and convert back to a wire 117 | aMirroredShape = aBRespTrsf.Shape() 118 | 119 | # A wire instead of a generic shape now 120 | aMirroredWire = topods.Wire(aMirroredShape) 121 | 122 | # Combine the two constituent wires 123 | mkWire = BRepBuilderAPI_MakeWire() 124 | mkWire.Add(aWire.Wire()) 125 | mkWire.Add(aMirroredWire) 126 | myWireProfile = mkWire.Wire() 127 | 128 | # The face that we'll sweep to make the prism 129 | myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile) 130 | 131 | # We want to sweep the face along the Z axis to the height 132 | aPrismVec = gp_Vec(0, 0, height) 133 | myBody = BRepPrimAPI_MakePrism(myFaceProfile.Face(), aPrismVec) 134 | 135 | # Add fillets to all edges through the explorer 136 | mkFillet = BRepFilletAPI_MakeFillet(myBody.Shape()) 137 | anEdgeExplorer = TopExp_Explorer(myBody.Shape(), TopAbs_EDGE) 138 | 139 | while anEdgeExplorer.More(): 140 | anEdge = topods.Edge(anEdgeExplorer.Current()) 141 | mkFillet.Add(thickness / 12.0, anEdge) 142 | 143 | anEdgeExplorer.Next() 144 | 145 | myBody = mkFillet.Shape() 146 | 147 | 148 | # Create the neck of the bottle 149 | neckLocation = gp_Pnt(0, 0, height) 150 | neckAxis = gp_DZ() 151 | neckAx2 = gp_Ax2(neckLocation, neckAxis) 152 | 153 | myNeckRadius = thickness / 4.0 154 | myNeckHeight = height / 10.0 155 | 156 | mkCylinder = BRepPrimAPI_MakeCylinder(neckAx2, myNeckRadius, myNeckHeight) 157 | myBody = BRepAlgoAPI_Fuse(myBody , mkCylinder.Shape()) 158 | if startOnly: # quit here 159 | uid = win.getNewPartUID(myBody.Shape(), name=partName) 160 | win.redraw() 161 | return 162 | 163 | partName = "Bottle-complete" 164 | # Our goal is to find the highest Z face and remove it 165 | faceToRemove = None 166 | zMax = -1 167 | 168 | # We have to work our way through all the faces to find the highest Z face 169 | aFaceExplorer = TopExp_Explorer(myBody.Shape(), TopAbs_FACE) 170 | while aFaceExplorer.More(): 171 | aFace = topods.Face(aFaceExplorer.Current()) 172 | 173 | if face_is_plane(aFace): 174 | aPlane = geom_plane_from_face(aFace) 175 | 176 | # We want the highest Z face, so compare this to the previous faces 177 | aPnt = aPlane.Location() 178 | aZ = aPnt.Z() 179 | if aZ > zMax: 180 | zMax = aZ 181 | faceToRemove = aFace 182 | 183 | aFaceExplorer.Next() 184 | 185 | facesToRemove = TopTools_ListOfShape() 186 | facesToRemove.Append(faceToRemove) 187 | 188 | myBody = BRepOffsetAPI_MakeThickSolid(myBody.Shape(), facesToRemove, -thickness / 50.0, 0.001) 189 | 190 | # Set up our surfaces for the threading on the neck 191 | neckAx2_Ax3 = gp_Ax3(neckLocation, gp_DZ()) 192 | aCyl1 = Geom_CylindricalSurface(neckAx2_Ax3, myNeckRadius * 0.99) 193 | aCyl2 = Geom_CylindricalSurface(neckAx2_Ax3, myNeckRadius * 1.05) 194 | 195 | # Set up the curves for the threads on the bottle's neck 196 | aPnt = gp_Pnt2d(2.0 * math.pi, myNeckHeight / 2.0) 197 | aDir = gp_Dir2d(2.0 * math.pi, myNeckHeight / 4.0) 198 | anAx2d = gp_Ax2d(aPnt, aDir) 199 | 200 | aMajor = 2.0 * math.pi 201 | aMinor = myNeckHeight / 10.0 202 | 203 | anEllipse1 = Geom2d_Ellipse(anAx2d, aMajor, aMinor) 204 | anEllipse2 = Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4.0) 205 | 206 | anArc1 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse1), 0, math.pi) 207 | anArc2 = Geom2d_TrimmedCurve(Handle_Geom2d_Ellipse(anEllipse2), 0, math.pi) 208 | anEllipsePnt1 = anEllipse1.Value(0) 209 | anEllipsePnt2 = anEllipse1.Value(math.pi) 210 | 211 | aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2) 212 | 213 | # Build edges and wires for threading 214 | anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(Handle_Geom2d_Curve(anArc1), Handle_Geom_Surface(aCyl1)) 215 | anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment.Value(), Handle_Geom_Surface(aCyl1)) 216 | anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(Handle_Geom2d_Curve(anArc2), Handle_Geom_Surface(aCyl2)) 217 | anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment.Value(), Handle_Geom_Surface(aCyl2)) 218 | 219 | threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1.Edge(), anEdge2OnSurf1.Edge()) 220 | threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2.Edge(), anEdge2OnSurf2.Edge()) 221 | 222 | # Compute the 3D representations of the edges/wires 223 | BRepLib.breplib.BuildCurves3d(threadingWire1.Shape()) 224 | BRepLib.breplib.BuildCurves3d(threadingWire2.Shape()) 225 | 226 | # Create the surfaces of the threading 227 | aTool = BRepOffsetAPI_ThruSections(True) 228 | aTool.AddWire(threadingWire1.Wire()) 229 | aTool.AddWire(threadingWire2.Wire()) 230 | aTool.CheckCompatibility(False) 231 | myThreading = aTool.Shape() 232 | 233 | # Build the resulting compound 234 | aRes = TopoDS_Compound() 235 | aBuilder = BRep.BRep_Builder() 236 | aBuilder.MakeCompound(aRes) 237 | aBuilder.Add(aRes, myBody.Shape()) 238 | aBuilder.Add(aRes, myThreading) 239 | uid = win.getNewPartUID(aRes, name=partName) 240 | win.redraw() 241 | 242 | # Make Bottle step by step... 243 | def makePoints(event=None): 244 | global aPnt1, aPnt2, aPnt3, aPnt4, aPnt5 245 | aPnt1 = gp_Pnt(-width / 2. , 0 , 0) 246 | aPnt2 = gp_Pnt(-width / 2. , -thickness / 4. , 0) 247 | aPnt3 = gp_Pnt(0 , -thickness / 2. , 0) 248 | aPnt4 = gp_Pnt(width / 2. , -thickness / 4. , 0) 249 | aPnt5 = gp_Pnt(width / 2. , 0 , 0) 250 | V1 = BRepBuilderAPI_MakeVertex(aPnt1) 251 | V2 = BRepBuilderAPI_MakeVertex(aPnt2) 252 | V3 = BRepBuilderAPI_MakeVertex(aPnt3) 253 | V4 = BRepBuilderAPI_MakeVertex(aPnt4) 254 | V5 = BRepBuilderAPI_MakeVertex(aPnt5) 255 | # add dummy point above bottle just to set view size 256 | V6 = BRepBuilderAPI_MakeVertex(gp_Pnt(0,0,height*1.1)) 257 | display.DisplayShape(V1.Vertex()) 258 | display.DisplayShape(V2.Vertex()) 259 | display.DisplayShape(V3.Vertex()) 260 | display.DisplayShape(V4.Vertex()) 261 | display.DisplayShape(V5.Vertex()) 262 | display.DisplayShape(V6.Vertex()) 263 | display.FitAll() 264 | display.EraseAll() 265 | display.DisplayShape(V1.Vertex()) 266 | display.DisplayShape(V2.Vertex()) 267 | display.DisplayShape(V3.Vertex()) 268 | display.DisplayShape(V4.Vertex()) 269 | display.DisplayShape(V5.Vertex()) 270 | display.Repaint() 271 | win.statusBar().showMessage('Make Points complete') 272 | 273 | def makeLines(event=None): 274 | global aEdge1, aEdge2, aEdge3 275 | aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3 ,aPnt4) 276 | aSegment1 = GC_MakeSegment(aPnt1 , aPnt2) 277 | aSegment2 = GC_MakeSegment(aPnt4 , aPnt5) 278 | # Display lines 279 | aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1.Value()) 280 | aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle.Value()) 281 | aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2.Value()) 282 | display.DisplayColoredShape(aEdge1.Edge(),'RED') 283 | display.DisplayColoredShape(aEdge2.Edge(),'RED') 284 | display.DisplayColoredShape(aEdge3.Edge(),'RED') 285 | display.Repaint() 286 | win.statusBar().showMessage('Make lines complete') 287 | 288 | def makeHalfWire(event=None): 289 | global aWire 290 | aWire = BRepBuilderAPI_MakeWire(aEdge1.Edge(), 291 | aEdge2.Edge(), 292 | aEdge3.Edge()).Wire() 293 | display.EraseAll() 294 | display.DisplayColoredShape(aWire, 'BLUE') 295 | display.Repaint() 296 | win.statusBar().showMessage('Make Half Wire complete') 297 | 298 | def makeWholeWire(event=None): 299 | global myWireProfile 300 | xAxis = gp_OX() 301 | # Set up the mirror 302 | aTrsf = gp_Trsf() 303 | aTrsf.SetMirror(xAxis) 304 | # Apply the mirror transform 305 | aBRepTrsf = BRepBuilderAPI_Transform(aWire, aTrsf) 306 | # Convert mirrored shape to a wire 307 | aMirroredShape = aBRepTrsf.Shape() 308 | aMirroredWire = topods_Wire(aMirroredShape) 309 | # Combine the two wires 310 | mkWire = BRepBuilderAPI_MakeWire() 311 | mkWire.Add(aWire) 312 | mkWire.Add(aMirroredWire) 313 | myWireProfile = mkWire.Wire() 314 | display.DisplayColoredShape(myWireProfile, 'BLUE') 315 | display.Repaint() 316 | win.statusBar().showMessage('Make whole wire complete') 317 | 318 | def makeFace(event=None): 319 | global myFaceProfile 320 | myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile) 321 | if myFaceProfile.IsDone(): 322 | bottomFace = myFaceProfile.Face() 323 | display.DisplayShape(bottomFace, color='YELLOW', transparency=0.6) 324 | display.Repaint() 325 | win.statusBar().showMessage('Make face complete') 326 | 327 | def makeBody(event=None): 328 | partName = 'body' 329 | aPrismVec = gp_Vec(0 , 0 , height) 330 | myBody = BRepPrimAPI_MakePrism(myFaceProfile.Shape(), 331 | aPrismVec).Shape() 332 | win.getNewPartUID(myBody, name=partName) 333 | win.statusBar().showMessage('Bottle body complete') 334 | win.redraw() 335 | 336 | def makeFillets(event=None): 337 | newPrtName = 'bodyWithFillets' 338 | workPart = win.activePart 339 | wrkPrtUID = win.activePartUID 340 | mkFillet = BRepFilletAPI_MakeFillet(workPart) 341 | aEdgeExplorer = TopExp_Explorer(workPart, 342 | TopAbs_EDGE) 343 | while aEdgeExplorer.More(): 344 | aEdge = topods_Edge(aEdgeExplorer.Current()) 345 | mkFillet.Add(thickness / 12. , aEdge) 346 | aEdgeExplorer.Next() 347 | myBody = mkFillet.Shape() 348 | win.getNewPartUID(myBody, name=newPrtName, ancestor=wrkPrtUID) 349 | win.statusBar().showMessage('Bottle with fillets complete') 350 | win.redraw() 351 | 352 | def addNeck(event=None): 353 | newPrtName = 'bodyWithNeck' 354 | workPart = win.activePart 355 | wrkPrtUID = win.activePartUID 356 | neckLocation = gp_Pnt(0 , 0 , height) 357 | neckNormal = gp_DZ() 358 | neckAx2 = gp_Ax2(neckLocation , neckNormal) 359 | myNeckRadius = thickness / 4. 360 | myNeckHeight = height / 10. 361 | MKCylinder = BRepPrimAPI_MakeCylinder(neckAx2, 362 | myNeckRadius, 363 | myNeckHeight) 364 | myNeck = MKCylinder.Shape() 365 | myBody = BRepAlgoAPI_Fuse(workPart, myNeck).Shape() 366 | win.getNewPartUID(myBody, name=newPrtName, ancestor=wrkPrtUID) 367 | win.statusBar().showMessage('Add neck complete') 368 | win.redraw() 369 | 370 | -------------------------------------------------------------------------------- /OCCUtils/edge.py: -------------------------------------------------------------------------------- 1 | ##Copyright 2008-2015 Jelle Feringa (jelleferinga@gmail.com) 2 | ## 3 | ##This file is part of pythonOCC. 4 | ## 5 | ##pythonOCC is free software: you can redistribute it and/or modify 6 | ##it under the terms of the GNU Lesser General Public License as published by 7 | ##the Free Software Foundation, either version 3 of the License, or 8 | ##(at your option) any later version. 9 | ## 10 | ##pythonOCC is distributed in the hope that it will be useful, 11 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | ##GNU Lesser General Public License for more details. 14 | ## 15 | ##You should have received a copy of the GNU Lesser General Public License 16 | ##along with pythonOCC. If not, see 17 | 18 | from OCC.Core.BRepAdaptor import BRepAdaptor_Curve, BRepAdaptor_HCurve 19 | from OCC.GCPnts import GCPnts_UniformAbscissa 20 | from OCC.Geom import Geom_OffsetCurve, Geom_TrimmedCurve 21 | from OCC.Core.TopExp import topexp 22 | from OCC.Core.TopoDS import TopoDS_Edge, TopoDS_Vertex, TopoDS_Face 23 | from OCC.gp import gp_Vec, gp_Dir, gp_Pnt 24 | from OCC.GeomLProp import GeomLProp_CurveTool 25 | from OCC.Core.BRepLProp import BRepLProp_CLProps 26 | from OCC.GeomLib import geomlib 27 | from OCC.GCPnts import GCPnts_AbscissaPoint 28 | from OCC.GeomAPI import GeomAPI_ProjectPointOnCurve 29 | from OCC.ShapeAnalysis import ShapeAnalysis_Edge 30 | from OCC.Core.BRep import BRep_Tool, BRep_Tool_Continuity 31 | from OCC.Core.BRepIntCurveSurface import BRepIntCurveSurface_Inter 32 | 33 | # high-level 34 | from OCCUtils.Common import vertex2pnt, minimum_distance, assert_isdone, fix_continuity 35 | from OCCUtils.Construct import make_edge 36 | from OCCUtils.types_lut import geom_lut 37 | from OCCUtils.base import BaseObject 38 | 39 | 40 | class IntersectCurve(object): 41 | def __init__(self, instance): 42 | self.instance = instance 43 | 44 | def intersect(self, other, tolerance=1e-2): 45 | '''Intersect self with a point, curve, edge, face, solid 46 | method wraps dealing with the various topologies 47 | ''' 48 | if isinstance(other, TopoDS_Face): 49 | face_curve_intersect = BRepIntCurveSurface_Inter() 50 | face_curve_intersect.Init(other, self.instance.adaptor.Curve(), tolerance) 51 | pnts = [] 52 | while face_curve_intersect.More(): 53 | next(face_curve_intersect) 54 | pnts.append(face_curve_intersect.Pnt()) 55 | return pnts 56 | 57 | 58 | class DiffGeomCurve(object): 59 | def __init__(self, instance): 60 | self.instance = instance 61 | self._local_props = BRepLProp_CLProps(self.instance.adaptor, 2, self.instance.tolerance) 62 | 63 | @property 64 | def _curvature(self): 65 | return self._local_props 66 | 67 | def radius(self, u): 68 | '''returns the radius at u 69 | ''' 70 | # NOT SO SURE IF THIS IS THE SAME THING!!! 71 | self._curvature.SetParameter(u) 72 | pnt = gp_Pnt() 73 | self._curvature.CentreOfCurvature(pnt) 74 | return pnt 75 | 76 | def curvature(self, u): 77 | # ugly 78 | self._curvature.SetParameter(u) 79 | return self._curvature.Curvature() 80 | 81 | def tangent(self, u): 82 | '''sets or gets ( iff vector ) the tangency at the u parameter 83 | tangency can be constrained so when setting the tangency, 84 | you're constrainting it in fact 85 | ''' 86 | self._curvature.SetParameter(u) 87 | if self._curvature.IsTangentDefined(): 88 | ddd = gp_Dir() 89 | self._curvature.Tangent(ddd) 90 | return ddd 91 | else: 92 | raise ValueError('no tangent defined') 93 | 94 | def normal(self, u): 95 | '''returns the normal at u 96 | 97 | computes the main normal if no normal is found 98 | see: 99 | www.opencascade.org/org/forum/thread_645+&cd=10&hl=nl&ct=clnk&gl=nl 100 | ''' 101 | try: 102 | self._curvature.SetParameter(u) 103 | a_dir = gp_Dir() 104 | self._curvature.Normal(a_dir) 105 | return a_dir 106 | except: 107 | raise ValueError('no normal was found') 108 | 109 | def derivative(self, u, n): 110 | ''' 111 | returns n derivatives at parameter b 112 | ''' 113 | self._curvature.SetParameter(u) 114 | deriv = {1: self._curvature.D1, 115 | 2: self._curvature.D2, 116 | 3: self._curvature.D3, 117 | } 118 | try: 119 | return deriv[n] 120 | except KeyError: 121 | raise AssertionError('n of derivative is one of [1,2,3]') 122 | 123 | def points_from_tangential_deflection(self): 124 | pass 125 | 126 | #=========================================================================== 127 | # Curve.Construct 128 | #=========================================================================== 129 | 130 | 131 | class ConstructFromCurve(): 132 | def __init__(self, instance): 133 | self.instance = instance 134 | 135 | def make_offset(self, offset, vec): 136 | ''' 137 | returns an offsetted curve 138 | @param offset: the distance between self.crv and the curve to offset 139 | @param vec: offset direction 140 | ''' 141 | return Geom_OffsetCurve(self.instance.h_crv, offset, vec) 142 | 143 | 144 | class Edge(TopoDS_Edge, BaseObject): 145 | def __init__(self, edge): 146 | assert isinstance(edge, TopoDS_Edge), 'need a TopoDS_Edge, got a %s' % edge.__class__ 147 | assert not edge.IsNull() 148 | super(Edge, self).__init__() 149 | BaseObject.__init__(self, 'edge') 150 | # we need to copy the base shape using the following three 151 | # lines 152 | assert self.IsNull() 153 | self.TShape(edge.TShape()) 154 | self.Location(edge.Location()) 155 | self.Orientation(edge.Orientation()) 156 | assert not self.IsNull() 157 | 158 | # tracking state 159 | self._local_properties_init = False 160 | self._curvature_init = False 161 | self._geometry_lookup_init = False 162 | self._curve_handle = None 163 | self._curve = None 164 | self._adaptor = None 165 | self._adaptor_handle = None 166 | 167 | # instantiating cooperative classes 168 | # cooperative classes are distinct through CamelCaps from 169 | # normal method -> pep8 170 | self.DiffGeom = DiffGeomCurve(self) 171 | self.Intersect = IntersectCurve(self) 172 | self.Construct = ConstructFromCurve(self) 173 | 174 | # GeomLProp object 175 | self._curvature = None 176 | 177 | def is_closed(self): 178 | return self.adaptor.IsClosed() 179 | 180 | def is_periodic(self): 181 | return self.adaptor.IsPeriodic() 182 | 183 | def is_rational(self): 184 | return self.adaptor.IsRational() 185 | 186 | def continuity(self): 187 | return self.adaptor.Continuity 188 | 189 | def degree(self): 190 | if 'line' in self.type: 191 | return 1 192 | elif 'curve' in self.type: 193 | return self.adaptor.Degree() 194 | else: 195 | # hyperbola, parabola, circle 196 | return 2 197 | 198 | def nb_knots(self): 199 | return self.adaptor.NbKnots() 200 | 201 | def nb_poles(self): 202 | return self.adaptor.NbPoles() 203 | 204 | @property 205 | def curve(self): 206 | if self._curve is not None and not self.is_dirty: 207 | pass 208 | else: 209 | self._curve_handle = BRep_Tool().Curve(self)[0] 210 | self._curve = self._curve_handle.GetObject() 211 | return self._curve 212 | 213 | @property 214 | def curve_handle(self): 215 | if self._curve_handle is not None and not self.is_dirty: 216 | return self._curve_handle 217 | else: 218 | return None 219 | 220 | @property 221 | def adaptor(self): 222 | if self._adaptor is not None and not self.is_dirty: 223 | pass 224 | else: 225 | self._adaptor = BRepAdaptor_Curve(self) 226 | self._adaptor_handle = BRepAdaptor_HCurve(self._adaptor) 227 | return self._adaptor 228 | 229 | @property 230 | def adaptor_handle(self): 231 | if self._adaptor_handle is not None and not self.is_dirty: 232 | pass 233 | else: 234 | self.adaptor 235 | return self._adaptor_handle 236 | 237 | @property 238 | def geom_curve_handle(self): 239 | """ 240 | :return: Handle_Geom_Curve adapted from `self` 241 | """ 242 | if self._adaptor_handle is not None and not self.is_dirty: 243 | return self._adaptor.Curve().Curve() 244 | else: 245 | return None 246 | 247 | @property 248 | def type(self): 249 | return geom_lut[self.adaptor.Curve().GetType()] 250 | 251 | def pcurve(self, face): 252 | """ 253 | computes the 2d parametric spline that lies on the surface of the face 254 | :return: Geom2d_Curve, u, v 255 | """ 256 | crv, u, v = BRep_Tool().CurveOnSurface(self, face) 257 | return crv.GetObject(), u, v 258 | 259 | def _local_properties(self): 260 | self._lprops_curve_tool = GeomLProp_CurveTool() 261 | self._local_properties_init = True 262 | 263 | def domain(self): 264 | '''returns the u,v domain of the curve''' 265 | return self.adaptor.FirstParameter(), self.adaptor.LastParameter() 266 | 267 | #=========================================================================== 268 | # Curve.GlobalProperties 269 | #=========================================================================== 270 | 271 | def length(self, lbound=None, ubound=None, tolerance=1e-5): 272 | '''returns the curve length 273 | if either lbound | ubound | both are given, than the length 274 | of the curve will be measured over that interval 275 | ''' 276 | _min, _max = self.domain() 277 | if _min < self.adaptor.FirstParameter(): 278 | raise ValueError('the lbound argument is lower than the first parameter of the curve: %s ' % (self.adaptor.FirstParameter())) 279 | if _max > self.adaptor.LastParameter(): 280 | raise ValueError('the ubound argument is greater than the last parameter of the curve: %s ' % (self.adaptor.LastParameter())) 281 | 282 | lbound = _min if lbound is None else lbound 283 | ubound = _max if ubound is None else ubound 284 | return GCPnts_AbscissaPoint().Length(self.adaptor, lbound, ubound, tolerance) 285 | 286 | #=========================================================================== 287 | # Curve.modify 288 | #=========================================================================== 289 | 290 | def trim(self, lbound, ubound): 291 | ''' 292 | trim the curve 293 | @param lbound: 294 | @param ubound: 295 | ''' 296 | a, b = sorted([lbound, ubound]) 297 | tr = Geom_TrimmedCurve(self.adaptor.Curve().Curve(), a, b).GetHandle() 298 | return Edge(make_edge(tr)) 299 | 300 | def extend_by_point(self, pnt, degree=3, beginning=True): 301 | '''extends the curve to point 302 | 303 | does not extend if the degree of self.curve > 3 304 | @param pnt: 305 | @param degree: 306 | @param beginning: 307 | ''' 308 | if self.degree > 3: 309 | raise ValueError('to extend you self.curve should be <= 3, is %s' % (self.degree)) 310 | return geomlib.ExtendCurveToPoint(self.curve, pnt, degree, beginning) 311 | 312 | #=========================================================================== 313 | # Curve. 314 | #=========================================================================== 315 | def closest(self, other): 316 | return minimum_distance(self, other) 317 | 318 | def project_vertex(self, pnt_or_vertex): 319 | ''' returns the closest orthogonal project on `pnt` on edge 320 | ''' 321 | if isinstance(pnt_or_vertex, TopoDS_Vertex): 322 | pnt_or_vertex = vertex2pnt(pnt_or_vertex) 323 | 324 | poc = GeomAPI_ProjectPointOnCurve(pnt_or_vertex, self.curve_handle) 325 | return poc.LowerDistanceParameter(), poc.NearestPoint() 326 | 327 | def distance_on_curve(self, distance, close_parameter, estimate_parameter): 328 | '''returns the parameter if there is a parameter 329 | on the curve with a distance length from u 330 | raises OutOfBoundary if no such parameter exists 331 | ''' 332 | gcpa = GCPnts_AbscissaPoint(self.adaptor, distance, close_parameter, estimate_parameter, 1e-5) 333 | with assert_isdone(gcpa, 'couldnt compute distance on curve'): 334 | return gcpa.Parameter() 335 | 336 | def mid_point(self): 337 | """ 338 | :return: the parameter at the mid point of the curve, and 339 | its corresponding gp_Pnt 340 | """ 341 | _min, _max = self.domain() 342 | _mid = (_min+_max) / 2. 343 | return _mid, self.adaptor.Value(_mid) 344 | 345 | def divide_by_number_of_points(self, n_pts, lbound=None, ubound=None): 346 | '''returns a nested list of parameters and points on the edge 347 | at the requested interval [(param, gp_Pnt),...] 348 | ''' 349 | _lbound, _ubound = self.domain() 350 | if lbound: 351 | _lbound = lbound 352 | elif ubound: 353 | _ubound = ubound 354 | 355 | # minimally two points or a Standard_ConstructionError is raised 356 | if n_pts <= 1: 357 | n_pts = 2 358 | 359 | try: 360 | npts = GCPnts_UniformAbscissa(self.adaptor, n_pts, _lbound, _ubound) 361 | except: 362 | print("Warning : GCPnts_UniformAbscissa failed") 363 | if npts.IsDone(): 364 | tmp = [] 365 | for i in xrange(1, npts.NbPoints()+1): 366 | param = npts.Parameter(i) 367 | pnt = self.adaptor.Value(param) 368 | tmp.append((param, pnt)) 369 | return tmp 370 | else: 371 | return None 372 | 373 | def __eq__(self, other): 374 | if hasattr(other, 'topo'): 375 | return self.IsEqual(other) 376 | else: 377 | return self.IsEqual(other) 378 | 379 | def __ne__(self, other): 380 | return not self.__eq__(other) 381 | 382 | def first_vertex(self): 383 | return topexp.FirstVertex(self) 384 | 385 | def last_vertex(self): 386 | return topexp.LastVertex(self) 387 | 388 | def common_vertex(self, edge): 389 | vert = TopoDS_Vertex() 390 | if topexp.CommonVertex(self, edge, vert): 391 | return vert 392 | else: 393 | return False 394 | 395 | def as_vec(self): 396 | if self.is_line(): 397 | first, last = map(vertex2pnt, [self.first_vertex(), self.last_vertex()]) 398 | return gp_Vec(first, last) 399 | else: 400 | raise ValueError("edge is not a line, hence no meaningful vector can be returned") 401 | 402 | #=========================================================================== 403 | # Curve. 404 | #=========================================================================== 405 | 406 | def parameter_to_point(self, u): 407 | '''returns the coordinate at parameter u 408 | ''' 409 | return self.adaptor.Value(u) 410 | 411 | def fix_continuity(self, continuity): 412 | """ 413 | splits an edge to achieve a level of continuity 414 | :param continuity: GeomAbs_C* 415 | """ 416 | return fix_continuity(self, continuity) 417 | 418 | def continuity_from_faces(self, f1, f2): 419 | return BRep_Tool_Continuity(self, f1, f2) 420 | 421 | #=========================================================================== 422 | # Curve. 423 | #=========================================================================== 424 | 425 | def is_line(self): 426 | '''checks if the curve is planar 427 | ''' 428 | if self.nb_knots() == 2 and self.nb_poles() == 2: 429 | return True 430 | else: 431 | return False 432 | 433 | def is_seam(self, face): 434 | """ 435 | :return: True if the edge has two pcurves on one surface 436 | ( in the case of a sphere for example... ) 437 | """ 438 | sae = ShapeAnalysis_Edge() 439 | return sae.IsSeam(self, face) 440 | 441 | def is_edge_on_face(self, face): 442 | '''checks whether curve lies on a surface or a face 443 | ''' 444 | return ShapeAnalysis_Edge().HasPCurve(self, face) 445 | 446 | #=========================================================================== 447 | # Curve.graphic 448 | #=========================================================================== 449 | def show(self): 450 | ''' 451 | poles, knots, should render all slightly different. 452 | here's how... 453 | 454 | http://www.opencascade.org/org/forum/thread_1125/ 455 | ''' 456 | super(Edge, self).show() 457 | 458 | 459 | if __name__ == '__main__': 460 | from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox 461 | from OCCUtils.Topology import Topo 462 | b = BRepPrimAPI_MakeBox(10, 20, 30).Shape() 463 | t = Topo(b) 464 | ed = next(t.edges()) 465 | my_e = Edge(ed) 466 | print(my_e.tolerance) 467 | -------------------------------------------------------------------------------- /step/tiltedCup.stp: -------------------------------------------------------------------------------- 1 | ISO-10303-21; 2 | HEADER; 3 | FILE_DESCRIPTION(('Open CASCADE Model'),'2;1'); 4 | FILE_NAME('Open CASCADE Shape Model','2016-08-04T20:07:36',('Author'),( 5 | 'Open CASCADE'),'Open CASCADE STEP processor 6.7','Open CASCADE 6.7' 6 | ,'Unknown'); 7 | FILE_SCHEMA(('AUTOMOTIVE_DESIGN_CC2 { 1 2 10303 214 -1 1 5 4 }')); 8 | ENDSEC; 9 | DATA; 10 | #1 = APPLICATION_PROTOCOL_DEFINITION('international standard', 11 | 'config_control_design',1994,#2); 12 | #2 = APPLICATION_CONTEXT( 13 | 'configuration controlled 3D designs of mechanical parts and assemblies' 14 | ); 15 | #3 = SHAPE_DEFINITION_REPRESENTATION(#4,#10); 16 | #4 = PRODUCT_DEFINITION_SHAPE('','',#5); 17 | #5 = PRODUCT_DEFINITION('design','',#6,#9); 18 | #6 = PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE('','',#7, 19 | .NOT_KNOWN.); 20 | #7 = PRODUCT('Open CASCADE STEP translator 6.7 1', 21 | 'Open CASCADE STEP translator 6.7 1','',(#8)); 22 | #8 = MECHANICAL_CONTEXT('',#2,'mechanical'); 23 | #9 = DESIGN_CONTEXT('',#2,'design'); 24 | #10 = ADVANCED_BREP_SHAPE_REPRESENTATION('',(#11,#15),#313); 25 | #11 = AXIS2_PLACEMENT_3D('',#12,#13,#14); 26 | #12 = CARTESIAN_POINT('',(0.E+000,0.E+000,0.E+000)); 27 | #13 = DIRECTION('',(0.E+000,0.E+000,1.)); 28 | #14 = DIRECTION('',(1.,0.E+000,-0.E+000)); 29 | #15 = MANIFOLD_SOLID_BREP('',#16); 30 | #16 = CLOSED_SHELL('',(#17,#105,#159,#163,#198,#253,#309)); 31 | #17 = ADVANCED_FACE('',(#18),#32,.T.); 32 | #18 = FACE_BOUND('',#19,.T.); 33 | #19 = EDGE_LOOP('',(#20,#50,#77,#78)); 34 | #20 = ORIENTED_EDGE('',*,*,#21,.T.); 35 | #21 = EDGE_CURVE('',#22,#24,#26,.T.); 36 | #22 = VERTEX_POINT('',#23); 37 | #23 = CARTESIAN_POINT('',(40.,-8.660254037844,5.)); 38 | #24 = VERTEX_POINT('',#25); 39 | #25 = CARTESIAN_POINT('',(40.,-69.28203230275,40.)); 40 | #26 = SEAM_CURVE('',#27,(#31,#43),.PCURVE_S1.); 41 | #27 = LINE('',#28,#29); 42 | #28 = CARTESIAN_POINT('',(40.,-4.898587196589E-015,-8.484601909799E-015) 43 | ); 44 | #29 = VECTOR('',#30,1.); 45 | #30 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 46 | #31 = PCURVE('',#32,#37); 47 | #32 = CYLINDRICAL_SURFACE('',#33,40.); 48 | #33 = AXIS2_PLACEMENT_3D('',#34,#35,#36); 49 | #34 = CARTESIAN_POINT('',(0.E+000,0.E+000,0.E+000)); 50 | #35 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 51 | #36 = DIRECTION('',(1.,0.E+000,0.E+000)); 52 | #37 = DEFINITIONAL_REPRESENTATION('',(#38),#42); 53 | #38 = LINE('',#39,#40); 54 | #39 = CARTESIAN_POINT('',(6.28318530718,-0.E+000)); 55 | #40 = VECTOR('',#41,1.); 56 | #41 = DIRECTION('',(0.E+000,1.)); 57 | #42 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 58 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 59 | ) ); 60 | #43 = PCURVE('',#32,#44); 61 | #44 = DEFINITIONAL_REPRESENTATION('',(#45),#49); 62 | #45 = LINE('',#46,#47); 63 | #46 = CARTESIAN_POINT('',(0.E+000,-0.E+000)); 64 | #47 = VECTOR('',#48,1.); 65 | #48 = DIRECTION('',(0.E+000,1.)); 66 | #49 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 67 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 68 | ) ); 69 | #50 = ORIENTED_EDGE('',*,*,#51,.F.); 70 | #51 = EDGE_CURVE('',#24,#24,#52,.T.); 71 | #52 = SURFACE_CURVE('',#53,(#58,#65),.PCURVE_S1.); 72 | #53 = CIRCLE('',#54,40.); 73 | #54 = AXIS2_PLACEMENT_3D('',#55,#56,#57); 74 | #55 = CARTESIAN_POINT('',(0.E+000,-69.28203230275,40.)); 75 | #56 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 76 | #57 = DIRECTION('',(1.,0.E+000,0.E+000)); 77 | #58 = PCURVE('',#32,#59); 78 | #59 = DEFINITIONAL_REPRESENTATION('',(#60),#64); 79 | #60 = LINE('',#61,#62); 80 | #61 = CARTESIAN_POINT('',(0.E+000,80.)); 81 | #62 = VECTOR('',#63,1.); 82 | #63 = DIRECTION('',(1.,0.E+000)); 83 | #64 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 84 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 85 | ) ); 86 | #65 = PCURVE('',#66,#71); 87 | #66 = PLANE('',#67); 88 | #67 = AXIS2_PLACEMENT_3D('',#68,#69,#70); 89 | #68 = CARTESIAN_POINT('',(0.E+000,-69.28203230275,40.)); 90 | #69 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 91 | #70 = DIRECTION('',(1.,0.E+000,0.E+000)); 92 | #71 = DEFINITIONAL_REPRESENTATION('',(#72),#76); 93 | #72 = CIRCLE('',#73,40.); 94 | #73 = AXIS2_PLACEMENT_2D('',#74,#75); 95 | #74 = CARTESIAN_POINT('',(0.E+000,0.E+000)); 96 | #75 = DIRECTION('',(1.,0.E+000)); 97 | #76 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 98 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 99 | ) ); 100 | #77 = ORIENTED_EDGE('',*,*,#21,.F.); 101 | #78 = ORIENTED_EDGE('',*,*,#79,.T.); 102 | #79 = EDGE_CURVE('',#22,#22,#80,.T.); 103 | #80 = SURFACE_CURVE('',#81,(#86,#93),.PCURVE_S1.); 104 | #81 = CIRCLE('',#82,40.); 105 | #82 = AXIS2_PLACEMENT_3D('',#83,#84,#85); 106 | #83 = CARTESIAN_POINT('',(0.E+000,-8.660254037844,5.)); 107 | #84 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 108 | #85 = DIRECTION('',(1.,0.E+000,0.E+000)); 109 | #86 = PCURVE('',#32,#87); 110 | #87 = DEFINITIONAL_REPRESENTATION('',(#88),#92); 111 | #88 = LINE('',#89,#90); 112 | #89 = CARTESIAN_POINT('',(0.E+000,10.)); 113 | #90 = VECTOR('',#91,1.); 114 | #91 = DIRECTION('',(1.,0.E+000)); 115 | #92 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 116 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 117 | ) ); 118 | #93 = PCURVE('',#94,#99); 119 | #94 = TOROIDAL_SURFACE('',#95,30.,10.); 120 | #95 = AXIS2_PLACEMENT_3D('',#96,#97,#98); 121 | #96 = CARTESIAN_POINT('',(0.E+000,-8.660254037844,5.)); 122 | #97 = DIRECTION('',(0.E+000,0.866025403784,-0.5)); 123 | #98 = DIRECTION('',(1.,0.E+000,0.E+000)); 124 | #99 = DEFINITIONAL_REPRESENTATION('',(#100),#104); 125 | #100 = LINE('',#101,#102); 126 | #101 = CARTESIAN_POINT('',(-0.E+000,0.E+000)); 127 | #102 = VECTOR('',#103,1.); 128 | #103 = DIRECTION('',(-1.,0.E+000)); 129 | #104 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 130 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 131 | ) ); 132 | #105 = ADVANCED_FACE('',(#106),#94,.T.); 133 | #106 = FACE_BOUND('',#107,.F.); 134 | #107 = EDGE_LOOP('',(#108,#130,#131,#132)); 135 | #108 = ORIENTED_EDGE('',*,*,#109,.F.); 136 | #109 = EDGE_CURVE('',#22,#110,#112,.T.); 137 | #110 = VERTEX_POINT('',#111); 138 | #111 = CARTESIAN_POINT('',(30.,0.E+000,0.E+000)); 139 | #112 = SEAM_CURVE('',#113,(#118,#124),.PCURVE_S1.); 140 | #113 = CIRCLE('',#114,10.); 141 | #114 = AXIS2_PLACEMENT_3D('',#115,#116,#117); 142 | #115 = CARTESIAN_POINT('',(30.,-8.660254037844,5.)); 143 | #116 = DIRECTION('',(-0.E+000,0.5,0.866025403784)); 144 | #117 = DIRECTION('',(1.,0.E+000,0.E+000)); 145 | #118 = PCURVE('',#94,#119); 146 | #119 = DEFINITIONAL_REPRESENTATION('',(#120),#123); 147 | #120 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#121,#122),.UNSPECIFIED.,.F.,.F., 148 | (2,2),(0.E+000,1.570796326795),.PIECEWISE_BEZIER_KNOTS.); 149 | #121 = CARTESIAN_POINT('',(0.E+000,0.E+000)); 150 | #122 = CARTESIAN_POINT('',(0.E+000,1.570796326795)); 151 | #123 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 152 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 153 | ) ); 154 | #124 = PCURVE('',#94,#125); 155 | #125 = DEFINITIONAL_REPRESENTATION('',(#126),#129); 156 | #126 = B_SPLINE_CURVE_WITH_KNOTS('',1,(#127,#128),.UNSPECIFIED.,.F.,.F., 157 | (2,2),(0.E+000,1.570796326795),.PIECEWISE_BEZIER_KNOTS.); 158 | #127 = CARTESIAN_POINT('',(-6.28318530718,0.E+000)); 159 | #128 = CARTESIAN_POINT('',(-6.28318530718,1.570796326795)); 160 | #129 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 161 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 162 | ) ); 163 | #130 = ORIENTED_EDGE('',*,*,#79,.T.); 164 | #131 = ORIENTED_EDGE('',*,*,#109,.T.); 165 | #132 = ORIENTED_EDGE('',*,*,#133,.F.); 166 | #133 = EDGE_CURVE('',#110,#110,#134,.T.); 167 | #134 = SURFACE_CURVE('',#135,(#140,#147),.PCURVE_S1.); 168 | #135 = CIRCLE('',#136,30.); 169 | #136 = AXIS2_PLACEMENT_3D('',#137,#138,#139); 170 | #137 = CARTESIAN_POINT('',(0.E+000,0.E+000,0.E+000)); 171 | #138 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 172 | #139 = DIRECTION('',(1.,0.E+000,0.E+000)); 173 | #140 = PCURVE('',#94,#141); 174 | #141 = DEFINITIONAL_REPRESENTATION('',(#142),#146); 175 | #142 = LINE('',#143,#144); 176 | #143 = CARTESIAN_POINT('',(-0.E+000,1.570796326795)); 177 | #144 = VECTOR('',#145,1.); 178 | #145 = DIRECTION('',(-1.,0.E+000)); 179 | #146 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 180 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 181 | ) ); 182 | #147 = PCURVE('',#148,#153); 183 | #148 = PLANE('',#149); 184 | #149 = AXIS2_PLACEMENT_3D('',#150,#151,#152); 185 | #150 = CARTESIAN_POINT('',(0.E+000,0.E+000,0.E+000)); 186 | #151 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 187 | #152 = DIRECTION('',(1.,0.E+000,0.E+000)); 188 | #153 = DEFINITIONAL_REPRESENTATION('',(#154),#158); 189 | #154 = CIRCLE('',#155,30.); 190 | #155 = AXIS2_PLACEMENT_2D('',#156,#157); 191 | #156 = CARTESIAN_POINT('',(0.E+000,0.E+000)); 192 | #157 = DIRECTION('',(1.,0.E+000)); 193 | #158 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 194 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 195 | ) ); 196 | #159 = ADVANCED_FACE('',(#160),#148,.F.); 197 | #160 = FACE_BOUND('',#161,.F.); 198 | #161 = EDGE_LOOP('',(#162)); 199 | #162 = ORIENTED_EDGE('',*,*,#133,.T.); 200 | #163 = ADVANCED_FACE('',(#164,#167),#66,.T.); 201 | #164 = FACE_BOUND('',#165,.T.); 202 | #165 = EDGE_LOOP('',(#166)); 203 | #166 = ORIENTED_EDGE('',*,*,#51,.T.); 204 | #167 = FACE_BOUND('',#168,.T.); 205 | #168 = EDGE_LOOP('',(#169)); 206 | #169 = ORIENTED_EDGE('',*,*,#170,.F.); 207 | #170 = EDGE_CURVE('',#171,#171,#173,.T.); 208 | #171 = VERTEX_POINT('',#172); 209 | #172 = CARTESIAN_POINT('',(35.,-69.28203230275,40.)); 210 | #173 = SURFACE_CURVE('',#174,(#179,#186),.PCURVE_S1.); 211 | #174 = CIRCLE('',#175,35.); 212 | #175 = AXIS2_PLACEMENT_3D('',#176,#177,#178); 213 | #176 = CARTESIAN_POINT('',(0.E+000,-69.28203230275,40.)); 214 | #177 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 215 | #178 = DIRECTION('',(1.,0.E+000,0.E+000)); 216 | #179 = PCURVE('',#66,#180); 217 | #180 = DEFINITIONAL_REPRESENTATION('',(#181),#185); 218 | #181 = CIRCLE('',#182,35.); 219 | #182 = AXIS2_PLACEMENT_2D('',#183,#184); 220 | #183 = CARTESIAN_POINT('',(0.E+000,0.E+000)); 221 | #184 = DIRECTION('',(1.,0.E+000)); 222 | #185 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 223 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 224 | ) ); 225 | #186 = PCURVE('',#187,#192); 226 | #187 = CYLINDRICAL_SURFACE('',#188,35.); 227 | #188 = AXIS2_PLACEMENT_3D('',#189,#190,#191); 228 | #189 = CARTESIAN_POINT('',(0.E+000,0.E+000,0.E+000)); 229 | #190 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 230 | #191 = DIRECTION('',(1.,0.E+000,0.E+000)); 231 | #192 = DEFINITIONAL_REPRESENTATION('',(#193),#197); 232 | #193 = LINE('',#194,#195); 233 | #194 = CARTESIAN_POINT('',(0.E+000,80.)); 234 | #195 = VECTOR('',#196,1.); 235 | #196 = DIRECTION('',(1.,0.E+000)); 236 | #197 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 237 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 238 | ) ); 239 | #198 = ADVANCED_FACE('',(#199),#187,.F.); 240 | #199 = FACE_BOUND('',#200,.F.); 241 | #200 = EDGE_LOOP('',(#201,#202,#225,#252)); 242 | #201 = ORIENTED_EDGE('',*,*,#170,.F.); 243 | #202 = ORIENTED_EDGE('',*,*,#203,.F.); 244 | #203 = EDGE_CURVE('',#204,#171,#206,.T.); 245 | #204 = VERTEX_POINT('',#205); 246 | #205 = CARTESIAN_POINT('',(35.,-8.660254037844,5.)); 247 | #206 = SEAM_CURVE('',#207,(#211,#218),.PCURVE_S1.); 248 | #207 = LINE('',#208,#209); 249 | #208 = CARTESIAN_POINT('',(35.,-4.286263797016E-015,-7.424026671074E-015 250 | )); 251 | #209 = VECTOR('',#210,1.); 252 | #210 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 253 | #211 = PCURVE('',#187,#212); 254 | #212 = DEFINITIONAL_REPRESENTATION('',(#213),#217); 255 | #213 = LINE('',#214,#215); 256 | #214 = CARTESIAN_POINT('',(0.E+000,-0.E+000)); 257 | #215 = VECTOR('',#216,1.); 258 | #216 = DIRECTION('',(0.E+000,1.)); 259 | #217 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 260 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 261 | ) ); 262 | #218 = PCURVE('',#187,#219); 263 | #219 = DEFINITIONAL_REPRESENTATION('',(#220),#224); 264 | #220 = LINE('',#221,#222); 265 | #221 = CARTESIAN_POINT('',(6.28318530718,-0.E+000)); 266 | #222 = VECTOR('',#223,1.); 267 | #223 = DIRECTION('',(0.E+000,1.)); 268 | #224 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 269 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 270 | ) ); 271 | #225 = ORIENTED_EDGE('',*,*,#226,.T.); 272 | #226 = EDGE_CURVE('',#204,#204,#227,.T.); 273 | #227 = SURFACE_CURVE('',#228,(#233,#240),.PCURVE_S1.); 274 | #228 = CIRCLE('',#229,35.); 275 | #229 = AXIS2_PLACEMENT_3D('',#230,#231,#232); 276 | #230 = CARTESIAN_POINT('',(0.E+000,-8.660254037844,5.)); 277 | #231 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 278 | #232 = DIRECTION('',(1.,0.E+000,0.E+000)); 279 | #233 = PCURVE('',#187,#234); 280 | #234 = DEFINITIONAL_REPRESENTATION('',(#235),#239); 281 | #235 = LINE('',#236,#237); 282 | #236 = CARTESIAN_POINT('',(0.E+000,10.)); 283 | #237 = VECTOR('',#238,1.); 284 | #238 = DIRECTION('',(1.,0.E+000)); 285 | #239 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 286 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 287 | ) ); 288 | #240 = PCURVE('',#241,#246); 289 | #241 = TOROIDAL_SURFACE('',#242,30.,5.); 290 | #242 = AXIS2_PLACEMENT_3D('',#243,#244,#245); 291 | #243 = CARTESIAN_POINT('',(0.E+000,-8.660254037844,5.)); 292 | #244 = DIRECTION('',(0.E+000,0.866025403784,-0.5)); 293 | #245 = DIRECTION('',(1.,0.E+000,0.E+000)); 294 | #246 = DEFINITIONAL_REPRESENTATION('',(#247),#251); 295 | #247 = LINE('',#248,#249); 296 | #248 = CARTESIAN_POINT('',(-0.E+000,0.E+000)); 297 | #249 = VECTOR('',#250,1.); 298 | #250 = DIRECTION('',(-1.,0.E+000)); 299 | #251 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 300 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 301 | ) ); 302 | #252 = ORIENTED_EDGE('',*,*,#203,.T.); 303 | #253 = ADVANCED_FACE('',(#254),#241,.F.); 304 | #254 = FACE_BOUND('',#255,.T.); 305 | #255 = EDGE_LOOP('',(#256,#280,#281,#282)); 306 | #256 = ORIENTED_EDGE('',*,*,#257,.F.); 307 | #257 = EDGE_CURVE('',#204,#258,#260,.T.); 308 | #258 = VERTEX_POINT('',#259); 309 | #259 = CARTESIAN_POINT('',(30.,-4.330127018922,2.5)); 310 | #260 = SEAM_CURVE('',#261,(#266,#273),.PCURVE_S1.); 311 | #261 = CIRCLE('',#262,5.); 312 | #262 = AXIS2_PLACEMENT_3D('',#263,#264,#265); 313 | #263 = CARTESIAN_POINT('',(30.,-8.660254037844,5.)); 314 | #264 = DIRECTION('',(-0.E+000,0.5,0.866025403784)); 315 | #265 = DIRECTION('',(1.,0.E+000,0.E+000)); 316 | #266 = PCURVE('',#241,#267); 317 | #267 = DEFINITIONAL_REPRESENTATION('',(#268),#272); 318 | #268 = LINE('',#269,#270); 319 | #269 = CARTESIAN_POINT('',(-0.E+000,0.E+000)); 320 | #270 = VECTOR('',#271,1.); 321 | #271 = DIRECTION('',(-0.E+000,1.)); 322 | #272 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 323 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 324 | ) ); 325 | #273 = PCURVE('',#241,#274); 326 | #274 = DEFINITIONAL_REPRESENTATION('',(#275),#279); 327 | #275 = LINE('',#276,#277); 328 | #276 = CARTESIAN_POINT('',(-6.28318530718,0.E+000)); 329 | #277 = VECTOR('',#278,1.); 330 | #278 = DIRECTION('',(-0.E+000,1.)); 331 | #279 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 332 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 333 | ) ); 334 | #280 = ORIENTED_EDGE('',*,*,#226,.T.); 335 | #281 = ORIENTED_EDGE('',*,*,#257,.T.); 336 | #282 = ORIENTED_EDGE('',*,*,#283,.F.); 337 | #283 = EDGE_CURVE('',#258,#258,#284,.T.); 338 | #284 = SURFACE_CURVE('',#285,(#290,#297),.PCURVE_S1.); 339 | #285 = CIRCLE('',#286,30.); 340 | #286 = AXIS2_PLACEMENT_3D('',#287,#288,#289); 341 | #287 = CARTESIAN_POINT('',(0.E+000,-4.330127018922,2.5)); 342 | #288 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 343 | #289 = DIRECTION('',(1.,0.E+000,0.E+000)); 344 | #290 = PCURVE('',#241,#291); 345 | #291 = DEFINITIONAL_REPRESENTATION('',(#292),#296); 346 | #292 = LINE('',#293,#294); 347 | #293 = CARTESIAN_POINT('',(-0.E+000,1.570796326795)); 348 | #294 = VECTOR('',#295,1.); 349 | #295 = DIRECTION('',(-1.,0.E+000)); 350 | #296 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 351 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 352 | ) ); 353 | #297 = PCURVE('',#298,#303); 354 | #298 = PLANE('',#299); 355 | #299 = AXIS2_PLACEMENT_3D('',#300,#301,#302); 356 | #300 = CARTESIAN_POINT('',(0.E+000,-4.330127018922,2.5)); 357 | #301 = DIRECTION('',(0.E+000,-0.866025403784,0.5)); 358 | #302 = DIRECTION('',(1.,0.E+000,0.E+000)); 359 | #303 = DEFINITIONAL_REPRESENTATION('',(#304),#308); 360 | #304 = CIRCLE('',#305,30.); 361 | #305 = AXIS2_PLACEMENT_2D('',#306,#307); 362 | #306 = CARTESIAN_POINT('',(0.E+000,0.E+000)); 363 | #307 = DIRECTION('',(1.,0.E+000)); 364 | #308 = ( GEOMETRIC_REPRESENTATION_CONTEXT(2) 365 | PARAMETRIC_REPRESENTATION_CONTEXT() REPRESENTATION_CONTEXT('2D SPACE','' 366 | ) ); 367 | #309 = ADVANCED_FACE('',(#310),#298,.T.); 368 | #310 = FACE_BOUND('',#311,.T.); 369 | #311 = EDGE_LOOP('',(#312)); 370 | #312 = ORIENTED_EDGE('',*,*,#283,.T.); 371 | #313 = ( GEOMETRIC_REPRESENTATION_CONTEXT(3) 372 | GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#317)) GLOBAL_UNIT_ASSIGNED_CONTEXT 373 | ((#314,#315,#316)) REPRESENTATION_CONTEXT('Context #1', 374 | '3D Context with UNIT and UNCERTAINTY') ); 375 | #314 = ( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) ); 376 | #315 = ( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) ); 377 | #316 = ( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() ); 378 | #317 = UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(5.E-004),#314, 379 | 'distance_accuracy_value','confusion accuracy'); 380 | #318 = PRODUCT_RELATED_PRODUCT_CATEGORY('detail',$,(#7)); 381 | #319 = PRODUCT_CATEGORY_RELATIONSHIP('','',#320,#318); 382 | #320 = PRODUCT_CATEGORY('part',$); 383 | #321 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#322,#325,(#6,#5)); 384 | #322 = PERSON_AND_ORGANIZATION(#323,#324); 385 | #323 = PERSON('IP192.168.001.000,410210336','','',$,$,$); 386 | #324 = ORGANIZATION('IP192.168.001.000','Unspecified',''); 387 | #325 = PERSON_AND_ORGANIZATION_ROLE('creator'); 388 | #326 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#322,#327,(#7)); 389 | #327 = PERSON_AND_ORGANIZATION_ROLE('design_owner'); 390 | #328 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#322,#329,(#6)); 391 | #329 = PERSON_AND_ORGANIZATION_ROLE('design_supplier'); 392 | #330 = CC_DESIGN_PERSON_AND_ORGANIZATION_ASSIGNMENT(#322,#331,(#332)); 393 | #331 = PERSON_AND_ORGANIZATION_ROLE('classification_officer'); 394 | #332 = SECURITY_CLASSIFICATION('','',#333); 395 | #333 = SECURITY_CLASSIFICATION_LEVEL('unclassified'); 396 | #334 = CC_DESIGN_SECURITY_CLASSIFICATION(#332,(#6)); 397 | #335 = CC_DESIGN_DATE_AND_TIME_ASSIGNMENT(#336,#340,(#5)); 398 | #336 = DATE_AND_TIME(#337,#338); 399 | #337 = CALENDAR_DATE(2016,4,8); 400 | #338 = LOCAL_TIME(20,7,$,#339); 401 | #339 = COORDINATED_UNIVERSAL_TIME_OFFSET(5,$,.BEHIND.); 402 | #340 = DATE_TIME_ROLE('creation_date'); 403 | #341 = CC_DESIGN_DATE_AND_TIME_ASSIGNMENT(#336,#342,(#332)); 404 | #342 = DATE_TIME_ROLE('classification_date'); 405 | #343 = CC_DESIGN_APPROVAL(#344,(#6,#5,#332)); 406 | #344 = APPROVAL(#345,''); 407 | #345 = APPROVAL_STATUS('not_yet_approved'); 408 | #346 = APPROVAL_PERSON_ORGANIZATION(#322,#344,#347); 409 | #347 = APPROVAL_ROLE('approver'); 410 | #348 = APPROVAL_DATE_TIME(#336,#344); 411 | ENDSEC; 412 | END-ISO-10303-21; 413 | -------------------------------------------------------------------------------- /OCCUtils/Topology.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ##Copyright 2008-2015 Jelle Feringa (jelleferinga@gmail.com) 4 | ## 5 | ##This file is part of pythonOCC. 6 | ## 7 | ##pythonOCC is free software: you can redistribute it and/or modify 8 | ##it under the terms of the GNU Lesser General Public License as published by 9 | ##the Free Software Foundation, either version 3 of the License, or 10 | ##(at your option) any later version. 11 | ## 12 | ##pythonOCC is distributed in the hope that it will be useful, 13 | ##but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | ##MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | ##GNU Lesser General Public License for more details. 16 | ## 17 | ##You should have received a copy of the GNU Lesser General Public License 18 | ##along with pythonOCC. If not, see . 19 | 20 | from __future__ import print_function 21 | 22 | __all__ = ['Topo', 'WireExplorer', 'dumpTopology'] 23 | 24 | from OCC.Core.BRep import BRep_Tool 25 | 26 | from OCC.Core.BRepTools import BRepTools_WireExplorer 27 | from OCC.Core.TopAbs import (TopAbs_VERTEX, TopAbs_EDGE, TopAbs_FACE, TopAbs_WIRE, 28 | TopAbs_SHELL, TopAbs_SOLID, TopAbs_COMPOUND, 29 | TopAbs_COMPSOLID) 30 | from OCC.Core.TopExp import TopExp_Explorer, topexp_MapShapesAndAncestors 31 | from OCC.Core.TopTools import (TopTools_ListOfShape, 32 | TopTools_ListIteratorOfListOfShape, 33 | TopTools_IndexedDataMapOfShapeListOfShape) 34 | from OCC.Core.TopoDS import (topods, TopoDS_Wire, TopoDS_Vertex, TopoDS_Edge, 35 | TopoDS_Face, TopoDS_Shell, TopoDS_Solid, 36 | TopoDS_Compound, TopoDS_CompSolid, topods_Edge, 37 | topods_Vertex, TopoDS_Iterator) 38 | 39 | 40 | class WireExplorer(object): 41 | ''' 42 | Wire traversal 43 | ''' 44 | def __init__(self, wire): 45 | assert isinstance(wire, TopoDS_Wire), 'not a TopoDS_Wire' 46 | self.wire = wire 47 | self.wire_explorer = BRepTools_WireExplorer(self.wire) 48 | self.done = False 49 | 50 | def _reinitialize(self): 51 | self.wire_explorer = BRepTools_WireExplorer(self.wire) 52 | self.done = False 53 | 54 | def _loop_topo(self, edges=True): 55 | if self.done: 56 | self._reinitialize() 57 | topologyType = topods_Edge if edges else topods_Vertex 58 | seq = [] 59 | hashes = [] # list that stores hashes to avoid redundancy 60 | occ_seq = TopTools_ListOfShape() 61 | while self.wire_explorer.More(): 62 | # loop edges 63 | if edges: 64 | current_item = self.wire_explorer.Current() 65 | # loop vertices 66 | else: 67 | current_item = self.wire_explorer.CurrentVertex() 68 | current_item_hash = current_item.__hash__() 69 | if not current_item_hash in hashes: 70 | hashes.append(current_item_hash) 71 | occ_seq.Append(current_item) 72 | self.wire_explorer.Next() 73 | 74 | # Convert occ_seq to python list 75 | occ_iterator = TopTools_ListIteratorOfListOfShape(occ_seq) 76 | while occ_iterator.More(): 77 | topo_to_add = topologyType(occ_iterator.Value()) 78 | seq.append(topo_to_add) 79 | occ_iterator.Next() 80 | self.done = True 81 | return iter(seq) 82 | 83 | def ordered_edges(self): 84 | return self._loop_topo(edges=True) 85 | 86 | def ordered_vertices(self): 87 | return self._loop_topo(edges=False) 88 | 89 | 90 | class Topo(object): 91 | ''' 92 | Topology traversal 93 | ''' 94 | 95 | def __init__(self, myShape, ignore_orientation=False): 96 | """ 97 | 98 | implements topology traversal from any TopoDS_Shape 99 | this class lets you find how various topological entities are connected from one to another 100 | find the faces connected to an edge, find the vertices this edge is made from, get all faces connected to 101 | a vertex, and find out how many topological elements are connected from a source 102 | 103 | *note* when traversing TopoDS_Wire entities, its advised to use the specialized 104 | ``WireExplorer`` class, which will return the vertices / edges in the expected order 105 | 106 | :param myShape: the shape which topology will be traversed 107 | 108 | :param ignore_orientation: filter out TopoDS_* entities of similar TShape but different Orientation 109 | 110 | for instance, a cube has 24 edges, 4 edges for each of 6 faces 111 | 112 | that results in 48 vertices, while there are only 8 vertices that have a unique 113 | geometric coordinate 114 | 115 | in certain cases ( computing a graph from the topology ) its preferable to return 116 | topological entities that share similar geometry, though differ in orientation 117 | by setting the ``ignore_orientation`` variable 118 | to True, in case of a cube, just 12 edges and only 8 vertices will be returned 119 | 120 | for further reference see TopoDS_Shape IsEqual / IsSame methods 121 | 122 | """ 123 | self.myShape = myShape 124 | self.ignore_orientation = ignore_orientation 125 | 126 | # the topoFactory dicts maps topology types and functions that can 127 | # create this topology 128 | self.topoFactory = { 129 | TopAbs_VERTEX: topods.Vertex, 130 | TopAbs_EDGE: topods.Edge, 131 | TopAbs_FACE: topods.Face, 132 | TopAbs_WIRE: topods.Wire, 133 | TopAbs_SHELL: topods.Shell, 134 | TopAbs_SOLID: topods.Solid, 135 | TopAbs_COMPOUND: topods.Compound, 136 | TopAbs_COMPSOLID: topods.CompSolid 137 | } 138 | 139 | def _loop_topo(self, topologyType, topologicalEntity=None, topologyTypeToAvoid=None): 140 | ''' 141 | this could be a faces generator for a python TopoShape class 142 | that way you can just do: 143 | for face in srf.faces: 144 | processFace(face) 145 | ''' 146 | topoTypes = {TopAbs_VERTEX: TopoDS_Vertex, 147 | TopAbs_EDGE: TopoDS_Edge, 148 | TopAbs_FACE: TopoDS_Face, 149 | TopAbs_WIRE: TopoDS_Wire, 150 | TopAbs_SHELL: TopoDS_Shell, 151 | TopAbs_SOLID: TopoDS_Solid, 152 | TopAbs_COMPOUND: TopoDS_Compound, 153 | TopAbs_COMPSOLID: TopoDS_CompSolid} 154 | 155 | assert topologyType in topoTypes.keys(), '%s not one of %s' % (topologyType, topoTypes.keys()) 156 | self.topExp = TopExp_Explorer() 157 | # use self.myShape if nothing is specified 158 | if topologicalEntity is None and topologyTypeToAvoid is None: 159 | self.topExp.Init(self.myShape, topologyType) 160 | elif topologicalEntity is None and topologyTypeToAvoid is not None: 161 | self.topExp.Init(self.myShape, topologyType, topologyTypeToAvoid) 162 | elif topologyTypeToAvoid is None: 163 | self.topExp.Init(topologicalEntity, topologyType) 164 | elif topologyTypeToAvoid: 165 | self.topExp.Init(topologicalEntity, 166 | topologyType, 167 | topologyTypeToAvoid) 168 | seq = [] 169 | hashes = [] # list that stores hashes to avoid redundancy 170 | occ_seq = TopTools_ListOfShape() 171 | while self.topExp.More(): 172 | current_item = self.topExp.Current() 173 | current_item_hash = current_item.__hash__() 174 | 175 | if not current_item_hash in hashes: 176 | hashes.append(current_item_hash) 177 | occ_seq.Append(current_item) 178 | 179 | self.topExp.Next() 180 | # Convert occ_seq to python list 181 | occ_iterator = TopTools_ListIteratorOfListOfShape(occ_seq) 182 | while occ_iterator.More(): 183 | topo_to_add = self.topoFactory[topologyType](occ_iterator.Value()) 184 | seq.append(topo_to_add) 185 | occ_iterator.Next() 186 | 187 | if self.ignore_orientation: 188 | # filter out those entities that share the same TShape 189 | # but do *not* share the same orientation 190 | filter_orientation_seq = [] 191 | for i in seq: 192 | _present = False 193 | for j in filter_orientation_seq: 194 | if i.IsSame(j): 195 | _present = True 196 | break 197 | if _present is False: 198 | filter_orientation_seq.append(i) 199 | return filter_orientation_seq 200 | else: 201 | return iter(seq) 202 | 203 | def faces(self): 204 | ''' 205 | loops over all faces 206 | ''' 207 | return self._loop_topo(TopAbs_FACE) 208 | 209 | def _number_of_topo(self, iterable): 210 | n = 0 211 | for i in iterable: 212 | n += 1 213 | return n 214 | 215 | def number_of_faces(self): 216 | return self._number_of_topo(self.faces()) 217 | 218 | def vertices(self): 219 | ''' 220 | loops over all vertices 221 | ''' 222 | return self._loop_topo(TopAbs_VERTEX) 223 | 224 | def number_of_vertices(self): 225 | return self._number_of_topo(self.vertices()) 226 | 227 | def edges(self): 228 | ''' 229 | loops over all edges 230 | ''' 231 | return self._loop_topo(TopAbs_EDGE) 232 | 233 | def number_of_edges(self): 234 | return self._number_of_topo(self.edges()) 235 | 236 | def wires(self): 237 | ''' 238 | loops over all wires 239 | ''' 240 | return self._loop_topo(TopAbs_WIRE) 241 | 242 | def number_of_wires(self): 243 | return self._number_of_topo(self.wires()) 244 | 245 | def shells(self): 246 | ''' 247 | loops over all shells 248 | ''' 249 | return self._loop_topo(TopAbs_SHELL, None) 250 | 251 | def number_of_shells(self): 252 | return self._number_of_topo(self.shells()) 253 | 254 | def solids(self): 255 | ''' 256 | loops over all solids 257 | ''' 258 | return self._loop_topo(TopAbs_SOLID, None) 259 | 260 | def number_of_solids(self): 261 | return self._number_of_topo(self.solids()) 262 | 263 | def comp_solids(self): 264 | ''' 265 | loops over all compound solids 266 | ''' 267 | return self._loop_topo(TopAbs_COMPSOLID) 268 | 269 | def number_of_comp_solids(self): 270 | return self._number_of_topo(self.comp_solids()) 271 | 272 | def compounds(self): 273 | ''' 274 | loops over all compounds 275 | ''' 276 | return self._loop_topo(TopAbs_COMPOUND) 277 | 278 | def number_of_compounds(self): 279 | return self._number_of_topo(self.compounds()) 280 | 281 | def ordered_vertices_from_wire(self, wire): 282 | ''' 283 | @param wire: TopoDS_Wire 284 | ''' 285 | we = WireExplorer(wire) 286 | return we.ordered_vertices() 287 | 288 | def number_of_ordered_vertices_from_wire(self, wire): 289 | return self._number_of_topo(self.ordered_vertices_from_wire(wire)) 290 | 291 | def ordered_edges_from_wire(self, wire): 292 | ''' 293 | @param wire: TopoDS_Wire 294 | ''' 295 | we = WireExplorer(wire) 296 | return we.ordered_edges() 297 | 298 | def number_of_ordered_edges_from_wire(self, wire): 299 | return self._number_of_topo(self.ordered_edges_from_wire(wire)) 300 | 301 | def _map_shapes_and_ancestors(self, topoTypeA, topoTypeB, topologicalEntity): 302 | ''' 303 | using the same method 304 | @param topoTypeA: 305 | @param topoTypeB: 306 | @param topologicalEntity: 307 | ''' 308 | topo_set = set() 309 | _map = TopTools_IndexedDataMapOfShapeListOfShape() 310 | topexp_MapShapesAndAncestors(self.myShape, topoTypeA, topoTypeB, _map) 311 | results = _map.FindFromKey(topologicalEntity) 312 | if results.IsEmpty(): 313 | yield None 314 | 315 | topology_iterator = TopTools_ListIteratorOfListOfShape(results) 316 | while topology_iterator.More(): 317 | 318 | topo_entity = self.topoFactory[topoTypeB](topology_iterator.Value()) 319 | 320 | # return the entity if not in set 321 | # to assure we're not returning entities several times 322 | if not topo_entity in topo_set: 323 | if self.ignore_orientation: 324 | unique = True 325 | for i in topo_set: 326 | if i.IsSame(topo_entity): 327 | unique = False 328 | break 329 | if unique: 330 | yield topo_entity 331 | else: 332 | yield topo_entity 333 | 334 | topo_set.add(topo_entity) 335 | topology_iterator.Next() 336 | 337 | def _number_shapes_ancestors(self, topoTypeA, topoTypeB, topologicalEntity): 338 | '''returns the number of shape ancestors 339 | If you want to know how many edges a faces has: 340 | _number_shapes_ancestors(self, TopAbs_EDGE, TopAbs_FACE, edg) 341 | will return the number of edges a faces has 342 | @param topoTypeA: 343 | @param topoTypeB: 344 | @param topologicalEntity: 345 | ''' 346 | topo_set = set() 347 | _map = TopTools_IndexedDataMapOfShapeListOfShape() 348 | topexp_MapShapesAndAncestors(self.myShape, topoTypeA, topoTypeB, _map) 349 | results = _map.FindFromKey(topologicalEntity) 350 | if results.IsEmpty(): 351 | return None 352 | topology_iterator = TopTools_ListIteratorOfListOfShape(results) 353 | while topology_iterator.More(): 354 | topo_set.add(topology_iterator.Value()) 355 | topology_iterator.Next() 356 | return len(topo_set) 357 | 358 | # ====================================================================== 359 | # EDGE <-> FACE 360 | # ====================================================================== 361 | def faces_from_edge(self, edge): 362 | """ 363 | 364 | :param edge: 365 | :return: 366 | """ 367 | return self._map_shapes_and_ancestors(TopAbs_EDGE, TopAbs_FACE, edge) 368 | 369 | def number_of_faces_from_edge(self, edge): 370 | """ 371 | 372 | :param edge: 373 | :return: 374 | """ 375 | return self._number_shapes_ancestors(TopAbs_EDGE, TopAbs_FACE, edge) 376 | 377 | def edges_from_face(self, face): 378 | """ 379 | 380 | :param face: 381 | :return: 382 | """ 383 | return self._loop_topo(TopAbs_EDGE, face) 384 | 385 | def number_of_edges_from_face(self, face): 386 | cnt = 0 387 | for i in self._loop_topo(TopAbs_EDGE, face): 388 | cnt += 1 389 | return cnt 390 | 391 | # ====================================================================== 392 | # VERTEX <-> EDGE 393 | # ====================================================================== 394 | def vertices_from_edge(self, edg): 395 | return self._loop_topo(TopAbs_VERTEX, edg) 396 | 397 | def number_of_vertices_from_edge(self, edg): 398 | cnt = 0 399 | for i in self._loop_topo(TopAbs_VERTEX, edg): 400 | cnt += 1 401 | return cnt 402 | 403 | def edges_from_vertex(self, vertex): 404 | return self._map_shapes_and_ancestors(TopAbs_VERTEX, TopAbs_EDGE, vertex) 405 | 406 | def number_of_edges_from_vertex(self, vertex): 407 | return self._number_shapes_ancestors(TopAbs_VERTEX, TopAbs_EDGE, vertex) 408 | 409 | # ====================================================================== 410 | # WIRE <-> EDGE 411 | # ====================================================================== 412 | def edges_from_wire(self, wire): 413 | return self._loop_topo(TopAbs_EDGE, wire) 414 | 415 | def number_of_edges_from_wire(self, wire): 416 | cnt = 0 417 | for i in self._loop_topo(TopAbs_EDGE, wire): 418 | cnt += 1 419 | return cnt 420 | 421 | def wires_from_edge(self, edg): 422 | return self._map_shapes_and_ancestors(TopAbs_EDGE, TopAbs_WIRE, edg) 423 | 424 | def wires_from_vertex(self, edg): 425 | return self._map_shapes_and_ancestors(TopAbs_VERTEX, TopAbs_WIRE, edg) 426 | 427 | def number_of_wires_from_edge(self, edg): 428 | return self._number_shapes_ancestors(TopAbs_EDGE, TopAbs_WIRE, edg) 429 | 430 | # ====================================================================== 431 | # WIRE <-> FACE 432 | # ====================================================================== 433 | def wires_from_face(self, face): 434 | return self._loop_topo(TopAbs_WIRE, face) 435 | 436 | def number_of_wires_from_face(self, face): 437 | cnt = 0 438 | for i in self._loop_topo(TopAbs_WIRE, face): 439 | cnt += 1 440 | return cnt 441 | 442 | def faces_from_wire(self, wire): 443 | return self._map_shapes_and_ancestors(TopAbs_WIRE, TopAbs_FACE, wire) 444 | 445 | def number_of_faces_from_wires(self, wire): 446 | return self._number_shapes_ancestors(TopAbs_WIRE, TopAbs_FACE, wire) 447 | 448 | # ====================================================================== 449 | # VERTEX <-> FACE 450 | # ====================================================================== 451 | def faces_from_vertex(self, vertex): 452 | return self._map_shapes_and_ancestors(TopAbs_VERTEX, TopAbs_FACE, vertex) 453 | 454 | def number_of_faces_from_vertex(self, vertex): 455 | return self._number_shapes_ancestors(TopAbs_VERTEX, TopAbs_FACE, vertex) 456 | 457 | def vertices_from_face(self, face): 458 | return self._loop_topo(TopAbs_VERTEX, face) 459 | 460 | def number_of_vertices_from_face(self, face): 461 | cnt = 0 462 | for i in self._loop_topo(TopAbs_VERTEX, face): 463 | cnt += 1 464 | return cnt 465 | 466 | # ====================================================================== 467 | # FACE <-> SOLID 468 | # ====================================================================== 469 | def solids_from_face(self, face): 470 | return self._map_shapes_and_ancestors(TopAbs_FACE, TopAbs_SOLID, face) 471 | 472 | def number_of_solids_from_face(self, face): 473 | return self._number_shapes_ancestors(TopAbs_FACE, TopAbs_SOLID, face) 474 | 475 | def faces_from_solids(self, solid): 476 | return self._loop_topo(TopAbs_FACE, solid) 477 | 478 | def number_of_faces_from_solids(self, solid): 479 | cnt = 0 480 | for i in self._loop_topo(TopAbs_FACE, solid): 481 | cnt += 1 482 | return cnt 483 | 484 | 485 | def dumpTopology(shape, level=0): 486 | """ 487 | Print the details of an object from the top down 488 | """ 489 | brt = BRep_Tool() 490 | s = shape.ShapeType() 491 | if s == TopAbs_VERTEX: 492 | pnt = brt.Pnt(topods_Vertex(shape)) 493 | print(".." * level + "" % (hash(shape), pnt.X(), pnt.Y(), pnt.Z())) 494 | else: 495 | print(".." * level, end="") 496 | print(shapeTypeString(shape)) 497 | it = TopoDS_Iterator(shape) 498 | while it.More(): 499 | shp = it.Value() 500 | it.Next() 501 | dumpTopology(shp, level + 1) 502 | 503 | 504 | def shapeTypeString(shape): 505 | st = shape.ShapeType() 506 | s = "?" 507 | if st == TopAbs_VERTEX: 508 | s = "Vertex" 509 | if st == TopAbs_SOLID: 510 | s = "Solid" 511 | if st == TopAbs_EDGE: 512 | s = "Edge" 513 | if st == TopAbs_FACE: 514 | s = "Face" 515 | if st == TopAbs_SHELL: 516 | s = "Shell" 517 | if st == TopAbs_WIRE: 518 | s = "Wire" 519 | if st == TopAbs_COMPOUND: 520 | s = "Compound." 521 | if st == TopAbs_COMPSOLID: 522 | s = "Compsolid." 523 | return "%s: %i" % (s, hash(shape)) 524 | -------------------------------------------------------------------------------- /unusedDynamic.py: -------------------------------------------------------------------------------- 1 | def tweakFace(mFace, workPart, tPlane): 2 | # Move mFace (on workPart) to target plane 3 | # All other faces remain in their original planes. 4 | # Only works if all adjacent faces are planar. 5 | """ 6 | The algorithm is to first find the four vertices of mFace (to be moved) then 7 | at each vertex, identify the edge that is *not* contained in mFace. The 8 | intersection of each of these edges with the target plane is a new vertex. 9 | The mFace is replaced by a new face defined by the four new vertices. 10 | The adjacent faces are stretched to the new vertices, remaining in their 11 | initial planes. The part is then sewn back together. 12 | """ 13 | topo = Topology.Topo(workPart) 14 | edgeList = [] # edges of face to be moved 15 | for edge in topo.edges_from_face(mFace): 16 | edgeList.append(edge) 17 | wires = topo.wires_from_face(mFace) 18 | for wire in wires: # assuming there is only one wire 19 | oldVertices = topo.ordered_vertices_from_wire(wire) 20 | oldVrtxList = [] # ordered list of vertices of mFace 21 | for vrtx in oldVertices: 22 | oldVrtxList.append(vrtx) 23 | 24 | # Find new points (which will become the vertices of moved mFace) 25 | newPntList = [] # also ordered to corespond with oldVrtxList 26 | for vrtx in oldVrtxList: # for each vertex of mFace 27 | edgs = topo.edges_from_vertex(vrtx) 28 | for edg in edgs: # for each edge connected to vertex 29 | edgeInFace = False # assume edge is not in face 30 | for e in edgeList: 31 | if (hash(edg) == hash(e)): # until discovered that it is 32 | edgeInFace = True 33 | if not edgeInFace: # edge between 2 adjacent faces 34 | pList = [] # end points of edge 35 | for vrtx in topo.vertices_from_edge(edg): 36 | pList.append(BRep_Tool().Pnt(vrtx)) 37 | gpvec = gp_Vec(pList[0], pList[1]) 38 | gpdir = gp_Dir(gpvec) 39 | line = gp_Lin(pList[0], gpdir) 40 | newPntList.append(intersectPnt(line, tPlane)) 41 | break 42 | 43 | print 'number of new points = ', len(newPntList) 44 | 45 | for i in range(len(newPntList)): 46 | P = newPntList[i] 47 | display.DisplayShape(P) 48 | pstring = "P%i" % i 49 | display.DisplayMessage(P, pstring) 50 | 51 | 52 | # sort through all the part's faces and stretch the adjacent ones 53 | faces = topo.faces_from_solids(workPart) # all faces 54 | adjFaces = [] # Adjacent faces (stretched) 55 | otherFaces = [] # Other faces (to be reused unchanged) 56 | for face in faces: 57 | edges = topo.edges_from_face(face) 58 | if face == mFace: 59 | pass # This is the face to be moved 60 | else: 61 | adjacentFace = False # assume face is not adjacent... 62 | for e in edges: 63 | for f in edgeList: 64 | if (hash(e) == hash(f)): # common edge 65 | adjacentFace = True # until discovered that it is 66 | if adjacentFace: 67 | wires = topo.wires_from_face(face) 68 | wireList = [] 69 | for wire in wires: 70 | wireList.append(wire) 71 | outerWire = wireList.pop(0) 72 | orderedVertices = topo.ordered_vertices_from_wire(outerWire) 73 | orderedPtList = [] 74 | for vrtx in orderedVertices: 75 | pnt = BRep_Tool().Pnt(vrtx) 76 | for i in range(len(oldVrtxList)): 77 | if (hash(vrtx) == hash(oldVrtxList[i])): # common vertex 78 | pnt = newPntList[i] 79 | orderedPtList.append(pnt) 80 | orderedPtList.reverse() # need to do this for faces with holes 81 | stretchedWire = pointsToWire(orderedPtList) 82 | makeFace = BRepBuilderAPI_MakeFace(stretchedWire) 83 | for wire in wireList: 84 | makeFace.Add(wire) 85 | if makeFace.IsDone(): 86 | stretchedFace = makeFace.Face() 87 | adjFaces.append(stretchedFace) 88 | else: 89 | otherFaces.append(face) 90 | 91 | # make newFace to replace mFace 92 | newWire = pointsToWire(newPntList) 93 | makeFace = BRepBuilderAPI_MakeFace(newWire) 94 | if makeFace.IsDone(): 95 | newFace = makeFace.Face() 96 | 97 | # sew all the faces together 98 | tolerance = 1e-7 99 | sew = BRepBuilderAPI_Sewing(tolerance) 100 | 101 | print 'Number of other faces: ', len(otherFaces) 102 | for f in otherFaces: 103 | sew.Add(f) 104 | 105 | for f in adjFaces: 106 | sew.Add(f) 107 | sew.Add(newFace) 108 | sew.Perform() 109 | res = sew.SewedShape() 110 | return res 111 | 112 | def makeToolBody(mFace, tPlane): 113 | # Make toolBody on mFace (of active part) to target surface. 114 | # (target surface is ignored for now) Far face is parallel to mFace. 115 | # All other faces remain in their original planes. 116 | """ 117 | This algorithm follows the procedure of the 'intelligent local operation' 118 | used by HP SolidDesigner, in which a toolbody is constructed so that its 119 | 'near' face mates against mFace and its 'far' face is aligned to the target 120 | surface. The toolbody's 'side faces' align with the faces adjacent to mFace 121 | on the workpart. The toolbody can then be fused to the workpart so mFace 122 | gets effectively 'moved' out to the target surface. If the target surface is 123 | 'inside' the workpart, then the toolbody is subtracted from the workpart. 124 | 125 | Here it is in detail, assuming mFace is planar and has only one wire (no 126 | holes.) Target surface is assumed to be completely outside the workpart. 127 | 128 | Use the underlying wire of mFace to create an identical tool face which can 129 | then be extruded into a prism. The side faces of the prism will corespond 130 | exactly with the edges of mFace. This prism becomes the toolbody. 131 | One by one, tweak the adjacent faces of the toolbody into alignment with the 132 | adjacent faces of the workpart, and align the 'far' face of the prism to the 133 | target surface. To correlate the toolbody side faces with the sidefaces of 134 | the workPart to which they will become aligned, keep track of their common 135 | edge. 136 | """ 137 | workPart = win.activePart 138 | wrkPrtUID = win.activePartUID 139 | topo = Topology.Topo(workPart) 140 | mF_wires = topo.wires_from_face(mFace) 141 | if topo.number_of_wires_from_face(mFace) > 1: 142 | print 'Not yet implemented for faces with holes.' 143 | return 144 | else: # Only one wire in mFace 145 | pass 146 | mF_wire = next(mF_wires) 147 | mF_vrtxList = [] # ordered list of vertices of mFace 148 | for vrtx in topo.ordered_vertices_from_wire(mF_wire): 149 | mF_vrtxList.append(vrtx) 150 | 151 | mF_edgeList = [] # ordered list of edges of mFace 152 | nEdges = 0 153 | for edge in topo.ordered_edges_from_wire(mF_wire): 154 | mF_edgeList.append(edge) 155 | nEdges += 1 156 | 157 | # make an ordered list of faces adjacent to mFace 158 | faces = topo.faces_from_solids(win.activePart) # all faces 159 | adjFacesDict = {} # key=seq : value=face 160 | for face in faces: 161 | edges = topo.edges_from_face(face) 162 | if face.IsSame(mFace): 163 | pass # This is mFace 164 | else: 165 | adjacentFace = False # assume face is not adjacent... 166 | for e in edges: 167 | seq = 0 # keep track of which edge in ordered list is matched 168 | for f in mF_edgeList: 169 | seq += 1 170 | if (hash(e) == hash(f)): # common edge 171 | adjacentFace = True # until discovered that it is 172 | break 173 | if adjacentFace: 174 | break 175 | if adjacentFace: 176 | adjFacesDict[seq] = face 177 | mF_adjFaceList = adjFacesDict.values() # ordered list of adjacent faces 178 | 179 | # create a toolBody that mates against mFace 180 | faceNormal = Construct.face_normal(mFace) # type: gp_Dir 181 | vctr = gp_Vec(faceNormal).Multiplied(10) 182 | toolBody = BRepPrimAPI_MakePrism(mFace, vctr).Shape() 183 | 184 | # find toolBody face sharing an edge with workpart adjacent face 185 | # compare face normals to see if toolBody face needs to be tweaked 186 | # keep track by index of wp ordered list 187 | facesToTweak = {} # key = edge index ; value = toolBody faceNormal 188 | tb_topo = Topology.Topo(toolBody) 189 | tb_faces = tb_topo.faces_from_solids(toolBody) # all faces of toolBody 190 | for i in range(len(mF_edgeList)): 191 | print i 192 | we = mF_edgeList[i] # workpart edge 193 | for tb_face in tb_faces: 194 | adjacentFace = False # assume face is not adjacent... 195 | if tb_face.IsSame(mFace): 196 | print 'found mated face' 197 | else: 198 | tb_edges = tb_topo.edges_from_face(tb_face) 199 | for tb_e in tb_edges: 200 | if tb_e.IsSame(we): # common edge 201 | adjacentFace = True # until discovered that it is 202 | #break 203 | if adjacentFace: 204 | print 'found TB face matching workPart adjFace %i:' % i 205 | # check coplanarity between tb_face and wp_face 206 | wp_face = mF_adjFaceList[i] 207 | wpFaceNormal = Construct.face_normal(wp_face) # type: gp_Dir 208 | tbFaceNormal = Construct.face_normal(tb_face) # type: gp_Dir 209 | angle = wpFaceNormal.Angle(tbFaceNormal) 210 | if angle > 1e-10: 211 | print 'face needs to be tweaked' 212 | facesToTweak[i] = tbFaceNormal 213 | else: 214 | print 'no need to tweak face' 215 | break 216 | else: 217 | print 'found other face' 218 | 219 | # Tweak toolBody faces that need to be aligned to workPart faces 220 | for k,v in facesToTweak.items(): 221 | print 'Tweaking face ', k 222 | tb_topo = Topology.Topo(toolBody) 223 | tb_faces = tb_topo.faces_from_solids(toolBody) # all faces of toolBody 224 | targetFace = mF_adjFaceList[k] 225 | targetPlane = planeOfFace(targetFace) 226 | for tb_face in tb_faces: 227 | tbFaceNormal = Construct.face_normal(tb_face) 228 | angle = tbFaceNormal.Angle(v) 229 | if angle < 1e-10: 230 | sharedEdge = edgeOnFaceP(mF_edgeList[k], tb_face) 231 | if sharedEdge: 232 | print 'Common edge test: ', sharedEdge 233 | toolBody = tweakFace(tb_face, toolBody, targetPlane) 234 | win.getNewPartUID(toolBody) 235 | 236 | def alignFace(initial=True): 237 | """ 238 | Align a selected face on the active part to some other face. 239 | This involves 'stretching' adjacent faces to reach the new aligned face. 240 | All the faces (of the active part) are then sewn back together. 241 | """ 242 | # This only works if all adjacent faces are planar. 243 | 244 | if initial: 245 | win.registerCallback(alignFaceC) 246 | display.SetSelectionModeFace() 247 | statusText = "Select face to move (on active part)." 248 | win.statusBar().showMessage(statusText) 249 | elif len(win.faceStack) == 2: 250 | workPart = win.activePart 251 | wrkPrtUID = win.activePartUID 252 | tFace = win.faceStack.pop() # target face (to be aligned to) 253 | tPlane = planeOfFace(tFace) 254 | mFace = win.faceStack.pop() # face to be moved 255 | res = tweakFace(mFace, workPart, tPlane) 256 | win.getNewPartUID(res, ancestor=wrkPrtUID) 257 | 258 | win.statusBar().showMessage('Align Face operation complete') 259 | win.clearCallback() 260 | 261 | def offsetEndFace(initial=True): 262 | """ 263 | Offset one end face of a simple cylinder (the active part) by a dist value. 264 | This involves 'stretching' the cylindrical face to reach the new end face. 265 | The faces are then sewn back together. 266 | """ 267 | if initial: 268 | win.registerCallback(offsetEndFaceC) 269 | display.SetSelectionModeFace() 270 | statusText = "Select face to move (on active part)." 271 | win.statusBar().showMessage(statusText) 272 | elif (win.lineEditStack and win.faceStack): 273 | text = win.lineEditStack.pop() 274 | value = float(text) * win.unitscale 275 | mFace = win.faceStack.pop() # face to be moved 276 | workPart = win.activePart 277 | wrkPrtUID = win.activePartUID 278 | topo = Topology.Topo(workPart) 279 | faces = topo.faces_from_solids(workPart) # all faces 280 | for face in faces: 281 | ''' 282 | This is tricky. 283 | Below, I decide a face is cylindrical if it has three edges. 284 | But that is only true if the cylinder is one 360-deg face. 285 | As I discovered in the 'plate' part of the step file, 286 | those cylindrical holes were comprised of two 180-deg faces, 287 | each with 4 edges. So this is not going to be reliable. 288 | 289 | Maybe it would be smarter to first decide whether a face 290 | is the end face of a cylinder, then look for adjacent faces. 291 | 292 | That gives me two problems that need reliable solutions: 293 | 1- How to test whether a face is the end face of a cylinder 294 | 2- How to find adjacent cylinder faces 295 | 296 | I recall a short utility function for testing 'IsPlanar'... 297 | ''' 298 | nbrEdges = topo.number_of_edges_from_face(face) 299 | print nbrEdges 300 | edges = topo.edges_from_face(face) 301 | if face == mFace: # This is the face to be moved 302 | pass 303 | elif nbrEdges == 3: 304 | cylFace = face 305 | else: 306 | othrEndFace = face 307 | print '\n' 308 | print 'Topology Info for mFace:' 309 | face = mFace 310 | print 'Number of Edges of mFace = ', topo.number_of_edges_from_face(face) 311 | print '\n' 312 | faceEdges = topo.edges_from_face(face) 313 | for faceEdge in faceEdges: 314 | hCurve, umin, umax = BRep_Tool.Curve(faceEdge) 315 | curve = hCurve.GetObject() 316 | vectr = curve.DN(0.0, 2) 317 | print 'curvature at u=0: ', vectr.Magnitude() 318 | print 'first: ', curve.FirstParameter() 319 | print 'last: ', curve.LastParameter() 320 | print '\n' 321 | print 'Number of Vertices of mFace = ', topo.number_of_vertices_from_face(face) 322 | 323 | print '\n' 324 | print 'Topology Info for cylFace:' 325 | face = cylFace 326 | print 'Number of Edges of cylFace = ', topo.number_of_edges_from_face(face) 327 | print '\n' 328 | faceEdges = topo.edges_from_face(face) 329 | for faceEdge in faceEdges: 330 | hCurve, umin, umax = BRep_Tool.Curve(faceEdge) 331 | curve = hCurve.GetObject() 332 | vectr = curve.DN(0.0, 2) 333 | print 'curvature at u=0: ', vectr.Magnitude() 334 | print 'first: ', curve.FirstParameter() 335 | print 'last: ', curve.LastParameter() 336 | print '\n' 337 | print 'Number of Vertices of cylFace = ', topo.number_of_vertices_from_face(face) 338 | brlSurf = BRepLib_FindSurface(face) # type: BRepLib_FindSurface 339 | print type(brlSurf) 340 | isPlanarSurf = GeomLib_IsPlanarSurface(brlSurf.Surface(), TOLERANCE).IsPlanar() 341 | print 'Planar: ', isPlanarSurf 342 | mFaceVertices = topo.vertices_from_face(mFace) 343 | 344 | win.statusBar().showMessage('Offset Face operation complete') 345 | win.clearCallback() 346 | 347 | def offsetEndFaceC(shapeList, *kwargs): # callback (collector) for offsetEndFace 348 | print shapeList 349 | print kwargs 350 | win.lineEdit.setFocus() 351 | for shape in shapeList: 352 | face = topods_Face(shape) 353 | win.faceStack.append(face) 354 | if (win.faceStack and win.lineEditStack): 355 | offsetEndFace(initial=False) 356 | elif len(win.faceStack) == 1: 357 | statusText = "Enter distance value." 358 | win.statusBar().showMessage(statusText) 359 | 360 | def alignEndFace(initial=True): 361 | """ 362 | Align one end face of a simple cylinder (the active part) to a target face. 363 | This involves 'stretching' the cylindrical face to reach the new end face. 364 | The faces are then sewn back together. 365 | """ 366 | if initial: 367 | win.registerCallback(alignEndFaceC) 368 | display.SetSelectionModeFace() 369 | statusText = "Select face to move (on active part)." 370 | win.statusBar().showMessage(statusText) 371 | elif len(win.faceStack) == 2: 372 | tFace = win.faceStack.pop() # target face 373 | tSurf = BRep_Tool_Surface(tFace) 374 | mFace = win.faceStack.pop() # face to be moved 375 | workPart = win.activePart 376 | wrkPrtUID = win.activePartUID 377 | topo = Topology.Topo(workPart) 378 | faces = topo.faces_from_solids(workPart) # all faces 379 | for face in faces: 380 | isPlanar = face_is_plane(face) 381 | print 'Planar: ', isPlanar 382 | if face == mFace: 383 | print 'found mFace' 384 | elif isPlanar: 385 | otherEndFace = face 386 | print 'found other end face' 387 | else: 388 | cylFace = face 389 | print 'found cylFace' 390 | cylSurf = BRep_Tool_Surface(cylFace) 391 | cylEdges = [] # edges of new cylindrical face 392 | for face in (tFace, otherEndFace): 393 | planeSurf = BRep_Tool_Surface(face) 394 | inters = GeomAPI_IntSS() 395 | inters.Perform(cylSurf, planeSurf, 1.0e-7) 396 | if inters.IsDone(): 397 | nbLines = inters.NbLines() 398 | print nbLines 399 | curve = inters.Line(nbLines) # type: Handle_Geom_curve 400 | edge = BRepBuilderAPI_MakeEdge(curve).Edge() 401 | cylEdges.append(edge) 402 | newCylFace = brepfill.Face(cylEdges[0], cylEdges[1]) 403 | newWire = BRepBuilderAPI_MakeWire(cylEdges[0]).Wire() 404 | newFace = BRepBuilderAPI_MakeFace(newWire).Face() 405 | # sew all the faces together 406 | tolerance = 1e-7 407 | sew = BRepBuilderAPI_Sewing(tolerance) 408 | sew.Add(otherEndFace) 409 | sew.Add(newCylFace) 410 | sew.Add(newFace) 411 | sew.Perform() 412 | res = sew.SewedShape() 413 | win.getNewPartUID(res, ancestor=wrkPrtUID) 414 | win.statusBar().showMessage('Offset Face operation complete') 415 | win.clearCallback() 416 | 417 | def alignEndFaceC(shapeList, *kwargs): # callback (collector) for alignEndFace 418 | print shapeList 419 | print kwargs 420 | win.lineEdit.setFocus() 421 | for shape in shapeList: 422 | face = topods_Face(shape) 423 | win.faceStack.append(face) 424 | if len(win.faceStack) == 1: 425 | statusText = "Select target face." 426 | win.statusBar().showMessage(statusText) 427 | elif len(win.faceStack) == 2: 428 | alignEndFace(initial=False) 429 | 430 | def testFace(initial=True): 431 | """ 432 | Test makeToolBody 433 | """ 434 | 435 | if initial: 436 | win.registerCallback(testFaceC) 437 | display.SetSelectionModeFace() 438 | statusText = "Select face to move (on active part)." 439 | win.statusBar().showMessage(statusText) 440 | elif len(win.faceStack) == 2: 441 | tFace = win.faceStack.pop() # target face (to be aligned to) 442 | tPlane = planeOfFace(tFace) 443 | mFace = win.faceStack.pop() # face to be moved 444 | makeToolBody(mFace, tPlane) 445 | win.statusBar().showMessage('Align Face operation complete') 446 | win.clearCallback() 447 | 448 | def testFaceC(shapeList, *kwargs): # callback (collector) for testFace 449 | print shapeList 450 | print kwargs 451 | for shape in shapeList: 452 | face = topods_Face(shape) 453 | win.faceStack.append(face) 454 | if len(win.faceStack) == 1: 455 | statusText = "Select face to align to." 456 | win.statusBar().showMessage(statusText) 457 | if len(win.faceStack) == 2: 458 | testFace(initial=False) 459 | 460 | --------------------------------------------------------------------------------