├── .gitattributes ├── TODO.rst ├── version.py ├── occmodel ├── @docs │ ├── images │ │ ├── bowl_example.jpg │ │ ├── box_example.jpg │ │ ├── demo_window.jpg │ │ └── cylinder_example.jpg │ ├── conf.py │ ├── content.rst │ ├── index.rst │ └── examples.rst ├── @tests │ ├── runAll.py │ ├── test_Face.py │ ├── test_Solid.py │ ├── test_Wire.py │ ├── test_Edge.py │ └── test_Base.py ├── @src │ ├── OCCIncludes.pxi │ ├── OCCVertex.pxi │ ├── OCCIncludes.h │ ├── OCCTools.pxi │ ├── OCCModelLib.pxd │ ├── OCCBase.cpp │ ├── OCCTools.cpp │ ├── OCCBase.pxi │ ├── OCCModel.h │ ├── OCCWire.cpp │ ├── OCCFace.cpp │ ├── OCCWire.pxi │ ├── OCCFace.pxi │ └── OCCEdge.cpp ├── occmodel.pyx └── test.py ├── .gitignore ├── MANIFEST.in ├── setup_docs.py ├── Makefile ├── README.rst └── setup_build.py /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /TODO.rst: -------------------------------------------------------------------------------- 1 | ToDo 2 | ---- 3 | 4 | * Optimize viewer 5 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | MAJOR = 0 3 | MINOR = 2 4 | BUILD = 0 5 | STRING = "%d.%d.%d" % (MAJOR,MINOR,BUILD) -------------------------------------------------------------------------------- /occmodel/@docs/images/bowl_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenko/occmodel/HEAD/occmodel/@docs/images/bowl_example.jpg -------------------------------------------------------------------------------- /occmodel/@docs/images/box_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenko/occmodel/HEAD/occmodel/@docs/images/box_example.jpg -------------------------------------------------------------------------------- /occmodel/@docs/images/demo_window.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenko/occmodel/HEAD/occmodel/@docs/images/demo_window.jpg -------------------------------------------------------------------------------- /occmodel/@docs/images/cylinder_example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tenko/occmodel/HEAD/occmodel/@docs/images/cylinder_example.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build folders 2 | occmodel/build/ 3 | occmodel/@docs/build/ 4 | # compiled python files 5 | *.pyc 6 | *.pyo 7 | # python extension modules 8 | *.pyd 9 | *.so 10 | # libraries 11 | *.dll 12 | *.a 13 | # object files 14 | *.o -------------------------------------------------------------------------------- /occmodel/@docs/conf.py: -------------------------------------------------------------------------------- 1 | source_suffix = '.rst' 2 | master_doc = 'index' 3 | project = 'occmodel' 4 | copyright = '2012, Runar Tenfjord' 5 | 6 | extensions = ['sphinx.ext.autodoc'] 7 | autodoc_docstring_signature = True 8 | autodoc_member_order = 'groupwise' 9 | 10 | html_style = 'default.css' 11 | htmlhelp_basename = 'occmodeldoc' -------------------------------------------------------------------------------- /occmodel/@tests/runAll.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | import sys 6 | from unittest import main 7 | 8 | if __name__ == '__main__': 9 | sys.dont_write_bytecode = True 10 | sys.argv.append('discover') 11 | sys.argv.append('--verbose') 12 | main(exit=False) -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include Makefile 2 | include setup_build.py 3 | include setup_docs.py 4 | include version.py 5 | include MANIFEST.in 6 | include README.rst 7 | include TODO.rst 8 | include LICENSE.txt 9 | recursive-include occmodel *.py *.pyx *.pxi *.pxd *.rst *.cpp *.h 10 | exclude occmodel/test.py occmodel/test2.py occmodel/Note.rst 11 | prune occmodel/@arch -------------------------------------------------------------------------------- /occmodel/@src/OCCIncludes.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | from cython cimport view 6 | from libc.stdlib cimport malloc, free 7 | from libc.math cimport fmin, fmax, fabs, copysign 8 | from libc.math cimport M_PI, sqrt, sin, cos, tan 9 | 10 | cdef extern from "math.h": 11 | bint isnan(double x) 12 | 13 | from OCCModelLib cimport * 14 | 15 | import sys 16 | import itertools 17 | 18 | from geotools cimport Transform, Plane, Point, Vector, AABBox 19 | from geotools import Transform, Plane, Point, Vector, AABBox 20 | 21 | # constants 22 | cdef double EPSILON = 2.2204460492503131e-16 23 | cdef double SQRT_EPSILON = 1.490116119385000000e-8 24 | cdef double ZERO_TOLERANCE = 1.0e-12 25 | cdef double DEFAULT_ANGLE_TOLERANCE = M_PI/180. -------------------------------------------------------------------------------- /setup_docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import os 8 | 9 | from distutils.core import setup 10 | from sphinx.setup_command import BuildDoc 11 | cmdclass = {'build_sphinx': BuildDoc} 12 | 13 | name = 'occmodel' 14 | version = '0.1' 15 | release = '0.1.0' 16 | 17 | try: 18 | setup( 19 | name = name, 20 | author = 'Runar Tenfjord', 21 | version = release, 22 | cmdclass = cmdclass, 23 | command_options = { 24 | 'build_sphinx': { 25 | 'builder': ('setup_docs.py', 'html'), 26 | } 27 | }, 28 | 29 | ) 30 | except: 31 | print('Traceback\n:%s\n' % str(sys.exc_info()[-2])) 32 | sys.exit(1) -------------------------------------------------------------------------------- /occmodel/@docs/content.rst: -------------------------------------------------------------------------------- 1 | Geometry 2 | ======== 3 | 4 | Base 5 | ---- 6 | .. autoclass:: occmodel.Base 7 | :members: 8 | 9 | Vertex 10 | ------ 11 | .. autoclass:: occmodel.Vertex 12 | :members: 13 | 14 | Edge 15 | ---- 16 | .. autoclass:: occmodel.Edge 17 | :members: 18 | 19 | Wire 20 | ---- 21 | .. autoclass:: occmodel.Wire 22 | :members: 23 | 24 | Face 25 | ---- 26 | .. autoclass:: occmodel.Face 27 | :members: 28 | 29 | Solid 30 | ----- 31 | .. autoclass:: occmodel.Solid 32 | :members: 33 | 34 | Tools 35 | ----- 36 | .. autoclass:: occmodel.Tools 37 | :members: 38 | 39 | Mesh 40 | ---- 41 | .. autoclass:: occmodel.Mesh 42 | :members: 43 | 44 | Tesselation 45 | ----------- 46 | .. autoclass:: occmodel.Tesselation 47 | :members: 48 | 49 | Visualization 50 | ============= 51 | 52 | .. autoclass:: occmodelviewer.Viewer 53 | :members: 54 | 55 | .. autofunction:: occmodelviewer.viewer -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # File: Makefile (for library) 3 | # 4 | # The variables 'PYTHON' and 'PYVER' can be modified by 5 | # passing parameters to make: make PYTHON=python PYVER=2.7 6 | # 7 | PYTHON=python3 8 | PYVER=3.5 9 | 10 | CC=g++ 11 | CFLAGS=-Wall -fPIC -O2 -frtti -fexceptions -Isrc -I/mingw64/include/oce 12 | LIB=occmodel/liboccmodel.a 13 | 14 | LIBSRC = $(wildcard occmodel/@src/*.cpp) 15 | 16 | LIBOBJ=$(LIBSRC:.cpp=.o) 17 | 18 | .PHONY: pylib docs test tests install clean 19 | 20 | $(LIB): $(LIBOBJ) 21 | @echo lib Makefile - archiving $(LIB) 22 | @$(AR) r $(LIB) $(LIBOBJ) 23 | 24 | .cpp.o: 25 | @echo lib Makefile - compiling $< 26 | @$(CC) $(CFLAGS) -c $< -o $@ 27 | 28 | pylib: $(LIB) 29 | @echo lib Makefile - building python extension 30 | $(PYTHON) setup_build.py build_ext --inplace --force 31 | 32 | docs: pylib 33 | @echo lib Makefile - building documentation 34 | @cd occmodel/@docs ; $(PYTHON) ../../setup_docs.py build_sphinx 35 | @cp -rf occmodel/@docs/build/sphinx/html/* occmodel/@docs/html/ 36 | 37 | test: pylib 38 | @echo lib Makefile - running test file 39 | $(PYTHON) occmodel/test.py 40 | 41 | tests: pylib 42 | @echo lib Makefile - running test suite 43 | @cd occmodel/@tests ; $(PYTHON) runAll.py 44 | 45 | install: pylib 46 | @cp occmodel.so ~/.local/lib/python$(PYVER)/site-packages/ 47 | @cp occmodelviewer.so ~/.local/lib/python$(PYVER)/site-packages/ 48 | 49 | sdist: clean 50 | @echo lib Makefile - creating source distribution 51 | $(PYTHON) setup_build.py sdist --formats=gztar,zip 52 | 53 | clean: 54 | -rm $(LIBOBJ) 55 | -rm $(LIB) 56 | -rm -rf build dist 57 | -rm -rf occmodel/@docs/build 58 | -rm MANIFEST occmodel/@src/Config.pxi 59 | -rm occmodel.dll occmodel.so occmodel/occmodel.cpp 60 | -rm occmodelviewer.dll occmodelviewer.so occmodel/occmodelviewer.c 61 | -find occmodel -iname '*.so' -exec rm {} \; 62 | -find occmodel -iname '*.pyc' -exec rm {} \; 63 | -find occmodel -iname '*.pyo' -exec rm {} \; 64 | -find occmodel -iname '*.pyd' -exec rm {} \; 65 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | occmodel is a small library which gives a high level access 5 | to the OpenCASCADE modelling kernel. 6 | 7 | For most users a direct use of the OpenCASCADE modelling 8 | kernel can be quite a hurdle as it is a huge library. 9 | 10 | The geometry can be visualized with the included viewer. 11 | This viewer is utilizing modern OpenGL methods like GLSL 12 | shaders and vertex buffers to ensure visual quality and 13 | maximum speed. To use the viewer OpenGL version 2.1 is 14 | needed. 15 | 16 | Most of the code have been adapted from the freecad (GPL 2). 17 | 18 | The license is GPL v2. 19 | 20 | Building 21 | ======== 22 | 23 | * Python 2.7/3.x and Cython 0.17 or later. 24 | * A working installation of OpenCASCADE (OCE prefered) 25 | * The geotools_ library. 26 | * The optional viewer and demo needs the gltools_ library. 27 | 28 | Note that currently I can not find a way to install the required 29 | Cython 'pxd' files with distutils and this file has to be copied 30 | manually. 31 | 32 | On the Windows platform installers are available on the 33 | pypi_ web site. It is possible to build the module from source 34 | with the help of the express edition of Visual Studio, but the 35 | process is rather involved compared to Linux. 36 | 37 | To complete the windows installation the OpenCASCADE dll's must be 38 | installed and placed in the system path. Prebuilt binaries are available 39 | on the OCE_ project site. The python 2.7 module is linked against 40 | 'OCE-0.10.0-Win-MSVC2008.zip' and the python 3.3 module is 41 | linked against 'OCE-0.10.0-Win-MSVC2010.zip'. 42 | 43 | For Linux build scripts are available for ArchLinux in the AUR_ 44 | repository. These could be adapted to other Linux distros. 45 | 46 | Documentation 47 | ============= 48 | 49 | See online Sphinx docs_ 50 | 51 | .. _docs: http://tenko.github.com/occmodel/index.html 52 | 53 | .. _geotools: http://github.com/tenko/geotools 54 | 55 | .. _gltools: https://github.com/tenko/gltools 56 | 57 | .. _pypi: http://pypi.python.org/pypi/occmodel 58 | 59 | .. _OCE: https://github.com/tpaviot/oce/downloads 60 | 61 | .. _AUR: https://aur.archlinux.org/packages/?O=0&K=occmodel -------------------------------------------------------------------------------- /occmodel/@docs/index.rst: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | occmodel is a small self-contained library which gives a 5 | high level access to the OpenCASCADE modelling kernel. 6 | 7 | For most users a direct use of the OpenCASCADE modelling 8 | kernel can be quite a hurdle as it is a huge and complex 9 | library. 10 | 11 | The geometry can be visualized with the included viewer. 12 | This viewer is utilizing modern OpenGL methods like GLSL 13 | shaders and vertex buffers to ensure visual quality and 14 | maximum speed. To use the viewer OpenGL version 2.1 is 15 | needed. 16 | 17 | Most of the code have been adapted from the freecad project (GPL 2). 18 | 19 | The license is GPL v2. 20 | 21 | Building 22 | ======== 23 | 24 | * Python 2.7/3.x and Cython 0.17 or later. 25 | * A working installation of OpenCASCADE (OCE prefered) 26 | * The geotools_ library. 27 | * The optional viewer and demo needs the gltools_ library. 28 | 29 | Note that currently I can not find a way to install the required 30 | Cython 'pxd' files with distutils and this file has to be copied 31 | manually. 32 | 33 | On the Windows platform installers are available on the 34 | pypi_ web site. It is possible to build the module from source 35 | with the help of the express edition of Visual Studio, but the 36 | process is rather involved compared to Linux. 37 | 38 | To complete the windows installation the OpenCASCADE dll's must be 39 | installed and placed in the system path. Prebuilt binaries are available 40 | on the OCE_ project site. The python 2.7 module is linked against 41 | 'OCE-0.10.0-Win-MSVC2008.zip' and the python 3.3 module is 42 | linked against 'OCE-0.10.0-Win-MSVC2010.zip'. 43 | 44 | For Linux build scripts are available for ArchLinux in the AUR_ 45 | repository. These could be adapted to other Linux distros. 46 | 47 | Examples 48 | ======== 49 | 50 | .. toctree:: 51 | :maxdepth: 2 52 | 53 | examples 54 | 55 | API Docs 56 | ======== 57 | 58 | .. toctree:: 59 | :maxdepth: 2 60 | 61 | content 62 | 63 | Indices and tables 64 | ================== 65 | 66 | * :ref:`genindex` 67 | * :ref:`modindex` 68 | * :ref:`search` 69 | 70 | .. _geotools: http://github.com/tenko/geotools 71 | 72 | .. _gltools: https://github.com/tenko/gltools 73 | 74 | .. _pypi: http://pypi.python.org/pypi/occmodel 75 | 76 | .. _OCE: https://github.com/tpaviot/oce/downloads 77 | 78 | .. _AUR: https://aur.archlinux.org/packages/?O=0&K=occmodel -------------------------------------------------------------------------------- /occmodel/@tests/test_Face.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import unittest 8 | 9 | from math import pi, sin, cos, sqrt 10 | 11 | from occmodel import Vertex, Edge, Wire, Face, OCCError 12 | 13 | class test_Face(unittest.TestCase): 14 | 15 | def test_createFace(self): 16 | eq = self.assertAlmostEqual 17 | 18 | self.assertRaises(OCCError, Face().createFace, Wire()) 19 | 20 | # square face 21 | p1 = Vertex(0.,0.,0.) 22 | p2 = Vertex(1.,0.,0.) 23 | p3 = Vertex(1.,1.,0.) 24 | p4 = Vertex(0.,1.,0.) 25 | e1 = Edge().createLine(p1,p2) 26 | e2 = Edge().createLine(p2,p3) 27 | e3 = Edge().createLine(p3,p4) 28 | e4 = Edge().createLine(p4,p1) 29 | 30 | w1 = Wire().createWire((e1,e1,e1,e1)) 31 | self.assertRaises(OCCError, Face().createFace, w1) 32 | 33 | w2 = Wire().createWire((e1,e2,e3,e4)) 34 | 35 | face = Face().createFace(w2) 36 | 37 | self.assertEqual(face.numWires(), 1) 38 | self.assertEqual(face.numFaces(), 1) 39 | eq(face.area(), 1.) 40 | 41 | # circular face 42 | e1 = Edge() 43 | center = (0.,0.,0.) 44 | normal = (0.,0.,1.) 45 | radius = 1. 46 | 47 | e1.createCircle(center, normal, radius) 48 | face = Face().createFace(e1) 49 | eq(face.area(), pi, places = 4) 50 | 51 | def test_createPolygonal(self): 52 | eq = self.assertAlmostEqual 53 | 54 | pnts = ((0.,0.,0.), (1.,0.,0.), (1.,1.,0.), (0.,1.,0.)) 55 | face = Face().createPolygonal(pnts) 56 | eq(face.area(), 1.) 57 | 58 | def test_extrude(self): 59 | eq = self.assertAlmostEqual 60 | 61 | # square face 62 | p1 = Vertex(0.,0.,0.) 63 | p2 = Vertex(1.,0.,0.) 64 | e1 = Edge().createLine(p1,p2) 65 | 66 | face = Face().extrude(e1, (0.,0.,0.),(0.,1.,0.)) 67 | eq(face.area(), 1.) 68 | 69 | def test_revolve(self): 70 | eq = self.assertAlmostEqual 71 | 72 | # square face 73 | p1 = Vertex(0.,0.,0.) 74 | p2 = Vertex(1.,0.,0.) 75 | e1 = Edge().createLine(p1,p2) 76 | 77 | face = Face().revolve(e1, (0.,1.,0.),(1.,1.,0.), pi) 78 | 79 | eq(face.area(), pi) 80 | 81 | if __name__ == "__main__": 82 | sys.dont_write_bytecode = True 83 | unittest.main() -------------------------------------------------------------------------------- /occmodel/@src/OCCVertex.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | 6 | cdef class Vertex(Base): 7 | ''' 8 | Vertex 9 | ''' 10 | def __init__(self, double x = 0., double y = 0., double z = 0.): 11 | self.thisptr = new c_OCCVertex(x,y,z) 12 | 13 | def __dealloc__(self): 14 | cdef c_OCCVertex *tmp 15 | 16 | if self.thisptr != NULL: 17 | tmp = self.thisptr 18 | del tmp 19 | 20 | def __str__(self): 21 | return "Vertex%s" % repr(self) 22 | 23 | def __repr__(self): 24 | return "(%g,%g,%g)" % (self.X(), self.Y(), self.Z()) 25 | 26 | def __getitem__(self, int key): 27 | if key == 0: 28 | return self.X() 29 | elif key == 1: 30 | return self.Y() 31 | elif key == 2: 32 | return self.Z() 33 | raise IndexError('index out of range') 34 | 35 | def __len__(self): 36 | return 3 37 | 38 | property x: 39 | def __get__(self): 40 | return self.X() 41 | 42 | property y: 43 | def __get__(self): 44 | return self.Y() 45 | 46 | property z: 47 | def __get__(self): 48 | return self.Z() 49 | 50 | cpdef double X(self): 51 | cdef c_OCCVertex *occ = self.thisptr 52 | return occ.X() 53 | 54 | cpdef double Y(self): 55 | cdef c_OCCVertex *occ = self.thisptr 56 | return occ.Y() 57 | 58 | cpdef double Z(self): 59 | cdef c_OCCVertex *occ = self.thisptr 60 | return occ.Z() 61 | 62 | cpdef project(self, Base target): 63 | ''' 64 | Project vertex towards edge, wire, face or solid. 65 | ''' 66 | cdef c_OCCVertex *occ = self.thisptr 67 | cdef int ret 68 | 69 | ret = occ.project(target.thisptr) 70 | if not ret: 71 | raise OCCError(errorMessage) 72 | 73 | return self 74 | 75 | cdef class VertexIterator: 76 | ''' 77 | Iterator of vertices 78 | ''' 79 | cdef c_OCCVertexIterator *thisptr 80 | cdef set seen 81 | cdef bint includeAll 82 | 83 | def __init__(self, Base arg, bint includeAll = False): 84 | self.thisptr = new c_OCCVertexIterator(arg.thisptr) 85 | self.includeAll = includeAll 86 | self.seen = set() 87 | 88 | def __dealloc__(self): 89 | del self.thisptr 90 | 91 | def __str__(self): 92 | return 'VertexIterator%s' % self.__repr__() 93 | 94 | def __repr__(self): 95 | return '()' 96 | 97 | def __iter__(self): 98 | return self 99 | 100 | def __next__(self): 101 | cdef c_OCCVertex *nxt 102 | cdef int hash 103 | 104 | while True: 105 | nxt = self.thisptr.next() 106 | if nxt == NULL: 107 | raise StopIteration() 108 | 109 | if self.includeAll: 110 | break 111 | else: 112 | # check for duplicate (same vertex different orientation) 113 | hash = (nxt).hashCode() 114 | if hash in self.seen: 115 | continue 116 | else: 117 | self.seen.add(hash) 118 | break 119 | 120 | cdef Vertex ret = Vertex.__new__(Vertex, 0.,0.,0.) 121 | ret.thisptr = nxt 122 | return ret 123 | 124 | cpdef reset(self): 125 | '''Restart iteration''' 126 | self.thisptr.reset() 127 | -------------------------------------------------------------------------------- /occmodel/@tests/test_Solid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import unittest 8 | 9 | from math import pi, sin, cos, sqrt 10 | 11 | from occmodel import Vertex, Edge, Face, Solid, OCCError 12 | 13 | class test_Solid(unittest.TestCase): 14 | def almostEqual(self, a, b, places = 7): 15 | for va,vb in zip(a,b): 16 | self.assertAlmostEqual(va, vb, places) 17 | 18 | def test_centreOfMass(self): 19 | eq = self.almostEqual 20 | 21 | solid = Solid() 22 | solid.createSphere((0.,0.,0.),1.) 23 | 24 | eq(solid.centreOfMass(), (0.,0.,0.)) 25 | 26 | def test_translate(self): 27 | eq = self.almostEqual 28 | 29 | solid = Solid() 30 | solid.createSphere((0.,0.,0.),1.) 31 | solid.translate((1.,2.,3.)) 32 | eq(solid.centreOfMass(), (1.,2.,3.)) 33 | 34 | def test_rotate(self): 35 | eq = self.almostEqual 36 | 37 | solid = Solid() 38 | solid.createSphere((0.,0.,0.),1.) 39 | solid.rotate(-pi/2., (0.,1.,0.),(1.,1.,0.)) 40 | eq(solid.centreOfMass(), (1.,0.,-1.)) 41 | 42 | def test_scale(self): 43 | eq = self.assertAlmostEqual 44 | 45 | scale = .5 46 | solid = Solid() 47 | solid.createSphere((0.,0.,0.),1.) 48 | solid.scale((0.,0.,0.), scale) 49 | 50 | #eq(solid.area(), scale*4.*pi, places = 3) 51 | #eq(solid.volume(), scale*4./3.*pi, places = 3) 52 | 53 | def test_addSolids(self): 54 | eq = self.assertAlmostEqual 55 | 56 | s1 = Solid().createSphere((0.,0.,0.),1.) 57 | s2 = Solid().createSphere((2.,0.,0.),1.) 58 | s3 = Solid().addSolids((s1,s2)) 59 | 60 | self.assertEqual(s3.numSolids(), 2) 61 | eq(s3.area(), 2.*4.*pi, places = 3) 62 | eq(s3.volume(), 2.*4./3.*pi, places = 3) 63 | 64 | s1 = Solid().createSphere((0.,0.,0.),.5) 65 | self.assertEqual(s1.numSolids(), 1) 66 | s2 = Solid().createSphere((2.,0.,0.),.5) 67 | s1.addSolids(s2) 68 | self.assertEqual(s1.numSolids(), 2) 69 | s3 = Solid().createSphere((4.,0.,0.),.5) 70 | s1.addSolids(s3) 71 | self.assertEqual(s1.numSolids(), 3) 72 | 73 | def test_createSphere(self): 74 | eq = self.assertAlmostEqual 75 | 76 | self.assertRaises(OCCError, Solid().createSphere, (0.,0.,0.),0.) 77 | self.assertRaises(OCCError, Solid().createSphere, (0.,0.,0.),-1.) 78 | 79 | solid = Solid() 80 | solid.createSphere((0.,0.,0.),1.) 81 | 82 | eq(solid.area(), 4.*pi, places = 3) 83 | eq(solid.volume(), 4./3.*pi, places = 3) 84 | 85 | def test_createCylinder(self): 86 | eq = self.assertAlmostEqual 87 | 88 | self.assertRaises(OCCError, Solid().createCylinder, (0.,0.,0.),(0.,0.,1.), 0.) 89 | self.assertRaises(OCCError, Solid().createCylinder, (0.,0.,0.),(0.,0.,1.), -1.) 90 | 91 | solid = Solid() 92 | solid.createCylinder((0.,0.,0.),(0.,0.,1.), 1.) 93 | 94 | eq(solid.area(), 4.*pi, places = 3) 95 | eq(solid.volume(), pi, places = 3) 96 | 97 | def test_createTorus(self): 98 | eq = self.assertAlmostEqual 99 | 100 | self.assertRaises(OCCError, Solid().createTorus, (0.,0.,0.),(0.,0.,.1), 0., 1.) 101 | self.assertRaises(OCCError, Solid().createTorus, (0.,0.,0.),(0.,0.,.1), 1., 0.) 102 | 103 | solid = Solid() 104 | solid.createTorus((0.,0.,0.),(0.,0.,.1), 2., 1.) 105 | 106 | eq(solid.area(), 4.*pi**2*2.*1., places = 1) 107 | eq(solid.volume(), 2.*pi**2*2.*1.**2, places = 3) 108 | 109 | def test_createTorus(self): 110 | eq = self.assertAlmostEqual 111 | 112 | self.assertRaises(OCCError, Solid().createCone, (0.,0.,0.),(0.,0.,1.), 0., 0.) 113 | self.assertRaises(OCCError, Solid().createCone, (0.,0.,0.),(0.,0.,1.), 1., 1.) 114 | 115 | solid = Solid() 116 | solid.createCone((0.,0.,0.),(0.,0.,.1), 2., 1.) 117 | 118 | self.assertEqual(solid.volume() > 0., True) 119 | 120 | def test_createBox(self): 121 | eq = self.assertAlmostEqual 122 | 123 | self.assertRaises(OCCError, Solid().createBox, (-.5,-.5,-.5),(-.5,-.5,-.5)) 124 | 125 | solid = Solid() 126 | solid.createBox((-.5,-.5,-.5),(.5,.5,.5)) 127 | 128 | eq(solid.volume(), 1.) 129 | 130 | if __name__ == "__main__": 131 | sys.dont_write_bytecode = True 132 | unittest.main() -------------------------------------------------------------------------------- /occmodel/@tests/test_Wire.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import unittest 8 | 9 | from math import pi, sin, cos, sqrt 10 | 11 | from occmodel import Vertex, Edge, Wire, OCCError 12 | 13 | class test_Wire(unittest.TestCase): 14 | def almostEqual(self, a, b, places = 7): 15 | for va,vb in zip(a,b): 16 | self.assertAlmostEqual(va, vb, places) 17 | 18 | def test_createWire(self): 19 | eq = self.assertEqual 20 | aeq = self.assertAlmostEqual 21 | 22 | p1 = Vertex(0.,0.,0.) 23 | p2 = Vertex(1.,0.,0.) 24 | p3 = Vertex(1.,1.,0.) 25 | p4 = Vertex(0.,1.,0.) 26 | e1 = Edge().createLine(p1,p2) 27 | e2 = Edge().createLine(p2,p3) 28 | e3 = Edge().createLine(p3,p4) 29 | e4 = Edge().createLine(p4,p1) 30 | w1 = Wire().createWire((e1,e2,e3,e4)) 31 | 32 | eq(len(w1), 4) 33 | eq(w1.numVertices(), 4) 34 | eq(w1.isNull(), False) 35 | eq(w1.isValid(), True) 36 | eq(w1.hasPlane(), True) 37 | aeq(w1.length(), 4.) 38 | 39 | def test_createRectangle(self): 40 | eq = self.assertEqual 41 | aeq = self.assertAlmostEqual 42 | 43 | self.assertRaises(OCCError, Wire().createRectangle, width = 1., height = 1., radius = -0.00001) 44 | self.assertRaises(OCCError, Wire().createRectangle, width = 1., height = 1., radius = .5000001) 45 | 46 | w1 = Wire().createRectangle(width = 1., height = 1., radius = 0.) 47 | aeq(w1.length(), 4.) 48 | 49 | w1 = Wire().createRectangle(width = 1., height = 1., radius = .5) 50 | aeq(w1.length(), pi) 51 | 52 | def test_createPolygon(self): 53 | eq = self.assertEqual 54 | aeq = self.assertAlmostEqual 55 | 56 | points = ( 57 | (0.,0.,0.), 58 | (1.,0.,0.), 59 | (1.,1.,0.), 60 | (0.,1.,0.) 61 | ) 62 | 63 | w1 = Wire().createPolygon(points, close = True) 64 | aeq(w1.length(), 4.) 65 | 66 | w1 = Wire().createPolygon(points, close = False) 67 | aeq(w1.length(), 3.) 68 | 69 | def test_offset(self): 70 | w1 = Wire() 71 | self.assertRaises(OCCError,w1.offset, 1.) 72 | 73 | w1 = Wire().createRectangle(width = 1., height = 1.) 74 | l = w1.length() 75 | w1.offset(0.1) 76 | self.assertEqual(w1.length() != l, True) 77 | 78 | def test_fillet(self): 79 | aeq = self.assertAlmostEqual 80 | 81 | w1 = Wire() 82 | self.assertRaises(OCCError,w1.fillet, 1.) 83 | 84 | w1 = Wire().createRectangle(width = 1., height = 1.) 85 | l1 = w1.length() 86 | w1.fillet(0.1) 87 | l2 = w1.length() 88 | self.assertEqual(l1 != l2, True) 89 | 90 | p1 = Vertex(0.,0.,0.) 91 | p2 = Vertex(1.,0.,0.) 92 | p3 = Vertex(1.,1.,0.) 93 | p4 = Vertex(0.,1.,0.) 94 | e1 = Edge().createLine(p1,p2) 95 | e2 = Edge().createLine(p2,p3) 96 | e3 = Edge().createLine(p3,p4) 97 | e4 = Edge().createLine(p4,p1) 98 | w2 = Wire().createWire((e1,e2,e3,e4)) 99 | w2.fillet(0.1,(p1,p2,p3,p4)) 100 | 101 | aeq(w1.length(), w2.length()) 102 | 103 | def test_chamfer(self): 104 | aeq = self.assertAlmostEqual 105 | 106 | w1 = Wire() 107 | self.assertRaises(OCCError,w1.chamfer, 1.) 108 | 109 | w1 = Wire().createRectangle(width = 1., height = 1.) 110 | l = w1.length() 111 | w1.chamfer(0.1) 112 | self.assertEqual(w1.length() != l, True) 113 | 114 | p1 = Vertex(0.,0.,0.) 115 | p2 = Vertex(1.,0.,0.) 116 | p3 = Vertex(1.,1.,0.) 117 | p4 = Vertex(0.,1.,0.) 118 | e1 = Edge().createLine(p1,p2) 119 | e2 = Edge().createLine(p2,p3) 120 | e3 = Edge().createLine(p3,p4) 121 | e4 = Edge().createLine(p4,p1) 122 | w2 = Wire().createWire((e1,e2,e3,e4)) 123 | w2.chamfer(0.1,(p1,p2,p3,p4)) 124 | 125 | aeq(w1.length(), w2.length()) 126 | 127 | def test_isClosed(self): 128 | eq = self.assertEqual 129 | 130 | for val in (True, False): 131 | w1 = Wire().createPolygon(( 132 | (0.,0.,0.), 133 | (0.,0.,5.), 134 | (5.,0.,5.)), 135 | close = val 136 | ) 137 | eq(w1.isClosed(), val) 138 | 139 | if __name__ == "__main__": 140 | sys.dont_write_bytecode = True 141 | unittest.main() -------------------------------------------------------------------------------- /setup_build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import os 8 | import glob 9 | import shutil 10 | 11 | from distutils.core import setup 12 | from distutils.extension import Extension 13 | 14 | try: 15 | from Cython.Distutils import build_ext 16 | except ImportError: 17 | print >>sys.stderr, "Cython is required to build occmodel" 18 | sys.exit(1) 19 | 20 | try: 21 | import geotools 22 | except ImportError: 23 | print >>sys.stderr, "geotools is required to build occmodel" 24 | sys.exit(1) 25 | 26 | viewer = True 27 | try: 28 | import gltools 29 | except ImportError: 30 | viewer = False 31 | 32 | #sys.argv.append('build_ext') 33 | #sys.argv.extend(['sdist','--formats=gztar,zip']) 34 | #sys.argv.append('bdist_wininst') 35 | 36 | # create config file 37 | sys.dont_write_bytecode = True 38 | import version 39 | 40 | CONFIG = 'occmodel/@src/Config.pxi' 41 | if not os.path.exists(CONFIG) and 'sdist' not in sys.argv: 42 | with open(CONFIG, 'w') as fh: 43 | fh.write("__version__ = '%s'\n" % version.STRING) 44 | args = version.MAJOR, version.MINOR, version.BUILD 45 | fh.write("__version_info__ = (%d,%d,%d)\n" % args) 46 | 47 | OCC = \ 48 | '''FWOSPlugin PTKernel TKAdvTools TKBO TKBRep TKBinL TKBool TKCDF TKFeat TKFillet 49 | TKG2d TKG3d TKGeomAlgo TKGeomBase TKHLR TKIGES TKLCAF TKMath TKMesh TKOffset 50 | TKPLCAF TKPShape TKPrim TKSTEP TKSTEP209 TKSTEPAttr TKSTEPBase TKSTL TKShHealing 51 | TKShapeSchema TKStdLSchema TKTObj TKTopAlgo TKXMesh TKXSBase TKXmlL TKernel 52 | ''' 53 | 54 | # platform specific settings 55 | SOURCES = ["occmodel/occmodel.pyx"] 56 | 57 | OBJECTS, LIBS, LINK_ARGS, COMPILE_ARGS = [],[],[],[] 58 | if sys.platform == 'win32': 59 | COMPILE_ARGS.append('/EHsc') 60 | OCCINCLUDE = r"C:\vs9include\oce" 61 | OCCLIBS = [] 62 | OBJECTS = [name + '.lib' for name in OCC.split()] + ['occmodel.lib',] 63 | 64 | elif sys.platform == 'darwin': 65 | SOURCES += glob.glob("occmodel/@src/*.cpp") 66 | OCCINCLUDE = '/usr/include/oce' 67 | OCCLIBS = OCC.split() 68 | COMPILE_ARGS.append("-fpermissive") 69 | 70 | else: 71 | OCCINCLUDE = '/usr/include/oce' 72 | OCCLIBS = OCC.split() 73 | OBJECTS = ["occmodel/liboccmodel.a"] 74 | COMPILE_ARGS.append("-fpermissive") 75 | 76 | EXTENSIONS = [ 77 | Extension("occmodel", 78 | sources = SOURCES, 79 | depends = glob.glob("occmodel/@src/*.pxd") + \ 80 | glob.glob("occmodel/@src/*.pxi"), 81 | include_dirs = ['occmodel/@src', OCCINCLUDE], 82 | library_dirs = ['/lib/','occmodel'], 83 | libraries = LIBS + OCCLIBS, 84 | extra_link_args = LINK_ARGS, 85 | extra_compile_args = COMPILE_ARGS, 86 | extra_objects = OBJECTS, 87 | language="c++" 88 | ) 89 | ] 90 | 91 | # only build viewer of gltools is available 92 | if viewer: 93 | EXTENSIONS.append( 94 | Extension("occmodelviewer", sources = ["occmodel/occmodelviewer.pyx"]), 95 | ) 96 | 97 | classifiers = '''\ 98 | Development Status :: 4 - Beta 99 | Environment :: MacOS X 100 | Environment :: Win32 (MS Windows) 101 | Environment :: X11 Applications 102 | Intended Audience :: Science/Research 103 | License :: OSI Approved :: GNU General Public License v2 (GPLv2) 104 | Operating System :: OS Independent 105 | Programming Language :: Cython 106 | Topic :: Scientific/Engineering 107 | ''' 108 | 109 | try: 110 | setup( 111 | name = 'occmodel', 112 | version = version.STRING, 113 | description = 'Easy access to the OpenCASCADE library', 114 | long_description = \ 115 | '''**occmodel** is a small library which gives a high level access 116 | to the OpenCASCADE modelling kernel. 117 | 118 | For most users a direct use of the OpenCASCADE modelling 119 | kernel can be quite a hurdle as it is a huge library. 120 | 121 | The geometry can be visualized with the included viewer. 122 | This viewer is utilizing modern OpenGL methods like GLSL 123 | shaders and vertex buffers to ensure visual quality and 124 | maximum speed. To use the viewer OpenGL version 2.1 is 125 | needed. 126 | 127 | In order to complete the installation OpenCASCADE must be installed 128 | on the system. Check the home page or the README file for details. 129 | ''', 130 | classifiers = [value for value in classifiers.split("\n") if value], 131 | author='Runar Tenfjord', 132 | author_email = 'runar.tenfjord@gmail.com', 133 | license = 'GPLv2', 134 | download_url='http://pypi.python.org/pypi/occmodel/', 135 | url = 'http://github.com/tenko/occmodel', 136 | platforms = ['any'], 137 | scripts = ['occmodel/occmodeldemo.py'], 138 | ext_modules = EXTENSIONS, 139 | cmdclass = {'build_ext': build_ext} 140 | ) 141 | except: 142 | print('Traceback\n:%s\n' % str(sys.exc_info()[-2])) 143 | sys.exit(1) -------------------------------------------------------------------------------- /occmodel/@tests/test_Edge.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import unittest 8 | 9 | from math import pi, sin, cos, sqrt 10 | 11 | from occmodel import Vertex, Edge, OCCError 12 | 13 | class test_Edge(unittest.TestCase): 14 | def almostEqual(self, a, b, places = 7): 15 | for va,vb in zip(a,b): 16 | self.assertAlmostEqual(va, vb, places) 17 | 18 | def test_length(self): 19 | 20 | e1 = Edge() 21 | # bug in Cython? 22 | #self.assertRaises(OCCError, e1.length) 23 | 24 | e1 = Edge().createLine((0.,0.,0.), (1.,0.,0.)) 25 | self.assertAlmostEqual(e1.length(), 1.) 26 | 27 | def test_createLine(self): 28 | eq = self.assertEqual 29 | aeq = self.assertAlmostEqual 30 | 31 | self.assertRaises(OCCError, Edge().createLine, (0.,0.,0.), (0.,0.,0.)) 32 | 33 | args = \ 34 | ( 35 | (Vertex(1.,0.,0.), Vertex(-1.,0.,0.)), 36 | ((1.,0.,0.), (-1.,0.,0.)), 37 | ((0.,1.,0.), (0.,-1.,0.)), 38 | ((0.,0.,1.), (0.,0.,-1.)), 39 | ) 40 | for start, end in args: 41 | e1 = Edge().createLine(start, end) 42 | 43 | eq(len(e1), 2) 44 | 45 | eq(e1.isNull(), False) 46 | eq(e1.isValid(), True) 47 | eq(e1.isDegenerated(), False) 48 | eq(e1.hasPlane(), False) 49 | 50 | aeq(e1.length(), 2) 51 | 52 | def test_createArc(self): 53 | eq = self.assertAlmostEqual 54 | 55 | v1 = (0.,0.,0.) 56 | self.assertRaises(OCCError, Edge().createArc, v1, v1, v1) 57 | 58 | args = \ 59 | ( 60 | (Vertex(0.,0.,0.), Vertex(1.,0.,1.), (1.,0.,0.)), 61 | ((0.,0.,0.), (1.,0.,1.), (1.,0.,0.)), 62 | ((0.,1.,0.), (1.,1.,1.), (1.,1.,0.)), 63 | ) 64 | for start, end, cen in args: 65 | e1 = Edge().createArc(start,end,cen) 66 | 67 | eq(e1.length(), .5*pi) 68 | 69 | def test_createArc3P(self): 70 | eq = self.assertAlmostEqual 71 | 72 | v1 = (0.,0.,0.) 73 | self.assertRaises(OCCError, Edge().createArc3P, v1, v1, v1) 74 | 75 | args = \ 76 | ( 77 | (Vertex(1.,0.,0.), Vertex(-1.,0.,0.), (0.,1.,0.)), 78 | ((1.,0.,0.), (-1.,0.,0.), (0.,1.,0.)), 79 | ((1.,1.,0.), (-1.,1.,0.), (0.,2.,0.)), 80 | ) 81 | for start, end, pnt in args: 82 | e1 = Edge().createArc3P(start,end,pnt) 83 | eq(e1.length(), pi) 84 | 85 | def test_createCircle(self): 86 | eq = self.assertAlmostEqual 87 | 88 | self.assertRaises(OCCError, Edge().createCircle, (0.,0.,0.), (0.,0.,1.), -1.) 89 | self.assertRaises(OCCError, Edge().createCircle, (0.,0.,0.), (0.,0.,1.), 0.) 90 | 91 | e1 = Edge() 92 | center = (0.,0.,0.) 93 | normal = (0.,0.,1.) 94 | radius = 1. 95 | 96 | e1.createCircle(center, normal, radius) 97 | 98 | eq(e1.length(), 2*pi) 99 | 100 | def test_createEllipse(self): 101 | eq = self.assertAlmostEqual 102 | 103 | self.assertRaises(OCCError, Edge().createEllipse, (0.,0.,0.), (0.,0.,1.), 0., 0.) 104 | 105 | e1 = Edge().createEllipse(center=(0.,0.,0.),normal=(0.,0.,1.), rMajor = 1., rMinor=.5) 106 | eq(e1.length(), .5*sqrt(93. + .5*sqrt(3.)), 1) 107 | 108 | def test_createHelix(self): 109 | eq = self.assertAlmostEqual 110 | 111 | self.assertRaises(OCCError, Edge().createHelix, pitch = .5, height = 0., radius = 0., angle = pi/5.) 112 | self.assertRaises(OCCError, Edge().createHelix, pitch = .5, height = 0., radius = .25, angle = pi/5.) 113 | 114 | e1 = Edge().createHelix(pitch = .5, height = 1., radius = .25, angle = pi/5.) 115 | self.assertEqual(e1.length() > 1., True) 116 | 117 | def test_createBezier(self): 118 | eq = self.almostEqual 119 | 120 | pnts = ((0.,0.,0.),(0.,0.,0.), (0.,0.,0.),(0.,0.,0.)) 121 | self.assertRaises(OCCError, Edge().createBezier, points = pnts) 122 | 123 | start = Vertex(0.,0.,0.) 124 | end = Vertex(1.,0.,0.) 125 | pnts = ((0.,2.,0.), (1.,1.5,0.)) 126 | e1 = Edge().createBezier(start,end,pnts) 127 | 128 | v1, v2 = e1 129 | eq(v1, start) 130 | eq(v2, end) 131 | 132 | pnts = ((0.,0.,0.),(0.,2.,0.), (1.,1.5,0.),(1.,0.,0.)) 133 | e2 = Edge().createBezier(points = pnts) 134 | 135 | v1, v2 = e2 136 | eq(v1, start) 137 | eq(v2, end) 138 | 139 | self.assertAlmostEqual(e1.length(), e2.length()) 140 | 141 | def test_createSpline(self): 142 | eq = self.almostEqual 143 | 144 | pnts = ((0.,0.,0.),(0.,0.,0.), (0.,0.,0.),(0.,0.,0.)) 145 | self.assertRaises(OCCError, Edge().createSpline, points = pnts) 146 | 147 | start = Vertex(0.,0.,0.) 148 | end = Vertex(1.,0.,0.) 149 | pnts = ((0.,2.,0.), (5.,1.5,0.)) 150 | e1 = Edge().createSpline(start,end,pnts) 151 | 152 | v1, v2 = e1 153 | eq(v1, start) 154 | eq(v2, end) 155 | 156 | pnts = ((0.,0.,0.),(0.,2.,0.), (5.,1.5,0.),(1.,0.,0.)) 157 | e2 = Edge().createSpline(points = pnts) 158 | self.assertAlmostEqual(e1.length(), e2.length()) 159 | 160 | def test_isClosed(self): 161 | eq = self.assertEqual 162 | 163 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 164 | eq(e1.isClosed(), True) 165 | 166 | e1 = Edge().createArc((0.,0.,0.),(1.,0.,1.),(1.,0.,0.)) 167 | eq(e1.isClosed(), False) 168 | 169 | if __name__ == "__main__": 170 | sys.dont_write_bytecode = True 171 | unittest.main() -------------------------------------------------------------------------------- /occmodel/@src/OCCIncludes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details. 3 | #include 4 | using std::iostream; 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | # include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | #include 103 | #include 104 | #include 105 | #include 106 | #include 107 | #include 108 | #include 109 | #include 110 | #include 111 | #include 112 | #include 113 | #include 114 | #include 115 | #include 116 | #include 117 | #include 118 | #include 119 | #include 120 | #include 121 | #include 122 | #include 123 | #include 124 | #include 125 | #include 126 | #include 127 | #include 128 | #include 129 | #include 130 | #include 131 | #include 132 | #include 133 | #include 134 | #include 135 | #include 136 | #include 137 | #include 138 | #include 139 | #include 140 | #include 141 | #include 142 | #include 143 | #include 144 | #include 145 | #include 146 | #include 147 | #include 148 | #include 149 | #include 150 | #include 151 | #include 152 | #include 153 | #include 154 | #include 155 | #include 156 | #include 157 | #include 158 | #include 159 | #include 160 | #include 161 | #include 162 | -------------------------------------------------------------------------------- /occmodel/@src/OCCTools.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | 6 | cdef class Tools: 7 | ''' 8 | Misc tools. 9 | ''' 10 | @staticmethod 11 | def writeBREP(filename, shapes): 12 | ''' 13 | Write a sequence of shapes or a single shape to 14 | a BREP file. 15 | ''' 16 | cdef vector[c_OCCBase *] cshapes 17 | cdef Base cobj 18 | cdef int ret 19 | 20 | cdef char *c_filename 21 | bytetext = unicode(filename).encode('UTF-8','ignore') 22 | c_filename = bytetext 23 | 24 | if isinstance(shapes, Base): 25 | shapes = (shapes,) 26 | 27 | for cobj in shapes: 28 | cshapes.push_back((cobj.thisptr)) 29 | 30 | ret = writeBREP(c_filename, cshapes) 31 | if not ret: 32 | raise OCCError(errorMessage) 33 | 34 | return True 35 | 36 | @staticmethod 37 | def writeSTEP(filename, shapes): 38 | ''' 39 | Write a sequence of shapes or a single shape to 40 | a STEP file. 41 | ''' 42 | cdef vector[c_OCCBase *] cshapes 43 | cdef Base cobj 44 | cdef int ret 45 | 46 | cdef char *c_filename 47 | bytetext = unicode(filename).encode('UTF-8','ignore') 48 | c_filename = bytetext 49 | 50 | if isinstance(shapes, Base): 51 | shapes = (shapes,) 52 | 53 | for cobj in shapes: 54 | cshapes.push_back((cobj.thisptr)) 55 | 56 | ret = writeSTEP(c_filename, cshapes) 57 | if not ret: 58 | raise OCCError(errorMessage) 59 | 60 | return True 61 | 62 | @staticmethod 63 | def writeSTL(filename, shapes): 64 | ''' 65 | Write a sequence of shapes or a single shape to 66 | a STL file. 67 | ''' 68 | cdef vector[c_OCCBase *] cshapes 69 | cdef Base cobj 70 | cdef int ret 71 | 72 | cdef char *c_filename 73 | bytetext = unicode(filename).encode('UTF-8','ignore') 74 | c_filename = bytetext 75 | 76 | if isinstance(shapes, Base): 77 | shapes = (shapes,) 78 | 79 | for cobj in shapes: 80 | cshapes.push_back((cobj.thisptr)) 81 | 82 | ret = writeSTL(c_filename, cshapes) 83 | if not ret: 84 | raise OCCError(errorMessage) 85 | 86 | return True 87 | 88 | @staticmethod 89 | def readBREP(filename): 90 | ''' 91 | Read shapes from a BREP file. 92 | 93 | A sequence of shapes are returned. 94 | ''' 95 | cdef vector[c_OCCBase *] cshapes 96 | cdef Solid solid 97 | cdef Face face 98 | cdef Wire wire 99 | cdef Edge edge 100 | cdef Vertex vertex 101 | cdef int i, ret 102 | 103 | cdef char *c_filename 104 | bytetext = unicode(filename).encode('UTF-8','ignore') 105 | c_filename = bytetext 106 | 107 | ret = readBREP(c_filename, cshapes) 108 | if not ret: 109 | raise OCCError(errorMessage) 110 | 111 | if cshapes.size() == 0: 112 | raise OCCError('Failed to import objects') 113 | 114 | res = [] 115 | for i in range(cshapes.size()): 116 | shapetype = cshapes[i].shapeType() 117 | 118 | if shapetype == TopAbs_COMPSOLID or \ 119 | shapetype == TopAbs_SOLID: 120 | solid = Solid.__new__(Solid, None) 121 | solid.thisptr = cshapes[i] 122 | res.append(solid) 123 | 124 | elif shapetype == TopAbs_SHELL or \ 125 | shapetype == TopAbs_FACE: 126 | face = Face.__new__(Face, None) 127 | face.thisptr = cshapes[i] 128 | res.append(face) 129 | 130 | elif shapetype == TopAbs_WIRE: 131 | wire = Wire.__new__(Wire, None) 132 | wire.thisptr = cshapes[i] 133 | res.append(wire) 134 | 135 | elif shapetype == TopAbs_EDGE: 136 | edge = Edge.__new__(Edge, None) 137 | edge.thisptr = cshapes[i] 138 | res.append(edge) 139 | 140 | elif shapetype == TopAbs_VERTEX: 141 | vertex = Vertex.__new__(Vertex, None) 142 | vertex.thisptr = cshapes[i] 143 | res.append(vertex) 144 | 145 | return res 146 | 147 | @staticmethod 148 | def readSTEP(filename): 149 | ''' 150 | Read shapes from a STEP file. 151 | 152 | A sequence of shapes are returned. 153 | ''' 154 | cdef vector[c_OCCBase *] cshapes 155 | cdef Solid solid 156 | cdef Face face 157 | cdef Wire wire 158 | cdef Edge edge 159 | cdef Vertex vertex 160 | cdef int i, ret 161 | 162 | cdef char *c_filename 163 | bytetext = unicode(filename).encode('UTF-8','ignore') 164 | c_filename = bytetext 165 | 166 | ret = readSTEP(c_filename, cshapes) 167 | if not ret or cshapes.size() == 0: 168 | raise OCCError(errorMessage) 169 | 170 | res = [] 171 | for i in range(cshapes.size()): 172 | shapetype = cshapes[i].shapeType() 173 | 174 | if shapetype == TopAbs_COMPSOLID or \ 175 | shapetype == TopAbs_SOLID: 176 | solid = Solid.__new__(Solid, None) 177 | solid.thisptr = cshapes[i] 178 | res.append(solid) 179 | 180 | elif shapetype == TopAbs_SHELL or \ 181 | shapetype == TopAbs_FACE: 182 | face = Face.__new__(Face, None) 183 | face.thisptr = cshapes[i] 184 | res.append(face) 185 | 186 | elif shapetype == TopAbs_WIRE: 187 | wire = Wire.__new__(Wire, None) 188 | wire.thisptr = cshapes[i] 189 | res.append(wire) 190 | 191 | elif shapetype == TopAbs_EDGE: 192 | edge = Edge.__new__(Edge, None) 193 | edge.thisptr = cshapes[i] 194 | res.append(edge) 195 | 196 | elif shapetype == TopAbs_VERTEX: 197 | vertex = Vertex.__new__(Vertex, None) 198 | vertex.thisptr = cshapes[i] 199 | res.append(vertex) 200 | 201 | return res -------------------------------------------------------------------------------- /occmodel/@tests/test_Base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | import sys 7 | import unittest 8 | 9 | from math import pi, sin, cos, sqrt 10 | 11 | from geotools import Point, Vector, Plane, Transform 12 | from occmodel import Edge, Vertex, OCCError 13 | 14 | class test_Base(unittest.TestCase): 15 | def almostEqual(self, a, b, places = 7): 16 | for va,vb in zip(a,b): 17 | self.assertAlmostEqual(va, vb, places) 18 | 19 | def test_empty(self): 20 | eq = self.assertEqual 21 | 22 | e1 = Edge() 23 | 24 | self.assertRaises(OCCError, e1.shapeType) 25 | eq(e1.isNull(), True) 26 | eq(e1.isValid(), False) 27 | eq(e1.isDegenerated(), True) 28 | eq(e1.hasPlane(), False) 29 | eq(len(e1), 0) 30 | eq(tuple(e1), ()) 31 | 32 | m = Transform().translate(1.,2.,3.) 33 | self.assertRaises(OCCError, e1.transform, m) 34 | self.assertRaises(OCCError, e1.translate, (1.,2.,3.)) 35 | self.assertRaises(OCCError, e1.rotate, pi, (0.,1.,0.)) 36 | self.assertRaises(OCCError, e1.scale, (0.,0.,0.), 2.) 37 | 38 | def test_base(self): 39 | eq = self.assertEqual 40 | 41 | start, end = Vertex(1.,0.,0.), Vertex(-1.,0.,0.) 42 | e1 = Edge().createLine(start, end) 43 | 44 | eq(e1.shapeType() is Edge, True) 45 | 46 | eq(e1.isNull(), False) 47 | eq(e1.isValid(), True) 48 | eq(e1.isDegenerated(), False) 49 | eq(e1.hasPlane(), False) 50 | 51 | e2 = e1.copy() 52 | eq(e2.isNull(), False) 53 | eq(e2.isValid(), True) 54 | eq(e2.isDegenerated(), False) 55 | eq(e2.hasPlane(), False) 56 | 57 | # test hallow copy equallity 58 | eq(e2.isEqual(e1), True) 59 | eq(e2 == e1, True) 60 | eq(e2 != e1, False) 61 | eq(e1.hashCode() == e2.hashCode(), True) 62 | 63 | # test copy of underlying geometry 64 | e3 = e1.copy(deepCopy = True) 65 | eq(e3.isEqual(e1), False) 66 | eq(e1.hashCode() == e3.hashCode(), False) 67 | 68 | # test serialize 69 | e4 = Edge().fromString(e1.toString()) 70 | eq(e4.isEqual(e1), False) 71 | eq(e1.hashCode() == e4.hashCode(), False) 72 | 73 | eq(len(e1), 2) 74 | 75 | vertices = set((start.hashCode(), end.hashCode())) 76 | for vertex in e1: 77 | hid = vertex.hashCode() 78 | if hid in vertices: 79 | vertices.remove(hid) 80 | 81 | eq(len(vertices), 0) 82 | 83 | def test_hasPlane(self): 84 | eq = self.assertEqual 85 | aeq = self.almostEqual 86 | 87 | e5 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 88 | eq(e5.hasPlane(), True) 89 | 90 | pnt = Point(-1.,-1.,-1.) 91 | vec = Vector(-1.,-1.,-1.) 92 | e5.hasPlane(pnt, vec) 93 | aeq(pnt, (0.,0.,0.)) 94 | aeq(vec, (0.,0.,1.)) 95 | 96 | def test_bbox(self): 97 | eq = self.assertEqual 98 | aeq = self.almostEqual 99 | 100 | e1 = Edge().createLine((1.,-2.,3.), (-1.,2.,-3.)) 101 | bbox = e1.boundingBox() 102 | 103 | eq(bbox.isValid(), True) 104 | aeq(bbox.min, (-1.,-2.,-3.)) 105 | aeq(bbox.max, (1.,2.,3.)) 106 | 107 | def test_transform(self): 108 | eq = self.almostEqual 109 | 110 | e1 = Edge().createLine((-1.,-2.,-3.), (0.,0.,0.)) 111 | m = Transform().translate(1.,2.,3.) 112 | e1.transform(m) 113 | 114 | v1, v2 = e1 115 | eq(v1, (0.,0.,0.)) 116 | eq(v2, (1.,2.,3.)) 117 | 118 | m = Transform().translate(-1.,-2.,-3.) 119 | e2 = e1.transform(m, copy = True) 120 | 121 | v1, v2 = e1 122 | eq(v1, (0.,0.,0.)) 123 | eq(v2, (1.,2.,3.)) 124 | 125 | v1, v2 = e2 126 | eq(v1, (-1.,-2.,-3.)) 127 | eq(v2, (0.,0.,0.)) 128 | 129 | def test_rotate(self): 130 | eq = self.almostEqual 131 | 132 | e1 = Edge().createLine((0.,0.,0.), (1.,0.,0.)) 133 | e1.rotate(-.5*pi, (0.,1.,0.)) 134 | 135 | v1, v2 = e1 136 | eq(v1, (0.,0.,0.)) 137 | eq(v2, (0.,0.,1.)) 138 | 139 | e1.rotate(.5*pi, (0.,1.,0.)) 140 | 141 | v1, v2 = e1 142 | eq(v1, (0.,0.,0.)) 143 | eq(v2, (1.,0.,0.)) 144 | 145 | e2 = e1.rotate(-.5*pi, (0.,1.,0.), copy = True) 146 | 147 | v1, v2 = e1 148 | eq(v1, (0.,0.,0.)) 149 | eq(v2, (1.,0.,0.)) 150 | 151 | v1, v2 = e2 152 | eq(v1, (0.,0.,0.)) 153 | eq(v2, (0.,0.,1.)) 154 | 155 | e1 = Edge().createLine((0.,0.,0.), (1.,0.,0.)) 156 | e1.rotate(-.5*pi, (0.,1.,0.), (.5,0.,0.)) 157 | v1, v2 = e1 158 | 159 | eq(v1, (.5,0.,-.5)) 160 | eq(v2, (.5,0.,.5)) 161 | 162 | def test_translate(self): 163 | eq = self.almostEqual 164 | 165 | e1 = Edge().createLine((-1.,-2.,-3.), (0.,0.,0.)) 166 | e1.translate((1.,2.,3.)) 167 | 168 | v1, v2 = e1 169 | eq(v1, (0.,0.,0.)) 170 | eq(v2, (1.,2.,3.)) 171 | 172 | e2 = e1.translate((-1.,-2.,-3.), copy = True) 173 | 174 | v1, v2 = e1 175 | eq(v1, (0.,0.,0.)) 176 | eq(v2, (1.,2.,3.)) 177 | 178 | v1, v2 = e2 179 | eq(v1, (-1.,-2.,-3.)) 180 | eq(v2, (0.,0.,0.)) 181 | 182 | def test_scale(self): 183 | eq = self.almostEqual 184 | 185 | e1 = Edge().createLine((0.,0.,0.), (1.,0.,1.)) 186 | e1.scale((0.,0.,0.), .5) 187 | v1, v2 = e1 188 | 189 | eq(v1, (0.,0.,0.)) 190 | eq(v2, (.5,0.,.5)) 191 | 192 | e2 = e1.scale((0.,0.,0.), 2., copy = True) 193 | 194 | v1, v2 = e1 195 | eq(v1, (0.,0.,0.)) 196 | eq(v2, (.5,0.,.5)) 197 | 198 | v1, v2 = e2 199 | 200 | eq(v1, (0.,0.,0.)) 201 | eq(v2, (1.,0.,1.)) 202 | 203 | def test_mirror(self): 204 | eq = self.almostEqual 205 | 206 | e1 = Edge().createLine((0.,0.,0.), (1.,0.,0.)) 207 | plane = Plane.fromNormal((0.,0.,0.), (1.,0.,0.)) 208 | e1.mirror(plane) 209 | v1, v2 = e1 210 | 211 | eq(v1, (0.,0.,0.)) 212 | eq(v2, (-1.,0.,0.)) 213 | 214 | e2 = e1.mirror(plane, copy = True) 215 | v1, v2 = e1 216 | eq(v1, (0.,0.,0.)) 217 | eq(v2, (-1.,0.,0.)) 218 | 219 | v1, v2 = e2 220 | eq(v1, (0.,0.,0.)) 221 | eq(v2, (1.,0.,0.)) 222 | 223 | if __name__ == "__main__": 224 | sys.dont_write_bytecode = True 225 | unittest.main() -------------------------------------------------------------------------------- /occmodel/occmodel.pyx: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | #cython: embedsignature=True 3 | # Copyright 2012 by Runar Tenfjord, Tenko as. 4 | # See LICENSE.txt for details on conditions. 5 | include "OCCIncludes.pxi" 6 | include "Config.pxi" 7 | 8 | class OCCError(Exception): 9 | pass 10 | 11 | cdef class Tesselation: 12 | ''' 13 | Tesselation - Representing Edge/Wire tesselation which result in 14 | possible multiple disconnected polylines. 15 | ''' 16 | cdef void *thisptr 17 | 18 | cdef readonly view.array vertices 19 | cdef readonly int verticesItemSize 20 | 21 | cdef readonly view.array ranges 22 | cdef readonly int rangesItemSize 23 | 24 | def __init__(self): 25 | self.thisptr = new c_OCCTesselation() 26 | 27 | def __dealloc__(self): 28 | cdef c_OCCTesselation *tmp 29 | 30 | if self.thisptr != NULL: 31 | tmp = self.thisptr 32 | del tmp 33 | 34 | def __str__(self): 35 | return "Tesselation%s" % repr(self) 36 | 37 | def __repr__(self): 38 | args = self.nvertices(), self.nranges() 39 | return "(nvertices = %d, ranges = %d)" % args 40 | 41 | cpdef bint isValid(self): 42 | cdef c_OCCTesselation *occ = self.thisptr 43 | return occ.vertices.size() > 0 and occ.ranges.size() > 0 44 | 45 | cdef setArrays(self): 46 | cdef c_OCCTesselation *occ = self.thisptr 47 | 48 | if not self.isValid(): 49 | return 50 | 51 | self.verticesItemSize = sizeof(float) 52 | self.rangesItemSize = sizeof(unsigned int) 53 | 54 | self.vertices = view.array( 55 | shape=(3*occ.vertices.size(),), 56 | itemsize=sizeof(float), 57 | format="f", 58 | allocate_buffer=False 59 | ) 60 | self.vertices.data = &occ.vertices[0] 61 | 62 | self.ranges = view.array( 63 | shape=(occ.ranges.size(),), 64 | itemsize=sizeof(unsigned int), 65 | format="I", 66 | allocate_buffer=False 67 | ) 68 | self.ranges.data = &occ.ranges[0] 69 | 70 | cpdef size_t nvertices(self): 71 | ''' 72 | Return number of vertices 73 | ''' 74 | cdef c_OCCTesselation *occ = self.thisptr 75 | return occ.vertices.size() 76 | 77 | cpdef size_t nranges(self): 78 | ''' 79 | Return number of range values 80 | ''' 81 | cdef c_OCCTesselation *occ = self.thisptr 82 | return occ.ranges.size() 83 | 84 | 85 | cdef class Mesh: 86 | ''' 87 | Mesh - Represent triangle mesh for viewing purpose 88 | ''' 89 | cdef void *thisptr 90 | 91 | cdef readonly view.array vertices 92 | cdef readonly int verticesItemSize 93 | 94 | cdef readonly view.array normals 95 | cdef readonly int normalsItemSize 96 | 97 | cdef readonly view.array triangles 98 | cdef readonly int trianglesItemSize 99 | 100 | cdef readonly view.array edgeIndices 101 | cdef readonly int edgeIndicesItemSize 102 | 103 | cdef readonly view.array edgeRanges 104 | cdef readonly int edgeRangesItemSize 105 | 106 | def __init__(self): 107 | self.thisptr = new c_OCCMesh() 108 | 109 | def __dealloc__(self): 110 | cdef c_OCCMesh *tmp 111 | 112 | if self.thisptr != NULL: 113 | tmp = self.thisptr 114 | del tmp 115 | 116 | def __str__(self): 117 | return "Mesh%s" % repr(self) 118 | 119 | def __repr__(self): 120 | args = self.nvertices(), self.ntriangles(), self.nnormals() 121 | return "(nvertices = %d, ntriangles = %d, nnormals = %d)" % args 122 | 123 | cpdef bint isValid(self): 124 | cdef c_OCCMesh *occ = self.thisptr 125 | return occ.vertices.size() > 0 and occ.normals.size() > 0 and \ 126 | occ.triangles.size() > 0 127 | 128 | cpdef optimize(self): 129 | ''' 130 | Vertex Cache Optimisation 131 | ''' 132 | cdef c_OCCMesh *occ = self.thisptr 133 | occ.optimize() 134 | 135 | cdef setArrays(self): 136 | cdef c_OCCMesh *occ = self.thisptr 137 | 138 | if not self.isValid(): 139 | return 140 | 141 | self.verticesItemSize = sizeof(float) 142 | self.normalsItemSize = sizeof(float) 143 | self.trianglesItemSize = sizeof(unsigned int) 144 | self.edgeIndicesItemSize = sizeof(unsigned int) 145 | self.edgeRangesItemSize = sizeof(int) 146 | 147 | self.vertices = view.array( 148 | shape=(3*occ.vertices.size(),), 149 | itemsize=sizeof(float), 150 | format="f", 151 | allocate_buffer=False 152 | ) 153 | self.vertices.data = &occ.vertices[0] 154 | 155 | self.normals = view.array( 156 | shape=(3*occ.normals.size(),), 157 | itemsize=sizeof(float), 158 | format="f", 159 | allocate_buffer=False 160 | ) 161 | self.normals.data = &occ.normals[0] 162 | 163 | self.triangles = view.array( 164 | shape=(3*occ.triangles.size(),), 165 | itemsize=sizeof(unsigned int), 166 | format="I", 167 | allocate_buffer=False 168 | ) 169 | self.triangles.data = &occ.triangles[0] 170 | 171 | if occ.edgeindices.size() > 0: 172 | self.edgeIndices = view.array( 173 | shape=(occ.edgeindices.size(),), 174 | itemsize=sizeof(unsigned int), 175 | format="I", 176 | allocate_buffer=False 177 | ) 178 | self.edgeIndices.data = &occ.edgeindices[0] 179 | 180 | self.edgeRanges = view.array( 181 | shape=(occ.edgeranges.size(),), 182 | itemsize=sizeof(int), 183 | format="i", 184 | allocate_buffer=False 185 | ) 186 | self.edgeRanges.data = &occ.edgeranges[0] 187 | 188 | 189 | cpdef size_t nvertices(self): 190 | ''' 191 | Return number of vertices 192 | ''' 193 | cdef c_OCCMesh *occ = self.thisptr 194 | return occ.vertices.size() 195 | 196 | cpdef size_t ntriangles(self): 197 | ''' 198 | Return number of triangles 199 | ''' 200 | cdef c_OCCMesh *occ = self.thisptr 201 | return occ.triangles.size() 202 | 203 | cpdef size_t nnormals(self): 204 | ''' 205 | Return number of normals 206 | ''' 207 | cdef c_OCCMesh *occ = self.thisptr 208 | return occ.normals.size() 209 | 210 | cpdef size_t nedgeIndices(self): 211 | ''' 212 | Return number of edge indices 213 | ''' 214 | cdef c_OCCMesh *occ = self.thisptr 215 | return occ.edgeindices.size() 216 | 217 | cpdef size_t nedgeRanges(self): 218 | ''' 219 | Return number of edge ranges 220 | ''' 221 | cdef c_OCCMesh *occ = self.thisptr 222 | return occ.edgeranges.size() 223 | 224 | cpdef vertex(self, size_t index): 225 | ''' 226 | Return vertex at given index 227 | ''' 228 | cdef c_OCCMesh *occ = self.thisptr 229 | cdef c_OCCStruct3f v = occ.vertices[index] 230 | return v.x, v.y, v.z 231 | 232 | cpdef normal(self, size_t index): 233 | ''' 234 | Return normal at given vertex index 235 | ''' 236 | cdef c_OCCMesh *occ = self.thisptr 237 | cdef c_OCCStruct3f n = occ.normals[index] 238 | return n.x, n.y, n.z 239 | 240 | cpdef triangle(self, size_t index): 241 | ''' 242 | Return triangle indices at given index 243 | ''' 244 | cdef c_OCCMesh *occ = self.thisptr 245 | cdef c_OCCStruct3I t = occ.triangles[index] 246 | return t.i, t.j, t.k 247 | 248 | include "OCCTools.pxi" 249 | include "OCCBase.pxi" 250 | include "OCCVertex.pxi" 251 | include "OCCEdge.pxi" 252 | include "OCCWire.pxi" 253 | include "OCCFace.pxi" 254 | include "OCCSolid.pxi" -------------------------------------------------------------------------------- /occmodel/@docs/examples.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | Examples are available by executing the demo 5 | script. 6 | 7 | :: 8 | 9 | # windows 10 | C:\Python2.7\Scripts\occmodeldemo.py 11 | 12 | # linux 13 | occmodeldemo.py 14 | 15 | .. figure:: images/demo_window.jpg 16 | 17 | Demo window including GUI elements. 18 | 19 | To view geometrical objects simply pass a sequece of object or a single 20 | object to the viewer function. 21 | 22 | .. code-block:: python 23 | 24 | from occmodelviewer import viewer 25 | viewer(solid) 26 | viewer((solid,face,edge)) 27 | 28 | The viewer can also be used interactive from the Python command 29 | prompt. Note that a reference must be keept of the returned 30 | Viewer to avoid beeing reclaimed by the garbage collector. 31 | 32 | .. code-block:: python 33 | 34 | from occmodelviewer import viewer 35 | view = viewer(interactive = True) 36 | view.add(solid) 37 | view.redraw() 38 | 39 | It is also possible to read objects from a STEP or BREP file and view 40 | the imported geometry. 41 | 42 | .. code-block:: python 43 | 44 | from occmodel import Tools 45 | from occmodelviewer import viewer 46 | objects = Tools.readSTEP('example.stp') 47 | viewer(objects) 48 | 49 | Geometry 50 | ======== 51 | 52 | :class:`occmodel.Vertex` are 3d points which are used to reference start, end 53 | or seam points of edges.. 54 | 55 | :class:`occmodel.Edge` are 3d curve which are combined into wires. 56 | 57 | :class:`occmodel.Wire` are composite curves created from edges defining boundaries of 58 | faces. 59 | 60 | :class:`occmodel.Face` are underlying surface geometry which are constrained by 61 | wires. 62 | 63 | :class:`occmodel.Solid` are the main object which contain rich functionalty to 64 | combine and edit solid objects. 65 | 66 | Point and vectors passed to the geometry functions can be any valid 67 | python sequence of three numbers. 68 | 69 | Edges 70 | ===== 71 | 72 | Line 73 | ---- 74 | 75 | Create single line Edge. 76 | 77 | .. code-block:: python 78 | 79 | e1 = Edge().createLine(start = (0.,0.,0.), end = (1.,1.,0.)) 80 | 81 | Arc 3P 82 | ------ 83 | 84 | Create an arc Edge defined by three points. 85 | 86 | .. code-block:: python 87 | 88 | e1 = Edge().createArc3P(start = (1.,0.,0.), end = (-1.,0.,0.), pnt = (0.,1.,0.)) 89 | 90 | Circle 91 | ------ 92 | 93 | Create circle Edge 94 | 95 | .. code-block:: python 96 | 97 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 98 | 99 | Bezier 100 | ------ 101 | 102 | Create bezier Edge 103 | 104 | .. code-block:: python 105 | 106 | start = Vertex(0.,0.,0.) 107 | end = Vertex(1.,0.,0.) 108 | pnts = ((0.,2.,0.), (1.,1.5,0.)) 109 | e1 = Edge().createBezier(start,end,pnts) 110 | 111 | Spline 112 | ------ 113 | 114 | Create a spline Edge 115 | 116 | .. code-block:: python 117 | 118 | start = Vertex(0.,0.,0.) 119 | end = Vertex(1.,0.,0.) 120 | pnts = ((0.,2.,0.), (5.,1.5,0.)) 121 | e1 = Edge().createSpline(start,end,pnts) 122 | 123 | Faces 124 | ===== 125 | 126 | Face interior point 127 | ------------------- 128 | 129 | Create face from circle edge and interior point. 130 | 131 | .. code-block:: python 132 | 133 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 134 | f1 = Face().createConstrained(e1, ((0.,.5,.25),)) 135 | 136 | Face edge sequence 137 | ------------------ 138 | 139 | Create face from sequence of edges. 140 | 141 | .. code-block:: python 142 | 143 | start = Vertex(1.,0.,0.) 144 | end = Vertex(-1.,0.,0.) 145 | e1 = Edge().createLine(end,start) 146 | 147 | pnt = (0.,1.,0.) 148 | e2 = Edge().createArc3P(start,end,pnt) 149 | 150 | w1 = Wire().createWire((e1,e2)) 151 | f1 = Face().createFace(w1) 152 | 153 | Polygonal face 154 | -------------- 155 | 156 | Create a planar polygonal face 157 | 158 | .. code-block:: python 159 | 160 | pnts = ((0.,0.,0.), (0.,2.,0.), (1.,2.,0.), (1.,0.,0.)) 161 | f1 = Face().createPolygonal(pnts) 162 | 163 | 164 | Solids 165 | ====== 166 | 167 | Primitive Solids 168 | ----------------- 169 | 170 | Create sphere primitive. 171 | 172 | .. code-block:: python 173 | 174 | solid = Solid() 175 | solid.createSphere((1.,2.,3.),.5) 176 | 177 | Create box primitive. 178 | 179 | .. code-block:: python 180 | 181 | solid = Solid().createBox((0.,0.,0.),(100.,100.,100.)) 182 | 183 | Create cylinder primitive. 184 | 185 | .. code-block:: python 186 | 187 | solid = Solid().createCylinder((0.,0.,0.),(0.,0.,1.), 1.) 188 | 189 | Create torus primitive. 190 | 191 | .. code-block:: python 192 | 193 | solid = Solid().createTorus((0.,0.,0.),(0.,0.,1.), 1., 2.) 194 | 195 | Create cone primitive. 196 | 197 | .. code-block:: python 198 | 199 | solid = Solid().createCone((0.,0.,0.),(0.,0.,1.), 1., 2.) 200 | 201 | Boolean 202 | ------- 203 | 204 | Boolean union between two solid spheres. 205 | 206 | .. code-block:: python 207 | 208 | s1 = Solid().createSphere((0.,0.,0.),.5) 209 | s2 = Solid().createSphere((.25,0.,0.),.5) 210 | s1.fuse(s2) 211 | 212 | Boolean difference between two solid spheres. 213 | 214 | .. code-block:: python 215 | 216 | s1 = Solid().createSphere((0.,0.,0.),.5) 217 | s2 = Solid().createSphere((.25,0.,0.),.5) 218 | s1.cut(s2) 219 | 220 | Boolean intersection between two solid spheres. 221 | 222 | .. code-block:: python 223 | 224 | s1 = Solid().createSphere((0.,0.,0.),.5) 225 | s2 = Solid().createSphere((.25,0.,0.),.5) 226 | s1.common(s2) 227 | 228 | Extrude 229 | ------- 230 | 231 | Extrude face along vector. 232 | 233 | .. code-block:: python 234 | 235 | pnts = ( 236 | (0.,0.,0.), 237 | (0.,2.,0.), 238 | (5.,1.5,0.), 239 | (0.,0.,0.) 240 | ) 241 | e1 = Edge().createSpline(points = pnts) 242 | face = Face().createFace(e1) 243 | 244 | solid = Solid().extrude(face, (0.,0.,0.), (0.,0.,5.)) 245 | 246 | Revolve 247 | ------- 248 | 249 | Revolve face to create solid. 250 | 251 | .. code-block:: python 252 | 253 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 254 | face = Face().createFace(e1) 255 | 256 | solid = Solid().revolve(face, (0.,2.,0.), (1.,2.,0.), pi/2.) 257 | 258 | Loft 259 | ---- 260 | 261 | Loft through edges. 262 | 263 | .. code-block:: python 264 | 265 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 266 | e2 = Edge().createEllipse(center=(0.,0.,5.),normal=(0.,0.,1.), rMajor = 2.0, rMinor=1.0) 267 | e3 = Edge().createCircle(center=(0.,0.,10.),normal=(0.,0.,1.),radius = 1.0) 268 | solid = Solid().loft((e1,e2,e3)) 269 | 270 | Pipe 271 | ---- 272 | 273 | Extrude circle along arc edge 274 | 275 | .. code-block:: python 276 | 277 | e1 = Edge().createArc((0.,0.,0.),(2.,0.,2.),(2.,0.,0.)) 278 | e2 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 279 | f1 = Face().createFace(e2) 280 | solid = Solid().pipe(f1, e1) 281 | 282 | Advanced solids 283 | --------------- 284 | 285 | Create open box with fillet edges. 286 | 287 | .. figure:: images/box_example.jpg 288 | 289 | Box example plot. 290 | 291 | .. code-block:: python 292 | 293 | solid = Solid().createBox((0.,0.,0.),(100.,100.,100.)) 294 | for face in FaceIterator(solid): 295 | bbox = face.boundingBox() 296 | if bbox.near.z > 50. and bbox.far.z > 50.: 297 | break 298 | solid.shell(-5., face) 299 | solid.fillet(2.) 300 | 301 | Union of cyllinders with fillet intersection edge. 302 | 303 | .. figure:: images/cylinder_example.jpg 304 | 305 | Cylinder example plot. 306 | 307 | .. code-block:: python 308 | 309 | s1 = Solid().createCylinder((0.,0.,-2.),(0.,0.,2.), 1.) 310 | s2 = Solid().createCylinder((0.,-2.,0.),(0.,2.,0.), .9) 311 | s1.fuse(s2) 312 | 313 | edges = [] 314 | origo = Point(0.,0.,0.) 315 | for edge in EdgeIterator(s1): 316 | bbox = edge.boundingBox() 317 | if bbox.near.distanceTo(origo) < 1.75: 318 | if bbox.far.distanceTo(origo) < 1.75: 319 | edges.append(edge) 320 | 321 | s1.fillet(0.3, edges) 322 | 323 | Construc bowl like solid. 324 | 325 | .. figure:: images/bowl_example.jpg 326 | 327 | Bowl example plot. 328 | 329 | .. code-block:: python 330 | 331 | # cut sphere in half 332 | solid = Solid().createSphere((0.,0.,0.),10.) 333 | box = Solid().createBox((-11.,-11.,0.),(11.,11.,11.)) 334 | solid.cut(box) 335 | 336 | # shell operation 337 | face = None 338 | for face in FaceIterator(solid): 339 | bbox = face.boundingBox() 340 | if bbox.near.z > -1. and bbox.far.z > -1.: 341 | break 342 | 343 | solid.shell(-2., face) 344 | 345 | # foot 346 | cone = Solid().createCone((0.,0.,-11.), (0.,0.,-7.), 5., 6.) 347 | solid.fuse(cone) 348 | 349 | # fillet all edges 350 | solid.fillet(.25) -------------------------------------------------------------------------------- /occmodel/@src/OCCModelLib.pxd: -------------------------------------------------------------------------------- 1 | cdef extern from *: 2 | ctypedef char* const_char_ptr "char*" 3 | 4 | cdef extern from "" namespace "std": 5 | cdef cppclass string: 6 | string() nogil except + 7 | string(char *) nogil except + 8 | char* c_str() nogil 9 | 10 | cdef extern from "" namespace "std": 11 | cdef cppclass vector[T]: 12 | vector() 13 | void clear() 14 | void push_back(T&) 15 | size_t size() 16 | T& operator[](size_t) 17 | 18 | cdef extern from "OCCModel.h": 19 | char errorMessage[256] 20 | 21 | cdef struct c_OCCStruct3d "OCCStruct3d": 22 | double x 23 | double y 24 | double z 25 | 26 | cdef struct c_OCCStruct3f "OCCStruct3f": 27 | float x 28 | float y 29 | float z 30 | 31 | cdef struct c_OCCStruct3I "OCCStruct3I": 32 | unsigned int i 33 | unsigned int j 34 | unsigned int k 35 | 36 | cdef cppclass c_OCCTesselation "OCCTesselation": 37 | vector[c_OCCStruct3f] vertices 38 | vector[int] ranges 39 | c_OCCTesselation() 40 | 41 | cdef cppclass c_OCCMesh "OCCMesh": 42 | vector[c_OCCStruct3f] vertices 43 | vector[c_OCCStruct3f] normals 44 | vector[c_OCCStruct3I] triangles 45 | vector[unsigned int] edgeindices 46 | vector[int] edgeranges 47 | vector[int] edgehash 48 | 49 | c_OCCMesh() 50 | void optimize() 51 | 52 | cdef enum c_BoolOpType "BoolOpType": 53 | BOOL_FUSE 54 | BOOL_CUT 55 | BOOL_COMMON 56 | 57 | cdef enum c_TopAbs_ShapeEnum "TopAbs_ShapeEnum": 58 | TopAbs_COMPOUND 59 | TopAbs_COMPSOLID 60 | TopAbs_SOLID 61 | TopAbs_SHELL 62 | TopAbs_FACE 63 | TopAbs_WIRE 64 | TopAbs_EDGE 65 | TopAbs_VERTEX 66 | 67 | cdef cppclass c_OCCBase "OCCBase": 68 | c_TopAbs_ShapeEnum shapeType() 69 | int hashCode() 70 | bint isEqual(c_OCCBase *other) 71 | bint isNull() 72 | bint isValid() 73 | int transform(vector[double] mat, c_OCCBase *target) 74 | int translate(c_OCCStruct3d delta, c_OCCBase *target) 75 | int rotate(double angle, c_OCCStruct3d p1, c_OCCStruct3d p2, c_OCCBase *target) 76 | int scale(c_OCCStruct3d pnt, double scale, c_OCCBase *target) 77 | int mirror(c_OCCStruct3d pnt, c_OCCStruct3d nor, c_OCCBase *target) 78 | vector[double] boundingBox(double tolerance) 79 | int findPlane(c_OCCStruct3d *origin, c_OCCStruct3d *normal, double tolerance) 80 | int toString(string *output) 81 | int fromString(string input) 82 | 83 | cdef cppclass c_OCCVertex "OCCVertex": 84 | c_OCCVertex(double x, double y, double z) 85 | double X() 86 | double Y() 87 | double Z() 88 | int project(c_OCCBase *target) 89 | 90 | cdef cppclass c_OCCVertexIterator "OCCVertexIterator": 91 | c_OCCVertexIterator(c_OCCBase *arg) 92 | void reset() 93 | c_OCCVertex *next() 94 | 95 | cdef cppclass c_OCCEdge "OCCEdge": 96 | c_OCCEdge() 97 | bint isSeam(c_OCCBase *face) 98 | bint isDegenerated() 99 | bint isClosed() 100 | c_OCCEdge *copy(bint deepCopy) 101 | int numVertices() 102 | c_OCCTesselation *tesselate(double factor, double angle) 103 | int createLine(c_OCCVertex *v1, c_OCCVertex *v2) 104 | int createArc(c_OCCVertex *start, c_OCCVertex *end, c_OCCStruct3d center) 105 | int createArc3P(c_OCCVertex *start, c_OCCVertex *end, c_OCCStruct3d pnt) 106 | int createCircle(c_OCCStruct3d center, c_OCCStruct3d normal, double radius) 107 | int createEllipse(c_OCCStruct3d pnt, c_OCCStruct3d nor, double rMajor, double rMinor) 108 | int createArcOfEllipse(c_OCCStruct3d pnt, c_OCCStruct3d nor, double rMajor, 109 | double rMinor, double a1, double a2, bint reverse) 110 | int createArcOfHyperbola(c_OCCStruct3d pnt, c_OCCStruct3d nor, double rMajor, 111 | double rMinor, double a1, double a2, bint reverse) 112 | int createArcOfParabola(c_OCCStruct3d pnt, c_OCCStruct3d nor, double focus, 113 | double a1, double a2, bint reverse) 114 | int createHelix(double pitch, double height, double radius, double angle, bint leftHanded) 115 | int createBezier(c_OCCVertex *start, c_OCCVertex *end, vector[c_OCCStruct3d] points) 116 | int createSpline(c_OCCVertex *start, c_OCCVertex *end, vector[c_OCCStruct3d] points, double tolerance) 117 | int createNURBS(c_OCCVertex *start, c_OCCVertex *end, vector[c_OCCStruct3d] points, 118 | vector[double] knots, vector[double] weights, vector[int] mult) 119 | double length() 120 | 121 | cdef cppclass c_OCCEdgeIterator "OCCEdgeIterator": 122 | c_OCCEdgeIterator(c_OCCBase *arg) 123 | void reset() 124 | c_OCCEdge *next() 125 | 126 | cdef cppclass c_OCCWire "OCCWire": 127 | c_OCCWire() 128 | c_OCCWire *copy(bint deepCopy) 129 | int numVertices() 130 | int numEdges() 131 | bint isClosed() 132 | c_OCCTesselation *tesselate(double factor, double angle) 133 | int createWire(vector[c_OCCEdge *] edges) 134 | int project(c_OCCBase *face) 135 | int offset(double distance, int joinType) 136 | int fillet(vector[c_OCCVertex *] vertices, vector[double] radius) 137 | int chamfer(vector[c_OCCVertex *] vertices, vector[double] distances) 138 | double length() 139 | 140 | cdef cppclass c_OCCWireIterator "OCCWireIterator": 141 | c_OCCWireIterator(c_OCCBase *arg) 142 | void reset() 143 | c_OCCWire *next() 144 | 145 | cdef cppclass c_OCCFace "OCCFace": 146 | c_OCCFace() 147 | c_OCCFace *copy(bint deepCopy) 148 | int numWires() 149 | int numFaces() 150 | int createFace(vector[c_OCCWire *] wires) 151 | int createConstrained(vector[c_OCCEdge *] edges, vector[c_OCCStruct3d] points) 152 | double area() 153 | vector[double] inertia() 154 | c_OCCStruct3d centreOfMass() 155 | int offset(double offset, double tolerance) 156 | int createPolygonal(vector[c_OCCStruct3d] points) 157 | int extrude(c_OCCBase *shape, c_OCCStruct3d p1, c_OCCStruct3d p2) 158 | int revolve(c_OCCBase *shape, c_OCCStruct3d p1, c_OCCStruct3d p2, double angle) 159 | int sweep(c_OCCWire *spine, vector[c_OCCBase *] profiles, int cornerMode) 160 | int loft(vector[c_OCCBase *] profiles, bint ruled, double tolerance) 161 | int boolean(c_OCCSolid *tool, c_BoolOpType op) 162 | c_OCCMesh *createMesh(double factor, double angle, bint qualityNormals) 163 | 164 | cdef cppclass c_OCCFaceIterator "OCCFaceIterator": 165 | c_OCCFaceIterator(c_OCCBase *arg) 166 | void reset() 167 | c_OCCFace *next() 168 | 169 | cdef cppclass c_OCCSolid "OCCSolid": 170 | c_OCCSolid() 171 | c_OCCSolid *copy(bint deepCopy) 172 | int numSolids() 173 | int numFaces() 174 | int createSolid(vector[c_OCCFace *] faces, double tolerance) 175 | double area() 176 | double volume() 177 | vector[double] inertia() 178 | c_OCCStruct3d centreOfMass() 179 | c_OCCMesh *createMesh(double factor, double angle, bint qualityNormals) 180 | int addSolids(vector[c_OCCSolid *] solids) 181 | int createSphere(c_OCCStruct3d center, double radius) 182 | int createCylinder(c_OCCStruct3d p1, c_OCCStruct3d p2, double radius) 183 | int createTorus(c_OCCStruct3d p1, c_OCCStruct3d p2, double ringRadius, double radius) 184 | int createCone(c_OCCStruct3d p1, c_OCCStruct3d p2, double radius1, double radius2) 185 | int createBox(c_OCCStruct3d p1, c_OCCStruct3d p2) 186 | int createPrism(c_OCCFace *face, c_OCCStruct3d normal, bint isInfinite) 187 | int createText(double height, double depth, char *text, char *fontpath) 188 | int extrude(c_OCCFace *face, c_OCCStruct3d p1, c_OCCStruct3d p2) 189 | int revolve(c_OCCFace *face, c_OCCStruct3d p1, c_OCCStruct3d p2, double angle) 190 | int loft(vector[c_OCCBase *] profiles, bint ruled, double tolerance) 191 | int sweep(c_OCCWire *spine, vector[c_OCCBase *] profiles, int cornerMode) 192 | int pipe(c_OCCFace *face, c_OCCWire *wire) 193 | int boolean(c_OCCSolid *tool, c_BoolOpType op) 194 | int fillet(vector[c_OCCEdge *] edges, vector[double] radius) 195 | int chamfer(vector[c_OCCEdge *] edges, vector[double] distances) 196 | int shell(vector[c_OCCFace *] faces, double offset, double tolerance) 197 | int offset(c_OCCFace *face, double offset, double tolerance) 198 | c_OCCFace *section(c_OCCStruct3d pnt, c_OCCStruct3d nor) 199 | 200 | cdef cppclass c_OCCSolidIterator "OCCSolidIterator": 201 | c_OCCSolidIterator(c_OCCBase *arg) 202 | void reset() 203 | c_OCCSolid *next() 204 | 205 | cdef extern from "OCCModel.h" namespace "OCCTools": 206 | int writeBREP(char *filename, vector[c_OCCBase *] shapes) 207 | int writeSTEP(char *filename, vector[c_OCCBase *] shapes) 208 | int writeSTL(char *filename, vector[c_OCCBase *] shapes) 209 | int readBREP(char *filename, vector[c_OCCBase *] shapes) 210 | int readSTEP(char *filename, vector[c_OCCBase *] shapes) 211 | -------------------------------------------------------------------------------- /occmodel/@src/OCCBase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #include "OCCModel.h" 4 | 5 | int OCCBase::transform(DVec mat, OCCBase *target) 6 | { 7 | try { 8 | TopoDS_Shape shape = this->getShape(); 9 | 10 | if (shape.IsNull()) 11 | StdFail_NotDone::Raise("Null shape"); 12 | 13 | // Check if scaling is non-uniform 14 | double scaleTol = mat[0]*mat[5]*mat[10] - 1.0; 15 | if (scaleTol > Precision::Confusion()) { 16 | gp_GTrsf trans; 17 | int k = 0; 18 | for (int i = 1; i <= 3; i++) { 19 | for (int j = 1; j <= 4; j++) { 20 | trans.SetValue(i,j,mat[k]); 21 | k += 1; 22 | } 23 | } 24 | BRepBuilderAPI_GTransform aTrans(shape, trans, Standard_True); 25 | aTrans.Build(); 26 | aTrans.Check(); 27 | target->setShape(aTrans.Shape()); 28 | } else { 29 | gp_Trsf trans; 30 | trans.SetValues( 31 | mat[0], mat[1], mat[2], mat[3], 32 | mat[4], mat[5], mat[6], mat[7], 33 | mat[8], mat[9], mat[10], mat[11] 34 | ); 35 | BRepBuilderAPI_Transform aTrans(shape, trans, Standard_True); 36 | aTrans.Build(); 37 | aTrans.Check(); 38 | target->setShape(aTrans.Shape()); 39 | } 40 | } catch(Standard_Failure &err) { 41 | Handle_Standard_Failure e = Standard_Failure::Caught(); 42 | const Standard_CString msg = e->GetMessageString(); 43 | if (msg != NULL && strlen(msg) > 1) { 44 | setErrorMessage(msg); 45 | } else { 46 | setErrorMessage("Failed to transform object"); 47 | } 48 | return 0; 49 | } 50 | return 1; 51 | } 52 | 53 | int OCCBase::translate(OCCStruct3d delta, OCCBase *target) 54 | { 55 | try { 56 | TopoDS_Shape shape = this->getShape(); 57 | 58 | if (shape.IsNull()) 59 | StdFail_NotDone::Raise("Null shape"); 60 | 61 | gp_Trsf trans; 62 | trans.SetTranslation(gp_Pnt(0,0,0), gp_Pnt(delta.x,delta.y,delta.z)); 63 | BRepBuilderAPI_Transform aTrans(shape, trans, Standard_True); 64 | aTrans.Build(); 65 | aTrans.Check(); 66 | target->setShape(aTrans.Shape()); 67 | } catch(Standard_Failure &err) { 68 | Handle_Standard_Failure e = Standard_Failure::Caught(); 69 | const Standard_CString msg = e->GetMessageString(); 70 | if (msg != NULL && strlen(msg) > 1) { 71 | setErrorMessage(msg); 72 | } else { 73 | setErrorMessage("Failed to translate object"); 74 | } 75 | return 0; 76 | } 77 | return 1; 78 | } 79 | 80 | int OCCBase::rotate(double angle, OCCStruct3d p1, OCCStruct3d p2, OCCBase *target) 81 | { 82 | try { 83 | TopoDS_Shape shape = this->getShape(); 84 | 85 | if (shape.IsNull()) 86 | StdFail_NotDone::Raise("Null shape"); 87 | 88 | gp_Trsf trans; 89 | gp_Vec dir(gp_Pnt(p1.x, p1.y, p1.z), gp_Pnt(p2.x, p2.y, p2.z)); 90 | gp_Ax1 axis(gp_Pnt(p1.x, p1.y, p1.z), dir); 91 | trans.SetRotation(axis, angle); 92 | BRepBuilderAPI_Transform aTrans(shape, trans, Standard_True); 93 | aTrans.Build(); 94 | aTrans.Check(); 95 | target->setShape(aTrans.Shape()); 96 | } catch(Standard_Failure &err) { 97 | Handle_Standard_Failure e = Standard_Failure::Caught(); 98 | const Standard_CString msg = e->GetMessageString(); 99 | if (msg != NULL && strlen(msg) > 1) { 100 | setErrorMessage(msg); 101 | } else { 102 | setErrorMessage("Failed to rotate object"); 103 | } 104 | return 0; 105 | } 106 | return 1; 107 | } 108 | 109 | int OCCBase::scale(OCCStruct3d pnt, double scale, OCCBase *target) 110 | { 111 | try { 112 | TopoDS_Shape shape = this->getShape(); 113 | 114 | if (shape.IsNull()) 115 | StdFail_NotDone::Raise("Null shape"); 116 | 117 | gp_Trsf trans; 118 | trans.SetScale(gp_Pnt(pnt.x,pnt.y,pnt.z), scale); 119 | BRepBuilderAPI_Transform aTrans(shape, trans, Standard_True); 120 | aTrans.Build(); 121 | aTrans.Check(); 122 | target->setShape(aTrans.Shape()); 123 | } catch(Standard_Failure &err) { 124 | Handle_Standard_Failure e = Standard_Failure::Caught(); 125 | const Standard_CString msg = e->GetMessageString(); 126 | if (msg != NULL && strlen(msg) > 1) { 127 | setErrorMessage(msg); 128 | } else { 129 | setErrorMessage("Failed to scale object"); 130 | } 131 | return 0; 132 | } 133 | return 1; 134 | } 135 | 136 | int OCCBase::mirror(OCCStruct3d pnt, OCCStruct3d nor, OCCBase *target) 137 | { 138 | try { 139 | TopoDS_Shape shape = this->getShape(); 140 | 141 | if (shape.IsNull()) 142 | StdFail_NotDone::Raise("Null shape"); 143 | 144 | gp_Ax2 ax2(gp_Pnt(pnt.x,pnt.y,pnt.z), gp_Dir(nor.x,nor.y,nor.z)); 145 | gp_Trsf trans; 146 | trans.SetMirror(ax2); 147 | BRepBuilderAPI_Transform aTrans(shape, trans, Standard_False); 148 | target->setShape(aTrans.Shape()); 149 | } catch(Standard_Failure &err) { 150 | Handle_Standard_Failure e = Standard_Failure::Caught(); 151 | const Standard_CString msg = e->GetMessageString(); 152 | if (msg != NULL && strlen(msg) > 1) { 153 | setErrorMessage(msg); 154 | } else { 155 | setErrorMessage("Failed to mirror object"); 156 | } 157 | return 0; 158 | } 159 | return 1; 160 | } 161 | 162 | DVec OCCBase::boundingBox(double tolerance = 1e-12) 163 | { 164 | DVec ret; 165 | try { 166 | const TopoDS_Shape& shape = this->getShape(); 167 | Bnd_Box aBox; 168 | BRepBndLib::Add(shape, aBox); 169 | aBox.SetGap(tolerance); 170 | Standard_Real aXmin, aYmin, aZmin; 171 | Standard_Real aXmax, aYmax, aZmax; 172 | aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax); 173 | ret.push_back(aXmin); 174 | ret.push_back(aYmin); 175 | ret.push_back(aZmin); 176 | ret.push_back(aXmax); 177 | ret.push_back(aYmax); 178 | ret.push_back(aZmax); 179 | } catch(Standard_Failure &err) { 180 | ret.push_back(0.0); 181 | ret.push_back(0.0); 182 | ret.push_back(0.0); 183 | ret.push_back(0.0); 184 | ret.push_back(0.0); 185 | ret.push_back(0.0); 186 | } 187 | return ret; 188 | } 189 | 190 | int OCCBase::findPlane(OCCStruct3d *origin, OCCStruct3d *normal, double tolerance = 1e-6) 191 | { 192 | try { 193 | const TopoDS_Shape& shape = this->getShape(); 194 | BRepBuilderAPI_FindPlane FP(shape, tolerance); 195 | if(!FP.Found()) 196 | StdFail_NotDone::Raise("Plane not found"); 197 | const Handle_Geom_Plane plane = FP.Plane(); 198 | const gp_Ax1 axis = plane->Axis(); 199 | 200 | const gp_Pnt loc = axis.Location(); 201 | origin->x = loc.X(); 202 | origin->y = loc.Y(); 203 | origin->z = loc.Z(); 204 | 205 | const gp_Dir dir = axis.Direction(); 206 | normal->x = dir.X(); 207 | normal->y = dir.Y(); 208 | normal->z = dir.Z(); 209 | 210 | } catch(Standard_Failure &err) { 211 | Handle_Standard_Failure e = Standard_Failure::Caught(); 212 | const Standard_CString msg = e->GetMessageString(); 213 | if (msg != NULL && strlen(msg) > 1) { 214 | setErrorMessage(msg); 215 | } else { 216 | setErrorMessage("Failed to find plane"); 217 | } 218 | return 0; 219 | } 220 | return 1; 221 | } 222 | 223 | TopAbs_ShapeEnum OCCBase::shapeType() { 224 | return this->getShape().ShapeType(); 225 | } 226 | 227 | int OCCBase::hashCode() { 228 | return this->getShape().HashCode(std::numeric_limits::max()); 229 | } 230 | 231 | bool OCCBase::isEqual(OCCBase *other) { 232 | if (this->getShape().IsEqual(other->getShape())) 233 | return true; 234 | return false; 235 | } 236 | 237 | bool OCCBase::isNull() { 238 | return this->getShape().IsNull() ? true : false; 239 | } 240 | 241 | bool OCCBase::isValid() { 242 | if (this->getShape().IsNull()) 243 | return false; 244 | BRepCheck_Analyzer aChecker(this->getShape()); 245 | return aChecker.IsValid() ? true : false; 246 | } 247 | 248 | bool OCCBase::fixShape() { 249 | if (this->getShape().IsNull()) 250 | return false; 251 | 252 | BRepCheck_Analyzer aChecker(this->getShape()); 253 | if (!aChecker.IsValid()) { 254 | ShapeFix_ShapeTolerance aSFT; 255 | aSFT.LimitTolerance(this->getShape(),Precision::Confusion(),Precision::Confusion()); 256 | 257 | Handle(ShapeFix_Shape) aSfs = new ShapeFix_Shape(this->getShape()); 258 | aSfs->SetPrecision(Precision::Confusion()); 259 | aSfs->Perform(); 260 | 261 | const TopoDS_Shape aShape = aSfs->Shape(); 262 | aChecker.Init(aShape, Standard_False); 263 | 264 | if (aChecker.IsValid() && this->canSetShape(aShape)) { 265 | this->setShape(aShape); 266 | } 267 | } 268 | return aChecker.IsValid(); 269 | } 270 | 271 | int OCCBase::toString(std::string *output) { 272 | std::stringstream str; 273 | OCCTools::writeBREP(str, this->getShape()); 274 | output->assign(str.str()); 275 | return 0; 276 | } 277 | 278 | int OCCBase::fromString(std::string input) { 279 | std::stringstream str(input); 280 | TopoDS_Shape shape = TopoDS_Shape(); 281 | 282 | int ret = OCCTools::readBREP(str, shape); 283 | if (ret) { 284 | if (this->canSetShape(shape)) 285 | this->setShape(shape); 286 | } 287 | return ret; 288 | } -------------------------------------------------------------------------------- /occmodel/@src/OCCTools.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #include "OCCModel.h" 4 | 5 | void printShapeType(const TopoDS_Shape& shape) 6 | { 7 | if (!shape.IsNull()) { 8 | TopAbs_ShapeEnum type = shape.ShapeType(); 9 | switch (type) 10 | { 11 | case TopAbs_COMPOUND: 12 | printf("TopAbs_COMPOUND\n"); 13 | break; 14 | case TopAbs_COMPSOLID: 15 | printf("TopAbs_COMPSOLID\n"); 16 | break; 17 | case TopAbs_SOLID: 18 | printf("TopAbs_SOLID\n"); 19 | break; 20 | case TopAbs_SHELL: 21 | printf("TopAbs_SHELL\n"); 22 | break; 23 | case TopAbs_FACE: 24 | printf("TopAbs_FACE\n"); 25 | break; 26 | case TopAbs_WIRE: 27 | printf("TopAbs_WIRE\n"); 28 | break; 29 | case TopAbs_EDGE: 30 | printf("TopAbs_EDGE\n"); 31 | break; 32 | case TopAbs_VERTEX: 33 | printf("TopAbs_VERTEX\n"); 34 | break; 35 | default: 36 | printf("Unknown\n"); 37 | break; 38 | } 39 | } 40 | else { 41 | printf("Empty shape\n"); 42 | } 43 | } 44 | 45 | int extractSubShape(const TopoDS_Shape& shape, std::vector& shapes) 46 | { 47 | TopAbs_ShapeEnum type = shape.ShapeType(); 48 | switch (type) 49 | { 50 | case TopAbs_COMPOUND: 51 | return 0; 52 | case TopAbs_COMPSOLID: 53 | case TopAbs_SOLID: 54 | { 55 | OCCSolid *ret = new OCCSolid(); 56 | ret->setShape(shape); 57 | if (!ret->fixShape()) { 58 | delete ret; 59 | return 0; 60 | } 61 | shapes.push_back((OCCBase *)ret); 62 | break; 63 | } 64 | case TopAbs_FACE: 65 | case TopAbs_SHELL: 66 | { 67 | OCCFace *ret = new OCCFace(); 68 | ret->setShape(shape); 69 | if (!ret->fixShape()) { 70 | delete ret; 71 | return 0; 72 | } 73 | shapes.push_back((OCCBase *)ret); 74 | break; 75 | } 76 | case TopAbs_WIRE: 77 | { 78 | OCCWire *ret = new OCCWire(); 79 | ret->setShape(shape); 80 | if (!ret->fixShape()) { 81 | delete ret; 82 | return 0; 83 | } 84 | shapes.push_back((OCCBase *)ret); 85 | break; 86 | } 87 | case TopAbs_EDGE: 88 | { 89 | OCCEdge *ret = new OCCEdge(); 90 | ret->setShape(shape); 91 | if (!ret->fixShape()) { 92 | delete ret; 93 | return 0; 94 | } 95 | shapes.push_back((OCCBase *)ret); 96 | break; 97 | } 98 | case TopAbs_VERTEX: 99 | { 100 | OCCVertex *ret = new OCCVertex(); 101 | ret->setShape(shape); 102 | if (!ret->fixShape()) { 103 | delete ret; 104 | return 0; 105 | } 106 | shapes.push_back((OCCBase *)ret); 107 | break; 108 | } 109 | default: 110 | return 0; 111 | } 112 | return 1; 113 | } 114 | 115 | int extractShape(const TopoDS_Shape& shape, std::vector& shapes) 116 | { 117 | TopAbs_ShapeEnum type = shape.ShapeType(); 118 | 119 | if (type != TopAbs_COMPOUND) { 120 | extractSubShape(shape, shapes); 121 | return 0; 122 | } 123 | 124 | TopExp_Explorer ex; 125 | int ret = 0; 126 | 127 | // extract compund 128 | for (ex.Init(shape, TopAbs_COMPOUND); ex.More(); ex.Next()) 129 | ret += extractSubShape(ex.Current(), shapes); 130 | 131 | // extract solids 132 | for (ex.Init(shape, TopAbs_COMPSOLID); ex.More(); ex.Next()) 133 | ret += extractSubShape(ex.Current(), shapes); 134 | for (ex.Init(shape, TopAbs_SOLID); ex.More(); ex.Next()) 135 | ret += extractSubShape(ex.Current(), shapes); 136 | 137 | // extract free faces 138 | for (ex.Init(shape, TopAbs_SHELL, TopAbs_SOLID); ex.More(); ex.Next()) 139 | ret += extractSubShape(ex.Current(), shapes); 140 | for (ex.Init(shape, TopAbs_FACE, TopAbs_SOLID); ex.More(); ex.Next()) 141 | ret += extractSubShape(ex.Current(), shapes); 142 | 143 | // extract free wires 144 | for (ex.Init(shape, TopAbs_WIRE, TopAbs_FACE); ex.More(); ex.Next()) 145 | ret += extractSubShape(ex.Current(), shapes); 146 | 147 | // extract free edges 148 | for (ex.Init(shape, TopAbs_EDGE, TopAbs_WIRE); ex.More(); ex.Next()) 149 | ret += extractSubShape(ex.Current(), shapes); 150 | 151 | // extract free vertices 152 | for (ex.Init(shape, TopAbs_VERTEX, TopAbs_EDGE); ex.More(); ex.Next()) 153 | ret += extractSubShape(ex.Current(), shapes); 154 | 155 | return ret; 156 | } 157 | 158 | int OCCTools::writeBREP(const char *filename, std::vector shapes) 159 | { 160 | try { 161 | BRep_Builder B; 162 | TopoDS_Compound C; 163 | B.MakeCompound(C); 164 | for (unsigned i = 0; i < shapes.size(); i++) { 165 | B.Add(C, shapes[i]->getShape()); 166 | } 167 | BRepTools::Write(C, filename); 168 | } catch(Standard_Failure &err) { 169 | Handle_Standard_Failure e = Standard_Failure::Caught(); 170 | const Standard_CString msg = e->GetMessageString(); 171 | //printf("ERROR: %s\n", e->GetMessageString()); 172 | if (msg != NULL && strlen(msg) > 1) { 173 | setErrorMessage(msg); 174 | } else { 175 | setErrorMessage("Failed to write BREP file"); 176 | } 177 | return 0; 178 | } 179 | return 1; 180 | } 181 | 182 | int OCCTools::writeBREP(std::ostream& str, const TopoDS_Shape& shape) 183 | { 184 | BRepTools::Write(shape, str); 185 | return 1; 186 | } 187 | 188 | int OCCTools::writeSTEP(const char *filename, std::vector shapes) 189 | { 190 | try { 191 | STEPControl_Writer writer; 192 | IFSelect_ReturnStatus status; 193 | 194 | Interface_Static::SetCVal("xstep.cascade.unit","M"); 195 | Interface_Static::SetIVal("read.step.nonmanifold", 1); 196 | 197 | for (unsigned i = 0; i < shapes.size(); i++) { 198 | status = writer.Transfer(shapes[i]->getShape(), STEPControl_AsIs); 199 | if (status != IFSelect_RetDone) { 200 | StdFail_NotDone::Raise("Failed to write STEP file"); 201 | } 202 | } 203 | status = writer.Write(filename); 204 | if (status != IFSelect_RetDone) { 205 | StdFail_NotDone::Raise("Failed to write STEP file"); 206 | } 207 | } catch(Standard_Failure &err) { 208 | Handle_Standard_Failure e = Standard_Failure::Caught(); 209 | const Standard_CString msg = e->GetMessageString(); 210 | //printf("ERROR: %s\n", e->GetMessageString()); 211 | if (msg != NULL && strlen(msg) > 1) { 212 | setErrorMessage(msg); 213 | } else { 214 | setErrorMessage("Failed to write STEP file"); 215 | } 216 | return 0; 217 | } 218 | return 1; 219 | } 220 | 221 | int OCCTools::writeSTL(const char *filename, std::vector shapes) 222 | { 223 | try { 224 | BRep_Builder B; 225 | TopoDS_Compound shape; 226 | B.MakeCompound(shape); 227 | 228 | for (unsigned i = 0; i < shapes.size(); i++) { 229 | B.Add(shape, shapes[i]->getShape()); 230 | } 231 | StlAPI_Writer writer; 232 | writer.Write(shape, filename); 233 | } catch(Standard_Failure &err) { 234 | Handle_Standard_Failure e = Standard_Failure::Caught(); 235 | const Standard_CString msg = e->GetMessageString(); 236 | //printf("ERROR: %s\n", e->GetMessageString()); 237 | if (msg != NULL && strlen(msg) > 1) { 238 | setErrorMessage(msg); 239 | } else { 240 | setErrorMessage("Failed to write STL file"); 241 | } 242 | return 0; 243 | } 244 | return 1; 245 | } 246 | 247 | int OCCTools::readBREP(const char *filename, std::vector& shapes) 248 | { 249 | try { 250 | // read brep-file 251 | TopoDS_Shape shape; 252 | BRep_Builder aBuilder; 253 | if (!BRepTools::Read(shape, filename, aBuilder)) { 254 | StdFail_NotDone::Raise("Failed to read BFREP file"); 255 | } 256 | extractShape(shape, shapes); 257 | } catch (Standard_Failure) { 258 | Handle_Standard_Failure e = Standard_Failure::Caught(); 259 | const Standard_CString msg = e->GetMessageString(); 260 | //printf("ERROR: %s\n", e->GetMessageString()); 261 | if (msg != NULL && strlen(msg) > 1) { 262 | setErrorMessage(msg); 263 | } else { 264 | setErrorMessage("Failed to read BREP file"); 265 | } 266 | return 0; 267 | } 268 | return 1; 269 | } 270 | 271 | int OCCTools::readBREP(std::istream& str, TopoDS_Shape& shape) 272 | { 273 | try { 274 | // read brep-file 275 | BRep_Builder aBuilder; 276 | BRepTools::Read(shape, str, aBuilder); 277 | } catch (Standard_Failure) { 278 | Handle_Standard_Failure e = Standard_Failure::Caught(); 279 | const Standard_CString msg = e->GetMessageString(); 280 | //printf("ERROR: %s\n", e->GetMessageString()); 281 | if (msg != NULL && strlen(msg) > 1) { 282 | setErrorMessage(msg); 283 | } else { 284 | setErrorMessage("Failed to read BREP file"); 285 | } 286 | return 0; 287 | } 288 | return 1; 289 | } 290 | 291 | int OCCTools::readSTEP(const char *filename, std::vector& shapes) 292 | { 293 | try { 294 | STEPControl_Reader aReader; 295 | 296 | Interface_Static::SetCVal("xstep.cascade.unit","M"); 297 | Interface_Static::SetIVal("read.step.nonmanifold", 1); 298 | 299 | if (aReader.ReadFile(filename) != IFSelect_RetDone) { 300 | StdFail_NotDone::Raise("Failed to read STEP file"); 301 | } 302 | 303 | // Root transfers 304 | int nbr = aReader.NbRootsForTransfer(); 305 | for (int n = 1; n<= nbr; n++) { 306 | aReader.TransferRoot(n); 307 | } 308 | 309 | // Collecting resulting entities 310 | int nbs = aReader.NbShapes(); 311 | if (nbs == 0) return 1; 312 | 313 | for (int i=1; i<=nbs; i++) { 314 | const TopoDS_Shape& aShape = aReader.Shape(i); 315 | extractShape(aShape, shapes); 316 | } 317 | } catch(Standard_Failure &err) { 318 | Handle_Standard_Failure e = Standard_Failure::Caught(); 319 | const Standard_CString msg = e->GetMessageString(); 320 | //printf("ERROR: %s\n", e->GetMessageString()); 321 | if (msg != NULL && strlen(msg) > 1) { 322 | setErrorMessage(msg); 323 | } else { 324 | setErrorMessage("Failed to read STEP file"); 325 | } 326 | return 0; 327 | } 328 | return 1; 329 | } 330 | -------------------------------------------------------------------------------- /occmodel/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | # This file is part of occmodel - See LICENSE.txt 5 | # 6 | from math import pi, sin, cos, copysign 7 | 8 | from geotools import * 9 | from occmodel import * 10 | 11 | ''' 12 | pnts = ((0.,0.,0.), (0.,2.,0.), (1.,2.,0.), (1.,0.,0.)) 13 | f1 = Face().createPolygonal(pnts) 14 | print(f1) 15 | print(f1.area()) 16 | ''' 17 | 18 | ''' 19 | start = Vertex(0.,0.,0.) 20 | end = Vertex(1.,0.,0.) 21 | pnts = ((0.,2.,0.), (1.,1.5,0.)) 22 | b1 = Edge().createBezier(start,end,pnts) 23 | print(b1) 24 | print(b1.length()) 25 | 26 | start = None 27 | end = None 28 | pnts = ((0.,0.,0.), (0.,2.,0.), (0.5,1.,0.), (1.,-1.,0.)) 29 | b1 = Edge().createBezier(start,end,pnts) 30 | print(b1) 31 | print(b1.length()) 32 | ''' 33 | 34 | ''' 35 | start = Vertex(0.,0.,0.) 36 | end = Vertex(1.,0.,0.) 37 | pnts = ((0.,2.,0.), (5.,1.5,0.)) 38 | s1 = Edge().createSpline(start,end,pnts) 39 | print(s1) 40 | print(s1.length()) 41 | ''' 42 | 43 | ''' 44 | start = None 45 | end = None 46 | pnts = ((0.,0.,0.),(0.,2.,0.), (5.,1.5,0.),(0.,0.,0.)) 47 | e1 = Edge().createSpline(start,end,pnts) 48 | print(e1) 49 | print(e1.length()) 50 | 51 | face = Face().createFace(e1) 52 | print(face) 53 | print(face.area()) 54 | 55 | solid = Solid().extrude(face, (0.,0.,0.), (0.,0.,5.)) 56 | print(solid) 57 | print('area = ', solid.area()) 58 | print('volume = ', solid.volume()) 59 | ''' 60 | 61 | ''' 62 | start = Vertex(1.,0.,0.) 63 | end = Vertex(-1.,0.,0.) 64 | e1 = Edge().createLine(end,start) 65 | print(e1) 66 | print(e1.length()) 67 | 68 | pnt = (0.,1.,0.) 69 | e2 = Edge().createArc3P(start,end,pnt) 70 | print(e2) 71 | print(e2.length()) 72 | 73 | face = Face().createConstrained((e1,e2), ((0.,.5,.5),)) 74 | print(face) 75 | print(face.area()) 76 | 77 | solid = Solid().extrude(face, (0.,0.,0.), (0.,0.,5.)) 78 | print(solid) 79 | print('area = ', solid.area()) 80 | print('volume = ', solid.volume()) 81 | #viewer((face, e1), ('red', 'green')) 82 | ''' 83 | 84 | ''' 85 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 86 | e2 = Edge().createCircle(center=(0.,0.,5.),normal=(0.,0.,1.),radius = 1.5) 87 | e3 = Edge().createCircle(center=(0.,0.,10.),normal=(0.,0.,1.),radius = 1.0) 88 | solid = Solid().loft((e1,e2,e3), True) 89 | print(solid.volume()) 90 | #solid.writeSTEP('test.stp') 91 | #viewer(solid) 92 | ''' 93 | 94 | ''' 95 | solid = Solid() 96 | solid.readSTEP('test.stp') 97 | solid.heal() 98 | viewer(solid) 99 | ''' 100 | 101 | ''' 102 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 103 | face = Face().createFace(e1) 104 | print(face) 105 | print(face.area()) 106 | print(face.inertia()) 107 | 108 | solid = Solid().extrude(face, (0.,0.,0.), (0.,0.,1.)) 109 | print(solid) 110 | print('area = ', solid.area()) 111 | print('volume = ', solid.volume()) 112 | ''' 113 | 114 | ''' 115 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 116 | face = Face().createFace(e1) 117 | print(face) 118 | print(face.area()) 119 | 120 | solid = Solid().revolve(face, (0.,2.,0.), (1.,2.,0.), 90.) 121 | print(solid) 122 | print('area = ', solid.area()) 123 | print('volume = ', solid.volume()) 124 | ''' 125 | 126 | ''' 127 | solid = Solid() 128 | solid.createSphere((1.,2.,3.),.5) 129 | 130 | plane = Plane.fromNormal((1.,2.,3.), (0.,1.,1.)) 131 | sec = solid.section(plane) 132 | print('area = ', sec.area()) 133 | #viewer(sec) 134 | ''' 135 | 136 | ''' 137 | s1 = Solid().createSphere((0.,0.,0.),.5) 138 | s2 = Solid().createSphere((.25,0.,0.),.5) 139 | 140 | #s1.fuse(s2) 141 | #s1.cut(s2) 142 | s1.common(s2) 143 | #s1.writeSTEP('test.stp') 144 | print(s1.volume()) 145 | ''' 146 | 147 | ''' 148 | s1 = Solid().createSphere((0.,0.,0.),.5) 149 | print(s1.centreOfMass()) 150 | s1.translate((1.,0.,0.)) 151 | print(s1.centreOfMass()) 152 | ''' 153 | 154 | ''' 155 | s1 = Solid().createSphere((0.,0.,0.),.5) 156 | print(s1.volume()) 157 | s2 = Solid().createSphere((2.,0.,0.),.5) 158 | print(s2.volume()) 159 | s3 = Solid().addSolids((s1,s2)) 160 | print(s3.volume()) 161 | ''' 162 | 163 | ''' 164 | sp1 = Solid().createSphere((0.,0.,0.),.5) 165 | print(sp1.volume()) 166 | sp2 = sp1.copy() 167 | print(sp1.volume()) 168 | sp2.translate((.5, 0., 0.)) 169 | #sp2.scale((.5, 0., 0.), 1.25) 170 | sp2.rotate(10.,(0.,-1.,0.),(0.,1.,0.)) 171 | sp1.cut(sp2) 172 | ''' 173 | 174 | ''' 175 | sp1 = Solid().createCylinder((0.,0.,0.),(0.,0.,1.), 1.) 176 | print(sp1.volume()) 177 | ''' 178 | 179 | ''' 180 | sp1 = Solid().createTorus((0.,0.,0.),(0.,0.,1.), 1., 2.) 181 | print(sp1.volume()) 182 | ''' 183 | 184 | ''' 185 | c1 = Solid().createCone((0.,0.,0.),(0.,0.,1.), 1., 2.) 186 | print(c1.volume()) 187 | ''' 188 | 189 | ''' 190 | b1 = Solid().createBox((0.,0.,0.),(1.,1.,1.)) 191 | print(b1.volume()) 192 | ''' 193 | 194 | ''' 195 | b1 = Solid().createBox((0.,0.,0.),(1.,1.,1.)) 196 | print(b1.volume()) 197 | b1.fillet(.25, lambda start,end: start[2] > .5 and end[2] > .5) 198 | print(b1.volume()) 199 | ''' 200 | 201 | ''' 202 | start = Vertex(0.,0.,0.) 203 | end = Vertex(1.,0.,1.) 204 | cen = (1.,0.,0.) 205 | e1 = Edge().createArc(start,end,cen) 206 | 207 | e2 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 208 | face = Face().createFace(e2) 209 | 210 | s1 = Solid().pipe(face, e1) 211 | print(s1.volume()) 212 | ''' 213 | 214 | ''' 215 | p1 = Vertex(0.,0.,0.) 216 | p2 = Vertex(1.,0.,0.) 217 | p3 = Vertex(1.,1.,0.) 218 | p4 = Vertex(0.,1.,0.) 219 | e1 = Edge().createLine(p1,p2) 220 | e2 = Edge().createLine(p2,p3) 221 | e3 = Edge().createLine(p3,p4) 222 | e4 = Edge().createLine(p4,p1) 223 | 224 | face = Face().createFace((e1,e2,e3,e4)) 225 | print(face.centreOfMass()) 226 | mesh = face.createMesh(0.1,.5) 227 | print(mesh) 228 | ''' 229 | 230 | ''' 231 | #solid = Solid().createSphere((0.,0.,0.),.5) 232 | solid = Solid().createBox((0.,0.,0.),(1.,1.,1.)) 233 | mesh = solid.createMesh(0.1,.5) 234 | print(mesh) 235 | print(mesh.vertex(0)) 236 | print(mesh.normal(0)) 237 | print(mesh.triangle(0)) 238 | ''' 239 | 240 | ''' 241 | start = Vertex(1.,0.,0.) 242 | end = Vertex(-1.,0.,0.) 243 | pnt = (0.,1.,0.) 244 | e1 = Edge().createArc3P(start,end,pnt) 245 | print(e1.boundingBox()) 246 | pnts = e1.tesselate() 247 | print(pnts) 248 | ''' 249 | 250 | ''' 251 | e1 = Edge().createHelix(.5, 2., 1.0, 0.) 252 | print(e1.length()) 253 | print(e1.start) 254 | print(e1.end) 255 | ''' 256 | 257 | ''' 258 | p1 = Vertex(0.,0.,0.) 259 | p2 = Vertex(1.,0.,0.) 260 | p3 = Vertex(1.,1.,0.) 261 | p4 = Vertex(0.,1.,0.) 262 | e1 = Edge().createLine(p1,p2) 263 | e2 = Edge().createLine(p2,p3) 264 | e3 = Edge().createLine(p3,p4) 265 | e4 = Edge().createLine(p4,p1) 266 | w1 = Wire().createWire((e1,e2,e3,e4)) 267 | print(w1.length()) 268 | f1 = Face().createFace(w1) 269 | print(f1.area()) 270 | ''' 271 | 272 | ''' 273 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 274 | e2 = Edge().createEllipse(center=(0.,0.,5.),normal=(0.,0.,1.), rMajor = 2.0, rMinor=1.0) 275 | e3 = Edge().createCircle(center=(0.,0.,10.),normal=(0.,0.,1.),radius = 1.0) 276 | solid = Solid().loft((e1,e2,e3), False) 277 | print(solid.volume()) 278 | ''' 279 | 280 | ''' 281 | rect = Wire().createRectangle(height = 2., radius = .5) 282 | print(rect.length()) 283 | ''' 284 | 285 | ''' 286 | w1 = Wire().createRegularPolygon() 287 | print(w1.length()) 288 | f1 = Face().createFace(w1) 289 | print(f1.area()) 290 | ''' 291 | 292 | ''' 293 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 294 | plane = Plane() 295 | print(e1.hasPlane(plane = plane)) 296 | print(plane) 297 | ''' 298 | 299 | ''' 300 | solid = Solid().createBox((0.,0.,0.),(1.,1.,1.)) 301 | e1 = Edge().createCircle(center=(0.5,0.,.5),normal=(0.,0.,1.),radius = 0.25) 302 | e2 = Edge().createCircle(center=(0.5,0.5,1.),normal=(0.,0.,1.),radius = 0.1) 303 | w1 = Wire().createWire(e2) 304 | e3 = Edge().createCircle(center=(1.0,0.5,1.),normal=(0.,0.,1.),radius = 0.25) 305 | f1 = Face().createFace(e3) 306 | solid.cut((e1,w1,f1)) 307 | ''' 308 | 309 | ''' 310 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 311 | e2 = Edge().createCircle(center=(0.,0.,.5),normal=(0.,0.,1.),radius = 1.5) 312 | v1 = Vertex(0.,0.,1.) 313 | solid = Solid().loft((e1,e2,v1)) 314 | print(solid.volume()) 315 | ''' 316 | 317 | ''' 318 | w1 = Wire().createPolygon(( 319 | (0.,0.,0.), 320 | (0.,0.,5.), 321 | (5.,0.,5.)), 322 | close = False 323 | ) 324 | 325 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 326 | solid = Solid().sweep(w1, e1, cornerMode = SWEEP_RIGHT_CORNER) 327 | print(solid.volume()) 328 | ''' 329 | 330 | ''' 331 | w1 = Wire().createPolygon(( 332 | (0.,0.,0.), 333 | (1.,0.,0.), 334 | (.5,1.,0.)), 335 | close = True 336 | ) 337 | f1 = Face().createFace(w1) 338 | e1 = Edge().createCircle(center=(.5,.5,-1.),normal=(0.,0.,1.),radius = .15) 339 | f1.booleanIntersection(e1) 340 | #f1.booleanDifference(e1) 341 | solid = Solid().extrude(f1, (0.,0.,0.), (0.,0.,1.)) 342 | solid.createMesh() 343 | ''' 344 | ''' 345 | w1 = Wire().createPolygon(( 346 | (0.,0.,0.), 347 | (0.,0.,5.), 348 | (5.,0.,5.)), 349 | close = False 350 | ) 351 | 352 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 353 | solid = Solid().sweep(w1, e1, cornerMode = SWEEP_RIGHT_CORNER) 354 | print(solid.volume()) 355 | ''' 356 | 357 | ''' 358 | start = Vertex(0.,0.,0.) 359 | end = Vertex(1.,0.,1.) 360 | cen = (1.,0.,0.) 361 | e1 = Edge().createArc(start,end,cen) 362 | 363 | e2 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 364 | face = Face().createFace(e2) 365 | e3 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = .8) 366 | face.cut(e3) 367 | 368 | s1 = Solid().pipe(face, e1) 369 | print(s1.volume()) 370 | ''' 371 | 372 | ''' 373 | start = Vertex(0.,0.,0.) 374 | end = Vertex(1.,0.,1.) 375 | cen = (1.,0.,0.) 376 | e1 = Edge().createArc(start,end,cen) 377 | 378 | start = Vertex(0.,1.,0.) 379 | end = Vertex(2.,1.,2.) 380 | cen = (2.,1.,0.) 381 | e2 = Edge().createArc(start,end,cen) 382 | 383 | face = Face().loft((e1,e2)) 384 | print(face.isValid()) 385 | ''' 386 | 387 | ''' 388 | start = Vertex(0.,0.,0.) 389 | end = Vertex(1.,0.,1.) 390 | cen = (1.,0.,0.) 391 | e1 = Edge().createArc(start,end,cen) 392 | 393 | e2 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 394 | 395 | face = Face().sweep(e2, e1) 396 | print(face.isValid()) 397 | ''' 398 | 399 | ''' 400 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = 1.) 401 | e2 = Edge().createCircle(center=(-1.,0.,0.),normal=(0.,0.,1.),radius = .5) 402 | e3 = Edge().createCircle(center=(1.,0.,0.),normal=(0.,0.,1.),radius = .5) 403 | w1 = Wire().createWire(e1) 404 | print(w1.length()) 405 | w1.cut((e2,e3)) 406 | print(w1.length()) 407 | ''' 408 | 409 | ''' 410 | face = Face().createPolygonal(((0.,0.,0.),(1.,1.,0.),(1.,1.,1.),(0.,0.,1.))) 411 | e1 = Edge().createCircle(center=(.5,0.2,.5),normal=(0.,1.,0.),radius = .25) 412 | w1 = Wire().createWire(e1) 413 | w1.project(face) 414 | print(w1.isValid()) 415 | ''' 416 | 417 | ''' 418 | p1 = Vertex(0.,0.,0.) 419 | p2 = Vertex(1.,0.,0.) 420 | p3 = Vertex(1.,1.,0.) 421 | e1 = Edge().createLine(p1,p2) 422 | e2 = Edge().createLine(p2,p3) 423 | w1 = Wire().createWire((e1,e2)) 424 | #face = Face().extrude(w1, (0.,0.,0.),(0.,0.,1.)) 425 | face = Face().revolve(w1, (0.,0.,0.),(0.,1.,0.), 90.) 426 | print(face.isValid()) 427 | #print(face.typeName()) 428 | #Tools.writeSTEP('test.stp', face) 429 | ''' 430 | #solid = Solid() 431 | #solid.createSphere((1.,2.,3.),.5) 432 | #print(solid.shapeType()) 433 | #Tools.writeSTEP('test.stp', solid) 434 | #Tools.writeBREP('test.brp', solid) 435 | #Tools.writeSTL('test.stl', solid) 436 | #print(Tools.readSTEP('test.stp')) -------------------------------------------------------------------------------- /occmodel/@src/OCCBase.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | 6 | JOINTYPE_ARC = 0 7 | JOINTYPE_TANGENT = 1 8 | JOINTYPE_INTERSECTION = 2 9 | 10 | cdef class Base: 11 | ''' 12 | Definition of virtual base object 13 | ''' 14 | cdef void *thisptr 15 | 16 | def __str__(self): 17 | return 'Base%s' % self.__repr__() 18 | 19 | def __repr__(self): 20 | return '()' 21 | 22 | def __hash__(self): 23 | return self.hashCode() 24 | 25 | def __richcmp__(self, other, int op): 26 | if not isinstance(other, Base): 27 | if op == 2: 28 | return False 29 | elif op == 3: 30 | return True 31 | else: 32 | raise TypeError('operation not supported') 33 | else: 34 | if op == 2: 35 | return self.hashCode() == other.hashCode() 36 | elif op == 3: 37 | return self.hashCode() != other.hashCode() 38 | else: 39 | raise TypeError('operation not supported') 40 | 41 | cdef CheckPtr(self): 42 | if self.thisptr == NULL: 43 | raise TypeError('Base object not initialized') 44 | 45 | cpdef shapeType(self): 46 | ''' 47 | Return class type or None if shape not known. 48 | ''' 49 | self.CheckPtr() 50 | 51 | if self.isNull(): 52 | raise OCCError('No type defined for Null shape') 53 | 54 | cdef c_OCCBase *occ = self.thisptr 55 | cdef c_TopAbs_ShapeEnum shapetype = occ.shapeType() 56 | 57 | if shapetype == TopAbs_COMPSOLID or \ 58 | shapetype == TopAbs_SOLID: 59 | return Solid 60 | elif shapetype == TopAbs_SHELL or \ 61 | shapetype == TopAbs_FACE: 62 | return Face 63 | elif shapetype == TopAbs_WIRE: 64 | return Wire 65 | elif shapetype == TopAbs_EDGE: 66 | return Edge 67 | elif shapetype == TopAbs_VERTEX: 68 | return Vertex 69 | else: 70 | return None 71 | 72 | cpdef int hashCode(self): 73 | ''' 74 | Shape hash code. 75 | 76 | Orientation is not included in the hash calculation. Instances of 77 | the same object therfore return the same hash code. 78 | ''' 79 | self.CheckPtr() 80 | 81 | cdef c_OCCBase *occ = self.thisptr 82 | return occ.hashCode() 83 | 84 | cpdef bint isEqual(self, Base other): 85 | ''' 86 | Check object for equallity. Returns True only if both the 87 | underlying geometry and location is similar. 88 | ''' 89 | self.CheckPtr() 90 | 91 | cdef c_OCCBase *occ = self.thisptr 92 | return occ.isEqual(other.thisptr) 93 | 94 | cpdef bint isNull(self): 95 | ''' 96 | Check if object is Null. 97 | ''' 98 | self.CheckPtr() 99 | cdef c_OCCBase *occ = self.thisptr 100 | return occ.isNull() 101 | 102 | cpdef bint isValid(self): 103 | ''' 104 | Return if object is valid. 105 | ''' 106 | self.CheckPtr() 107 | cdef c_OCCBase *occ = self.thisptr 108 | return occ.isValid() 109 | 110 | cpdef bint hasPlane(self, Point origin = None, Vector normal = None, double tolerance = 1e-12): 111 | ''' 112 | Check if object has plane defined. Optional pass origin and normal 113 | argument to fetch the plane definition. 114 | 115 | :param origin: Plane origin 116 | :param normal: Plane normal 117 | :param tolerance: Plane tolerance 118 | ''' 119 | self.CheckPtr() 120 | 121 | cdef c_OCCBase *occ = self.thisptr 122 | cdef c_OCCStruct3d corigin, cnormal 123 | 124 | if occ.findPlane(&corigin, &cnormal, tolerance) == 0: 125 | return False 126 | 127 | if not normal is None: 128 | normal.set(cnormal.x, cnormal.y, cnormal.z) 129 | 130 | if not origin is None: 131 | origin.set(corigin.x, corigin.y, corigin.z) 132 | 133 | return True 134 | 135 | cpdef AABBox boundingBox(self, double tolerance = 1e-12): 136 | ''' 137 | Return bounding box 138 | 139 | :param tolerance: Tolerance of calculation. 140 | ''' 141 | self.CheckPtr() 142 | 143 | cdef c_OCCBase *occ = self.thisptr 144 | cdef vector[double] bbox = occ.boundingBox(tolerance) 145 | cdef AABBox ret = AABBox.__new__(AABBox, None) 146 | 147 | ret.min = Point(bbox[0], bbox[1], bbox[2]) 148 | ret.max = Point(bbox[3], bbox[4], bbox[5]) 149 | 150 | return ret 151 | 152 | cpdef transform(self, Transform mat, bint copy = False): 153 | ''' 154 | Apply transformation matrix to object. 155 | 156 | :param mat: Transformation matrix 157 | :param copy: If True the object is translated in place otherwise a 158 | new translated object is returned. 159 | ''' 160 | self.CheckPtr() 161 | 162 | cdef c_OCCBase *occ = self.thisptr 163 | cdef Base target 164 | cdef vector[double] cmat 165 | cdef int ret 166 | 167 | if copy: 168 | target = self.__class__() 169 | else: 170 | target = self 171 | 172 | cmat.push_back(mat.m[0][0]) 173 | cmat.push_back(mat.m[0][1]) 174 | cmat.push_back(mat.m[0][2]) 175 | cmat.push_back(mat.m[0][3]) 176 | 177 | cmat.push_back(mat.m[1][0]) 178 | cmat.push_back(mat.m[1][1]) 179 | cmat.push_back(mat.m[1][2]) 180 | cmat.push_back(mat.m[1][3]) 181 | 182 | cmat.push_back(mat.m[2][0]) 183 | cmat.push_back(mat.m[2][1]) 184 | cmat.push_back(mat.m[2][2]) 185 | cmat.push_back(mat.m[2][3]) 186 | 187 | ret = occ.transform(cmat, target.thisptr) 188 | if not ret: 189 | raise OCCError(errorMessage) 190 | 191 | return target 192 | 193 | cpdef translate(self, delta, bint copy = False): 194 | ''' 195 | Translate object. 196 | 197 | :param delta: translation vector (dx,dy,dz) 198 | :param copy: If True the object is translated in place otherwise a 199 | new translated object is returned. 200 | ''' 201 | self.CheckPtr() 202 | 203 | cdef c_OCCBase *occ = self.thisptr 204 | cdef Base target 205 | cdef c_OCCStruct3d cdelta 206 | cdef int ret 207 | 208 | if copy: 209 | target = self.__class__() 210 | else: 211 | target = self 212 | 213 | cdelta.x = delta[0] 214 | cdelta.y = delta[1] 215 | cdelta.z = delta[2] 216 | 217 | ret = occ.translate(cdelta, target.thisptr) 218 | if not ret: 219 | raise OCCError(errorMessage) 220 | 221 | return target 222 | 223 | cpdef rotate(self, double angle, axis, center = (0.,0.,0.), bint copy = False): 224 | ''' 225 | Rotate object. 226 | 227 | :param angle: rotation angle in radians 228 | :param axis: axis vector 229 | :param center: rotation center 230 | :param copy: If True the object is transformed in place otherwise a 231 | new transformed object is returned. 232 | ''' 233 | self.CheckPtr() 234 | 235 | cdef c_OCCBase *occ = self.thisptr 236 | cdef Base target 237 | cdef c_OCCStruct3d cp1, cp2 238 | cdef int ret 239 | 240 | if copy: 241 | target = self.__class__() 242 | else: 243 | target = self 244 | 245 | axis = Vector(axis) 246 | p1 = Point(center) 247 | p2 = p1 + axis 248 | 249 | cp1.x = p1.x 250 | cp1.y = p1.y 251 | cp1.z = p1.z 252 | 253 | cp2.x = p2.x 254 | cp2.y = p2.y 255 | cp2.z = p2.z 256 | 257 | ret = occ.rotate(angle, cp1, cp2, target.thisptr) 258 | if not ret: 259 | raise OCCError(errorMessage) 260 | 261 | return target 262 | 263 | cpdef scale(self, pnt, double scale, bint copy = False): 264 | ''' 265 | Scale object. 266 | 267 | :param pnt: reference point 268 | :param scale: scale factor 269 | :param copy: If True the object is translated in place otherwise a 270 | new translated object is returned. 271 | ''' 272 | self.CheckPtr() 273 | 274 | cdef c_OCCBase *occ = self.thisptr 275 | cdef Base target 276 | cdef c_OCCStruct3d cpnt 277 | cdef int ret 278 | 279 | if copy: 280 | target = self.__class__() 281 | else: 282 | target = self 283 | 284 | cpnt.x = pnt[0] 285 | cpnt.y = pnt[1] 286 | cpnt.z = pnt[2] 287 | 288 | ret = occ.scale(cpnt, scale, target.thisptr) 289 | if not ret: 290 | raise OCCError(errorMessage) 291 | 292 | return target 293 | 294 | cpdef mirror(self, Plane plane, bint copy = False): 295 | ''' 296 | Mirror object 297 | 298 | :param plane: mirror plane 299 | :param copy: If True the object is translated in place otherwise a 300 | new translated object is returned. 301 | ''' 302 | self.CheckPtr() 303 | 304 | cdef c_OCCBase *occ = self.thisptr 305 | cdef Base target 306 | cdef c_OCCStruct3d cpnt, cnor 307 | cdef int ret 308 | 309 | if copy: 310 | target = self.__class__() 311 | else: 312 | target = self 313 | 314 | cpnt.x = plane.origin.x 315 | cpnt.y = plane.origin.y 316 | cpnt.z = plane.origin.z 317 | 318 | cnor.x = plane.zaxis.x 319 | cnor.y = plane.zaxis.y 320 | cnor.z = plane.zaxis.z 321 | 322 | ret = occ.mirror(cpnt, cnor, target.thisptr) 323 | if not ret: 324 | raise OCCError(errorMessage) 325 | 326 | return target 327 | 328 | cpdef toString(self): 329 | ''' 330 | Seralize object to string. 331 | 332 | The format used is the OpenCASCADE internal BREP format. 333 | ''' 334 | self.CheckPtr() 335 | 336 | cdef c_OCCBase *occ = self.thisptr 337 | cdef string res = string() 338 | 339 | occ.toString(&res) 340 | ret = str(res.c_str()) 341 | 342 | return ret 343 | 344 | cpdef fromString(self, char *st): 345 | ''' 346 | Restore shape from string. 347 | 348 | The format used is the OpenCASCADE internal BREP format. 349 | ''' 350 | self.CheckPtr() 351 | 352 | cdef c_OCCBase *occ = self.thisptr 353 | cdef string cst= string(st) 354 | 355 | if not occ.fromString(cst): 356 | raise OCCError(errorMessage) 357 | 358 | return self -------------------------------------------------------------------------------- /occmodel/@src/OCCModel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #ifndef OCCMODEL_H 4 | #define OCCMODEL_H 5 | #include "OCCIncludes.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | typedef std::vector FVec; 16 | typedef std::vector DVec; 17 | typedef std::vector IVec; 18 | 19 | struct OCCStruct3d { 20 | double x; 21 | double y; 22 | double z; 23 | }; 24 | 25 | struct OCCStruct3f { 26 | float x; 27 | float y; 28 | float z; 29 | }; 30 | 31 | struct OCCStruct3I { 32 | unsigned int i; 33 | unsigned int j; 34 | unsigned int k; 35 | }; 36 | 37 | struct OptFace; 38 | 39 | struct OptVertex 40 | { 41 | unsigned int index; // Index in vertex array 42 | float score; 43 | std::set faces; // Faces that are using this vertex 44 | void updateScore(int cacheIndex); 45 | }; 46 | 47 | struct OptFace 48 | { 49 | OptVertex *verts[3]; 50 | float getScore() {return verts[0]->score + verts[1]->score + verts[2]->score;} 51 | }; 52 | 53 | enum BoolOpType {BOOL_FUSE, BOOL_CUT, BOOL_COMMON}; 54 | 55 | class OCCBase; 56 | class OCCEdge; 57 | class OCCSolid; 58 | 59 | extern char errorMessage[256]; 60 | void setErrorMessage(const char *err); 61 | 62 | class OCCTesselation { 63 | public: 64 | std::vector vertices; 65 | std::vector ranges; 66 | OCCTesselation() { ; } 67 | }; 68 | 69 | class OCCMesh { 70 | public: 71 | std::vector normals; 72 | std::vector vertices; 73 | std::vector triangles; 74 | std::vector edgeindices; 75 | std::vector edgeranges; 76 | std::vector edgehash; 77 | OCCMesh() { ; } 78 | int extractFaceMesh(const TopoDS_Face& face, bool qualityNormals); 79 | void optimize(); 80 | }; 81 | 82 | class MeshOptimizer 83 | { 84 | public: 85 | static const unsigned int maxCacheSize = 16; 86 | static float calcCacheEfficiency(OCCMesh *mesh, 87 | const unsigned int cacheSize = maxCacheSize); 88 | static void optimizeIndexOrder(OCCMesh *mesh); 89 | }; 90 | 91 | unsigned int decutf8(unsigned int* state, unsigned int* codep, unsigned int byte); 92 | 93 | void printShapeType(const TopoDS_Shape& shape); 94 | int extractSubShape(const TopoDS_Shape& shape, std::vector& shapes); 95 | int extractShape(const TopoDS_Shape& shape, std::vector& shapes); 96 | 97 | class OCCTools { 98 | public: 99 | static int writeBREP(const char *filename, std::vector shapes); 100 | static int writeBREP(std::ostream& str, const TopoDS_Shape& shape); 101 | static int writeSTEP(const char *filename, std::vector shapes); 102 | static int writeSTL(const char *filename, std::vector shapes); 103 | static int readBREP(const char *filename, std::vector& shapes); 104 | static int readBREP(std::istream& str, TopoDS_Shape& shape); 105 | static int readSTEP(const char *filename, std::vector& shapes); 106 | }; 107 | 108 | class OCCBase { 109 | public: 110 | int transform(DVec mat, OCCBase *target); 111 | int translate(OCCStruct3d delta, OCCBase *target); 112 | int rotate(double angle, OCCStruct3d p1, OCCStruct3d p2, OCCBase *target); 113 | int scale(OCCStruct3d pnt, double scale, OCCBase *target); 114 | int mirror(OCCStruct3d pnt, OCCStruct3d nor, OCCBase *target); 115 | DVec boundingBox(double tolerance); 116 | int findPlane(OCCStruct3d *origin, OCCStruct3d *normal, double tolerance); 117 | TopAbs_ShapeEnum shapeType(); 118 | int hashCode(); 119 | bool isEqual(OCCBase *other); 120 | bool isNull(); 121 | bool isValid(); 122 | bool fixShape(); 123 | int toString(std::string *output); 124 | int fromString(std::string input); 125 | virtual bool canSetShape(const TopoDS_Shape&) { return true; } 126 | virtual const TopoDS_Shape& getShape() { return TopoDS_Shape(); } 127 | virtual void setShape(TopoDS_Shape shape) { ; } 128 | }; 129 | 130 | class OCCVertex : public OCCBase { 131 | public: 132 | TopoDS_Vertex vertex; 133 | OCCVertex() { ; } 134 | OCCVertex(double x, double y, double z) { 135 | gp_Pnt aPnt; 136 | aPnt = gp_Pnt(x, y, z); 137 | BRepBuilderAPI_MakeVertex mkVertex(aPnt); 138 | this->setShape(mkVertex.Vertex()); 139 | } 140 | double X() const { 141 | gp_Pnt pnt = BRep_Tool::Pnt(vertex); 142 | return pnt.X(); 143 | } 144 | double Y() const { 145 | gp_Pnt pnt = BRep_Tool::Pnt(vertex); 146 | return pnt.Y(); 147 | } 148 | double Z() const { 149 | gp_Pnt pnt = BRep_Tool::Pnt(vertex); 150 | return pnt.Z(); 151 | } 152 | int project(OCCBase *target); 153 | bool canSetShape(const TopoDS_Shape& shape) { 154 | return shape.ShapeType() == TopAbs_VERTEX; 155 | } 156 | std::string typeName() { return std::string("OCCVertex"); } 157 | const TopoDS_Shape& getShape() { return vertex; } 158 | const TopoDS_Vertex& getVertex() { return vertex; } 159 | void setShape(TopoDS_Shape shape) { vertex = TopoDS::Vertex(shape); } 160 | }; 161 | 162 | class OCCVertexIterator { 163 | public: 164 | TopExp_Explorer ex; 165 | OCCVertexIterator(OCCBase *arg) { 166 | ex.Init(arg->getShape(), TopAbs_VERTEX); 167 | } 168 | void reset() { 169 | ex.ReInit(); 170 | } 171 | OCCVertex *next() { 172 | if (ex.More()) { 173 | OCCVertex *ret = new OCCVertex(); 174 | ret->setShape(ex.Current()); 175 | ex.Next(); 176 | return ret; 177 | } else { 178 | return NULL; 179 | } 180 | } 181 | }; 182 | 183 | class OCCEdge : public OCCBase { 184 | public: 185 | TopoDS_Edge edge; 186 | OCCEdge() { ; } 187 | bool isSeam(OCCBase *face); 188 | bool isDegenerated(); 189 | bool isClosed(); 190 | OCCEdge *copy(bool deepCopy); 191 | int numVertices(); 192 | OCCTesselation *tesselate(double factor, double angle); 193 | int createLine(OCCVertex *start, OCCVertex *end); 194 | int createArc(OCCVertex *start, OCCVertex *end, OCCStruct3d center); 195 | int createArc3P(OCCVertex *start, OCCVertex *end, OCCStruct3d pnt); 196 | int createCircle(OCCStruct3d center, OCCStruct3d normal, double radius); 197 | int createEllipse(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, double rMinor); 198 | int createArcOfEllipse(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, 199 | double rMinor, double a1, double a2, bool reverse); 200 | int createArcOfHyperbola(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, 201 | double rMinor, double a1, double a2, bool reverse); 202 | int createArcOfParabola(OCCStruct3d pnt, OCCStruct3d nor, double focus, 203 | double a1, double a2, bool reverse); 204 | int createHelix(double pitch, double height, double radius, double angle, 205 | bool leftHanded); 206 | int createBezier(OCCVertex *start, OCCVertex *end, std::vector points); 207 | int createSpline(OCCVertex *start, OCCVertex *end, std::vector points, 208 | double tolerance); 209 | int createNURBS(OCCVertex *start, OCCVertex *end, std::vector points, 210 | DVec knots, DVec weights, IVec mult); 211 | double length(); 212 | bool canSetShape(const TopoDS_Shape& shape) { 213 | return shape.ShapeType() == TopAbs_EDGE; 214 | } 215 | const TopoDS_Shape& getShape() { return edge; } 216 | const TopoDS_Edge& getEdge() { return edge; } 217 | void setShape(TopoDS_Shape shape) { edge = TopoDS::Edge(shape); } 218 | }; 219 | 220 | class OCCEdgeIterator { 221 | public: 222 | TopExp_Explorer ex; 223 | OCCEdgeIterator(OCCBase *arg) { 224 | ex.Init(arg->getShape(), TopAbs_EDGE); 225 | } 226 | void reset() { 227 | ex.ReInit(); 228 | } 229 | OCCEdge *next() { 230 | if (ex.More()) { 231 | OCCEdge *ret = new OCCEdge(); 232 | ret->setShape(ex.Current()); 233 | ex.Next(); 234 | return ret; 235 | } else { 236 | return NULL; 237 | } 238 | } 239 | }; 240 | 241 | class OCCWire : public OCCBase { 242 | public: 243 | TopoDS_Wire wire; 244 | OCCWire() { ; } 245 | OCCWire *copy(bool deepCopy); 246 | int numVertices(); 247 | int numEdges(); 248 | bool isClosed(); 249 | int createWire(std::vector edges); 250 | int project(OCCBase *face); 251 | int offset(double distance, int joinType); 252 | int fillet(std::vector vertices, std::vector radius); 253 | int chamfer(std::vector vertices, std::vector distances); 254 | OCCTesselation *tesselate(double factor, double angle); 255 | double length(); 256 | bool canSetShape(const TopoDS_Shape& shape) { 257 | return shape.ShapeType() == TopAbs_WIRE; 258 | } 259 | const TopoDS_Shape& getShape() { return wire; } 260 | const TopoDS_Wire& getWire() { return wire; } 261 | void setShape(TopoDS_Shape shape) { wire = TopoDS::Wire(shape); } 262 | }; 263 | 264 | class OCCWireIterator { 265 | public: 266 | TopExp_Explorer ex; 267 | OCCWireIterator(OCCBase *arg) { 268 | ex.Init(arg->getShape(), TopAbs_WIRE); 269 | } 270 | void reset() { 271 | ex.ReInit(); 272 | } 273 | OCCWire *next() { 274 | if (ex.More()) { 275 | OCCWire *ret = new OCCWire(); 276 | ret->setShape(ex.Current()); 277 | ex.Next(); 278 | return ret; 279 | } else { 280 | return NULL; 281 | } 282 | } 283 | }; 284 | 285 | class OCCFace : public OCCBase { 286 | public: 287 | TopoDS_Shape face; 288 | OCCFace() { ; } 289 | OCCFace *copy(bool deepCopy); 290 | int numWires(); 291 | int numFaces(); 292 | int createFace(std::vector wires); 293 | int createConstrained(std::vector edges, std::vector points); 294 | double area(); 295 | DVec inertia(); 296 | OCCStruct3d centreOfMass(); 297 | int createPolygonal(std::vector points); 298 | int offset(double offset, double tolerance); 299 | int extrude(OCCBase *shape, OCCStruct3d p1, OCCStruct3d p2); 300 | int revolve(OCCBase *shape, OCCStruct3d p1, OCCStruct3d p2, double angle); 301 | int sweep(OCCWire *spine, std::vector profiles, int cornerMode); 302 | int loft(std::vector profiles, bool ruled, double tolerance); 303 | int boolean(OCCSolid *tool, BoolOpType op); 304 | OCCMesh *createMesh(double defle, double angle, bool qualityNormals); 305 | bool canSetShape(const TopoDS_Shape& shape) { 306 | return shape.ShapeType() == TopAbs_FACE || shape.ShapeType() == TopAbs_SHELL; 307 | } 308 | const TopoDS_Shape& getShape() { return face; } 309 | const TopoDS_Face& getFace() { return TopoDS::Face(face); } 310 | const TopoDS_Shell& getShell() { return TopoDS::Shell(face); } 311 | void setShape(TopoDS_Shape shape) { face = shape; } 312 | }; 313 | 314 | class OCCFaceIterator { 315 | public: 316 | TopExp_Explorer ex; 317 | OCCFaceIterator(OCCBase *arg) { 318 | ex.Init(arg->getShape(), TopAbs_FACE); 319 | } 320 | void reset() { 321 | ex.ReInit(); 322 | } 323 | OCCFace *next() { 324 | if (ex.More()) { 325 | OCCFace *ret = new OCCFace(); 326 | ret->setShape(ex.Current()); 327 | ex.Next(); 328 | return ret; 329 | } else { 330 | return NULL; 331 | } 332 | } 333 | }; 334 | 335 | class OCCSolid : public OCCBase { 336 | public: 337 | TopoDS_Shape solid; 338 | OCCSolid() { ; } 339 | OCCSolid *copy(bool deepCopy); 340 | int numSolids(); 341 | int numFaces(); 342 | int createSolid(std::vector faces, double tolerance); 343 | double area() ; 344 | double volume(); 345 | DVec inertia(); 346 | OCCStruct3d centreOfMass(); 347 | OCCMesh *createMesh(double defle, double angle, bool qualityNormals); 348 | int addSolids(std::vector solids); 349 | int createSphere(OCCStruct3d center, double radius); 350 | int createCylinder(OCCStruct3d p1, OCCStruct3d p2, double radius); 351 | int createTorus(OCCStruct3d p1, OCCStruct3d p2, double ringRadius, double radius); 352 | int createCone(OCCStruct3d p1, OCCStruct3d p2, double radius1, double radius2); 353 | int createBox(OCCStruct3d p1, OCCStruct3d p2); 354 | int createPrism(OCCFace *face, OCCStruct3d normal, bool isInfinite); 355 | int createText(double height, double depth, const char *text, const char *fontpath); 356 | int extrude(OCCFace *face, OCCStruct3d p1, OCCStruct3d p2); 357 | int revolve(OCCFace *face, OCCStruct3d p1, OCCStruct3d p2, double angle); 358 | int loft(std::vector profiles, bool ruled, double tolerance); 359 | int pipe(OCCFace *face, OCCWire *wire); 360 | int sweep(OCCWire *spine, std::vector profiles, int cornerMode); 361 | int boolean(OCCSolid *tool, BoolOpType op); 362 | int fillet(std::vector edges, std::vector radius); 363 | int chamfer(std::vector edges, std::vector distances); 364 | int shell(std::vector faces, double offset, double tolerance); 365 | int offset(OCCFace *face, double offset, double tolerance); 366 | OCCFace *section(OCCStruct3d pnt, OCCStruct3d nor); 367 | bool canSetShape(const TopoDS_Shape& shape) { 368 | TopAbs_ShapeEnum type = shape.ShapeType(); 369 | return type == TopAbs_SOLID || type == TopAbs_COMPSOLID || type == TopAbs_COMPOUND; 370 | } 371 | const TopoDS_Shape& getShape() { return solid; } 372 | const TopoDS_Shape& getSolid() { return solid; } 373 | void setShape(TopoDS_Shape shape); 374 | }; 375 | 376 | class OCCSolidIterator { 377 | public: 378 | TopExp_Explorer ex; 379 | OCCSolidIterator(OCCBase *arg) { 380 | ex.Init(arg->getShape(), TopAbs_SOLID); 381 | } 382 | void reset() { 383 | ex.ReInit(); 384 | } 385 | OCCSolid *next() { 386 | if (ex.More()) { 387 | OCCSolid *ret = new OCCSolid(); 388 | ret->setShape(ex.Current()); 389 | ex.Next(); 390 | return ret; 391 | } else { 392 | return NULL; 393 | } 394 | } 395 | }; 396 | #endif 397 | -------------------------------------------------------------------------------- /occmodel/@src/OCCWire.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #include "OCCModel.h" 4 | 5 | OCCWire *OCCWire::copy(bool deepCopy = false) 6 | { 7 | OCCWire *ret = new OCCWire(); 8 | try { 9 | if (deepCopy) { 10 | BRepBuilderAPI_Copy A; 11 | A.Perform(this->getWire()); 12 | ret->setShape(A.Shape()); 13 | } else { 14 | ret->setShape(this->getShape()); 15 | } 16 | } catch(Standard_Failure &err) { 17 | Handle_Standard_Failure e = Standard_Failure::Caught(); 18 | const Standard_CString msg = e->GetMessageString(); 19 | if (msg != NULL && strlen(msg) > 1) { 20 | setErrorMessage(msg); 21 | } else { 22 | setErrorMessage("Failed to copy wire"); 23 | } 24 | return NULL; 25 | } 26 | return ret; 27 | } 28 | 29 | int OCCWire::numVertices() 30 | { 31 | TopTools_IndexedMapOfShape anIndices; 32 | TopExp::MapShapes(this->getWire(), TopAbs_VERTEX, anIndices); 33 | return anIndices.Extent(); 34 | } 35 | 36 | int OCCWire::numEdges() 37 | { 38 | TopTools_IndexedMapOfShape anIndices; 39 | TopExp::MapShapes(this->getWire(), TopAbs_EDGE, anIndices); 40 | return anIndices.Extent(); 41 | } 42 | 43 | bool OCCWire::isClosed() 44 | { 45 | TopoDS_Vertex aV1, aV2; 46 | TopExp::Vertices(this->getWire(), aV1, aV2); 47 | if (!aV1.IsNull() && !aV2.IsNull() && aV1.IsSame(aV2)) 48 | return true; 49 | return false; 50 | } 51 | 52 | int OCCWire::createWire(std::vector edges) 53 | { 54 | try { 55 | BRepBuilderAPI_MakeWire MW; 56 | for (unsigned i=0; igetEdge()); 59 | } 60 | 61 | BRepBuilderAPI_WireError error = MW.Error(); 62 | switch (error) 63 | { 64 | case BRepBuilderAPI_EmptyWire: 65 | { 66 | StdFail_NotDone::Raise("Wire empty"); 67 | break; 68 | } 69 | case BRepBuilderAPI_DisconnectedWire: 70 | { 71 | StdFail_NotDone::Raise("Disconnected wire"); 72 | break; 73 | } 74 | case BRepBuilderAPI_NonManifoldWire : 75 | { 76 | StdFail_NotDone::Raise("non-manifold wire"); 77 | break; 78 | } 79 | } 80 | 81 | this->setShape(MW.Wire()); 82 | 83 | // possible fix shape 84 | if (!this->fixShape()) 85 | StdFail_NotDone::Raise("Shapes not valid"); 86 | 87 | } catch(Standard_Failure &err) { 88 | Handle_Standard_Failure e = Standard_Failure::Caught(); 89 | const Standard_CString msg = e->GetMessageString(); 90 | if (msg != NULL && strlen(msg) > 1) { 91 | setErrorMessage(msg); 92 | } else { 93 | setErrorMessage("Failed to create wire"); 94 | } 95 | return 0; 96 | } 97 | return 1; 98 | } 99 | 100 | int OCCWire::project(OCCBase *face) { 101 | Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape; 102 | Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape; 103 | TopExp_Explorer ex; 104 | try { 105 | BRepOffsetAPI_NormalProjection NP(face->getShape()); 106 | NP.SetLimit(Standard_True); 107 | NP.Add(this->getWire()); 108 | NP.Build(); 109 | if (!NP.IsDone()) 110 | StdFail_NotDone::Raise("project operation failed"); 111 | 112 | for (ex.Init(NP.Shape(), TopAbs_EDGE); ex.More(); ex.Next()) { 113 | if (!ex.Current().IsNull()) { 114 | edges->Append(TopoDS::Edge(ex.Current())); 115 | } 116 | } 117 | ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges,Precision::Confusion(),Standard_True,wires); 118 | if (wires->Length() != 1) 119 | StdFail_NotDone::Raise("project operation created empty result"); 120 | 121 | this->setShape(wires->Value(1)); 122 | 123 | // possible fix shape 124 | if (!this->fixShape()) 125 | StdFail_NotDone::Raise("Shapes not valid"); 126 | 127 | } catch(Standard_Failure &err) { 128 | Handle_Standard_Failure e = Standard_Failure::Caught(); 129 | const Standard_CString msg = e->GetMessageString(); 130 | if (msg != NULL && strlen(msg) > 1) { 131 | setErrorMessage(msg); 132 | } else { 133 | setErrorMessage("Failed to project wire"); 134 | } 135 | return 0; 136 | } 137 | return 1; 138 | } 139 | 140 | int OCCWire::offset(double distance, int joinType = 0) { 141 | Handle(TopTools_HSequenceOfShape) wires = new TopTools_HSequenceOfShape; 142 | Handle(TopTools_HSequenceOfShape) edges = new TopTools_HSequenceOfShape; 143 | TopExp_Explorer ex; 144 | 145 | try { 146 | GeomAbs_JoinType join = GeomAbs_Arc; 147 | switch (joinType) { 148 | case 1: 149 | join = GeomAbs_Tangent; 150 | break; 151 | case 2: 152 | join = GeomAbs_Intersection; 153 | break; 154 | } 155 | BRepOffsetAPI_MakeOffset MO(this->getWire(), join); 156 | MO.Perform(distance); 157 | 158 | for (ex.Init(MO.Shape(), TopAbs_EDGE); ex.More(); ex.Next()) { 159 | if (!ex.Current().IsNull()) { 160 | edges->Append(TopoDS::Edge(ex.Current())); 161 | } 162 | } 163 | ShapeAnalysis_FreeBounds::ConnectEdgesToWires(edges,Precision::Confusion(),Standard_True,wires); 164 | if (wires->Length() != 1) 165 | StdFail_NotDone::Raise("offset operation created empty result"); 166 | 167 | this->setShape(wires->Value(1)); 168 | 169 | // possible fix shape 170 | if (!this->fixShape()) 171 | StdFail_NotDone::Raise("Shapes not valid"); 172 | 173 | } catch(Standard_Failure &err) { 174 | Handle_Standard_Failure e = Standard_Failure::Caught(); 175 | const Standard_CString msg = e->GetMessageString(); 176 | if (msg != NULL && strlen(msg) > 1) { 177 | setErrorMessage(msg); 178 | } else { 179 | setErrorMessage("Failed to offset wire"); 180 | } 181 | return 0; 182 | } 183 | return 1; 184 | } 185 | 186 | int OCCWire::fillet(std::vector vertices, std::vector radius) { 187 | int vertices_size = vertices.size(); 188 | int radius_size = radius.size(); 189 | 190 | BRepFilletAPI_MakeFillet2d MF; 191 | try { 192 | if (this->getShape().IsNull()) { 193 | StdFail_NotDone::Raise("Shapes is Null"); 194 | } 195 | 196 | MF.Init(BRepBuilderAPI_MakeFace(this->getWire())); 197 | 198 | for (unsigned i=0; igetVertex(), radius[0]); 204 | } else if (radius_size == vertices_size) { 205 | // radius given for each vertex 206 | MF.AddFillet(vertex->getVertex(), radius[i]); 207 | } else { 208 | StdFail_NotDone::Raise("radius argument has wrong size"); 209 | } 210 | } 211 | 212 | if(MF.Status() != ChFi2d_IsDone) 213 | StdFail_NotDone::Raise("fillet not completed"); 214 | 215 | BRepBuilderAPI_MakeWire wire; 216 | TopTools_IndexedMapOfShape aMap; 217 | BRepTools_WireExplorer Ex; 218 | 219 | TopExp::MapShapes(MF.Shape(), TopAbs_WIRE, aMap); 220 | if(aMap.Extent() != 1) 221 | StdFail_NotDone::Raise("Fillet operation did not result in single wire"); 222 | 223 | //add edges to the wire 224 | Ex.Clear(); 225 | for(Ex.Init(TopoDS::Wire(aMap(1))); Ex.More(); Ex.Next()) 226 | { 227 | wire.Add(Ex.Current()); 228 | } 229 | 230 | this->setShape(wire); 231 | 232 | // possible fix shape 233 | if (!this->fixShape()) 234 | StdFail_NotDone::Raise("Shapes not valid"); 235 | 236 | } catch(Standard_Failure &err) { 237 | Handle_Standard_Failure e = Standard_Failure::Caught(); 238 | const Standard_CString msg = e->GetMessageString(); 239 | if (msg != NULL && strlen(msg) > 1) { 240 | setErrorMessage(msg); 241 | } else { 242 | setErrorMessage("Failed to fillet wire"); 243 | } 244 | return 0; 245 | } 246 | return 1; 247 | } 248 | 249 | int OCCWire::chamfer(std::vector vertices, std::vector distances) { 250 | int vertices_size = vertices.size(); 251 | int distances_size = distances.size(); 252 | 253 | BRepFilletAPI_MakeFillet2d MF; 254 | try { 255 | if (this->getShape().IsNull()) { 256 | StdFail_NotDone::Raise("Shapes is Null"); 257 | } 258 | 259 | MF.Init(BRepBuilderAPI_MakeFace(this->getWire())); 260 | 261 | // creat map of vertices 262 | TopTools_IndexedMapOfShape vertMap; 263 | for (unsigned i=0; igetShape()); 265 | 266 | bool first = true; 267 | TopoDS_Edge firstEdge, nextEdge; 268 | TopoDS_Vertex vertex; 269 | 270 | BRepTools_WireExplorer Ex1; 271 | for (Ex1.Init(this->getWire()); Ex1.More(); ) { 272 | if(first == true) { 273 | firstEdge = Ex1.Current(); 274 | first = false; 275 | } 276 | 277 | Ex1.Next(); 278 | 279 | //if the number of edges is odd don't proceed 280 | if(Ex1.More() == Standard_False) 281 | break; 282 | 283 | nextEdge = Ex1.Current(); 284 | 285 | //get the common vertex of the two edges 286 | if (!TopExp::CommonVertex(firstEdge, nextEdge, vertex)) { 287 | // disconnected wire 288 | first = true; 289 | continue; 290 | } 291 | 292 | if (vertMap.Contains(vertex)) { 293 | int i = vertMap.FindIndex(vertex) - 1; 294 | 295 | if (distances_size == 1) { 296 | // single distance 297 | MF.AddChamfer(firstEdge, nextEdge, distances[0], distances[0]); 298 | } else if (distances_size == vertices_size) { 299 | // distance given for each vertex 300 | MF.AddChamfer(firstEdge, nextEdge, distances[i], distances[i]); 301 | } else { 302 | StdFail_NotDone::Raise("distances argument has wrong size"); 303 | } 304 | 305 | } 306 | 307 | firstEdge = nextEdge; 308 | } 309 | 310 | // special case for closed wire 311 | if (isClosed()) { 312 | // find seam vertex 313 | TopoDS_Vertex aV1; 314 | TopExp::Vertices(this->getWire(), vertex, aV1); 315 | 316 | // check if seam vertex has chamfer value 317 | if (vertMap.Contains(vertex)) { 318 | int i = vertMap.FindIndex(vertex) - 1; 319 | 320 | // map vertices to edges to find edge pair 321 | TopTools_IndexedDataMapOfShapeListOfShape mapVertexEdge; 322 | TopExp::MapShapesAndAncestors(this->getWire(), TopAbs_VERTEX, TopAbs_EDGE, mapVertexEdge); 323 | 324 | const TopTools_ListOfShape& edges = mapVertexEdge.FindFromKey(vertex); 325 | firstEdge = TopoDS::Edge(edges.First()); 326 | nextEdge = TopoDS::Edge(edges.Last()); 327 | 328 | if (distances_size == 1) { 329 | // single distance 330 | MF.AddChamfer(firstEdge, nextEdge, distances[0], distances[0]); 331 | } else if (distances_size == vertices_size) { 332 | // distance given for each vertex 333 | MF.AddChamfer(firstEdge, nextEdge, distances[i], distances[i]); 334 | } else { 335 | StdFail_NotDone::Raise("distances argument has wrong size"); 336 | } 337 | } 338 | } 339 | 340 | if(MF.Status() != ChFi2d_IsDone) 341 | StdFail_NotDone::Raise("chamfer operation failed"); 342 | 343 | TopTools_IndexedMapOfShape aMap; 344 | TopExp::MapShapes(MF.Shape(), TopAbs_WIRE, aMap); 345 | if(aMap.Extent() != 1) 346 | StdFail_NotDone::Raise("chamfer result did not result in single wire");; 347 | 348 | //add edges to the wire 349 | BRepBuilderAPI_MakeWire wire; 350 | BRepTools_WireExplorer Ex2; 351 | for(Ex2.Init(TopoDS::Wire(aMap(1))); Ex2.More(); Ex2.Next()) 352 | { 353 | wire.Add(Ex2.Current()); 354 | } 355 | 356 | this->setShape(wire.Shape()); 357 | 358 | // possible fix shape 359 | if (!this->fixShape()) 360 | StdFail_NotDone::Raise("Shapes not valid"); 361 | 362 | } catch(Standard_Failure &err) { 363 | Handle_Standard_Failure e = Standard_Failure::Caught(); 364 | const Standard_CString msg = e->GetMessageString(); 365 | if (msg != NULL && strlen(msg) > 1) { 366 | setErrorMessage(msg); 367 | } else { 368 | setErrorMessage("Failed to chamfer wire"); 369 | } 370 | return 0; 371 | } 372 | return 1; 373 | } 374 | 375 | OCCTesselation *OCCWire::tesselate(double angular, double curvature) 376 | { 377 | OCCTesselation *ret = new OCCTesselation(); 378 | try { 379 | Standard_Real start, end; 380 | OCCStruct3f dtmp; 381 | 382 | // explore wire edges in connected order 383 | int lastSize = 0; 384 | BRepTools_WireExplorer exWire; 385 | 386 | for (exWire.Init(this->getWire()); exWire.More(); exWire.Next()) { 387 | const TopoDS_Edge& edge = exWire.Current(); 388 | TopLoc_Location loc = edge.Location(); 389 | gp_Trsf location = loc.Transformation(); 390 | 391 | const Handle(Geom_Curve)& curve = BRep_Tool::Curve(edge, start, end); 392 | const GeomAdaptor_Curve& aCurve(curve); 393 | 394 | GCPnts_TangentialDeflection TD(aCurve, start, end, angular, curvature); 395 | 396 | ret->ranges.push_back(ret->vertices.size()); 397 | 398 | for (Standard_Integer i = 1; i <= TD.NbPoints(); i++) 399 | { 400 | gp_Pnt pnt = TD.Value(i).Transformed(location); 401 | dtmp.x = (float)pnt.X(); 402 | dtmp.y = (float)pnt.Y(); 403 | dtmp.z = (float)pnt.Z(); 404 | ret->vertices.push_back(dtmp); 405 | } 406 | 407 | ret->ranges.push_back(ret->vertices.size() - lastSize); 408 | lastSize = ret->vertices.size(); 409 | } 410 | } catch(Standard_Failure &err) { 411 | return NULL; 412 | } 413 | return ret; 414 | } 415 | 416 | double OCCWire::length() { 417 | GProp_GProps prop; 418 | BRepGProp::LinearProperties(this->getWire(), prop); 419 | return prop.Mass(); 420 | } 421 | -------------------------------------------------------------------------------- /occmodel/@src/OCCFace.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #include "OCCModel.h" 4 | 5 | OCCFace *OCCFace::copy(bool deepCopy = false) 6 | { 7 | OCCFace *ret = new OCCFace(); 8 | try { 9 | if (deepCopy) { 10 | BRepBuilderAPI_Copy A; 11 | A.Perform(this->getShape()); 12 | ret->setShape(A.Shape()); 13 | } else { 14 | ret->setShape(this->getShape()); 15 | } 16 | } catch(Standard_Failure &err) { 17 | Handle_Standard_Failure e = Standard_Failure::Caught(); 18 | const Standard_CString msg = e->GetMessageString(); 19 | if (msg != NULL && strlen(msg) > 1) { 20 | setErrorMessage(msg); 21 | } else { 22 | setErrorMessage("Failed to copy face"); 23 | } 24 | return NULL; 25 | } 26 | return ret; 27 | } 28 | 29 | int OCCFace::numFaces() 30 | { 31 | TopTools_IndexedMapOfShape anIndices; 32 | const TopoDS_Shape& shp = this->getShape(); 33 | if (shp.ShapeType() == TopAbs_FACE) { 34 | return 1; 35 | } else { 36 | // Shell 37 | TopExp::MapShapes(shp, TopAbs_FACE, anIndices); 38 | return anIndices.Extent(); 39 | } 40 | } 41 | 42 | int OCCFace::numWires() 43 | { 44 | TopTools_IndexedMapOfShape anIndices; 45 | TopExp::MapShapes(this->getShape(), TopAbs_WIRE, anIndices); 46 | return anIndices.Extent(); 47 | } 48 | 49 | int OCCFace::createFace(std::vector wires) { 50 | try { 51 | const TopoDS_Wire& outerwire = wires[0]->getWire(); 52 | 53 | if (!wires[0]->isClosed()) 54 | StdFail_NotDone::Raise("Outer wire not closed"); 55 | 56 | BRepBuilderAPI_MakeFace MF(outerwire); 57 | 58 | // add optional holes 59 | for (unsigned i = 1; i < wires.size(); i++) { 60 | const TopoDS_Wire& wire = wires[i]->getWire(); 61 | if (!wires[i]->isClosed()) 62 | StdFail_NotDone::Raise("Outer wire not closed"); 63 | 64 | if (wire.Orientation() != outerwire.Orientation()) { 65 | MF.Add(TopoDS::Wire(wire.Reversed())); 66 | } else { 67 | MF.Add(wire); 68 | } 69 | } 70 | this->setShape(MF.Shape()); 71 | 72 | // possible fix shape 73 | if (!this->fixShape()) 74 | StdFail_NotDone::Raise("Shapes not valid"); 75 | 76 | } catch(Standard_Failure &err) { 77 | Handle_Standard_Failure e = Standard_Failure::Caught(); 78 | const Standard_CString msg = e->GetMessageString(); 79 | if (msg != NULL && strlen(msg) > 1) { 80 | setErrorMessage(msg); 81 | } else { 82 | setErrorMessage("Failed to create face"); 83 | } 84 | return 0; 85 | } 86 | return 1; 87 | } 88 | 89 | int OCCFace::createConstrained(std::vector edges, std::vector points) { 90 | try { 91 | BRepOffsetAPI_MakeFilling aGenerator; 92 | for (unsigned i = 0; i < edges.size(); i++) { 93 | OCCEdge *edge = edges[i]; 94 | aGenerator.Add(edge->edge, GeomAbs_C0); 95 | } 96 | for (unsigned i = 0; i < points.size(); i++) { 97 | gp_Pnt aPnt(points[i].x, points[i].y, points[i].z); 98 | aGenerator.Add(aPnt); 99 | } 100 | aGenerator.Build(); 101 | this->setShape(aGenerator.Shape()); 102 | 103 | // possible fix shape 104 | if (!this->fixShape()) 105 | StdFail_NotDone::Raise("Shapes not valid"); 106 | 107 | } catch(Standard_Failure &err) { 108 | Handle_Standard_Failure e = Standard_Failure::Caught(); 109 | const Standard_CString msg = e->GetMessageString(); 110 | if (msg != NULL && strlen(msg) > 1) { 111 | setErrorMessage(msg); 112 | } else { 113 | setErrorMessage("Failed to create face"); 114 | } 115 | return 0; 116 | } 117 | return 1; 118 | } 119 | 120 | double OCCFace::area() { 121 | GProp_GProps prop; 122 | BRepGProp::SurfaceProperties(this->getShape(), prop); 123 | return prop.Mass(); 124 | } 125 | 126 | DVec OCCFace::inertia() { 127 | DVec ret; 128 | GProp_GProps prop; 129 | BRepGProp::SurfaceProperties(this->getShape(), prop); 130 | gp_Mat mat = prop.MatrixOfInertia(); 131 | ret.push_back(mat(1,1)); // Ixx 132 | ret.push_back(mat(2,2)); // Iyy 133 | ret.push_back(mat(3,3)); // Izz 134 | ret.push_back(mat(1,2)); // Ixy 135 | ret.push_back(mat(1,3)); // Ixz 136 | ret.push_back(mat(2,3)); // Iyz 137 | return ret; 138 | } 139 | 140 | OCCStruct3d OCCFace::centreOfMass() { 141 | OCCStruct3d ret; 142 | GProp_GProps prop; 143 | BRepGProp::SurfaceProperties(this->getShape(), prop); 144 | gp_Pnt cg = prop.CentreOfMass(); 145 | ret.x = cg.X(); 146 | ret.y = cg.Y(); 147 | ret.z = cg.Z(); 148 | return ret; 149 | } 150 | 151 | int OCCFace::offset(double offset, double tolerance = 1e-6) { 152 | try { 153 | BRepOffset_MakeOffset MO(this->getShape(), offset, tolerance, BRepOffset_Skin, 154 | Standard_False, Standard_False, GeomAbs_Intersection, Standard_False); 155 | 156 | if (!MO.IsDone()) { 157 | StdFail_NotDone::Raise("Failed to offset face"); 158 | } 159 | 160 | const TopoDS_Shape& tmp = MO.Shape(); 161 | BRepCheck_Analyzer aChecker(tmp); 162 | 163 | if (tmp.IsNull() || !aChecker.IsValid()) { 164 | StdFail_NotDone::Raise("offset result not valid"); 165 | } 166 | 167 | this->setShape(tmp); 168 | 169 | // possible fix shape 170 | if (!this->fixShape()) 171 | StdFail_NotDone::Raise("Shapes not valid"); 172 | 173 | } catch(Standard_Failure &err) { 174 | Handle_Standard_Failure e = Standard_Failure::Caught(); 175 | const Standard_CString msg = e->GetMessageString(); 176 | if (msg != NULL && strlen(msg) > 1) { 177 | setErrorMessage(msg); 178 | } else { 179 | setErrorMessage("Failed to offset face"); 180 | } 181 | return 0; 182 | } 183 | return 1; 184 | } 185 | 186 | int OCCFace::createPolygonal(std::vector points) 187 | { 188 | try { 189 | BRepBuilderAPI_MakePolygon MP; 190 | for (unsigned i=0; isetShape(MF.Face()); 199 | 200 | // possible fix shape 201 | if (!this->fixShape()) 202 | StdFail_NotDone::Raise("Shapes not valid"); 203 | 204 | } catch(Standard_Failure &err) { 205 | Handle_Standard_Failure e = Standard_Failure::Caught(); 206 | const Standard_CString msg = e->GetMessageString(); 207 | if (msg != NULL && strlen(msg) > 1) { 208 | setErrorMessage(msg); 209 | } else { 210 | setErrorMessage("Failed to create face"); 211 | } 212 | return 0; 213 | } 214 | return 1; 215 | } 216 | 217 | int OCCFace::extrude(OCCBase *shape, OCCStruct3d p1, OCCStruct3d p2) { 218 | try { 219 | const TopoDS_Shape& shp = shape->getShape(); 220 | // Only accept Edge or Wire 221 | TopAbs_ShapeEnum type = shp.ShapeType(); 222 | if (type != TopAbs_EDGE && type != TopAbs_WIRE) { 223 | StdFail_NotDone::Raise("expected Edge or Wire"); 224 | } 225 | 226 | gp_Vec direction(gp_Pnt(p1.x, p1.y, p1.z), 227 | gp_Pnt(p2.x, p2.y, p2.z)); 228 | gp_Ax1 axisOfRevolution(gp_Pnt(p1.x, p1.y, p1.z), direction); 229 | 230 | BRepPrimAPI_MakePrism MP(shp, direction, Standard_False); 231 | this->setShape(MP.Shape()); 232 | 233 | // possible fix shape 234 | if (!this->fixShape()) 235 | StdFail_NotDone::Raise("Shapes not valid"); 236 | 237 | } catch(Standard_Failure &err) { 238 | Handle_Standard_Failure e = Standard_Failure::Caught(); 239 | const Standard_CString msg = e->GetMessageString(); 240 | if (msg != NULL && strlen(msg) > 1) { 241 | setErrorMessage(msg); 242 | } else { 243 | setErrorMessage("Failed to extrude"); 244 | } 245 | return 0; 246 | } 247 | return 1; 248 | } 249 | 250 | int OCCFace::revolve(OCCBase *shape, OCCStruct3d p1, OCCStruct3d p2, double angle) 251 | { 252 | try { 253 | const TopoDS_Shape& shp = shape->getShape(); 254 | // Only accept Edge or Wire 255 | TopAbs_ShapeEnum type = shp.ShapeType(); 256 | if (type != TopAbs_EDGE && type != TopAbs_WIRE) { 257 | StdFail_NotDone::Raise("Expected Edge or Wire"); 258 | } 259 | 260 | gp_Dir direction(p2.x - p1.x, p2.y - p1.y, p2.z - p1.z); 261 | gp_Ax1 axisOfRevolution(gp_Pnt(p1.x, p1.y, p1.z), direction); 262 | 263 | BRepPrimAPI_MakeRevol MR(shp, axisOfRevolution, angle, Standard_False); 264 | if (!MR.IsDone()) { 265 | StdFail_NotDone::Raise("Failed in revolve operation");; 266 | } 267 | this->setShape(MR.Shape()); 268 | 269 | // possible fix shape 270 | if (!this->fixShape()) 271 | StdFail_NotDone::Raise("Shapes not valid"); 272 | 273 | } catch(Standard_Failure &err) { 274 | Handle_Standard_Failure e = Standard_Failure::Caught(); 275 | const Standard_CString msg = e->GetMessageString(); 276 | if (msg != NULL && strlen(msg) > 1) { 277 | setErrorMessage(msg); 278 | } else { 279 | setErrorMessage("Failed to revolve"); 280 | } 281 | return 0; 282 | } 283 | return 1; 284 | } 285 | 286 | int OCCFace::sweep(OCCWire *spine, std::vector profiles, int cornerMode = 0) 287 | { 288 | try { 289 | BRepOffsetAPI_MakePipeShell PS(spine->getWire()); 290 | // set corner mode 291 | switch (cornerMode) { 292 | case 1: PS.SetTransitionMode(BRepBuilderAPI_RightCorner); 293 | break; 294 | case 2: PS.SetTransitionMode(BRepBuilderAPI_RoundCorner); 295 | break; 296 | default: PS.SetTransitionMode(BRepBuilderAPI_Transformed); 297 | break; 298 | } 299 | // add profiles 300 | for (unsigned i=0; igetShape()); 302 | } 303 | if (!PS.IsReady()) { 304 | StdFail_NotDone::Raise("Failed in sweep operation"); 305 | } 306 | PS.Build(); 307 | 308 | this->setShape(PS.Shape()); 309 | 310 | // possible fix shape 311 | if (!this->fixShape()) 312 | StdFail_NotDone::Raise("Shapes not valid"); 313 | 314 | } catch(Standard_Failure &err) { 315 | Handle_Standard_Failure e = Standard_Failure::Caught(); 316 | const Standard_CString msg = e->GetMessageString(); 317 | if (msg != NULL && strlen(msg) > 1) { 318 | setErrorMessage(msg); 319 | } else { 320 | setErrorMessage("Failed to sweep"); 321 | } 322 | return 0; 323 | } 324 | return 1; 325 | } 326 | 327 | int OCCFace::loft(std::vector profiles, bool ruled, double tolerance) 328 | { 329 | try { 330 | Standard_Boolean isSolid = Standard_False; 331 | Standard_Boolean isRuled = Standard_True; 332 | 333 | if (!ruled) isRuled = Standard_False; 334 | 335 | BRepOffsetAPI_ThruSections TS(isSolid, isRuled, tolerance); 336 | 337 | for (unsigned i=0; igetShape().ShapeType() == TopAbs_WIRE) { 339 | TS.AddWire(TopoDS::Wire(profiles[i]->getShape())); 340 | } else { 341 | TS.AddVertex(TopoDS::Vertex(profiles[i]->getShape())); 342 | } 343 | } 344 | //TS.CheckCompatibility(Standard_False); 345 | TS.Build(); 346 | if (!TS.IsDone()) { 347 | StdFail_NotDone::Raise("Failed in loft operation");; 348 | } 349 | this->setShape(TS.Shape()); 350 | 351 | // possible fix shape 352 | if (!this->fixShape()) 353 | StdFail_NotDone::Raise("Shapes not valid"); 354 | 355 | } catch(Standard_Failure &err) { 356 | Handle_Standard_Failure e = Standard_Failure::Caught(); 357 | const Standard_CString msg = e->GetMessageString(); 358 | if (msg != NULL && strlen(msg) > 1) { 359 | setErrorMessage(msg); 360 | } else { 361 | setErrorMessage("Failed to loft"); 362 | } 363 | return 0; 364 | } 365 | return 1; 366 | } 367 | 368 | int OCCFace::boolean(OCCSolid *tool, BoolOpType op) { 369 | try { 370 | TopoDS_Shape shape; 371 | switch (op) { 372 | case BOOL_CUT: 373 | { 374 | BRepAlgoAPI_Cut CU (this->getShape(), tool->getShape()); 375 | if (!CU.IsDone()) 376 | Standard_ConstructionError::Raise("operation failed"); 377 | shape = CU.Shape(); 378 | break; 379 | } 380 | case BOOL_COMMON: 381 | { 382 | BRepAlgoAPI_Common CO (this->getShape(), tool->getShape()); 383 | if (!CO.IsDone()) 384 | Standard_ConstructionError::Raise("operation failed"); 385 | shape = CO.Shape(); 386 | break; 387 | } 388 | default: 389 | Standard_ConstructionError::Raise("unknown operation"); 390 | break; 391 | } 392 | // extract single face or single shell 393 | int idx = 0; 394 | TopExp_Explorer exBO; 395 | for (exBO.Init(shape, TopAbs_SHELL); exBO.More(); exBO.Next()) { 396 | if (idx > 0) { 397 | Standard_ConstructionError::Raise("multiple object in result"); 398 | } 399 | const TopoDS_Shape& cur = exBO.Current(); 400 | this->setShape(cur); 401 | idx++; 402 | } 403 | if (idx == 0) { 404 | idx = 0; 405 | for (exBO.Init(shape, TopAbs_FACE); exBO.More(); exBO.Next()) { 406 | if (idx > 0) { 407 | Standard_ConstructionError::Raise("multiple object in result"); 408 | } 409 | const TopoDS_Shape& cur = exBO.Current(); 410 | this->setShape(cur); 411 | idx++; 412 | } 413 | } 414 | if (idx == 0) 415 | StdFail_NotDone::Raise("no results from boolean operation");; 416 | this->setShape(shape); 417 | 418 | // possible fix shape 419 | if (!this->fixShape()) 420 | StdFail_NotDone::Raise("Shapes not valid"); 421 | 422 | } catch(Standard_Failure &err) { 423 | Handle_Standard_Failure e = Standard_Failure::Caught(); 424 | const Standard_CString msg = e->GetMessageString(); 425 | if (msg != NULL && strlen(msg) > 1) { 426 | setErrorMessage(msg); 427 | } else { 428 | setErrorMessage("Failed in boolean operation"); 429 | } 430 | return 0; 431 | } 432 | return 1; 433 | } 434 | 435 | OCCMesh *OCCFace::createMesh(double factor, double angle, bool qualityNormals = true) 436 | { 437 | OCCMesh *mesh = new OCCMesh(); 438 | 439 | try { 440 | Bnd_Box aBox; 441 | BRepBndLib::Add(this->getShape(), aBox); 442 | 443 | Standard_Real aXmin, aYmin, aZmin; 444 | Standard_Real aXmax, aYmax, aZmax; 445 | aBox.Get(aXmin, aYmin, aZmin, aXmax, aYmax, aZmax); 446 | 447 | Standard_Real maxd = fabs(aXmax - aXmin); 448 | maxd = std::max(maxd, fabs(aYmax - aYmin)); 449 | maxd = std::max(maxd, fabs(aZmax - aZmin)); 450 | 451 | BRepMesh_FastDiscret MSH(factor*maxd, angle, aBox, Standard_False, Standard_False, 452 | Standard_True, Standard_True); 453 | 454 | MSH.Perform(this->getShape()); 455 | 456 | BRepMesh_IncrementalMesh(this->getShape(),factor*maxd); 457 | 458 | if (this->getShape().ShapeType() != TopAbs_FACE) { 459 | TopExp_Explorer exFace; 460 | for (exFace.Init(this->getShape(), TopAbs_FACE); exFace.More(); exFace.Next()) { 461 | const TopoDS_Face& faceref = static_cast(exFace.Current()); 462 | mesh->extractFaceMesh(faceref, qualityNormals); 463 | } 464 | } else { 465 | mesh->extractFaceMesh(this->getFace(), qualityNormals); 466 | } 467 | } catch(Standard_Failure &err) { 468 | Handle_Standard_Failure e = Standard_Failure::Caught(); 469 | const Standard_CString msg = e->GetMessageString(); 470 | if (msg != NULL && strlen(msg) > 1) { 471 | setErrorMessage(msg); 472 | } else { 473 | setErrorMessage("Failed to create mesh"); 474 | } 475 | return NULL; 476 | } 477 | return mesh; 478 | } -------------------------------------------------------------------------------- /occmodel/@src/OCCWire.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | 6 | INSCRIBE = 'inscribe' 7 | CIRCUMSCRIBE = 'circumscribe' 8 | 9 | cdef class Wire(Base): 10 | ''' 11 | Wire - represent wire geometry (composite of edges). 12 | 13 | Wires defines boundaries of faces. 14 | ''' 15 | def __init__(self, edges = None): 16 | ''' 17 | Create empty Wire or a planar Wire from 18 | given edges. 19 | ''' 20 | self.thisptr = new c_OCCWire() 21 | if not edges is None: 22 | self.createWire(edges) 23 | 24 | def __dealloc__(self): 25 | cdef c_OCCWire *tmp 26 | 27 | if self.thisptr != NULL: 28 | tmp = self.thisptr 29 | del tmp 30 | 31 | def __str__(self): 32 | return "Wire%s" % repr(self) 33 | 34 | def __repr__(self): 35 | return "()" 36 | 37 | def __len__(self): 38 | return self.numEdges() 39 | 40 | def __iter__(self): 41 | return EdgeIterator(self) 42 | 43 | cpdef Wire copy(self, bint deepCopy = False): 44 | ''' 45 | Create copy of wire 46 | 47 | :param deepCopy: If true a full copy of the underlying geometry 48 | is done. Defaults to False. 49 | ''' 50 | cdef c_OCCWire *occ = self.thisptr 51 | cdef Wire ret = Wire.__new__(Wire, None) 52 | 53 | ret.thisptr = occ.copy(deepCopy) 54 | 55 | return ret 56 | 57 | cpdef Wire copyFrom(self, Wire wire, bint deepCopy = False): 58 | ''' 59 | Set self from copy of wire 60 | 61 | :param wire: Wire to copy 62 | :param deepCopy: If true a full copy of the underlying geometry 63 | is done. Defaults to False. 64 | ''' 65 | cdef c_OCCWire *tmp 66 | 67 | # remove object 68 | tmp = self.thisptr 69 | del tmp 70 | 71 | # set to copy 72 | tmp = wire.thisptr 73 | self.thisptr = tmp.copy(deepCopy) 74 | 75 | return self 76 | 77 | cpdef int numVertices(self): 78 | ''' 79 | Return number of vertices 80 | ''' 81 | cdef c_OCCWire *occ = self.thisptr 82 | return occ.numVertices() 83 | 84 | cpdef int numEdges(self): 85 | ''' 86 | Return number of edges 87 | ''' 88 | cdef c_OCCWire *occ = self.thisptr 89 | return occ.numEdges() 90 | 91 | cpdef bint isClosed(self): 92 | ''' 93 | Check if wire is closed 94 | ''' 95 | cdef c_OCCWire *occ = self.thisptr 96 | return occ.isClosed() 97 | 98 | cpdef createWire(self, edges): 99 | ''' 100 | Create wire by connecting edges or a single closed edge. 101 | ''' 102 | cdef c_OCCWire *occ = self.thisptr 103 | cdef vector[c_OCCEdge *] cedges 104 | cdef Edge edge 105 | cdef int ret 106 | 107 | if isinstance(edges, Edge): 108 | edges = (edges,) 109 | 110 | for edge in edges: 111 | cedges.push_back((edge.thisptr)) 112 | 113 | ret = occ.createWire(cedges) 114 | if not ret: 115 | raise OCCError(errorMessage) 116 | 117 | return self 118 | 119 | cpdef Tesselation tesselate(self, double factor = .1, double angle = .1): 120 | ''' 121 | Tesselate wire to given max angle or distance factor 122 | ''' 123 | cdef c_OCCWire *occ = self.thisptr 124 | cdef c_OCCTesselation *tess = occ.tesselate(factor, angle) 125 | cdef Tesselation ret = Tesselation.__new__(Tesselation, None) 126 | 127 | if tess == NULL: 128 | raise OCCError(errorMessage) 129 | 130 | ret.thisptr = tess 131 | ret.setArrays() 132 | return ret 133 | 134 | cpdef createRectangle(self, double width = 1., double height = 1., double radius = 0.): 135 | ''' 136 | Create planar rectangle in the xy plan. 137 | 138 | The rectangle is centered at 0,0 with given 139 | width, height and optional corner radius. 140 | 141 | example:: 142 | 143 | w1 = Wire().createRectangle(width = 1., height = 0.75, radius = .25) 144 | ''' 145 | hw, hh = .5*width, .5*height 146 | 147 | # types 148 | NORMAL,ROUNDED,HROUNDED,VROUNDED,CIRCLE = range(5) 149 | 150 | rtyp = NORMAL 151 | if radius > EPSILON: 152 | rtyp = ROUNDED 153 | if radius > hh: 154 | raise OCCError('Height to small for radius') 155 | elif hh - radius < EPSILON or hh - radius == 0.: 156 | rtyp = VROUNDED 157 | radius = hh 158 | 159 | if radius > hw: 160 | raise OCCError('Width to small for radius') 161 | elif hw - radius < EPSILON: 162 | if rtyp == VROUNDED: 163 | rtyp = CIRCLE 164 | else: 165 | rtyp = HROUNDED 166 | radius = hw 167 | elif radius < 0.: 168 | raise OCCError('negative radius not allowed') 169 | else: 170 | radius = 0. 171 | 172 | if rtyp == NORMAL: 173 | p1 = Vertex(-hw,-hh,0.) 174 | p2 = Vertex(+hw,-hh,0.) 175 | p3 = Vertex(+hw,+hh,0.) 176 | p4 = Vertex(-hw,+hh,0.) 177 | e1 = Edge().createLine(p1,p2) 178 | e2 = Edge().createLine(p2,p3) 179 | e3 = Edge().createLine(p3,p4) 180 | e4 = Edge().createLine(p4,p1) 181 | self.createWire((e1,e2,e3,e4)) 182 | 183 | elif rtyp == CIRCLE: 184 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = radius) 185 | self.createWire(e1) 186 | 187 | elif rtyp == VROUNDED: 188 | r = radius 189 | p1 = Vertex(-hw + r,-hh,0.) 190 | p2 = Vertex(+hw - r,-hh,0.) 191 | p3 = Vertex(+hw - r,+hh,0.) 192 | p4 = Vertex(-hw + r,+hh,0.) 193 | 194 | a1 = (hw,0.,0.) 195 | a2 = (-hw,0.,0.) 196 | 197 | e1 = Edge().createLine(p1,p2) 198 | e2 = Edge().createArc3P(p2,p3,a1) 199 | e3 = Edge().createLine(p3,p4) 200 | e4 = Edge().createArc3P(p4,p1,a2) 201 | self.createWire((e1,e2,e3,e4)) 202 | 203 | elif rtyp == HROUNDED: 204 | r = radius 205 | p1 = Vertex(-hw,-hh + r,0.) 206 | p2 = Vertex(+hw,-hh + r,0.) 207 | p3 = Vertex(+hw,+hh - r,0.) 208 | p4 = Vertex(-hw,+hh - r,0.) 209 | 210 | a1 = (0.,-hh,0.) 211 | a2 = (0.,+hh,0.) 212 | 213 | e1 = Edge().createArc3P(p1,p2,a1) 214 | e2 = Edge().createLine(p2,p3) 215 | e3 = Edge().createArc3P(p3,p4,a2) 216 | e4 = Edge().createLine(p4,p1) 217 | 218 | self.createWire((e1,e2,e3,e4)) 219 | 220 | elif rtyp == ROUNDED: 221 | r = radius 222 | p1 = Vertex(-hw + r,-hh + 0,0.) 223 | p2 = Vertex(+hw - r,-hh + 0,0.) 224 | p3 = Vertex(+hw + 0,-hh + r,0.) 225 | p4 = Vertex(+hw + 0,+hh - r,0.) 226 | p5 = Vertex(+hw - r,+hh + 0,0.) 227 | p6 = Vertex(-hw + r,+hh + 0,0.) 228 | p7 = Vertex(-hw + 0,+hh - r,0.) 229 | p8 = Vertex(-hw + 0,-hh + r,0.) 230 | 231 | c1 = (-hw + r,-hh + r,0.) 232 | c2 = (+hw - r,-hh + r,0.) 233 | c3 = (+hw - r,+hh - r,0.) 234 | c4 = (-hw + r,+hh - r,0.) 235 | 236 | e1 = Edge().createArc(p8,p1,c1) 237 | e2 = Edge().createLine(p1,p2) 238 | e3 = Edge().createArc(p2,p3,c2) 239 | e4 = Edge().createLine(p3,p4) 240 | e5 = Edge().createArc(p4,p5,c3) 241 | e6 = Edge().createLine(p5,p6) 242 | e7 = Edge().createArc(p6,p7,c4) 243 | e8 = Edge().createLine(p7,p8) 244 | 245 | self.createWire((e1,e2,e3,e4,e5,e6,e7,e8)) 246 | else: 247 | raise OCCError('Unknown type %d' % rtyp) 248 | 249 | return self 250 | 251 | cpdef createPolygon(self, points, bint close = True): 252 | ''' 253 | Create a polygon from given points. 254 | 255 | :param point: Point sequence. 256 | :param close: Close the polygon. 257 | 258 | example:: 259 | 260 | w1 = Wire().createPolygon(( 261 | (0.,0.,0.), 262 | (0.,0.,1.), 263 | (.75,0.,1.), 264 | (.75,0.,0.)), 265 | close = False 266 | ) 267 | ''' 268 | cdef Edge edge 269 | cdef Vertex first, last, nxt 270 | 271 | first = Vertex(*points[0]) 272 | last = Vertex(*points[1]) 273 | edges = [Edge().createLine(first,last)] 274 | 275 | for point in points[2:]: 276 | nxt = Vertex(*point) 277 | edge = Edge().createLine(last,nxt) 278 | last = nxt 279 | edges.append(edge) 280 | 281 | # close 282 | if close: 283 | edge = Edge().createLine(last,first) 284 | edges.append(edge) 285 | 286 | self.createWire(edges) 287 | return self 288 | 289 | 290 | cpdef createRegularPolygon(self, double radius = 1., int sides = 6, mode = INSCRIBE): 291 | ''' 292 | Create a planar regular polygon in the xy plane centered at (0,0). 293 | 294 | The polygon can either be inscribed or circumscribe the circle by setting 295 | the mode argument. 296 | 297 | :param radius: circle radius 298 | :param sides: number of sides (>3) 299 | :param mode: INSCRIBE or CIRCUMSCRIBE the given circle radius. 300 | 301 | example:: 302 | 303 | w1 = Wire().createRegularPolygon(radius = .5, sides = 6.) 304 | ''' 305 | if sides < 3 or radius < 1e-16: 306 | raise OCCError('Arguments not consistent') 307 | 308 | points = [] 309 | delta = 2.*M_PI/sides 310 | 311 | r = radius 312 | if mode == CIRCUMSCRIBE: 313 | r /= cos(.5*delta) 314 | elif mode != INSCRIBE: 315 | raise OCCError('Unknown mode %s' % mode) 316 | 317 | angle = .5*delta 318 | for i in range(sides): 319 | x = cos(angle)*r 320 | if abs(x - radius) < SQRT_EPSILON: 321 | x = copysign(radius, x) 322 | 323 | y = sin(angle)*r 324 | if abs(y - radius) < SQRT_EPSILON: 325 | y = copysign(radius, y) 326 | 327 | points.append((x, y)) 328 | angle += delta 329 | 330 | self.createPolygon(points) 331 | return self 332 | 333 | cpdef project(self, Face face): 334 | ''' 335 | Project wire towards face. 336 | ''' 337 | cdef c_OCCWire *occ = self.thisptr 338 | cdef int ret 339 | 340 | ret = occ.project(face.thisptr) 341 | if not ret: 342 | raise OCCError(errorMessage) 343 | 344 | return self 345 | 346 | cpdef offset(self, double distance, int joinType = JOINTYPE_ARC): 347 | ''' 348 | Offset wire inplace the given distance. 349 | ''' 350 | cdef c_OCCWire *occ = self.thisptr 351 | cdef int ret 352 | 353 | ret = occ.offset(distance, joinType) 354 | if not ret: 355 | raise OCCError(errorMessage) 356 | 357 | return self 358 | 359 | cpdef fillet(self, radius, vertices = None): 360 | ''' 361 | Fillet vertices inplace. 362 | 363 | :param radius: sequence of radiuses or single radius. 364 | :param vertices: sequence of vertices or single vertex. Setting the 365 | argument to None will select all vertices (default) 366 | ''' 367 | cdef c_OCCWire *occ = self.thisptr 368 | cdef vector[c_OCCVertex *] cvertices 369 | cdef vector[double] cradius 370 | cdef Vertex vertex 371 | cdef double r 372 | cdef int ret 373 | 374 | if vertices is None: 375 | vertices = tuple(VertexIterator(self)) 376 | 377 | elif isinstance(vertices, Vertex): 378 | vertices = (vertices,) 379 | 380 | for vertex in vertices: 381 | cvertices.push_back((vertex.thisptr)) 382 | 383 | if isinstance(radius, (float, int)): 384 | radius = (radius,) 385 | 386 | for r in radius: 387 | cradius.push_back(r) 388 | 389 | ret = occ.fillet(cvertices, cradius) 390 | if not ret: 391 | raise OCCError(errorMessage) 392 | 393 | return self 394 | 395 | cpdef chamfer(self, distance, vertices = None): 396 | ''' 397 | Chamfer vertices inplace. 398 | 399 | :param distance: sequence of distances or single distance. 400 | :param vertices: sequence of vertices or single vertex. Setting the 401 | argument to None will select all vertices (default) 402 | ''' 403 | cdef c_OCCWire *occ = self.thisptr 404 | cdef vector[c_OCCVertex *] cvertices 405 | cdef vector[double] cdistance 406 | cdef Vertex vertex 407 | cdef double dist 408 | cdef int ret 409 | 410 | if vertices is None: 411 | vertices = tuple(VertexIterator(self)) 412 | 413 | elif isinstance(vertices, Vertex): 414 | vertices = (vertices,) 415 | 416 | for vertex in vertices: 417 | cvertices.push_back((vertex.thisptr)) 418 | 419 | if isinstance(distance, (float, int)): 420 | distance = (distance,) 421 | 422 | for dist in distance: 423 | cdistance.push_back(dist) 424 | 425 | ret = occ.chamfer(cvertices, cdistance) 426 | if not ret: 427 | raise OCCError(errorMessage) 428 | 429 | return self 430 | 431 | cpdef cut(self, arg): 432 | ''' 433 | Create boolean difference inplace. 434 | The wire must be planar and the operation 435 | must result in a single wire. 436 | 437 | Multiple objects are supported. 438 | 439 | Edges, wires and faces are extruded in the normal 440 | directions to intersect the wire. 441 | ''' 442 | cdef c_OCCWire *tmp 443 | cdef Face face = Face().createFace(self) 444 | cdef Wire wire 445 | 446 | face.cut(arg) 447 | 448 | it = WireIterator(face) 449 | wire = next(it) 450 | try: 451 | next(it) 452 | except StopIteration: 453 | pass 454 | else: 455 | raise OCCError('multiple wires created') 456 | 457 | return self.copyFrom(wire, False) 458 | 459 | cpdef common(self, arg): 460 | ''' 461 | Create boolean intersection inplace. 462 | The wire must be planar and the operation 463 | must result in a single wire. 464 | 465 | Multiple objects are supported. 466 | 467 | Edges, wires and faces are extruded in the normal 468 | directions to intersect the wire. 469 | ''' 470 | cdef c_OCCWire *tmp 471 | cdef Face face = Face().createFace(self) 472 | cdef Wire wire 473 | 474 | face.common(arg) 475 | 476 | it = WireIterator(face) 477 | wire = next(it) 478 | try: 479 | next(it) 480 | except StopIteration: 481 | pass 482 | else: 483 | raise OCCError('multiple wires created') 484 | 485 | return self.copyFrom(wire, False) 486 | 487 | cpdef double length(self): 488 | ''' 489 | Return wire length 490 | ''' 491 | cdef c_OCCWire *occ = self.thisptr 492 | return occ.length() 493 | 494 | cdef class WireIterator: 495 | ''' 496 | Iterator of wires 497 | ''' 498 | cdef c_OCCWireIterator *thisptr 499 | 500 | def __init__(self, Base arg): 501 | self.thisptr = new c_OCCWireIterator(arg.thisptr) 502 | 503 | def __dealloc__(self): 504 | del self.thisptr 505 | 506 | def __str__(self): 507 | return 'WireIterator%s' % self.__repr__() 508 | 509 | def __repr__(self): 510 | return '()' 511 | 512 | def __iter__(self): 513 | return self 514 | 515 | def __next__(self): 516 | cdef c_OCCWire *nxt = self.thisptr.next() 517 | if nxt == NULL: 518 | raise StopIteration() 519 | 520 | cdef Wire ret = Wire.__new__(Wire) 521 | ret.thisptr = nxt 522 | return ret 523 | 524 | cpdef reset(self): 525 | '''Restart iteration''' 526 | self.thisptr.reset() -------------------------------------------------------------------------------- /occmodel/@src/OCCFace.pxi: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is part of occmodel - See LICENSE.txt 4 | # 5 | cdef class Face(Base): 6 | ''' 7 | Face - Reprecent face geometry 8 | 9 | The face geometry could be represented by several 10 | underlying faces (a OpenCASCADE shell) or a single 11 | face. 12 | ''' 13 | def __init__(self, arg = None): 14 | ''' 15 | Create empty Face or a planar Face from 16 | given closed Edge or Wire. 17 | ''' 18 | self.thisptr = new c_OCCFace() 19 | if not arg is None: 20 | self.createFace(arg) 21 | 22 | def __dealloc__(self): 23 | cdef c_OCCFace *tmp 24 | 25 | if self.thisptr != NULL: 26 | tmp = self.thisptr 27 | del tmp 28 | 29 | def __str__(self): 30 | return "Face%s" % repr(self) 31 | 32 | def __repr__(self): 33 | return "()" 34 | 35 | def __len__(self): 36 | return self.numFaces() 37 | 38 | def __iter__(self): 39 | return WireIterator(self) 40 | 41 | cpdef Face copy(self, bint deepCopy = False): 42 | ''' 43 | Create copy of face 44 | 45 | :param deepCopy: If true a full copy of the underlying geometry 46 | is done. Defaults to False. 47 | ''' 48 | cdef c_OCCFace *occ = self.thisptr 49 | cdef Face ret = Face.__new__(Face, None) 50 | 51 | ret.thisptr = occ.copy(deepCopy) 52 | 53 | return ret 54 | 55 | cpdef int numWires(self): 56 | ''' 57 | Return number of wires 58 | ''' 59 | cdef c_OCCFace *occ = self.thisptr 60 | return occ.numWires() 61 | 62 | cpdef int numFaces(self): 63 | ''' 64 | Return number of faces 65 | ''' 66 | cdef c_OCCFace *occ = self.thisptr 67 | return occ.numFaces() 68 | 69 | cpdef Mesh createMesh(self, double factor = .01, double angle = .25, 70 | bint qualityNormals = False): 71 | ''' 72 | Create triangle mesh of face. 73 | 74 | :param factor: deflection from true position 75 | :param angle: max angle 76 | :param qualityNormals: create normals by evaluating surface parameters 77 | ''' 78 | cdef c_OCCFace *occ = self.thisptr 79 | cdef c_OCCMesh *mesh = occ.createMesh(factor, angle, qualityNormals) 80 | cdef Mesh ret = Mesh.__new__(Mesh, None) 81 | 82 | if mesh == NULL: 83 | raise OCCError(errorMessage) 84 | 85 | ret.thisptr = mesh 86 | ret.setArrays() 87 | return ret 88 | 89 | cpdef createFace(self, arg): 90 | ''' 91 | Create planar face from one or more wires or 92 | closed edges. 93 | 94 | The first argument must be the outer contour. 95 | Additional arguments define holes in the face. 96 | 97 | example:: 98 | 99 | w1 = Wire().createRectangle(width = 1., height = 1., radius = 0.) 100 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = .25) 101 | f1 = Face().createFace((w1, e1)) 102 | ''' 103 | cdef c_OCCFace *occ = self.thisptr 104 | cdef vector[c_OCCWire *] cwires 105 | cdef Wire wire 106 | 107 | if isinstance(arg, (Edge,Wire)): 108 | arg = (arg,) 109 | 110 | ref = [] 111 | for obj in arg: 112 | if isinstance(obj, Edge): 113 | wire = Wire().createWire(obj) 114 | # keep reference to avoid to be bitten by 115 | # the garbage collector. 116 | ref.append(wire) 117 | else: 118 | wire = obj 119 | 120 | if not wire.hasPlane(): 121 | raise OCCError('Plane not defined for object') 122 | 123 | cwires.push_back(wire.thisptr) 124 | 125 | if not occ.createFace(cwires): 126 | raise OCCError(errorMessage) 127 | 128 | return self 129 | 130 | cpdef createConstrained(self, edges, points = None): 131 | ''' 132 | Create general face constrained by edges 133 | and optional points. 134 | 135 | :param edges: sequence of face edges 136 | :param points: optional sequence of point constraints 137 | 138 | example:: 139 | 140 | e1 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = .5) 141 | f1 = Face().createConstrained(e1, ((0.,.0,.25),)) 142 | ''' 143 | cdef Edge edge 144 | cdef c_OCCFace *occ = self.thisptr 145 | cdef vector[c_OCCEdge *] cedges 146 | cdef vector[c_OCCStruct3d] cpoints 147 | cdef c_OCCStruct3d tmp 148 | cdef int ret 149 | 150 | if isinstance(edges, Edge): 151 | edge = edges 152 | cedges.push_back(edge.thisptr) 153 | else: 154 | for edge in edges: 155 | cedges.push_back(edge.thisptr) 156 | 157 | if points: 158 | for point in points: 159 | tmp.x = point[0] 160 | tmp.y = point[1] 161 | tmp.z = point[2] 162 | cpoints.push_back(tmp) 163 | 164 | ret = occ.createConstrained(cedges, cpoints) 165 | if not ret: 166 | raise OCCError(errorMessage) 167 | 168 | if not self.isValid() or self.isNull(): 169 | raise OCCError('Failed to create face') 170 | 171 | return self 172 | 173 | cpdef createPolygonal(self, points): 174 | ''' 175 | Create polygonal face from given 176 | points. The points must lie in a 177 | common plane. 178 | 179 | example:: 180 | 181 | pnts = ((-.5,-.5,0.), (0.,.5,0.), (1.,.5,0.), (.5,-.5,0.)) 182 | f1 = Face().createPolygonal(pnts) 183 | ''' 184 | cdef c_OCCFace *occ = self.thisptr 185 | cdef vector[c_OCCStruct3d] cpoints 186 | cdef c_OCCStruct3d tmp 187 | 188 | for point in points: 189 | tmp.x = point[0] 190 | tmp.y = point[1] 191 | tmp.z = point[2] 192 | cpoints.push_back(tmp) 193 | 194 | if not occ.createPolygonal(cpoints): 195 | raise OCCError(errorMessage) 196 | 197 | if not self.isValid() or self.isNull(): 198 | raise OCCError('Failed to create face') 199 | 200 | return self 201 | 202 | cpdef area(self): 203 | ''' 204 | Return face area 205 | ''' 206 | cdef c_OCCFace *occ = self.thisptr 207 | return occ.area() 208 | 209 | cpdef inertia(self): 210 | ''' 211 | return intertia of face with respect 212 | to center of gravity. 213 | 214 | Return Ixx, Iyy, Izz, Ixy, Ixz, Iyz 215 | ''' 216 | cdef c_OCCFace *occ = self.thisptr 217 | cdef vector[double] res = occ.inertia() 218 | return res[0],res[1],res[2],res[3],res[4],res[5] 219 | 220 | cpdef centreOfMass(self): 221 | ''' 222 | Return center of face 223 | ''' 224 | cdef c_OCCFace *occ = self.thisptr 225 | cdef c_OCCStruct3d cg = occ.centreOfMass() 226 | return cg.x,cg.y,cg.z 227 | 228 | 229 | cpdef offset(self, double offset, double tolerance = 1e-6): 230 | ''' 231 | Offseting face given distance. 232 | 233 | :param offset: offset distance 234 | ''' 235 | cdef c_OCCFace *occ = self.thisptr 236 | cdef int ret 237 | 238 | ret = occ.offset(offset, tolerance) 239 | if not ret: 240 | raise OCCError(errorMessage) 241 | 242 | return self 243 | 244 | cpdef extrude(self, Base shape, p1, p2): 245 | ''' 246 | Create extrusion face. 247 | 248 | :param shape: Wire or edge 249 | :param p1: start point 250 | :param p2: end point. 251 | 252 | example:: 253 | 254 | e1 = Edge().createArc(start = (-.5,-.25,0.), end = (.5,.75,0.), center = (.5,-.25,0.)) 255 | f1 = Face().extrude(e1, (0.,0.,0.), (0.,0.,1.)) 256 | ''' 257 | cdef c_OCCFace *occ = self.thisptr 258 | cdef c_OCCStruct3d cp1, cp2 259 | cdef int ret 260 | 261 | cp1.x = p1[0] 262 | cp1.y = p1[1] 263 | cp1.z = p1[2] 264 | 265 | cp2.x = p2[0] 266 | cp2.y = p2[1] 267 | cp2.z = p2[2] 268 | 269 | ret = occ.extrude(shape.thisptr, cp1, cp2) 270 | if not ret: 271 | raise OCCError(errorMessage) 272 | 273 | return self 274 | 275 | cpdef revolve(self, Base shape, p1, p2, double angle): 276 | ''' 277 | Create by revolving shape. 278 | 279 | :param shape: Edge or wire. 280 | :param p1: Start point of axis 281 | :param p2: End point of axis 282 | :param angle: Angle in radians 283 | 284 | example:: 285 | 286 | pnts = ((0.,0.,0.), (0.,1.,0.), (1.,.5,0.), (1.,0.,0.)) 287 | e1 = Edge().createBezier(points = pnts) 288 | f1 = Face().revolve(e1, (0.,-1.,0.), (1.,-1.,0.), pi/2.) 289 | ''' 290 | cdef c_OCCFace *occ = self.thisptr 291 | cdef c_OCCStruct3d cp1, cp2 292 | cdef int ret 293 | 294 | cp1.x = p1[0] 295 | cp1.y = p1[1] 296 | cp1.z = p1[2] 297 | 298 | cp2.x = p2[0] 299 | cp2.y = p2[1] 300 | cp2.z = p2[2] 301 | 302 | ret = occ.revolve(shape.thisptr, cp1, cp2, angle) 303 | if not ret: 304 | raise OCCError(errorMessage) 305 | 306 | return self 307 | 308 | cpdef sweep(self, spine, profiles, int cornerMode = 0): 309 | ''' 310 | Create face by sweeping along spine through 311 | sequence of shapes. Optionally the start and 312 | end can be a vertex. 313 | 314 | :param spine: Edge or wire to define sweep path 315 | :param profiles: Sequence of edges, wires or optional 316 | start and end vertex. 317 | 318 | example:: 319 | 320 | e1 = Edge().createArc((0.,0.,0.), (1.,0.,1.), (1.,0.,0.)) 321 | e2 = Edge().createCircle(center=(0.,0.,0.),normal=(0.,0.,1.),radius = .25) 322 | f1 = Face().sweep(e1, e2) 323 | ''' 324 | cdef c_OCCFace *occ = self.thisptr 325 | cdef vector[c_OCCBase *] cprofiles 326 | cdef Wire cspine 327 | cdef Base cobj 328 | cdef int ret 329 | 330 | if isinstance(spine, Edge): 331 | cspine = Wire().createWire((spine,)) 332 | else: 333 | cspine = spine 334 | 335 | if not isinstance(profiles, (tuple, list)): 336 | profiles = (profiles,) 337 | 338 | ref = [] 339 | for obj in profiles: 340 | if isinstance(obj, Edge): 341 | obj = Wire().createWire((obj,)) 342 | # keep reference to temporary object 343 | ref.append(obj) 344 | elif not isinstance(obj, (Wire, Vertex)): 345 | raise OCCError('Expected wire, edge or vertex') 346 | cobj = obj 347 | cprofiles.push_back((cobj.thisptr)) 348 | 349 | ret = occ.sweep(cspine.thisptr, cprofiles, cornerMode) 350 | 351 | if not ret: 352 | raise OCCError(errorMessage) 353 | 354 | return self 355 | 356 | cpdef loft(self, profiles, bint ruled = True, double tolerance = 1e-6): 357 | ''' 358 | Create face by lofting through profiles. 359 | 360 | :param profiles: sequence of edges, wires and optional a vertex 361 | at the start and end. 362 | :param ruled: Smooth or ruled result shape 363 | :param tolerance: Operation tolerance. 364 | 365 | example:: 366 | 367 | e1 = Edge().createArc((0.,0.,0.),(1.,0.,1.),(1.,0.,0.)) 368 | e2 = Edge().createArc((0.,1.,0.),(2.,1.,2.),(2.,1.,0.)) 369 | f1 = Face().loft((e1,e2)) 370 | ''' 371 | cdef c_OCCFace *occ = self.thisptr 372 | cdef vector[c_OCCBase *] cprofiles 373 | cdef Base cobj 374 | cdef int ret 375 | 376 | ref = [] 377 | for obj in profiles: 378 | if isinstance(obj, Edge): 379 | obj = Wire().createWire((obj,)) 380 | # keep reference to temporary object 381 | ref.append(obj) 382 | elif not isinstance(obj, (Wire, Vertex)): 383 | raise OCCError('Expected wire, edge or vertex') 384 | cobj = obj 385 | cprofiles.push_back((cobj.thisptr)) 386 | 387 | ret = occ.loft(cprofiles, ruled, tolerance) 388 | 389 | if not ret: 390 | raise OCCError(errorMessage) 391 | 392 | return self 393 | 394 | cdef boolean(self, arg, c_BoolOpType op): 395 | cdef c_OCCFace *occ = self.thisptr 396 | cdef Solid tool 397 | cdef int ret 398 | 399 | if not isinstance(arg, Solid): 400 | if not isinstance(arg, (tuple,list,set)): 401 | args = arg, 402 | else: 403 | args = arg 404 | 405 | solids = [] 406 | origin = Point() 407 | normal = Vector() 408 | 409 | for arg in args: 410 | if isinstance(arg, (Edge,Wire,Face)): 411 | if not arg.hasPlane(origin, normal): 412 | raise OCCError('Plane not defined for object') 413 | 414 | if isinstance(arg, Edge): 415 | wire = Wire().createWire(arg) 416 | elif isinstance(arg, Wire): 417 | wire = arg 418 | 419 | if not isinstance(arg, Face): 420 | face = Face().createFace(wire) 421 | else: 422 | face = arg 423 | 424 | # create infinite cutting object 425 | # in direction of normal 426 | solid = Solid().createPrism(face, normal, True) 427 | 428 | solids.append(solid) 429 | 430 | elif isinstance(arg, Solid): 431 | solids.append(arg) 432 | 433 | else: 434 | raise OCCError('unknown object type %s' % arg) 435 | 436 | if not solids: 437 | raise OCCError('No objects created') 438 | 439 | # create compound of solid objects 440 | tool = Solid().addSolids(solids) 441 | else: 442 | tool = arg 443 | 444 | if op in (BOOL_CUT, BOOL_COMMON): 445 | ret = occ.boolean(tool.thisptr, op) 446 | else: 447 | raise OCCError('uknown operation') 448 | 449 | if not ret: 450 | raise OCCError('Failed to create boolean %s' % op) 451 | 452 | return self 453 | 454 | cpdef cut(self, arg): 455 | ''' 456 | Create boolean difference inplace. 457 | The face must be planar and the operation 458 | must result in a single face. 459 | 460 | Multiple objects are supported. 461 | 462 | Edges, wires and faces are extruded in the normal 463 | directions to intersect the face. 464 | ''' 465 | return self.boolean(arg, BOOL_CUT) 466 | 467 | cpdef common(self, arg): 468 | ''' 469 | Create boolean intersection inplace. 470 | The face must be planar and the operation 471 | must result in a single face. 472 | 473 | Multiple objects are supported. 474 | 475 | Edges, wires and faces are extruded in the normal 476 | directions to intersect the face. 477 | ''' 478 | return self.boolean(arg, BOOL_COMMON) 479 | 480 | cdef class FaceIterator: 481 | ''' 482 | Iterator of faces 483 | ''' 484 | cdef c_OCCFaceIterator *thisptr 485 | 486 | def __init__(self, Base arg): 487 | self.thisptr = new c_OCCFaceIterator(arg.thisptr) 488 | 489 | def __dealloc__(self): 490 | del self.thisptr 491 | 492 | def __str__(self): 493 | return 'FaceIterator%s' % self.__repr__() 494 | 495 | def __repr__(self): 496 | return '()' 497 | 498 | def __iter__(self): 499 | return self 500 | 501 | def __next__(self): 502 | cdef c_OCCFace *nxt = self.thisptr.next() 503 | if nxt == NULL: 504 | raise StopIteration() 505 | 506 | cdef Face ret = Face.__new__(Face) 507 | ret.thisptr = nxt 508 | return ret 509 | 510 | cpdef reset(self): 511 | '''Restart iteration''' 512 | self.thisptr.reset() -------------------------------------------------------------------------------- /occmodel/@src/OCCEdge.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2012 by Runar Tenfjord, Tenko as. 2 | // See LICENSE.txt for details on conditions. 3 | #include "OCCModel.h" 4 | 5 | bool OCCEdge::isSeam(OCCBase *face) { 6 | if (this->getShape().IsNull()) 7 | return false; 8 | return BRep_Tool::IsClosed (this->getEdge(), TopoDS::Face(face->getShape())); 9 | } 10 | 11 | bool OCCEdge::isDegenerated() { 12 | if (this->getShape().IsNull()) 13 | return true; 14 | return BRep_Tool::Degenerated(this->getEdge()); 15 | } 16 | 17 | bool OCCEdge::isClosed() 18 | { 19 | TopoDS_Vertex aV1, aV2; 20 | TopExp::Vertices(this->getEdge(), aV1, aV2); 21 | if (!aV1.IsNull() && !aV2.IsNull() && aV1.IsSame(aV2)) 22 | return true; 23 | return false; 24 | } 25 | 26 | OCCEdge *OCCEdge::copy(bool deepCopy = false) 27 | { 28 | OCCEdge *ret = new OCCEdge(); 29 | try { 30 | if (deepCopy) { 31 | BRepBuilderAPI_Copy A; 32 | A.Perform(this->getEdge()); 33 | ret->setShape(A.Shape()); 34 | } else { 35 | ret->setShape(this->getShape()); 36 | } 37 | } catch(Standard_Failure &err) { 38 | Handle_Standard_Failure e = Standard_Failure::Caught(); 39 | const Standard_CString msg = e->GetMessageString(); 40 | if (msg != NULL && strlen(msg) > 1) { 41 | setErrorMessage(msg); 42 | } else { 43 | setErrorMessage("Failed to copy edge"); 44 | } 45 | return NULL; 46 | } 47 | return ret; 48 | } 49 | 50 | int OCCEdge::numVertices() 51 | { 52 | TopTools_IndexedMapOfShape anIndices; 53 | TopExp::MapShapes(this->getEdge(), TopAbs_VERTEX, anIndices); 54 | return anIndices.Extent(); 55 | } 56 | 57 | OCCTesselation *OCCEdge::tesselate(double angular, double curvature) 58 | { 59 | OCCTesselation *ret = new OCCTesselation(); 60 | try { 61 | Standard_Real start, end; 62 | OCCStruct3f vert; 63 | 64 | TopLoc_Location loc = this->getEdge().Location(); 65 | gp_Trsf location = loc.Transformation(); 66 | 67 | const Handle(Geom_Curve)& curve = BRep_Tool::Curve(this->getEdge(), start, end); 68 | const GeomAdaptor_Curve& aCurve(curve); 69 | 70 | GCPnts_TangentialDeflection TD(aCurve, start, end, angular, curvature); 71 | 72 | for (Standard_Integer i = 1; i <= TD.NbPoints(); i++) 73 | { 74 | gp_Pnt pnt = TD.Value(i).Transformed(location); 75 | vert.x = (float)pnt.X(); 76 | vert.y = (float)pnt.Y(); 77 | vert.z = (float)pnt.Z(); 78 | ret->vertices.push_back(vert); 79 | } 80 | 81 | ret->ranges.push_back(0); 82 | ret->ranges.push_back(ret->vertices.size()); 83 | 84 | } catch(Standard_Failure &err) { 85 | return NULL; 86 | } 87 | return ret; 88 | } 89 | 90 | int OCCEdge::createLine(OCCVertex *start, OCCVertex *end) { 91 | try { 92 | gp_Pnt aP1(start->X(), start->Y(), start->Z()); 93 | gp_Pnt aP2(end->X(), end->Y(), end->Z()); 94 | GC_MakeLine line(aP1, aP2); 95 | this->setShape(BRepBuilderAPI_MakeEdge(line, start->vertex, end->vertex)); 96 | } catch(Standard_Failure &err) { 97 | Handle_Standard_Failure e = Standard_Failure::Caught(); 98 | const Standard_CString msg = e->GetMessageString(); 99 | if (msg != NULL && strlen(msg) > 1) { 100 | setErrorMessage(msg); 101 | } else { 102 | setErrorMessage("Failed to create line"); 103 | } 104 | return 0; 105 | } 106 | return 1; 107 | } 108 | 109 | int OCCEdge::createArc(OCCVertex *start, OCCVertex *end, OCCStruct3d center) { 110 | try { 111 | gp_Pnt aP1(start->X(), start->Y(), start->Z()); 112 | gp_Pnt aP2(center.x, center.y, center.z); 113 | gp_Pnt aP3(end->X(), end->Y(), end->Z()); 114 | 115 | Standard_Real Radius = aP1.Distance(aP2); 116 | gce_MakeCirc MC(aP2,gce_MakePln(aP1, aP2, aP3).Value(), Radius); 117 | const gp_Circ& Circ = MC.Value(); 118 | 119 | Standard_Real Alpha1 = ElCLib::Parameter(Circ, aP1); 120 | Standard_Real Alpha2 = ElCLib::Parameter(Circ, aP3); 121 | Handle(Geom_Circle) C = new Geom_Circle(Circ); 122 | Handle(Geom_TrimmedCurve) arc = new Geom_TrimmedCurve(C, Alpha1, Alpha2, false); 123 | 124 | this->setShape(BRepBuilderAPI_MakeEdge(arc, start->vertex, end->vertex)); 125 | } catch(Standard_Failure &err) { 126 | Handle_Standard_Failure e = Standard_Failure::Caught(); 127 | const Standard_CString msg = e->GetMessageString(); 128 | if (msg != NULL && strlen(msg) > 1) { 129 | setErrorMessage(msg); 130 | } else { 131 | setErrorMessage("Failed to create arc"); 132 | } 133 | return 0; 134 | } 135 | return 1; 136 | } 137 | 138 | int OCCEdge::createArc3P(OCCVertex *start, OCCVertex *end, OCCStruct3d aPoint) { 139 | try { 140 | gp_Pnt aP1(start->X(), start->Y(), start->Z()); 141 | gp_Pnt aP2(aPoint.x, aPoint.y, aPoint.z); 142 | gp_Pnt aP3(end->X(), end->Y(), end->Z()); 143 | GC_MakeArcOfCircle arc(aP1, aP2, aP3); 144 | this->setShape(BRepBuilderAPI_MakeEdge(arc, start->vertex, end->vertex)); 145 | } catch(Standard_Failure &err) { 146 | Handle_Standard_Failure e = Standard_Failure::Caught(); 147 | const Standard_CString msg = e->GetMessageString(); 148 | if (msg != NULL && strlen(msg) > 1) { 149 | setErrorMessage(msg); 150 | } else { 151 | setErrorMessage("Failed to create arc"); 152 | } 153 | return 0; 154 | } 155 | return 1; 156 | } 157 | 158 | int OCCEdge::createCircle(OCCStruct3d center, OCCStruct3d normal, double radius) 159 | { 160 | try { 161 | gp_Pnt aP1(center.x, center.y, center.z); 162 | gp_Dir aD1(normal.x, normal.y, normal.z); 163 | 164 | if (radius <= Precision::Confusion()) { 165 | StdFail_NotDone::Raise("radius to small"); 166 | } 167 | 168 | gce_MakeCirc circle(aP1, aD1, radius); 169 | this->setShape(BRepBuilderAPI_MakeEdge(circle)); 170 | } catch(Standard_Failure &err) { 171 | Handle_Standard_Failure e = Standard_Failure::Caught(); 172 | const Standard_CString msg = e->GetMessageString(); 173 | if (msg != NULL && strlen(msg) > 1) { 174 | setErrorMessage(msg); 175 | } else { 176 | setErrorMessage("Failed to create circle"); 177 | } 178 | return 0; 179 | } 180 | return 1; 181 | } 182 | 183 | int OCCEdge::createEllipse(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, double rMinor) 184 | { 185 | try { 186 | gp_Ax2 ax2(gp_Pnt(pnt.x,pnt.y,pnt.z), gp_Dir(nor.x,nor.y,nor.z)); 187 | 188 | if (rMajor <= Precision::Confusion() || rMinor <= Precision::Confusion()) { 189 | StdFail_NotDone::Raise("radius to small"); 190 | } 191 | 192 | gce_MakeElips ellipse(ax2, rMajor, rMinor); 193 | this->setShape(BRepBuilderAPI_MakeEdge(ellipse)); 194 | } catch(Standard_Failure &err) { 195 | Handle_Standard_Failure e = Standard_Failure::Caught(); 196 | const Standard_CString msg = e->GetMessageString(); 197 | if (msg != NULL && strlen(msg) > 1) { 198 | setErrorMessage(msg); 199 | } else { 200 | setErrorMessage("Failed to create ellipse"); 201 | } 202 | return 0; 203 | } 204 | return 1; 205 | } 206 | 207 | int OCCEdge::createArcOfEllipse(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, 208 | double rMinor, double a1, double a2, bool reverse) 209 | { 210 | try { 211 | gp_Ax2 ax2(gp_Pnt(pnt.x,pnt.y,pnt.z), gp_Dir(nor.x,nor.y,nor.z)); 212 | 213 | if (rMajor <= Precision::Confusion() || rMinor <= Precision::Confusion()) { 214 | StdFail_NotDone::Raise("radius to small"); 215 | } 216 | 217 | gce_MakeElips ellipse(ax2, rMajor, rMinor); 218 | GC_MakeArcOfEllipse arc(ellipse, a1, a2, reverse); 219 | 220 | this->setShape(BRepBuilderAPI_MakeEdge(arc)); 221 | } catch(Standard_Failure &err) { 222 | Handle_Standard_Failure e = Standard_Failure::Caught(); 223 | const Standard_CString msg = e->GetMessageString(); 224 | if (msg != NULL && strlen(msg) > 1) { 225 | setErrorMessage(msg); 226 | } else { 227 | setErrorMessage("Failed to create arc"); 228 | } 229 | return 0; 230 | } 231 | return 1; 232 | } 233 | 234 | int OCCEdge::createArcOfHyperbola(OCCStruct3d pnt, OCCStruct3d nor, double rMajor, 235 | double rMinor, double a1, double a2, bool reverse) 236 | { 237 | try { 238 | gp_Ax2 ax2(gp_Pnt(pnt.x,pnt.y,pnt.z), gp_Dir(nor.x,nor.y,nor.z)); 239 | 240 | if (rMajor <= Precision::Confusion() || rMinor <= Precision::Confusion()) { 241 | StdFail_NotDone::Raise("radius to small"); 242 | } 243 | 244 | gp_Hypr hyperbola(ax2, rMajor, rMinor); 245 | GC_MakeArcOfHyperbola arc(hyperbola, a1, a2, reverse); 246 | 247 | this->setShape(BRepBuilderAPI_MakeEdge(arc)); 248 | } catch(Standard_Failure &err) { 249 | Handle_Standard_Failure e = Standard_Failure::Caught(); 250 | const Standard_CString msg = e->GetMessageString(); 251 | if (msg != NULL && strlen(msg) > 1) { 252 | setErrorMessage(msg); 253 | } else { 254 | setErrorMessage("Failed to create arc"); 255 | } 256 | return 0; 257 | } 258 | return 1; 259 | } 260 | 261 | int OCCEdge::createArcOfParabola(OCCStruct3d pnt, OCCStruct3d nor, double focus, 262 | double a1, double a2, bool reverse) 263 | { 264 | try { 265 | gp_Ax2 ax2(gp_Pnt(pnt.x,pnt.y,pnt.z), gp_Dir(nor.x,nor.y,nor.z)); 266 | 267 | if (focus <= Precision::Confusion()) { 268 | StdFail_NotDone::Raise("focus to small"); 269 | } 270 | 271 | gp_Parab parabola(ax2, focus); 272 | GC_MakeArcOfParabola arc(parabola, a1, a2, reverse); 273 | 274 | this->setShape(BRepBuilderAPI_MakeEdge(arc)); 275 | } catch(Standard_Failure &err) { 276 | Handle_Standard_Failure e = Standard_Failure::Caught(); 277 | const Standard_CString msg = e->GetMessageString(); 278 | if (msg != NULL && strlen(msg) > 1) { 279 | setErrorMessage(msg); 280 | } else { 281 | setErrorMessage("Failed to create arc"); 282 | } 283 | return 0; 284 | } 285 | return 1; 286 | } 287 | 288 | int OCCEdge::createHelix(double pitch, double height, double radius, double angle, bool leftHanded) 289 | { 290 | try { 291 | gp_Ax2 cylAx2(gp_Pnt(0.0,0.0,0.0) , gp::DZ()); 292 | 293 | if (radius <= Precision::Confusion()) { 294 | StdFail_NotDone::Raise("radius to small"); 295 | } 296 | 297 | Handle_Geom_Surface surf; 298 | if (angle <= 0.0) { 299 | surf = new Geom_CylindricalSurface(cylAx2, radius); 300 | } else { 301 | surf = new Geom_ConicalSurface(gp_Ax3(cylAx2), angle, radius); 302 | } 303 | 304 | gp_Pnt2d aPnt(0, 0); 305 | gp_Dir2d aDir(2. * M_PI, pitch); 306 | if (leftHanded) { 307 | aPnt.SetCoord(2. * M_PI, 0.0); 308 | aDir.SetCoord(-2. * M_PI, pitch); 309 | } 310 | gp_Ax2d aAx2d(aPnt, aDir); 311 | 312 | Handle(Geom2d_Line) line = new Geom2d_Line(aAx2d); 313 | gp_Pnt2d pnt_beg = line->Value(0); 314 | gp_Pnt2d pnt_end = line->Value(sqrt(4.0*M_PI*M_PI+pitch*pitch)*(height/pitch)); 315 | const Handle(Geom2d_TrimmedCurve)& segm = GCE2d_MakeSegment(pnt_beg , pnt_end); 316 | 317 | this->setShape(BRepBuilderAPI_MakeEdge(segm , surf)); 318 | BRepLib::BuildCurves3d(edge); 319 | 320 | } catch(Standard_Failure &err) { 321 | Handle_Standard_Failure e = Standard_Failure::Caught(); 322 | const Standard_CString msg = e->GetMessageString(); 323 | if (msg != NULL && strlen(msg) > 1) { 324 | setErrorMessage(msg); 325 | } else { 326 | setErrorMessage("Failed to create helix"); 327 | } 328 | return 0; 329 | } 330 | return 1; 331 | } 332 | 333 | int OCCEdge::createBezier(OCCVertex *start, OCCVertex *end, std::vector points) 334 | { 335 | try { 336 | int nbControlPoints = points.size(); 337 | int vertices = 0; 338 | if (start != NULL && end != NULL) { 339 | vertices = 2; 340 | } 341 | 342 | TColgp_Array1OfPnt ctrlPoints(1, nbControlPoints + vertices); 343 | 344 | int index = 1; 345 | if (vertices) { 346 | ctrlPoints.SetValue(index++, gp_Pnt(start->X(), start->Y(), start->Z())); 347 | } 348 | 349 | for (int i = 0; i < nbControlPoints; i++) { 350 | gp_Pnt aP(points[i].x,points[i].y,points[i].z); 351 | ctrlPoints.SetValue(index++, aP); 352 | } 353 | 354 | if (vertices) 355 | ctrlPoints.SetValue(index++, gp_Pnt(end->X(), end->Y(), end->Z())); 356 | 357 | Handle(Geom_BezierCurve) bezier = new Geom_BezierCurve(ctrlPoints); 358 | 359 | if (vertices) { 360 | this->setShape(BRepBuilderAPI_MakeEdge(bezier, start->vertex, end->vertex)); 361 | } else { 362 | this->setShape(BRepBuilderAPI_MakeEdge(bezier)); 363 | } 364 | 365 | if (this->length() <= Precision::Confusion()) { 366 | StdFail_NotDone::Raise("bezier not valid"); 367 | } 368 | 369 | } catch(Standard_Failure &err) { 370 | Handle_Standard_Failure e = Standard_Failure::Caught(); 371 | const Standard_CString msg = e->GetMessageString(); 372 | if (msg != NULL && strlen(msg) > 1) { 373 | setErrorMessage(msg); 374 | } else { 375 | setErrorMessage("Failed to create bezier"); 376 | } 377 | return 0; 378 | } 379 | return 1; 380 | } 381 | 382 | int OCCEdge::createSpline(OCCVertex *start, OCCVertex *end, std::vector points, 383 | double tolerance) 384 | { 385 | try { 386 | Standard_Boolean periodic = false; 387 | Standard_Real tol = tolerance; 388 | 389 | int vertices = 0; 390 | if (start != NULL && end != NULL) { 391 | vertices = 2; 392 | } 393 | 394 | int nbControlPoints = points.size(); 395 | Handle(TColgp_HArray1OfPnt) ctrlPoints; 396 | ctrlPoints = new TColgp_HArray1OfPnt(1, nbControlPoints + vertices); 397 | 398 | int index = 1; 399 | 400 | if (vertices) { 401 | ctrlPoints->SetValue(index++, gp_Pnt(start->X(), start->Y(), start->Z())); 402 | } 403 | 404 | for (int i = 0; i < nbControlPoints; i++) { 405 | gp_Pnt aP(points[i].x,points[i].y,points[i].z); 406 | ctrlPoints->SetValue(index++, aP); 407 | } 408 | 409 | if (vertices) { 410 | ctrlPoints->SetValue(index++, gp_Pnt(end->X(), end->Y(), end->Z())); 411 | } 412 | 413 | GeomAPI_Interpolate INT(ctrlPoints, periodic, tol); 414 | INT.Perform(); 415 | 416 | Handle(Geom_BSplineCurve) curve = INT.Curve(); 417 | if (vertices) { 418 | this->setShape(BRepBuilderAPI_MakeEdge(curve, start->vertex, end->vertex)); 419 | } else { 420 | this->setShape(BRepBuilderAPI_MakeEdge(curve)); 421 | } 422 | } catch(Standard_Failure &err) { 423 | Handle_Standard_Failure e = Standard_Failure::Caught(); 424 | const Standard_CString msg = e->GetMessageString(); 425 | if (msg != NULL && strlen(msg) > 1) { 426 | setErrorMessage(msg); 427 | } else { 428 | setErrorMessage("Failed to create spline"); 429 | } 430 | return 0; 431 | } 432 | return 1; 433 | } 434 | 435 | int OCCEdge::createNURBS(OCCVertex *start, OCCVertex *end, std::vector points, 436 | DVec knots, DVec weights, IVec mult) 437 | { 438 | try { 439 | Standard_Boolean periodic = false; 440 | 441 | int vertices = 0; 442 | if (start != NULL && end != NULL) { 443 | vertices = 2; 444 | periodic = true; 445 | } 446 | 447 | int nbControlPoints = points.size() + vertices; 448 | TColgp_Array1OfPnt ctrlPoints(1, nbControlPoints); 449 | 450 | TColStd_Array1OfReal _knots(1, knots.size()); 451 | TColStd_Array1OfReal _weights(1, weights.size()); 452 | TColStd_Array1OfInteger _mult(1, mult.size()); 453 | 454 | for (unsigned i = 0; i < knots.size(); i++) { 455 | _knots.SetValue(i+1, knots[i]); 456 | } 457 | 458 | for (unsigned i = 0; i < weights.size(); i++) { 459 | _weights.SetValue(i+1, weights[i]); 460 | } 461 | 462 | int totKnots = 0; 463 | for (unsigned i = 0; i < mult.size(); i++) { 464 | _mult.SetValue(i+1, mult[i]); 465 | totKnots += mult[i]; 466 | } 467 | 468 | const int degree = totKnots - nbControlPoints - 1; 469 | 470 | int index = 1; 471 | 472 | if (!periodic) { 473 | ctrlPoints.SetValue(index++, gp_Pnt(start->X(), start->Y(), start->Z())); 474 | } 475 | 476 | for (unsigned i = 0; i < points.size(); i++) { 477 | gp_Pnt aP(points[i].x,points[i].y,points[i].z); 478 | ctrlPoints.SetValue(index++, aP); 479 | } 480 | 481 | if (!periodic) { 482 | ctrlPoints.SetValue(index++, gp_Pnt(end->X(), end->Y(), end->Z())); 483 | } 484 | 485 | Handle(Geom_BSplineCurve) NURBS = new Geom_BSplineCurve 486 | (ctrlPoints, _weights, _knots, _mult, degree, periodic); 487 | 488 | if (!periodic) { 489 | this->setShape(BRepBuilderAPI_MakeEdge(NURBS, start->vertex, end->vertex)); 490 | } else { 491 | this->setShape(BRepBuilderAPI_MakeEdge(NURBS)); 492 | } 493 | } catch(Standard_Failure &err) { 494 | Handle_Standard_Failure e = Standard_Failure::Caught(); 495 | const Standard_CString msg = e->GetMessageString(); 496 | if (msg != NULL && strlen(msg) > 1) { 497 | setErrorMessage(msg); 498 | } else { 499 | setErrorMessage("Failed to create nurbs"); 500 | } 501 | return 1; 502 | } 503 | return 0; 504 | } 505 | 506 | double OCCEdge::length() { 507 | GProp_GProps prop; 508 | BRepGProp::LinearProperties(this->getEdge(), prop); 509 | return prop.Mass(); 510 | } 511 | --------------------------------------------------------------------------------