Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
837e02328f |
24 changed files with 157 additions and 600 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,4 @@
|
||||||
examples/*.stl
|
examples/*.stl
|
||||||
examples/*.png
|
|
||||||
.coverage
|
.coverage
|
||||||
*.py[oc]
|
*.py[oc]
|
||||||
build
|
build
|
||||||
|
|
|
||||||
27
README.md
27
README.md
|
|
@ -5,33 +5,30 @@
|
||||||
*poc* is a tool in the vein of *OpenSCAD* for creating 3D models in a high
|
*poc* is a tool in the vein of *OpenSCAD* for creating 3D models in a high
|
||||||
level language with a minimum of boilerplate.
|
level language with a minimum of boilerplate.
|
||||||
|
|
||||||
*poc* programs are Python2 programs, executed in an environment that
|
*poc* programs are Python3 programs, executed in an environment that
|
||||||
provides convenient shorthand for performing geometric operations.
|
provides convenient shorthand for performing geometric operations.
|
||||||
|
|
||||||
Python2 is used instead of Python3 because a python3 compatible version of
|
*poc* uses OpenCASCADE (via occmodel) to implement its geometric operations.
|
||||||
vtk is not availble in debian stretch. However, the python3-like features of
|
This means it has different strengths and weaknesses compared to *OpenSCAD*,
|
||||||
`print_function` and `division` are automatically enabled.
|
which uses CGAL. For instance, OpenCASCADE has `fillet` as a first-class
|
||||||
|
operation, while it lacks `minkowski` and `hull` which are quite frequently
|
||||||
*poc* uses OpenCASCADE (via pythonocc-core) to implement its geometric
|
used in *OpenSCAD*.
|
||||||
operations. This means it has different strengths and weaknesses compared to
|
|
||||||
*OpenSCAD*, which uses CGAL. For instance, OpenCASCADE has `fillet` as a
|
|
||||||
first-class operation, while it lacks `minkowski` and `hull` which are quite
|
|
||||||
frequently used in *OpenSCAD*.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
* Install dependencies
|
* Install dependencies
|
||||||
* Run setup, e.g., `sudo python setup.py install`
|
* Run setup, e.g., `sudo python3 setup.py install`
|
||||||
* Invoke `pocview somefile.poc` to lanuch a viewer. It autoupdates if you modify the input file.
|
* Invoke `pocview somefile.poc` to lanuch a viewer. It autoupdates if you modify the input file. Note that some versions of `gltools` create fullscreen windows unconditionally (and iconify them when they lose focus), which is inconvenient for this use.
|
||||||
* Invoke `poc somefile.poc` to create `somefile.stl`
|
* Invoke `poc somefile.poc` to create `somefile.stl`
|
||||||
* or use `#!/usr/bin/env poc` so that `./somefile.poc` is executable
|
* or use `#!/usr/bin/env poc` so that `./somefile.poc` is executable
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
* [OpenCASCADE Community Edition (OCE)](https://github.com/tpaviot/oce)
|
* [OpenCASCADE Community Edition (OCE)](https://github.com/tpaviot/oce/pulls)
|
||||||
* pythonocc-core
|
* [occmodel](https://github.com/tenko/occmodel)
|
||||||
* python-vtk6
|
* [geotools](https://github.com/tenko/geotools)
|
||||||
|
* [gltools](https://github.com/tenko/gltools)
|
||||||
|
|
||||||
# Stability
|
# Stability
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/sh
|
||||||
set -eo pipefail
|
python3-coverage run ./poc || true
|
||||||
python-coverage erase
|
for i in examples/*.poc; do
|
||||||
python-coverage run ./poc || true
|
echo $i
|
||||||
find examples -name \*.poc -print0 | xargs -0n1 -P`getconf _NPROCESSORS_ONLN` python-coverage run -p ./poc
|
python3-coverage run -a ./poc $i || exit $?
|
||||||
#for i in examples/*.poc; do
|
done
|
||||||
# echo $i
|
python3-coverage report -m
|
||||||
# python-coverage run -a ./poc $i || exit $?
|
|
||||||
#done
|
|
||||||
python-coverage combine -a .coverage.*
|
|
||||||
python-coverage report -m --include poctools.py
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
if [ -z "${DISPLAY-}" ]; then
|
|
||||||
exec xvfb-run -s "-screen 0 640x480x24" bash "$0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
find examples -name "*.poc" -print0 | xargs -0 -n1 -P`getconf _NPROCESSORS_ONLN` ./pocimg
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line.
|
||||||
SPHINXOPTS =
|
SPHINXOPTS =
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXBUILD = sphinx3-build
|
||||||
PAPER =
|
PAPER =
|
||||||
BUILDDIR = _build
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
|
|
||||||
28
docs/conf.py
28
docs/conf.py
|
|
@ -18,34 +18,8 @@
|
||||||
#
|
#
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import mock
|
|
||||||
sys.path.insert(0, os.path.abspath('..'))
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
for s in '''
|
sys.modules['occmodel'] = object()
|
||||||
OCC
|
|
||||||
OCC.BRepAlgo
|
|
||||||
OCC.BRepAlgoAPI
|
|
||||||
OCC.BRepBuilderAPI
|
|
||||||
OCC.BRepBndLib
|
|
||||||
OCC.BRepFilletAPI
|
|
||||||
OCC.BRepGProp
|
|
||||||
OCC.BRepLib
|
|
||||||
OCC.BRepOffsetAPI
|
|
||||||
OCC.BRepPrimAPI
|
|
||||||
OCC.BRepTools
|
|
||||||
OCC.Bnd
|
|
||||||
OCC.GC
|
|
||||||
OCC.GCE2d
|
|
||||||
OCC.Geom
|
|
||||||
OCC.Geom2d
|
|
||||||
OCC.gp
|
|
||||||
OCC.GProp
|
|
||||||
OCC.StlAPI
|
|
||||||
OCC.TopAbs
|
|
||||||
OCC.TopExp
|
|
||||||
OCC.TopTools
|
|
||||||
OCC.TopoDS
|
|
||||||
'''.split():
|
|
||||||
sys.modules[s] = mock.Mock()
|
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
poc environment
|
poc environment
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The poc environment is just a Python (2.x) interpreter with some extras
|
The poc environment is just a Python (3.x) interpreter with some extras
|
||||||
preloaded::
|
preloaded::
|
||||||
|
|
||||||
from __future__ import division, print_function
|
|
||||||
from math import *
|
from math import *
|
||||||
|
from geotools import *
|
||||||
|
Xform = Transform
|
||||||
|
from occmodel import *
|
||||||
from poctools import *
|
from poctools import *
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,9 @@ Examples
|
||||||
Selective fillet
|
Selective fillet
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. literalinclude:: ../examples/selective_fillet.poc
|
.. include:: ../examples/selective_fillet.poc
|
||||||
:language: python
|
:code: python
|
||||||
:linenos:
|
|
||||||
|
|
||||||
.. image:: ../images/selective_fillet.png
|
.. image:: ../images/selective_fillet.png
|
||||||
:alt: The result of rendering selective_fillet.poc. All edges entirely above the centerline of the object are filleted.
|
|
||||||
:width: 75%
|
|
||||||
|
|
||||||
For more, see the 'examples' directory in the source distribution.
|
For more, see the 'examples' directory in the source distribution.
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ Python OCE Composer (poc)
|
||||||
*poc* is a tool in the vein of *OpenSCAD* for creating 3D models in a high
|
*poc* is a tool in the vein of *OpenSCAD* for creating 3D models in a high
|
||||||
level language with a minimum of boilerplate.
|
level language with a minimum of boilerplate.
|
||||||
|
|
||||||
*poc* programs are Python2 programs, executed in an environment that
|
*poc* programs are Python3 programs, executed in an environment that
|
||||||
provides convenient shorthand for performing geometric operations.
|
provides convenient shorthand for performing geometric operations.
|
||||||
|
|
||||||
*poc* uses OpenCASCADE to implement its geometric operations.
|
*poc* uses OpenCASCADE (via occmodel) to implement its geometric operations.
|
||||||
This means it has different strengths and weaknesses compared to *OpenSCAD*,
|
This means it has different strengths and weaknesses compared to *OpenSCAD*,
|
||||||
which uses CGAL. For instance, OpenCASCADE has `fillet` as a first-class
|
which uses CGAL. For instance, OpenCASCADE has `fillet` as a first-class
|
||||||
operation, while it lacks `minkowski` and `hull` which are quite frequently
|
operation, while it lacks `minkowski` and `hull` which are quite frequently
|
||||||
|
|
@ -21,7 +21,7 @@ used in *OpenSCAD*.
|
||||||
The the design of the *poc* standard library is very much in flux, and
|
The the design of the *poc* standard library is very much in flux, and
|
||||||
there are likely to be compatibility-breaking changes as it develops.
|
there are likely to be compatibility-breaking changes as it develops.
|
||||||
|
|
||||||
For installation and setup instructions, see README.md.
|
Contents:
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ poctools module
|
||||||
|
|
||||||
.. automodule:: poctools
|
.. automodule:: poctools
|
||||||
|
|
||||||
Solid Primitives
|
Primitives
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
Each primitive is added to the currently active group operation.
|
Each primitive is added to the currently active group operation.
|
||||||
At the outermost level, the active group operation is union().
|
At the outermost level, the active group operation is union().
|
||||||
|
|
||||||
|
|
@ -12,35 +12,13 @@ At the outermost level, the active group operation is union().
|
||||||
.. autofunction:: Cylinder
|
.. autofunction:: Cylinder
|
||||||
.. autofunction:: Cone
|
.. autofunction:: Cone
|
||||||
.. autofunction:: Sphere
|
.. autofunction:: Sphere
|
||||||
|
.. autofunction:: Text
|
||||||
.. autofunction:: Torus
|
.. autofunction:: Torus
|
||||||
.. autofunction:: Extrude
|
.. autofunction:: Extrude
|
||||||
.. autofunction:: Loft
|
.. autofunction:: Loft
|
||||||
.. autofunction:: Pipe
|
.. autofunction:: Pipe
|
||||||
.. autofunction:: Revolve
|
.. autofunction:: Revolve
|
||||||
|
|
||||||
Other primitives
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
These classes have classmethods to construct objects of the given
|
|
||||||
type. They may be useful in constructing solid primitives;
|
|
||||||
for example, `poctools.Loft` needs a sequence of `Edge` objects.
|
|
||||||
|
|
||||||
This syntax is for compatibility with `occmodel`. A future improvement
|
|
||||||
to `poc` should change them so that e.g., `Loft` becomes a group operation,
|
|
||||||
and `Edge` becomes a solid primitive that implicitly adds itself to the
|
|
||||||
surrounding `Loft`.
|
|
||||||
|
|
||||||
.. autoclass:: Edge
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
.. autoclass:: Face
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
.. autoclass:: Wire
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
||||||
.. autofunction:: Vertex
|
|
||||||
.. autofunction:: Matrix
|
|
||||||
|
|
||||||
Postfix operations
|
Postfix operations
|
||||||
~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~
|
||||||
A postfix operation modifies the currently active group operation
|
A postfix operation modifies the currently active group operation
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,3 @@ Execute *input.poc* and show the result onscreen. When *input.poc* is
|
||||||
modified, **pocview** updates the preview.
|
modified, **pocview** updates the preview.
|
||||||
|
|
||||||
|
|
||||||
Program: pocimg
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Usage: **pocimg** *input.poc* *optional-args...*
|
|
||||||
|
|
||||||
Execute *input.poc* and render an image of it to *input.png. On
|
|
||||||
Linux, needs an X server; you can use `xvfb` for this purpose if you
|
|
||||||
need to run in a headless fashion::
|
|
||||||
|
|
||||||
xvfb-run -s "-screen 0 640x480x24" pocimg ...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
# This example isn't useful, except that it increases code coverage
|
# This example isn't useful, except that it increases code coverage
|
||||||
|
import geotools
|
||||||
with Union():
|
with Union():
|
||||||
with Rotated(1, (0,0,1)):
|
with Rotated(1, (0,0,1)):
|
||||||
Sphere((0,0,0), 6)
|
Sphere((0,0,0), 6)
|
||||||
with Filleted(.5), Transformed(Matrix()), Chamfered(2):
|
with Filleted(2), Transformed(geotools.Transform()):
|
||||||
Box((-5,-5,-5), (5,5,5))
|
Box((-5,-5,-5), (5,5,5))
|
||||||
print(Bbox())
|
print(Bbox())
|
||||||
print(CenterOfMass())
|
print(CenterOfMass())
|
||||||
|
|
@ -11,31 +12,3 @@ with Union():
|
||||||
print(len(list(Wires())))
|
print(len(list(Wires())))
|
||||||
|
|
||||||
Text(8, .25, 'Hello_world')
|
Text(8, .25, 'Hello_world')
|
||||||
|
|
||||||
import poctools
|
|
||||||
poctools._dir(poctools._dir((1,2,3)))
|
|
||||||
poctools._vec(poctools._vec((1,2,3)))
|
|
||||||
poctools._pt(poctools._pt((1,2,3)))
|
|
||||||
poctools.Matrix(
|
|
||||||
(1,0,0),
|
|
||||||
(0,1,0),
|
|
||||||
(0,0,1))
|
|
||||||
poctools.Matrix(
|
|
||||||
(1,0,0,0),
|
|
||||||
(0,1,0,0),
|
|
||||||
(0,0,1,0))
|
|
||||||
poctools.Matrix(
|
|
||||||
1,0,0,0,
|
|
||||||
0,1,0,0,
|
|
||||||
0,0,1,0)
|
|
||||||
poctools.Matrix(
|
|
||||||
1,0,0,
|
|
||||||
0,1,0,
|
|
||||||
0,0,1)
|
|
||||||
poctools._dump(Object())
|
|
||||||
try:
|
|
||||||
poctools.do_op(None)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
1/0
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
e1 = Edge().createLine((-.5,0.,0.),(.5,0.,0.))
|
e1 = Edge().createLine((-.5,0.,0.),(.5,0.,0.))
|
||||||
e2 = Edge().createArc3P((.5,0.,0.),(-.5,0.,0.),(0.,.5,0.))
|
e2 = Edge().createArc3P((.5,0.,0.),(-.5,0.,0.),(0.,.5,0.))
|
||||||
w1 = Wire().createWire((e1,e2))
|
w1 = Wire().createWire((e1,e2))
|
||||||
x = Edge().createCircle((0,.25,0), (0,0,1), (.1))
|
f1 = Face().createFace(w1)
|
||||||
f1 = Face().createFace((w1, x))
|
|
||||||
#do_op(f1)
|
|
||||||
Extrude(f1, (0.,0.,0.), (0.,0.,1.))
|
Extrude(f1, (0.,0.,0.), (0.,0.,1.))
|
||||||
Fillet(.05)
|
Fillet(.05)
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,4 @@ e1 = Edge().createCircle(center=(.25,0.,0.),normal=(-.25,0.,1.),radius = .25)
|
||||||
e2 = Edge().createCircle(center=(.25,0.,.5),normal=(0.,.25,1.),radius = .5)
|
e2 = Edge().createCircle(center=(.25,0.,.5),normal=(0.,.25,1.),radius = .5)
|
||||||
v1 = Vertex(.25,0.,1.)
|
v1 = Vertex(.25,0.,1.)
|
||||||
|
|
||||||
Loft((e1, Wire.createWire((e2)), v1))
|
Loft((e1, e2, v1))
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
|
Fudge = Xform().scale((1+1e-7, 1+1e-7, 1+1e-7))
|
||||||
|
|
||||||
e1 = Edge().createHelix(.5, 4, 1, 0)
|
e1 = Edge().createHelix(.5, 4, 1, 0)
|
||||||
e2 = Edge().createCircle(center=(1.,0.,0.),normal=(0.,-1.,0.),radius = 0.2)
|
e2 = Edge().createCircle(center=(1.,0.,0.),normal=(0.,-1.,0.),radius = 0.2)
|
||||||
f1 = Face().createFace(e2)
|
f1 = Face().createFace(e2)
|
||||||
|
|
||||||
e1b = Edge().createHelix(.5, 4, 1, 15, True)
|
|
||||||
with Translated((10,0,0)):
|
|
||||||
Pipe(f1, Wire.createWire(e1b))
|
|
||||||
|
|
||||||
with Difference():
|
with Difference():
|
||||||
Cylinder((0,0,-.5), (0,0,3.5), 1)
|
Cylinder((0,0,-.5), (0,0,3.5), 1)
|
||||||
Pipe(f1, e1)
|
Pipe(f1, e1)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env poc
|
#!/usr/bin/env poc
|
||||||
|
import geotools
|
||||||
|
|
||||||
with Translated((0,0,0)):
|
with Translated((0,0,0)):
|
||||||
Sphere((0,0,0), 12)
|
Sphere((0,0,0), 12)
|
||||||
|
|
||||||
|
|
@ -8,7 +10,7 @@ with Translated((-20,0,0)):
|
||||||
with Translated((20,0,0)), Filleted(1):
|
with Translated((20,0,0)), Filleted(1):
|
||||||
Cylinder((0,0,-5), (0,0,5), 5)
|
Cylinder((0,0,-5), (0,0,5), 5)
|
||||||
|
|
||||||
with Translated((0,20,0)), Chamfered(2, lambda e: Bbox(e)[2] < 0):
|
with Translated((0,20,0)), Chamfered(1, lambda e: e.boundingBox().max.z < 0):
|
||||||
Cone((0,0,-5), (0,0,5), 5, 2)
|
Cone((0,0,-5), (0,0,5), 5, 2)
|
||||||
|
|
||||||
with Translated((0,-20,0)):
|
with Translated((0,-20,0)):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
e = Edge().createEllipse(center=(0.,0.,0.),normal=(0.,0.,1.), rMajor = .5, rMinor=.2)
|
e = Edge().createEllipse(center=(0.,0.,0.),normal=(0.,0.,1.), rMajor = .5, rMinor=.2)
|
||||||
f = Face().createFace(e)
|
f = Face().createFace(e)
|
||||||
Revolve(f, (1.,0.,0.), (1.,1.,0.), 90)
|
Revolve(f, (1.,0.,0.), (1.,1.,0.), pi/2.)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,4 @@ with Difference():
|
||||||
Cylinder((-100,0,0), (100,0,0), 25)
|
Cylinder((-100,0,0), (100,0,0), 25)
|
||||||
Cylinder((0,-100,0), (0,100,0), 25)
|
Cylinder((0,-100,0), (0,100,0), 25)
|
||||||
Cylinder((0,0,-100), (0,0,100), 25)
|
Cylinder((0,0,-100), (0,0,100), 25)
|
||||||
Fillet(12, lambda e: Bbox(e)[2] > 0)
|
Fillet(12, lambda e: e.boundingBox().min.z > 0)
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 86 KiB |
2
poc
2
poc
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python2
|
#!/usr/bin/python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# main module of 'poc' modeling program
|
# main module of 'poc' modeling program
|
||||||
# Copyright © 2017 Jeff Epler <jepler@gmail.com>
|
# Copyright © 2017 Jeff Epler <jepler@gmail.com>
|
||||||
|
|
|
||||||
83
pocimg
83
pocimg
|
|
@ -1,83 +0,0 @@
|
||||||
#!/usr/bin/python2
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# auto-updating viewer of 'poc' modeling program
|
|
||||||
# Copyright © 2017 Jeff Epler <jepler@gmail.com>
|
|
||||||
#
|
|
||||||
# This program is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from occmodelviewer import Viewer
|
|
||||||
import os
|
|
||||||
import poctools
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
import vtk
|
|
||||||
|
|
||||||
filename = sys.argv[1]
|
|
||||||
|
|
||||||
with poctools.TemporaryDirectory() as d:
|
|
||||||
stlfile = os.path.join(d, "out.stl")
|
|
||||||
ns = poctools.execpoc(sys.argv[1:])
|
|
||||||
poctools.output(stlfile)
|
|
||||||
|
|
||||||
reader = vtk.vtkSTLReader()
|
|
||||||
reader.SetFileName(stlfile)
|
|
||||||
|
|
||||||
mapper = vtk.vtkPolyDataMapper()
|
|
||||||
mapper.SetInputConnection(reader.GetOutputPort())
|
|
||||||
|
|
||||||
actor = vtk.vtkActor()
|
|
||||||
actor.SetMapper(mapper)
|
|
||||||
actor.GetBounds() # forces STL file to be read now
|
|
||||||
|
|
||||||
b = poctools.Bbox(poctools.Object())
|
|
||||||
diag = ((b[3] - b[0]) ** 2 + (b[4] - b[1]) ** 2 + (b[5] - b[2]) ** 2) ** .5
|
|
||||||
center = ((b[0] + b[3]) / 2, (b[4] + b[1]) / 2, (b[5]+ b[2]) / 2)
|
|
||||||
|
|
||||||
ren = vtk.vtkRenderer()
|
|
||||||
ren.AddActor(actor)
|
|
||||||
ren.SetBackground(0,0,.5)
|
|
||||||
ren.SetBackground2(.1,.1,.2)
|
|
||||||
ren.SetGradientBackground(True)
|
|
||||||
|
|
||||||
camera = vtk.vtkCamera()
|
|
||||||
camera.SetFocalPoint(center)
|
|
||||||
camera.SetViewUp((0,0,1))
|
|
||||||
camera.ParallelProjectionOff()
|
|
||||||
camera.Azimuth(-37.5)
|
|
||||||
camera.Elevation(30)
|
|
||||||
camera.SetPosition(1.4*diag, diag, diag)
|
|
||||||
|
|
||||||
ren.SetActiveCamera(camera)
|
|
||||||
ren.ResetCamera()
|
|
||||||
|
|
||||||
renWin = vtk.vtkRenderWindow()
|
|
||||||
renWin.OffScreenRenderingOn()
|
|
||||||
renWin.AddRenderer(ren)
|
|
||||||
|
|
||||||
|
|
||||||
writer = vtk.vtkPNGWriter()
|
|
||||||
|
|
||||||
renWin.Render()
|
|
||||||
|
|
||||||
w2if = vtk.vtkWindowToImageFilter()
|
|
||||||
w2if.SetInput(renWin)
|
|
||||||
w2if.ReadFrontBufferOff()
|
|
||||||
w2if.SetMagnification(3)
|
|
||||||
w2if.Update()
|
|
||||||
|
|
||||||
writer = vtk.vtkPNGWriter()
|
|
||||||
writer.SetFileName(os.path.splitext(filename)[0] + ".png")
|
|
||||||
writer.SetInputConnection(w2if.GetOutputPort())
|
|
||||||
writer.Write()
|
|
||||||
410
poctools.py
410
poctools.py
|
|
@ -19,70 +19,13 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
import __future__
|
import __future__
|
||||||
import itertools
|
import itertools
|
||||||
|
import geotools
|
||||||
import math
|
import math
|
||||||
|
import occmodel
|
||||||
import os
|
import os
|
||||||
import six
|
import six
|
||||||
import shutil
|
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
|
||||||
import traceback
|
|
||||||
|
|
||||||
import OCC.BRepAlgo
|
|
||||||
import OCC.BRepAlgoAPI
|
|
||||||
import OCC.BRepBuilderAPI
|
|
||||||
import OCC.BRepBndLib
|
|
||||||
import OCC.BRepFilletAPI
|
|
||||||
import OCC.BRepGProp
|
|
||||||
import OCC.BRepLib
|
|
||||||
import OCC.BRepOffsetAPI
|
|
||||||
import OCC.BRepPrimAPI
|
|
||||||
import OCC.BRepTools
|
|
||||||
import OCC.Bnd
|
|
||||||
import OCC.GC
|
|
||||||
import OCC.GCE2d
|
|
||||||
import OCC.Geom
|
|
||||||
import OCC.Geom2d
|
|
||||||
import OCC.gp
|
|
||||||
import OCC.GProp
|
|
||||||
import OCC.StlAPI
|
|
||||||
import OCC.TopAbs
|
|
||||||
import OCC.TopExp
|
|
||||||
import OCC.TopTools
|
|
||||||
import OCC.TopoDS
|
|
||||||
|
|
||||||
def _dir(x):
|
|
||||||
if isinstance(x, (tuple, list)):
|
|
||||||
return OCC.gp.gp_Dir(*x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
def _vec(x):
|
|
||||||
if isinstance(x, (tuple, list)):
|
|
||||||
return OCC.gp.gp_Vec(*x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
def _pt(x):
|
|
||||||
if isinstance(x, (tuple, list)):
|
|
||||||
return OCC.gp.gp_Pnt(*x)
|
|
||||||
return x
|
|
||||||
|
|
||||||
def _axpt(p1, p2):
|
|
||||||
p1 = _pt(p1)
|
|
||||||
p2 = _pt(p2)
|
|
||||||
dx = p2.X() - p1.X()
|
|
||||||
dy = p2.Y() - p1.Y()
|
|
||||||
dz = p2.Z() - p1.Z()
|
|
||||||
length = (dx*dx + dy*dy + dz*dz) ** .5
|
|
||||||
return OCC.gp.gp_Ax2(p1, OCC.gp.gp_Dir(dx/length, dy/length, dz/length))
|
|
||||||
|
|
||||||
def _axcn(p, n):
|
|
||||||
p = _pt(p)
|
|
||||||
n = _pt(n)
|
|
||||||
dx = n.X()
|
|
||||||
dy = n.Y()
|
|
||||||
dz = n.Z()
|
|
||||||
length = (dx*dx + dy*dy + dz*dz) ** .5
|
|
||||||
return OCC.gp.gp_Ax2(p, OCC.gp.gp_Dir(dx/length, dy/length, dz/length))
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'Box', 'Cylinder', 'Cone', 'Sphere', 'Text', 'Torus',
|
'Box', 'Cylinder', 'Cone', 'Sphere', 'Text', 'Torus',
|
||||||
|
|
@ -92,7 +35,6 @@ __all__ = [
|
||||||
'Intersection', 'Difference', 'Union', 'Op',
|
'Intersection', 'Difference', 'Union', 'Op',
|
||||||
'Object', 'Bbox', 'CenterOfMass', 'CentreOfMass',
|
'Object', 'Bbox', 'CenterOfMass', 'CentreOfMass',
|
||||||
'Edges', 'Faces', 'Vertices', 'Wires',
|
'Edges', 'Faces', 'Vertices', 'Wires',
|
||||||
'Matrix', 'Vertex', 'Edge', 'Wire', 'Face',
|
|
||||||
'execpoc', 'occ_to_stl', 'do_op',
|
'execpoc', 'occ_to_stl', 'do_op',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -106,6 +48,9 @@ def initial_ns():
|
||||||
}
|
}
|
||||||
six.exec_("""if 1:
|
six.exec_("""if 1:
|
||||||
from math import *
|
from math import *
|
||||||
|
from geotools import *
|
||||||
|
Xform = Transform
|
||||||
|
from occmodel import *
|
||||||
from poctools import *
|
from poctools import *
|
||||||
""", ns)
|
""", ns)
|
||||||
return ns
|
return ns
|
||||||
|
|
@ -114,8 +59,7 @@ compile_flags = (__future__.division.compiler_flag
|
||||||
| __future__.print_function.compiler_flag)
|
| __future__.print_function.compiler_flag)
|
||||||
|
|
||||||
def getsource(filename):
|
def getsource(filename):
|
||||||
with open(filename, "rU") as f:
|
with open(filename, "rU") as f: return f.read()
|
||||||
return f.read()
|
|
||||||
|
|
||||||
def execpoc(args, **kw):
|
def execpoc(args, **kw):
|
||||||
"""Execute the named .poc file from disk
|
"""Execute the named .poc file from disk
|
||||||
|
|
@ -137,158 +81,127 @@ Returns the resulting top level object"""
|
||||||
|
|
||||||
def do_op(b):
|
def do_op(b):
|
||||||
"""Adds the object 'b' to the current operation"""
|
"""Adds the object 'b' to the current operation"""
|
||||||
if b is None:
|
if b is None: raise ValueError
|
||||||
raise ValueError
|
|
||||||
n = next(op)
|
n = next(op)
|
||||||
n(obj, b)
|
n(obj, b)
|
||||||
|
|
||||||
def _assign(a, b):
|
def _assign(a, b):
|
||||||
global obj
|
return a.copyFrom(b)
|
||||||
obj = b
|
|
||||||
|
|
||||||
def _fuse(a, b):
|
def _fuse(a, b):
|
||||||
global obj
|
return a.fuse(b)
|
||||||
obj = OCC.BRepAlgoAPI.BRepAlgoAPI_Fuse(a, b).Shape()
|
|
||||||
|
|
||||||
def _common(a, b):
|
def _common(a, b):
|
||||||
global obj
|
return a.common(b)
|
||||||
obj = OCC.BRepAlgoAPI.BRepAlgoAPI_Common(a, b).Shape()
|
|
||||||
|
|
||||||
def _cut(a, b):
|
def _cut(a, b):
|
||||||
global obj
|
return a.cut(b)
|
||||||
obj = OCC.BRepAlgoAPI.BRepAlgoAPI_Cut(a, b).Shape()
|
|
||||||
|
|
||||||
def op1(x):
|
def op1(x):
|
||||||
return iter(itertools.chain([_assign], itertools.repeat(x)))
|
return iter(itertools.chain([_assign], itertools.repeat(x)))
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
global obj, op
|
global obj, op
|
||||||
obj = OCC.TopoDS.TopoDS_Shape()
|
obj = occmodel.Solid()
|
||||||
op = op1(_fuse)
|
op = op1(_fuse)
|
||||||
|
|
||||||
def output(fn):
|
def output(fn):
|
||||||
occ_to_stl(obj, fn)
|
with open(fn + ".tmp", "wb") as f: occ_to_stl(obj, f)
|
||||||
|
os.rename(fn + ".tmp", fn)
|
||||||
|
|
||||||
def occ_to_stl(obj, filename, prec=.05):
|
def mesh_to_stl(m, dest):
|
||||||
"""Convert a solid to stl"""
|
dest.write(b"\0" * 80)
|
||||||
w = OCC.StlAPI.StlAPI_Writer()
|
dest.write(struct.pack("<i", m.ntriangles()))
|
||||||
w.SetASCIIMode(False)
|
|
||||||
w.SetDeflection(prec)
|
n0 = struct.pack("<fff", 0., 0., 0.)
|
||||||
w.SetRelativeMode(False)
|
for i in range(0, m.ntriangles()*3, 3):
|
||||||
w.Write(obj, filename + ".tmp", True)
|
dest.write(n0)
|
||||||
os.rename(filename + ".tmp", filename)
|
dest.write(struct.pack("<fff", *m.vertex(m.triangles[i])))
|
||||||
|
dest.write(struct.pack("<fff", *m.vertex(m.triangles[i+1])))
|
||||||
|
dest.write(struct.pack("<fff", *m.vertex(m.triangles[i+2])))
|
||||||
|
dest.write(b'\0\0')
|
||||||
|
|
||||||
|
def occ_to_stl(o, dest, prec=.001):
|
||||||
|
"""Convert a mesh or solid to stl
|
||||||
|
|
||||||
|
Writes to the open file object 'dest'
|
||||||
|
|
||||||
|
If a solid is passed it, it is converted to a mesh with the given
|
||||||
|
precision, defaulting to .001."""
|
||||||
|
if isinstance(o, occmodel.Mesh): mesh_to_stl(o, dest)
|
||||||
|
mesh_to_stl(o.createMesh(prec), dest)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def withhelper(newop, newobj=None, finalop=None):
|
def withhelper(newop, newobj=None, finalop=None):
|
||||||
global obj, op
|
global obj, op
|
||||||
holdobj = obj
|
holdobj = obj
|
||||||
holdop = op
|
holdop = op
|
||||||
obj = newobj = newobj or OCC.TopoDS.TopoDS_Shape()
|
obj = newobj = newobj or occmodel.Solid()
|
||||||
op = iter(newop)
|
op = iter(newop)
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
if finalop: finalop()
|
if finalop: finalop()
|
||||||
newobj = obj
|
|
||||||
obj = holdobj
|
obj = holdobj
|
||||||
op = holdop
|
op = holdop
|
||||||
do_op(newobj)
|
do_op(newobj)
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def TemporaryDirectory(*args):
|
|
||||||
d = tempfile.mkdtemp(*args)
|
|
||||||
try:
|
|
||||||
yield d
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(d)
|
|
||||||
|
|
||||||
### Primitives
|
### Primitives
|
||||||
|
|
||||||
def Box(p1, p2):
|
def Box(p1, p2):
|
||||||
"""Create a box primitive"""
|
"""Create a box primitive"""
|
||||||
do_op(OCC.BRepPrimAPI.BRepPrimAPI_MakeBox(_pt(p1), _pt(p2)).Shape())
|
do_op(occmodel.Solid().createBox(p1, p2))
|
||||||
|
|
||||||
def Cylinder(p1, p2, radius):
|
def Cylinder(p1, p2, radius):
|
||||||
"""Create a cylinder primitive"""
|
"""Create a cylinder primitive"""
|
||||||
p1 = _pt(p1)
|
do_op(occmodel.Solid().createCylinder(p1, p2, radius))
|
||||||
p2 = _pt(p2)
|
|
||||||
dx = p2.X() - p1.X()
|
|
||||||
dy = p2.Y() - p1.Y()
|
|
||||||
dz = p2.Z() - p1.Z()
|
|
||||||
length = (dx*dx + dy*dy + dz*dz) ** .5
|
|
||||||
ax = OCC.gp.gp_Ax2(p1, OCC.gp.gp_Dir(dx/length, dy/length, dz/length))
|
|
||||||
do_op(OCC.BRepPrimAPI.BRepPrimAPI_MakeCylinder(ax, radius, length).Shape())
|
|
||||||
|
|
||||||
def Cone(p1, p2, radius1, radius2):
|
def Cone(p1, p2, radius1, radius2):
|
||||||
"""Create a cone primitive"""
|
"""Create a cone primitive"""
|
||||||
p1 = _pt(p1)
|
do_op(occmodel.Solid().createCone(p1, p2, radius1, radius2))
|
||||||
p2 = _pt(p2)
|
|
||||||
dx = p2.X() - p1.X()
|
|
||||||
dy = p2.Y() - p1.Y()
|
|
||||||
dz = p2.Z() - p1.Z()
|
|
||||||
length = (dx*dx + dy*dy + dz*dz) ** .5
|
|
||||||
ax = OCC.gp.gp_Ax2(p1, OCC.gp.gp_Dir(dx/length, dy/length, dz/length))
|
|
||||||
builder = OCC.BRepPrimAPI.BRepPrimAPI_MakeCone(
|
|
||||||
ax, radius1, radius2, length)
|
|
||||||
shape = builder.Shape()
|
|
||||||
do_op(shape)
|
|
||||||
|
|
||||||
def Sphere(center, radius):
|
def Sphere(center, radius):
|
||||||
"""Create a sphere primitive"""
|
"""Create a sphere primitive"""
|
||||||
do_op(OCC.BRepPrimAPI.BRepPrimAPI_MakeSphere(_pt(center), radius).Shape())
|
e1 = occmodel.Edge().createArc((1,0,0), (0,1,0), (0,0,0))
|
||||||
|
e2 = occmodel.Edge().createLine((0,1,0), (0,0,0))
|
||||||
|
e3 = occmodel.Edge().createLine((0,0,0), (1,0,0))
|
||||||
|
w1 = occmodel.Wire().createWire((e1, e2, e3))
|
||||||
|
f1 = occmodel.Face().createFace(w1)
|
||||||
|
|
||||||
|
o = occmodel.Solid()
|
||||||
|
o.revolve(f1, (0,0,0), (1,0,0), 2*math.pi)
|
||||||
|
p = o.copy().mirror(geotools.Plane.fromNormal((0,0,0), (1,0,0)))
|
||||||
|
o.fuse(p)
|
||||||
|
o.scale((0,0,0), radius)
|
||||||
|
o.translate(center)
|
||||||
|
do_op(o)
|
||||||
|
|
||||||
def Text(height, depth, text, fontpath=None):
|
def Text(height, depth, text, fontpath=None):
|
||||||
"""TODO Create extruded text
|
"""Create extruded text
|
||||||
|
|
||||||
Note that the text may not contain whitespace!
|
Note that the text may not contain whitespace!
|
||||||
(this appears to be a bug in occmodel, failing with occmodel.OCCError:
|
(this appears to be a bug in occmodel, failing with occmodel.OCCError:
|
||||||
b'failed to create edges')"""
|
b'failed to create edges')"""
|
||||||
return Box((0,0,0), (1,1,1))
|
do_op(occmodel.Solid().createText(height, depth, text, fontpath))
|
||||||
|
|
||||||
def Torus(p1, p2, ringRadius, radius):
|
def Torus(p1, p2, ringRadius, radius):
|
||||||
"""Create a torus"""
|
"""Create a torus"""
|
||||||
axis = _axpt(p1, p2)
|
do_op(occmodel.Solid().createTorus(p1, p2, ringRadius, radius))
|
||||||
builder = OCC.BRepPrimAPI.BRepPrimAPI_MakeTorus(axis, ringRadius, radius)
|
|
||||||
do_op(builder.Shape())
|
|
||||||
|
|
||||||
def Extrude(obj, p1, p2):
|
def Extrude(obj, p1, p2):
|
||||||
"""Create a solid by extruding edge, wire, or face from p1 to p2"""
|
"""Create a solid by extruding edge, wire, or face from p1 to p2"""
|
||||||
p1 = _pt(p1)
|
do_op(occmodel.Solid().extrude(obj, p1, p2))
|
||||||
p2 = _pt(p2)
|
|
||||||
direction = OCC.gp.gp_Vec(p1, p2)
|
|
||||||
do_op(OCC.BRepPrimAPI.BRepPrimAPI_MakePrism(obj, direction).Shape())
|
|
||||||
|
|
||||||
def Revolve(face, p1, p2, angle):
|
def Revolve(face, p1, p2, angle):
|
||||||
"""Create a solid by revolving the face around the given axis"""
|
"""Create a solid by revolving the face around the given axis"""
|
||||||
p1 = _pt(p1)
|
do_op(occmodel.Solid().revolve(face, p1, p2, angle))
|
||||||
p2 = _pt(p2)
|
|
||||||
dx = p2.X() - p1.X()
|
|
||||||
dy = p2.Y() - p1.Y()
|
|
||||||
dz = p2.Z() - p1.Z()
|
|
||||||
axis = OCC.gp.gp_Ax1(p1, _dir((dx, dy, dz)))
|
|
||||||
angle = math.radians(angle)
|
|
||||||
do_op(OCC.BRepPrimAPI.BRepPrimAPI_MakeRevol(face, axis, angle, False).Shape())
|
|
||||||
|
|
||||||
def Loft(profiles, ruled=True, tolerance=1e-6):
|
def Loft(profiles, ruled=True, tolerance=1e-6):
|
||||||
"""Create a solid by lofting through a sequence of wires or closed edges"""
|
"""Create a solid by lofting through a sequence of wires or closed edges"""
|
||||||
builder = OCC.BRepOffsetAPI.BRepOffsetAPI_ThruSections(True, ruled,
|
do_op(occmodel.Solid().loft(profiles, ruled, tolerance))
|
||||||
tolerance)
|
|
||||||
for i in profiles:
|
|
||||||
if isinstance(i, OCC.TopoDS.TopoDS_Wire):
|
|
||||||
builder.AddWire(i)
|
|
||||||
elif isinstance(i, OCC.TopoDS.TopoDS_Vertex):
|
|
||||||
builder.AddVertex(i)
|
|
||||||
else:
|
|
||||||
builder.AddWire(Wire.createWire(i))
|
|
||||||
do_op(builder.Shape())
|
|
||||||
|
|
||||||
def Pipe(face, path):
|
def Pipe(face, path):
|
||||||
if isinstance(path, OCC.TopoDS.TopoDS_Edge):
|
do_op(occmodel.Solid().pipe(face, path))
|
||||||
wire = Wire.createWire((path,))
|
|
||||||
else:
|
|
||||||
wire = path
|
|
||||||
builder = OCC.BRepOffsetAPI.BRepOffsetAPI_MakePipe(wire, face)
|
|
||||||
do_op(builder.Shape())
|
|
||||||
|
|
||||||
### Group operations
|
### Group operations
|
||||||
|
|
||||||
|
|
@ -331,7 +244,9 @@ def Translated(delta):
|
||||||
return Op(Translate, delta)
|
return Op(Translate, delta)
|
||||||
|
|
||||||
def Transformed(mat):
|
def Transformed(mat):
|
||||||
"""Perform a transformation."""
|
"""Perform a transformation.
|
||||||
|
|
||||||
|
Note that `geotools.Transform` is imported as `Xform` within poc files."""
|
||||||
return Op(Transform, mat)
|
return Op(Transform, mat)
|
||||||
|
|
||||||
def Filleted(radius, edges=None):
|
def Filleted(radius, edges=None):
|
||||||
|
|
@ -344,58 +259,19 @@ def Chamfered(distance, edges=None):
|
||||||
|
|
||||||
### Postfix operations
|
### Postfix operations
|
||||||
|
|
||||||
def _transform(obj, t):
|
|
||||||
_assign(obj,
|
|
||||||
OCC.BRepBuilderAPI.BRepBuilderAPI_Transform(obj, t, True).Shape())
|
|
||||||
|
|
||||||
def Rotate(angle, axis, center=(0,0,0)):
|
def Rotate(angle, axis, center=(0,0,0)):
|
||||||
"""Rotate the active object"""
|
"""Rotate the active object"""
|
||||||
angle = math.radians(angle)
|
obj.rotate(angle, axis, center)
|
||||||
a = OCC.gp.gp_Ax1()
|
|
||||||
a.SetLocation(_pt(center))
|
|
||||||
a.SetDirection(_dir(axis))
|
|
||||||
t = OCC.gp.gp_Trsf()
|
|
||||||
t.SetRotation(a, angle)
|
|
||||||
_transform(obj, t)
|
|
||||||
|
|
||||||
def Translate(delta):
|
def Translate(delta):
|
||||||
"""Translate the active object"""
|
"""Translate the active object"""
|
||||||
t = OCC.gp.gp_Trsf()
|
obj.translate(delta)
|
||||||
t.SetTranslation(_vec(delta))
|
|
||||||
_transform(obj, t)
|
|
||||||
|
|
||||||
def Matrix(*args):
|
|
||||||
"""Construct a 4x3 matrix from arguments, which may be
|
|
||||||
|
|
||||||
- A list of 12 values
|
|
||||||
- A list of 9 values (in which case the last column is taken to be zeros)
|
|
||||||
- A list of 3 4-tuples, each taken as a row
|
|
||||||
- A list of 3 3-tuples, each taken as a row (the last column taken to be zeros)
|
|
||||||
"""
|
|
||||||
result = OCC.gp.gp_Trsf()
|
|
||||||
if len(args) == 0:
|
|
||||||
return result
|
|
||||||
if len(args) == 3:
|
|
||||||
if len(args[0]) == 4:
|
|
||||||
result.SetValues(*(args[0] + args[1] + args[2]))
|
|
||||||
return result
|
|
||||||
elif len(args[0]) == 3:
|
|
||||||
result.SetValues(*(args[0] + (0,) + args[1] + (0,) + args[2] + (0,)))
|
|
||||||
return result
|
|
||||||
elif len(args) == 9:
|
|
||||||
result.SetValues(
|
|
||||||
args[0], args[1], args[2], 0,
|
|
||||||
args[3], args[4], args[5], 0,
|
|
||||||
args[6], args[7], args[8], 0)
|
|
||||||
return result
|
|
||||||
result.SetValues(*args)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def Transform(mat):
|
def Transform(mat):
|
||||||
"""Transform the active object
|
"""Transform the active object
|
||||||
|
|
||||||
Note that `geotools.Transform` is imported as `Xform` within poc files."""
|
Note that `geotools.Transform` is imported as `Xform` within poc files."""
|
||||||
_transform(obj, mat)
|
obj.transform(mat)
|
||||||
|
|
||||||
def Fillet(radius, edges=None):
|
def Fillet(radius, edges=None):
|
||||||
"""Fillet the active object
|
"""Fillet the active object
|
||||||
|
|
@ -407,14 +283,8 @@ True for each edge that should be filleted.
|
||||||
|
|
||||||
Otherwise, `edges` must be a sequence of edges to fillet.
|
Otherwise, `edges` must be a sequence of edges to fillet.
|
||||||
"""
|
"""
|
||||||
if callable(edges):
|
if callable(edges): edges = [e for e in Edges() if edges(e)]
|
||||||
edges = [e for e in Edges() if edges(e)]
|
obj.fillet(radius, edges)
|
||||||
elif edges is None:
|
|
||||||
edges = [e for e in Edges()]
|
|
||||||
fillet = OCC.BRepFilletAPI.BRepFilletAPI_MakeFillet(obj)
|
|
||||||
for e in edges:
|
|
||||||
fillet.Add(radius, e)
|
|
||||||
_assign(obj, fillet.Shape())
|
|
||||||
|
|
||||||
def Chamfer(distance, edges=None):
|
def Chamfer(distance, edges=None):
|
||||||
"""Chamfer the active object
|
"""Chamfer the active object
|
||||||
|
|
@ -426,159 +296,35 @@ True for each edge that should be filleted.
|
||||||
|
|
||||||
Otherwise, `edges` must be a sequence of edges to fillet.
|
Otherwise, `edges` must be a sequence of edges to fillet.
|
||||||
"""
|
"""
|
||||||
if callable(edges):
|
if callable(edges): edges = [e for e in Edges() if edges(e)]
|
||||||
edges = [e for e in Edges() if edges(e)]
|
obj.chamfer(distance, edges)
|
||||||
elif edges is None:
|
|
||||||
edges = [e for e in Edges()]
|
|
||||||
chamfer = OCC.BRepFilletAPI.BRepFilletAPI_MakeChamfer(obj)
|
|
||||||
m = OCC.TopTools.TopTools_IndexedDataMapOfShapeListOfShape();
|
|
||||||
OCC.TopExp.topexp.MapShapesAndAncestors(obj, OCC.TopAbs.TopAbs_EDGE,
|
|
||||||
OCC.TopAbs.TopAbs_FACE, m)
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
f = m.FindFromKey(e).First()
|
|
||||||
f = OCC.TopoDS.topods.Face(f)
|
|
||||||
chamfer.Add(distance, e, f)
|
|
||||||
_assign(obj, chamfer.Shape())
|
|
||||||
|
|
||||||
### Inquiries
|
### Inquiries
|
||||||
|
|
||||||
def visit(shape, topologyType, factory):
|
|
||||||
explorer = OCC.TopExp.TopExp_Explorer()
|
|
||||||
explorer.Init(shape, topologyType)
|
|
||||||
while explorer.More():
|
|
||||||
it = explorer.Current()
|
|
||||||
# XXX pythonocc-core examples _loop_topo avoids yielding
|
|
||||||
# items with equal _hash__ more than once but this seems bogus
|
|
||||||
yield factory(it)
|
|
||||||
explorer.Next()
|
|
||||||
|
|
||||||
def Object():
|
def Object():
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
def CenterOfMass():
|
def CenterOfMass():
|
||||||
"""Return the center of mass box of the current item"""
|
"""Return the bounding box of the current item"""
|
||||||
prop = OCC.GProp.GProp_GProps()
|
return obj.centreOfMass()
|
||||||
OCC.BRepGProp.brepgprop_VolumeProperties(obj, prop)
|
|
||||||
return prop.CentreOfMass()
|
|
||||||
CentreOfMass = CenterOfMass
|
CentreOfMass = CenterOfMass
|
||||||
|
|
||||||
def Bbox(o=None):
|
def Bbox():
|
||||||
"""Return the bounding box of given object or the current item a a 6-tuple
|
"""Return the bounding box of the current item"""
|
||||||
|
return obj.boundingBox()
|
||||||
(minx, miny, minz, maxx, maxy, maxz)"""
|
|
||||||
box = OCC.Bnd.Bnd_Box()
|
|
||||||
OCC.BRepBndLib.brepbndlib.Add(o or obj, box)
|
|
||||||
lo = box.CornerMin()
|
|
||||||
hi = box.CornerMax()
|
|
||||||
return ((lo.X(), lo.Y(), lo.Z(), hi.X(), hi.Y(), hi.Z()))
|
|
||||||
|
|
||||||
def Edges():
|
def Edges():
|
||||||
"""Return the edge iterator of the current item"""
|
"""Return the edge iterator of the current item"""
|
||||||
return visit(Object(), OCC.TopAbs.TopAbs_EDGE, OCC.TopoDS.topods.Edge)
|
return occmodel.EdgeIterator(Object())
|
||||||
|
|
||||||
def Faces():
|
def Faces():
|
||||||
"""Return the face iterator of the current item"""
|
"""Return the face iterator of the current item"""
|
||||||
return visit(Object(), OCC.TopAbs.TopAbs_FACE, OCC.TopoDS.topods.Face)
|
return occmodel.FaceIterator(Object())
|
||||||
|
|
||||||
def Vertices():
|
def Vertices():
|
||||||
"""Return the vertex iterator of the current item"""
|
"""Return the vertex iterator of the current item"""
|
||||||
return visit(Object(), OCC.TopAbs.TopAbs_VERTEX, OCC.TopoDS.topods.Vertex)
|
return occmodel.VertexIterator(Object())
|
||||||
|
|
||||||
def Wires():
|
def Wires():
|
||||||
"""Return the wire iterator of the current item"""
|
"""Return the wire iterator of the current item"""
|
||||||
return visit(Object(), OCC.TopAbs.TopAbs_WIRE, OCC.TopoDS.topods.Wire)
|
return occmodel.WireIterator(Object())
|
||||||
|
|
||||||
class Edge:
|
|
||||||
@classmethod
|
|
||||||
def createLine(cls, p1, p2):
|
|
||||||
p1 = _pt(p1)
|
|
||||||
p2 = _pt(p2)
|
|
||||||
return OCC.BRepBuilderAPI.BRepBuilderAPI_MakeEdge(p1, p2).Edge()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def createArc3P(cls, start, end, mid):
|
|
||||||
p1 = _pt(start)
|
|
||||||
p2 = _pt(mid)
|
|
||||||
p3 = _pt(end)
|
|
||||||
arc = OCC.GC.GC_MakeArcOfCircle(p1, p2, p3).Value()
|
|
||||||
return OCC.BRepBuilderAPI.BRepBuilderAPI_MakeEdge(arc).Edge()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def createCircle(cls, center, normal, radius):
|
|
||||||
ax = _axcn(center, normal)
|
|
||||||
arc = OCC.GC.GC_MakeCircle(ax, radius).Value()
|
|
||||||
return OCC.BRepBuilderAPI.BRepBuilderAPI_MakeEdge(arc).Edge()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def createHelix(cls, pitch, height, radius, angle, leftHanded=False):
|
|
||||||
axis = OCC.gp.gp_Ax2(_pt((0,0,0)), OCC.gp.gp.DZ())
|
|
||||||
axis = OCC.gp.gp_Ax3(axis)
|
|
||||||
if angle <= 0:
|
|
||||||
surf = OCC.Geom.Geom_CylindricalSurface(axis, radius)
|
|
||||||
else:
|
|
||||||
angle = math.radians(angle)
|
|
||||||
surf = OCC.Geom.Geom_ConicalSurface(axis, angle, radius)
|
|
||||||
if leftHanded:
|
|
||||||
p = OCC.gp.gp_Pnt2d(2*math.pi,0)
|
|
||||||
d = OCC.gp.gp_Dir2d(-2 * math.pi, pitch)
|
|
||||||
else:
|
|
||||||
p = OCC.gp.gp_Pnt2d(0,0)
|
|
||||||
d = OCC.gp.gp_Dir2d(2 * math.pi, pitch)
|
|
||||||
|
|
||||||
axis2 = OCC.gp.gp_Ax2d(p, d)
|
|
||||||
line = OCC.Geom2d.Geom2d_Line(axis2)
|
|
||||||
end_u = (4*math.pi*math.pi+pitch*pitch)**.5*(height/pitch)
|
|
||||||
begin = line.Value(0)
|
|
||||||
end = line.Value(end_u)
|
|
||||||
|
|
||||||
seg = OCC.GCE2d.GCE2d_MakeSegment(begin, end).Value()
|
|
||||||
edge = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeEdge(seg,
|
|
||||||
OCC.Geom.Handle_Geom_Surface(surf)).Edge()
|
|
||||||
OCC.BRepLib.breplib.BuildCurves3d(edge)
|
|
||||||
return edge
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def createEllipse(cls, center, normal, rMajor, rMinor):
|
|
||||||
ax = _axcn(center, normal)
|
|
||||||
arc = OCC.GC.GC_MakeEllipse(ax, rMajor, rMinor).Value()
|
|
||||||
return OCC.BRepBuilderAPI.BRepBuilderAPI_MakeEdge(arc).Edge()
|
|
||||||
|
|
||||||
class Wire:
|
|
||||||
@classmethod
|
|
||||||
def createWire(cls, arg):
|
|
||||||
if isinstance(arg, (list, tuple)): pass
|
|
||||||
else: arg = (arg,)
|
|
||||||
builder = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeWire()
|
|
||||||
for i in arg:
|
|
||||||
builder.Add(i)
|
|
||||||
return builder.Wire()
|
|
||||||
|
|
||||||
def Vertex(x,y,z):
|
|
||||||
builder = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeVertex(_pt((x,y,z)))
|
|
||||||
return builder.Vertex()
|
|
||||||
|
|
||||||
def _dump(obj):
|
|
||||||
import OCC.BRepTools
|
|
||||||
OCC.BRepTools.breptools.Write(obj, "/dev/stdout")
|
|
||||||
|
|
||||||
class Face:
|
|
||||||
@classmethod
|
|
||||||
def createFace(cls, arg):
|
|
||||||
def f(x):
|
|
||||||
if isinstance(x, OCC.TopoDS.TopoDS_Edge):
|
|
||||||
return Wire.createWire(x)
|
|
||||||
else:
|
|
||||||
return x
|
|
||||||
if isinstance(arg, (list, tuple)):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
arg = (arg,)
|
|
||||||
outer = arg[0]
|
|
||||||
rest = arg[1:]
|
|
||||||
outer = f(outer)
|
|
||||||
builder = OCC.BRepBuilderAPI.BRepBuilderAPI_MakeFace(outer)
|
|
||||||
for i in rest:
|
|
||||||
i = f(i)
|
|
||||||
builder.Add(i)
|
|
||||||
return builder.Face()
|
|
||||||
|
|
|
||||||
63
pocview
63
pocview
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/python2
|
#!/usr/bin/python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# auto-updating viewer of 'poc' modeling program
|
# auto-updating viewer of 'poc' modeling program
|
||||||
# Copyright © 2017 Jeff Epler <jepler@gmail.com>
|
# Copyright © 2017 Jeff Epler <jepler@gmail.com>
|
||||||
|
|
@ -17,12 +17,13 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from occmodelviewer import Viewer
|
from occmodelviewer import Viewer
|
||||||
|
import gltools
|
||||||
|
import geotools
|
||||||
import os
|
import os
|
||||||
import poctools
|
import poctools
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import vtk
|
|
||||||
|
|
||||||
filename = sys.argv[1]
|
filename = sys.argv[1]
|
||||||
|
|
||||||
|
|
@ -34,48 +35,46 @@ def getmtime(filename):
|
||||||
|
|
||||||
class PocViewer(Viewer):
|
class PocViewer(Viewer):
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
|
title = "poc viewer - 'r' to reload, 'q' to quit"
|
||||||
|
Viewer.__init__(self, 640, 480, title, fullscreen=False)
|
||||||
|
|
||||||
|
self.defaultColor = gltools.ColorRGBA(100,100,100,255)
|
||||||
|
self.edgeColor = gltools.ColorRGBA(255,255,255,255)
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.modtime = -2
|
|
||||||
self.actor = vtk.vtkActor()
|
|
||||||
self.ren = vtk.vtkRenderer()
|
|
||||||
self.renWin = vtk.vtkRenderWindow()
|
|
||||||
self.renWin.AddRenderer(self.ren)
|
|
||||||
|
|
||||||
self.iren = vtk.vtkRenderWindowInteractor()
|
|
||||||
self.iren.SetRenderWindow(self.renWin)
|
|
||||||
|
|
||||||
self.ren.AddActor(self.actor)
|
|
||||||
self.iren.Initialize()
|
|
||||||
self.iren.CreateRepeatingTimer(100)
|
|
||||||
self.iren.AddObserver('TimerEvent', self.idle)
|
|
||||||
|
|
||||||
def Start(self):
|
|
||||||
self.reloadModel()
|
self.reloadModel()
|
||||||
self.iren.Start()
|
|
||||||
|
def onChar(self, ch):
|
||||||
|
if ch == 'r': self.reloadModel()
|
||||||
|
if ch == 'q': self.running = False
|
||||||
|
|
||||||
def reloadModel(self, modtime=None):
|
def reloadModel(self, modtime=None):
|
||||||
self.modtime = modtime or getmtime(self.filename)
|
self.modtime = modtime or getmtime(self.filename)
|
||||||
try:
|
try:
|
||||||
ns = poctools.execpoc(sys.argv[1:],
|
poctools.execpoc(sys.argv[1:])
|
||||||
__output__= os.path.splitext(filename)[0] + ".stl")
|
|
||||||
poctools.output(ns['__output__'])
|
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return
|
return
|
||||||
|
|
||||||
reader = vtk.vtkSTLReader()
|
self.clear()
|
||||||
reader.SetFileName(ns['__output__'])
|
o = poctools.obj
|
||||||
|
bb = o.boundingBox()
|
||||||
|
if bb.min != bb.max:
|
||||||
|
self.add(o)
|
||||||
|
|
||||||
self.mapper = vtk.vtkPolyDataMapper()
|
# Viewer.mainLoop seems to have no provision for code to run when
|
||||||
self.mapper.SetInputConnection(reader.GetOutputPort())
|
# idle, so we are forced to do this..
|
||||||
|
def mainLoop(self):
|
||||||
self.actor.SetMapper(self.mapper)
|
while self.running:
|
||||||
|
time.sleep(1/120.)
|
||||||
def idle(self, obj, event):
|
gltools.PollEvents()
|
||||||
newmodtime = getmtime(self.filename)
|
newmodtime = getmtime(self.filename)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if newmodtime + .1 < now and newmodtime != self.modtime:
|
if newmodtime + .1 > now and newmodtime != self.modtime:
|
||||||
self.reloadModel(newmodtime)
|
self.reloadModel(newmodtime)
|
||||||
self.renWin.Render()
|
self.updateBounds()
|
||||||
|
self.redraw()
|
||||||
|
|
||||||
mw = PocViewer(filename)
|
mw = PocViewer(filename)
|
||||||
mw.Start()
|
mw.running = True
|
||||||
|
mw.onIsoView()
|
||||||
|
mw.mainLoop()
|
||||||
|
|
|
||||||
2
setup.py
2
setup.py
|
|
@ -10,5 +10,5 @@ setup(name='poc',
|
||||||
author_email='jepler@gmail.com',
|
author_email='jepler@gmail.com',
|
||||||
url='https://github.com/jepler/poc',
|
url='https://github.com/jepler/poc',
|
||||||
py_modules=['poctools'],
|
py_modules=['poctools'],
|
||||||
scripts=['poc', 'pocview', 'pocimg'],
|
scripts=['poc', 'pocview'],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue