Add Black formatting check to CI (#255)
* Add Black formatting check to CI * Add some documentation for code contributors * Use uncompromised code formatting
This commit is contained in:
committed by
Adam Urbańczyk
parent
74573fc3bb
commit
102c16c14e
28
.travis.yml
28
.travis.yml
@ -24,27 +24,31 @@ env:
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- env: PYTHON_VERSION=3.6
|
- name: "Python 3.6 - osx"
|
||||||
|
env: PYTHON_VERSION=3.6
|
||||||
os: osx
|
os: osx
|
||||||
- env: PYTHON_VERSION=3.6
|
- name: "Python 3.6 - linux"
|
||||||
|
env: PYTHON_VERSION=3.6
|
||||||
os: linux
|
os: linux
|
||||||
- env: PYTHON_VERSION=3.7
|
- name: "Python 3.7 - osx"
|
||||||
|
env: PYTHON_VERSION=3.7
|
||||||
os: osx
|
os: osx
|
||||||
- env: PYTHON_VERSION=3.7
|
- name: "Python 3.7 - linux"
|
||||||
|
env: PYTHON_VERSION=3.7
|
||||||
os: linux
|
os: linux
|
||||||
|
- name: "Lint"
|
||||||
|
env: PYTHON_VERSION=3.7
|
||||||
|
os: linux
|
||||||
|
script:
|
||||||
|
- black . --diff --check
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$PYTHON_VERSION" == "2.7" ]]; then
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||||
PY_MAJOR=2 ;
|
|
||||||
else
|
|
||||||
PY_MAJOR=3 ;
|
|
||||||
fi ;
|
|
||||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
|
||||||
OS=Linux ;
|
OS=Linux ;
|
||||||
else
|
else
|
||||||
OS=MacOSX ;
|
OS=MacOSX ;
|
||||||
fi ;
|
fi ;
|
||||||
wget https://repo.continuum.io/miniconda/Miniconda$PY_MAJOR-latest-$OS-x86_64.sh -O miniconda.sh
|
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-$OS-x86_64.sh -O miniconda.sh
|
||||||
- bash miniconda.sh -b -p $HOME/miniconda;
|
- bash miniconda.sh -b -p $HOME/miniconda;
|
||||||
- export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH";
|
- export PATH="$HOME/miniconda/bin:$HOME/miniconda/lib:$PATH";
|
||||||
- conda config --set always_yes yes --set changeps1 no;
|
- conda config --set always_yes yes --set changeps1 no;
|
||||||
@ -67,4 +71,4 @@ after_success:
|
|||||||
|
|
||||||
after_failure:
|
after_failure:
|
||||||
- ls /cores/core.*
|
- ls /cores/core.*
|
||||||
- lldb --core `ls /cores/core.*` --batch --one-line "bt"
|
- lldb --core `ls /cores/core.*` --batch --one-line "bt"
|
||||||
|
32
README.md
32
README.md
@ -123,6 +123,38 @@ You do not need to be a software developer to have a big impact on this project.
|
|||||||
|
|
||||||
It is asked that all contributions to this project be made in a respectful and considerate way. Please use the [Python Community Code of Conduct's](https://www.python.org/psf/codeofconduct/) guidelines as a reference.
|
It is asked that all contributions to this project be made in a respectful and considerate way. Please use the [Python Community Code of Conduct's](https://www.python.org/psf/codeofconduct/) guidelines as a reference.
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
|
||||||
|
If you are going to contribute code, make sure to follow this steps:
|
||||||
|
|
||||||
|
- Consider opening an issue first to discuss what you have in mind
|
||||||
|
- Try to keep it as short and simple as possible (if you want to change several
|
||||||
|
things, start with just one!)
|
||||||
|
- Fork the CadQuery repository, clone your fork and create a new branch to
|
||||||
|
start working on your changes
|
||||||
|
- Start with the tests! How should CadQuery behave after your changes? Make
|
||||||
|
sure to add some tests to the test suite to ensure proper behavior
|
||||||
|
- Make sure your tests have assertions checking all the expected results
|
||||||
|
- Add a nice docstring to the test indicating what the test is doing; if there
|
||||||
|
is too much to explain, consider splitting the test in two!
|
||||||
|
- Go ahead and implement the changes
|
||||||
|
- Add a nice docstring to the functions/methods/classes you implement
|
||||||
|
describing what they do, what the expected parameters are and what it returns
|
||||||
|
(if anything)
|
||||||
|
- Update the documentation if there is any change to the public API
|
||||||
|
- Consider adding an example to the documentation showing your cool new
|
||||||
|
feature!
|
||||||
|
- Make sure nothing is broken (run the complete test suite with `pytest`)
|
||||||
|
- Run `black` to autoformat your code and make sure your code style complies
|
||||||
|
with CadQuery's
|
||||||
|
- Push the changes to your fork and open a pull-request upstream
|
||||||
|
- Keep an eye on the automated feedback you will receive from the CI pipelines;
|
||||||
|
if there is a test failing or some code is not properly formatted, you will
|
||||||
|
be notified without human intervention
|
||||||
|
- Be prepared for constructive feedback and criticism!
|
||||||
|
- Be patient and respectful, remember that those reviewing your code are also
|
||||||
|
working hard (sometimes reviewing changes is harder than implementing them!)
|
||||||
|
|
||||||
### How to Report a Bug
|
### How to Report a Bug
|
||||||
When filing a bug report [issue](https://github.com/CadQuery/cadquery/issues), please be sure to answer these questions:
|
When filing a bug report [issue](https://github.com/CadQuery/cadquery/issues), please be sure to answer these questions:
|
||||||
|
|
||||||
|
@ -1,25 +1,65 @@
|
|||||||
# these items point to the OCC implementation
|
# these items point to the OCC implementation
|
||||||
from .occ_impl.geom import Plane, BoundBox, Vector, Matrix
|
from .occ_impl.geom import Plane, BoundBox, Vector, Matrix
|
||||||
from .occ_impl.shapes import (Shape, Vertex, Edge, Face, Wire, Solid, Shell,
|
from .occ_impl.shapes import (
|
||||||
Compound, sortWiresByBuildOrder)
|
Shape,
|
||||||
|
Vertex,
|
||||||
|
Edge,
|
||||||
|
Face,
|
||||||
|
Wire,
|
||||||
|
Solid,
|
||||||
|
Shell,
|
||||||
|
Compound,
|
||||||
|
sortWiresByBuildOrder,
|
||||||
|
)
|
||||||
from .occ_impl import exporters
|
from .occ_impl import exporters
|
||||||
from .occ_impl import importers
|
from .occ_impl import importers
|
||||||
|
|
||||||
# these items are the common implementation
|
# these items are the common implementation
|
||||||
|
|
||||||
# the order of these matter
|
# the order of these matter
|
||||||
from .selectors import (NearestToPointSelector, ParallelDirSelector,
|
from .selectors import (
|
||||||
DirectionSelector, PerpendicularDirSelector, TypeSelector,
|
NearestToPointSelector,
|
||||||
DirectionMinMaxSelector, StringSyntaxSelector, Selector)
|
ParallelDirSelector,
|
||||||
|
DirectionSelector,
|
||||||
|
PerpendicularDirSelector,
|
||||||
|
TypeSelector,
|
||||||
|
DirectionMinMaxSelector,
|
||||||
|
StringSyntaxSelector,
|
||||||
|
Selector,
|
||||||
|
)
|
||||||
from .cq import CQ, Workplane, selectors
|
from .cq import CQ, Workplane, selectors
|
||||||
from . import plugins
|
from . import plugins
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'CQ', 'Workplane', 'plugins', 'selectors', 'Plane', 'BoundBox', 'Matrix', 'Vector', 'sortWiresByBuildOrder',
|
"CQ",
|
||||||
'Shape', 'Vertex', 'Edge', 'Wire', 'Face', 'Solid', 'Shell', 'Compound', 'exporters', 'importers',
|
"Workplane",
|
||||||
'NearestToPointSelector', 'ParallelDirSelector', 'DirectionSelector', 'PerpendicularDirSelector',
|
"plugins",
|
||||||
'TypeSelector', 'DirectionMinMaxSelector', 'StringSyntaxSelector', 'Selector', 'plugins'
|
"selectors",
|
||||||
|
"Plane",
|
||||||
|
"BoundBox",
|
||||||
|
"Matrix",
|
||||||
|
"Vector",
|
||||||
|
"sortWiresByBuildOrder",
|
||||||
|
"Shape",
|
||||||
|
"Vertex",
|
||||||
|
"Edge",
|
||||||
|
"Wire",
|
||||||
|
"Face",
|
||||||
|
"Solid",
|
||||||
|
"Shell",
|
||||||
|
"Compound",
|
||||||
|
"exporters",
|
||||||
|
"importers",
|
||||||
|
"NearestToPointSelector",
|
||||||
|
"ParallelDirSelector",
|
||||||
|
"DirectionSelector",
|
||||||
|
"PerpendicularDirSelector",
|
||||||
|
"TypeSelector",
|
||||||
|
"DirectionMinMaxSelector",
|
||||||
|
"StringSyntaxSelector",
|
||||||
|
"Selector",
|
||||||
|
"plugins",
|
||||||
]
|
]
|
||||||
|
|
||||||
__version__ = "2.0.0dev"
|
__version__ = "2.0.0dev"
|
||||||
|
399
cadquery/cq.py
399
cadquery/cq.py
@ -18,8 +18,19 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from . import Vector, Plane, Shape, Edge, Wire, Face, Solid, Compound, \
|
from . import (
|
||||||
sortWiresByBuildOrder, selectors, exporters
|
Vector,
|
||||||
|
Plane,
|
||||||
|
Shape,
|
||||||
|
Edge,
|
||||||
|
Wire,
|
||||||
|
Face,
|
||||||
|
Solid,
|
||||||
|
Compound,
|
||||||
|
sortWiresByBuildOrder,
|
||||||
|
selectors,
|
||||||
|
exporters,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CQContext(object):
|
class CQContext(object):
|
||||||
@ -31,7 +42,9 @@ class CQContext(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pendingWires = [] # a list of wires that have been created and need to be extruded
|
self.pendingWires = (
|
||||||
|
[]
|
||||||
|
) # a list of wires that have been created and need to be extruded
|
||||||
# a list of created pending edges that need to be joined into wires
|
# a list of created pending edges that need to be joined into wires
|
||||||
self.pendingEdges = []
|
self.pendingEdges = []
|
||||||
# a reference to the first point for a set of edges.
|
# a reference to the first point for a set of edges.
|
||||||
@ -99,8 +112,12 @@ class CQ(object):
|
|||||||
# tricky-- if an object is a compound of solids,
|
# tricky-- if an object is a compound of solids,
|
||||||
# do not return all of the solids underneath-- typically
|
# do not return all of the solids underneath-- typically
|
||||||
# then we'll keep joining to ourself
|
# then we'll keep joining to ourself
|
||||||
if propName == 'Solids' and isinstance(o, Solid) and o.ShapeType() == 'Compound':
|
if (
|
||||||
for i in getattr(o, 'Compounds')():
|
propName == "Solids"
|
||||||
|
and isinstance(o, Solid)
|
||||||
|
and o.ShapeType() == "Compound"
|
||||||
|
):
|
||||||
|
for i in getattr(o, "Compounds")():
|
||||||
all[i.hashCode()] = i
|
all[i.hashCode()] = i
|
||||||
else:
|
else:
|
||||||
if hasattr(o, propName):
|
if hasattr(o, propName):
|
||||||
@ -259,8 +276,9 @@ class CQ(object):
|
|||||||
|
|
||||||
return self.objects[0].wrapped
|
return self.objects[0].wrapped
|
||||||
|
|
||||||
def workplane(self, offset=0.0, invert=False, centerOption='CenterOfMass',
|
def workplane(
|
||||||
origin=None):
|
self, offset=0.0, invert=False, centerOption="CenterOfMass", origin=None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Creates a new 2-D workplane, located relative to the first face on the stack.
|
Creates a new 2-D workplane, located relative to the first face on the stack.
|
||||||
|
|
||||||
@ -310,6 +328,7 @@ class CQ(object):
|
|||||||
For now you can work around by creating a workplane and then offsetting the center
|
For now you can work around by creating a workplane and then offsetting the center
|
||||||
afterwards.
|
afterwards.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _isCoPlanar(f0, f1):
|
def _isCoPlanar(f0, f1):
|
||||||
"""Test if two faces are on the same plane."""
|
"""Test if two faces are on the same plane."""
|
||||||
p0 = f0.Center()
|
p0 = f0.Center()
|
||||||
@ -318,9 +337,11 @@ class CQ(object):
|
|||||||
n1 = f1.normalAt()
|
n1 = f1.normalAt()
|
||||||
|
|
||||||
# test normals (direction of planes)
|
# test normals (direction of planes)
|
||||||
if not ((abs(n0.x - n1.x) < self.ctx.tolerance) or
|
if not (
|
||||||
(abs(n0.y - n1.y) < self.ctx.tolerance) or
|
(abs(n0.x - n1.x) < self.ctx.tolerance)
|
||||||
(abs(n0.z - n1.z) < self.ctx.tolerance)):
|
or (abs(n0.y - n1.y) < self.ctx.tolerance)
|
||||||
|
or (abs(n0.z - n1.z) < self.ctx.tolerance)
|
||||||
|
):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# test if p1 is on the plane of f0 (offset of planes)
|
# test if p1 is on the plane of f0 (offset of planes)
|
||||||
@ -339,22 +360,23 @@ class CQ(object):
|
|||||||
xd = Vector(1, 0, 0)
|
xd = Vector(1, 0, 0)
|
||||||
return xd
|
return xd
|
||||||
|
|
||||||
if centerOption not in {'CenterOfMass', 'ProjectedOrigin', 'CenterOfBoundBox'}:
|
if centerOption not in {"CenterOfMass", "ProjectedOrigin", "CenterOfBoundBox"}:
|
||||||
raise ValueError('Undefined centerOption value provided.')
|
raise ValueError("Undefined centerOption value provided.")
|
||||||
|
|
||||||
if len(self.objects) > 1:
|
if len(self.objects) > 1:
|
||||||
# are all objects 'PLANE'?
|
# are all objects 'PLANE'?
|
||||||
if not all(o.geomType() in ('PLANE', 'CIRCLE') for o in self.objects):
|
if not all(o.geomType() in ("PLANE", "CIRCLE") for o in self.objects):
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"If multiple objects selected, they all must be planar faces.")
|
"If multiple objects selected, they all must be planar faces."
|
||||||
|
)
|
||||||
|
|
||||||
# are all faces co-planar with each other?
|
# are all faces co-planar with each other?
|
||||||
if not all(_isCoPlanar(self.objects[0], f) for f in self.objects[1:]):
|
if not all(_isCoPlanar(self.objects[0], f) for f in self.objects[1:]):
|
||||||
raise ValueError("Selected faces must be co-planar.")
|
raise ValueError("Selected faces must be co-planar.")
|
||||||
|
|
||||||
if centerOption in {'CenterOfMass', 'ProjectedOrigin'}:
|
if centerOption in {"CenterOfMass", "ProjectedOrigin"}:
|
||||||
center = Shape.CombinedCenter(self.objects)
|
center = Shape.CombinedCenter(self.objects)
|
||||||
elif centerOption == 'CenterOfBoundBox':
|
elif centerOption == "CenterOfBoundBox":
|
||||||
center = Shape.CombinedCenterOfBoundBox(self.objects)
|
center = Shape.CombinedCenterOfBoundBox(self.objects)
|
||||||
|
|
||||||
normal = self.objects[0].normalAt()
|
normal = self.objects[0].normalAt()
|
||||||
@ -364,26 +386,27 @@ class CQ(object):
|
|||||||
obj = self.objects[0]
|
obj = self.objects[0]
|
||||||
|
|
||||||
if isinstance(obj, Face):
|
if isinstance(obj, Face):
|
||||||
if centerOption in {'CenterOfMass', 'ProjectedOrigin'}:
|
if centerOption in {"CenterOfMass", "ProjectedOrigin"}:
|
||||||
center = obj.Center()
|
center = obj.Center()
|
||||||
elif centerOption == 'CenterOfBoundBox':
|
elif centerOption == "CenterOfBoundBox":
|
||||||
center = obj.CenterOfBoundBox()
|
center = obj.CenterOfBoundBox()
|
||||||
normal = obj.normalAt(center)
|
normal = obj.normalAt(center)
|
||||||
xDir = _computeXdir(normal)
|
xDir = _computeXdir(normal)
|
||||||
else:
|
else:
|
||||||
if hasattr(obj, 'Center'):
|
if hasattr(obj, "Center"):
|
||||||
if centerOption in {'CenterOfMass', 'ProjectedOrigin'}:
|
if centerOption in {"CenterOfMass", "ProjectedOrigin"}:
|
||||||
center = obj.Center()
|
center = obj.Center()
|
||||||
elif centerOption == 'CenterOfBoundBox':
|
elif centerOption == "CenterOfBoundBox":
|
||||||
center = obj.CenterOfBoundBox()
|
center = obj.CenterOfBoundBox()
|
||||||
normal = self.plane.zDir
|
normal = self.plane.zDir
|
||||||
xDir = self.plane.xDir
|
xDir = self.plane.xDir
|
||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Needs a face or a vertex or point on a work plane")
|
"Needs a face or a vertex or point on a work plane"
|
||||||
|
)
|
||||||
|
|
||||||
# update center to projected origin if desired
|
# update center to projected origin if desired
|
||||||
if centerOption == 'ProjectedOrigin':
|
if centerOption == "ProjectedOrigin":
|
||||||
if origin is None:
|
if origin is None:
|
||||||
origin = self.plane.origin
|
origin = self.plane.origin
|
||||||
elif isinstance(origin, tuple):
|
elif isinstance(origin, tuple):
|
||||||
@ -459,9 +482,7 @@ class CQ(object):
|
|||||||
return rv[0]
|
return rv[0]
|
||||||
|
|
||||||
if searchParents and self.parent is not None:
|
if searchParents and self.parent is not None:
|
||||||
return self.parent._findType(types,
|
return self.parent._findType(types, searchStack=True, searchParents=True)
|
||||||
searchStack=True,
|
|
||||||
searchParents=True)
|
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -554,7 +575,7 @@ class CQ(object):
|
|||||||
:py:class:`StringSyntaxSelector`
|
:py:class:`StringSyntaxSelector`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Vertices', selector)
|
return self._selectObjects("Vertices", selector)
|
||||||
|
|
||||||
def faces(self, selector=None):
|
def faces(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -586,7 +607,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Faces', selector)
|
return self._selectObjects("Faces", selector)
|
||||||
|
|
||||||
def edges(self, selector=None):
|
def edges(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -617,7 +638,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Edges', selector)
|
return self._selectObjects("Edges", selector)
|
||||||
|
|
||||||
def wires(self, selector=None):
|
def wires(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -640,7 +661,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Wires', selector)
|
return self._selectObjects("Wires", selector)
|
||||||
|
|
||||||
def solids(self, selector=None):
|
def solids(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -666,7 +687,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Solids', selector)
|
return self._selectObjects("Solids", selector)
|
||||||
|
|
||||||
def shells(self, selector=None):
|
def shells(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -686,7 +707,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Shells', selector)
|
return self._selectObjects("Shells", selector)
|
||||||
|
|
||||||
def compounds(self, selector=None):
|
def compounds(self, selector=None):
|
||||||
"""
|
"""
|
||||||
@ -704,7 +725,7 @@ class CQ(object):
|
|||||||
|
|
||||||
See more about selectors HERE
|
See more about selectors HERE
|
||||||
"""
|
"""
|
||||||
return self._selectObjects('Compounds', selector)
|
return self._selectObjects("Compounds", selector)
|
||||||
|
|
||||||
def toSvg(self, opts=None):
|
def toSvg(self, opts=None):
|
||||||
"""
|
"""
|
||||||
@ -777,8 +798,9 @@ class CQ(object):
|
|||||||
:type angleDegrees: float
|
:type angleDegrees: float
|
||||||
:returns: a CQ object
|
:returns: a CQ object
|
||||||
"""
|
"""
|
||||||
return self.newObject([o.rotate(axisStartPoint, axisEndPoint, angleDegrees)
|
return self.newObject(
|
||||||
for o in self.objects])
|
[o.rotate(axisStartPoint, axisEndPoint, angleDegrees) for o in self.objects]
|
||||||
|
)
|
||||||
|
|
||||||
def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)):
|
def mirror(self, mirrorPlane="XY", basePointVector=(0, 0, 0)):
|
||||||
"""
|
"""
|
||||||
@ -789,8 +811,7 @@ class CQ(object):
|
|||||||
:param basePointVector: the base point to mirror about
|
:param basePointVector: the base point to mirror about
|
||||||
:type basePointVector: tuple
|
:type basePointVector: tuple
|
||||||
"""
|
"""
|
||||||
newS = self.newObject(
|
newS = self.newObject([self.objects[0].mirror(mirrorPlane, basePointVector)])
|
||||||
[self.objects[0].mirror(mirrorPlane, basePointVector)])
|
|
||||||
return newS.first()
|
return newS.first()
|
||||||
|
|
||||||
def translate(self, vec):
|
def translate(self, vec):
|
||||||
@ -943,7 +964,7 @@ class Workplane(CQ):
|
|||||||
:py:meth:`CQ.workplane`
|
:py:meth:`CQ.workplane`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
FOR_CONSTRUCTION = 'ForConstruction'
|
FOR_CONSTRUCTION = "ForConstruction"
|
||||||
|
|
||||||
def __init__(self, inPlane, origin=(0, 0, 0), obj=None):
|
def __init__(self, inPlane, origin=(0, 0, 0), obj=None):
|
||||||
"""
|
"""
|
||||||
@ -967,7 +988,7 @@ class Workplane(CQ):
|
|||||||
the *current point* is on the origin.
|
the *current point* is on the origin.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if inPlane.__class__.__name__ == 'Plane':
|
if inPlane.__class__.__name__ == "Plane":
|
||||||
tmpPlane = inPlane
|
tmpPlane = inPlane
|
||||||
elif isinstance(inPlane, str) or isinstance(inPlane, str):
|
elif isinstance(inPlane, str) or isinstance(inPlane, str):
|
||||||
tmpPlane = Plane.named(inPlane, origin)
|
tmpPlane = Plane.named(inPlane, origin)
|
||||||
@ -976,7 +997,8 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
if tmpPlane is None:
|
if tmpPlane is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'Provided value {} is not a valid work plane'.format(inPlane))
|
"Provided value {} is not a valid work plane".format(inPlane)
|
||||||
|
)
|
||||||
|
|
||||||
self.obj = obj
|
self.obj = obj
|
||||||
self.plane = tmpPlane
|
self.plane = tmpPlane
|
||||||
@ -999,10 +1021,10 @@ class Workplane(CQ):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# old api accepted a vector, so we'll check for that.
|
# old api accepted a vector, so we'll check for that.
|
||||||
if rotate.__class__.__name__ == 'Vector':
|
if rotate.__class__.__name__ == "Vector":
|
||||||
rotate = rotate.toTuple()
|
rotate = rotate.toTuple()
|
||||||
|
|
||||||
if offset.__class__.__name__ == 'Vector':
|
if offset.__class__.__name__ == "Vector":
|
||||||
offset = offset.toTuple()
|
offset = offset.toTuple()
|
||||||
|
|
||||||
p = self.plane.rotated(rotate)
|
p = self.plane.rotated(rotate)
|
||||||
@ -1060,8 +1082,7 @@ class Workplane(CQ):
|
|||||||
elif isinstance(obj, Vector):
|
elif isinstance(obj, Vector):
|
||||||
p = obj
|
p = obj
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError("Cannot convert object type '%s' to vector " % type(obj))
|
||||||
"Cannot convert object type '%s' to vector " % type(obj))
|
|
||||||
|
|
||||||
if useLocalCoords:
|
if useLocalCoords:
|
||||||
return self.plane.toLocalCoords(p)
|
return self.plane.toLocalCoords(p)
|
||||||
@ -1367,28 +1388,35 @@ class Workplane(CQ):
|
|||||||
:return: A CQ object representing a slot
|
:return: A CQ object representing a slot
|
||||||
"""
|
"""
|
||||||
|
|
||||||
radius = diameter/2
|
radius = diameter / 2
|
||||||
|
|
||||||
p1 = pnt + Vector((-length/2) + radius, diameter/2)
|
p1 = pnt + Vector((-length / 2) + radius, diameter / 2)
|
||||||
p2 = p1 + Vector(length - diameter, 0)
|
p2 = p1 + Vector(length - diameter, 0)
|
||||||
p3 = p1 + Vector(length - diameter, -diameter)
|
p3 = p1 + Vector(length - diameter, -diameter)
|
||||||
p4 = p1 + Vector(0, -diameter)
|
p4 = p1 + Vector(0, -diameter)
|
||||||
arc1 = p2 + Vector(radius, -radius)
|
arc1 = p2 + Vector(radius, -radius)
|
||||||
arc2 = p4 + Vector(-radius, radius)
|
arc2 = p4 + Vector(-radius, radius)
|
||||||
|
|
||||||
edges=[(Edge.makeLine(p1,p2))]
|
edges = [(Edge.makeLine(p1, p2))]
|
||||||
edges.append(Edge.makeThreePointArc(p2, arc1, p3))
|
edges.append(Edge.makeThreePointArc(p2, arc1, p3))
|
||||||
edges.append(Edge.makeLine(p3, p4))
|
edges.append(Edge.makeLine(p3, p4))
|
||||||
edges.append(Edge.makeThreePointArc(p4, arc2, p1))
|
edges.append(Edge.makeThreePointArc(p4, arc2, p1))
|
||||||
|
|
||||||
slot = Wire.assembleEdges(edges)
|
slot = Wire.assembleEdges(edges)
|
||||||
|
|
||||||
return slot.rotate(pnt, pnt + Vector(0,0,1), angle)
|
return slot.rotate(pnt, pnt + Vector(0, 0, 1), angle)
|
||||||
|
|
||||||
return self.eachpoint(_makeslot, True)
|
return self.eachpoint(_makeslot, True)
|
||||||
|
|
||||||
def spline(self, listOfXYTuple, tangents=None, periodic=False,
|
def spline(
|
||||||
forConstruction=False, includeCurrent=False, makeWire=False):
|
self,
|
||||||
|
listOfXYTuple,
|
||||||
|
tangents=None,
|
||||||
|
periodic=False,
|
||||||
|
forConstruction=False,
|
||||||
|
includeCurrent=False,
|
||||||
|
makeWire=False,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create a spline interpolated through the provided points.
|
Create a spline interpolated through the provided points.
|
||||||
|
|
||||||
@ -1435,8 +1463,7 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
if tangents:
|
if tangents:
|
||||||
t1, t2 = tangents
|
t1, t2 = tangents
|
||||||
tangents = (self.plane.toWorldCoords(t1),
|
tangents = (self.plane.toWorldCoords(t1), self.plane.toWorldCoords(t2))
|
||||||
self.plane.toWorldCoords(t2))
|
|
||||||
|
|
||||||
e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic)
|
e = Edge.makeSpline(allPoints, tangents=tangents, periodic=periodic)
|
||||||
|
|
||||||
@ -1464,9 +1491,9 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
allPoints = [func(start+stop*t/N) for t in range(N+1)]
|
allPoints = [func(start + stop * t / N) for t in range(N + 1)]
|
||||||
|
|
||||||
return self.spline(allPoints,includeCurrent=False,makeWire=True)
|
return self.spline(allPoints, includeCurrent=False, makeWire=True)
|
||||||
|
|
||||||
def threePointArc(self, point1, point2, forConstruction=False):
|
def threePointArc(self, point1, point2, forConstruction=False):
|
||||||
"""
|
"""
|
||||||
@ -1516,10 +1543,16 @@ class Workplane(CQ):
|
|||||||
midPoint = endPoint.add(startPoint).multiply(0.5)
|
midPoint = endPoint.add(startPoint).multiply(0.5)
|
||||||
|
|
||||||
sagVector = endPoint.sub(startPoint).normalized().multiply(abs(sag))
|
sagVector = endPoint.sub(startPoint).normalized().multiply(abs(sag))
|
||||||
if(sag > 0):
|
if sag > 0:
|
||||||
sagVector.x, sagVector.y = -sagVector.y, sagVector.x # Rotate sagVector +90 deg
|
sagVector.x, sagVector.y = (
|
||||||
|
-sagVector.y,
|
||||||
|
sagVector.x,
|
||||||
|
) # Rotate sagVector +90 deg
|
||||||
else:
|
else:
|
||||||
sagVector.x, sagVector.y = sagVector.y, -sagVector.x # Rotate sagVector -90 deg
|
sagVector.x, sagVector.y = (
|
||||||
|
sagVector.y,
|
||||||
|
-sagVector.x,
|
||||||
|
) # Rotate sagVector -90 deg
|
||||||
|
|
||||||
sagPoint = midPoint.add(sagVector)
|
sagPoint = midPoint.add(sagVector)
|
||||||
|
|
||||||
@ -1545,7 +1578,7 @@ class Workplane(CQ):
|
|||||||
# Calculate the sagitta from the radius
|
# Calculate the sagitta from the radius
|
||||||
length = endPoint.sub(startPoint).Length / 2.0
|
length = endPoint.sub(startPoint).Length / 2.0
|
||||||
try:
|
try:
|
||||||
sag = abs(radius) - math.sqrt(radius**2 - length**2)
|
sag = abs(radius) - math.sqrt(radius ** 2 - length ** 2)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("Arc radius is not large enough to reach the end point.")
|
raise ValueError("Arc radius is not large enough to reach the end point.")
|
||||||
|
|
||||||
@ -1580,8 +1613,7 @@ class Workplane(CQ):
|
|||||||
# attempt to consolidate wires together.
|
# attempt to consolidate wires together.
|
||||||
consolidated = n.consolidateWires()
|
consolidated = n.consolidateWires()
|
||||||
|
|
||||||
rotatedWires = self.plane.rotateShapes(
|
rotatedWires = self.plane.rotateShapes(consolidated.wires().vals(), matrix)
|
||||||
consolidated.wires().vals(), matrix)
|
|
||||||
|
|
||||||
for w in rotatedWires:
|
for w in rotatedWires:
|
||||||
consolidated.objects.append(w)
|
consolidated.objects.append(w)
|
||||||
@ -1616,8 +1648,7 @@ class Workplane(CQ):
|
|||||||
# attempt to consolidate wires together.
|
# attempt to consolidate wires together.
|
||||||
consolidated = n.consolidateWires()
|
consolidated = n.consolidateWires()
|
||||||
|
|
||||||
mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(),
|
mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), "Y")
|
||||||
'Y')
|
|
||||||
|
|
||||||
for w in mirroredWires:
|
for w in mirroredWires:
|
||||||
consolidated.objects.append(w)
|
consolidated.objects.append(w)
|
||||||
@ -1646,8 +1677,7 @@ class Workplane(CQ):
|
|||||||
# attempt to consolidate wires together.
|
# attempt to consolidate wires together.
|
||||||
consolidated = n.consolidateWires()
|
consolidated = n.consolidateWires()
|
||||||
|
|
||||||
mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(),
|
mirroredWires = self.plane.mirrorInPlane(consolidated.wires().vals(), "X")
|
||||||
'X')
|
|
||||||
|
|
||||||
for w in mirroredWires:
|
for w in mirroredWires:
|
||||||
consolidated.objects.append(w)
|
consolidated.objects.append(w)
|
||||||
@ -1859,6 +1889,7 @@ class Workplane(CQ):
|
|||||||
better way to handle forConstruction
|
better way to handle forConstruction
|
||||||
project points not in the workplane plane onto the workplane plane
|
project points not in the workplane plane onto the workplane plane
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def makeRectangleWire(pnt):
|
def makeRectangleWire(pnt):
|
||||||
# Here pnt is in local coordinates due to useLocalCoords=True
|
# Here pnt is in local coordinates due to useLocalCoords=True
|
||||||
# (xc,yc,zc) = pnt.toTuple()
|
# (xc,yc,zc) = pnt.toTuple()
|
||||||
@ -1909,6 +1940,7 @@ class Workplane(CQ):
|
|||||||
project points not in the workplane plane onto the workplane plane
|
project points not in the workplane plane onto the workplane plane
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def makeCircleWire(obj):
|
def makeCircleWire(obj):
|
||||||
cir = Wire.makeCircle(radius, obj, Vector(0, 0, 1))
|
cir = Wire.makeCircle(radius, obj, Vector(0, 0, 1))
|
||||||
cir.forConstruction = forConstruction
|
cir.forConstruction = forConstruction
|
||||||
@ -1927,19 +1959,25 @@ class Workplane(CQ):
|
|||||||
:param diameter: the size of the circle the polygon is inscribed into
|
:param diameter: the size of the circle the polygon is inscribed into
|
||||||
:return: a polygon wire
|
:return: a polygon wire
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _makePolygon(center):
|
def _makePolygon(center):
|
||||||
# pnt is a vector in local coordinates
|
# pnt is a vector in local coordinates
|
||||||
angle = 2.0 * math.pi / nSides
|
angle = 2.0 * math.pi / nSides
|
||||||
pnts = []
|
pnts = []
|
||||||
for i in range(nSides + 1):
|
for i in range(nSides + 1):
|
||||||
pnts.append(center + Vector((diameter / 2.0 * math.cos(angle * i)),
|
pnts.append(
|
||||||
(diameter / 2.0 * math.sin(angle * i)), 0))
|
center
|
||||||
|
+ Vector(
|
||||||
|
(diameter / 2.0 * math.cos(angle * i)),
|
||||||
|
(diameter / 2.0 * math.sin(angle * i)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
)
|
||||||
return Wire.makePolygon(pnts, forConstruction)
|
return Wire.makePolygon(pnts, forConstruction)
|
||||||
|
|
||||||
return self.eachpoint(_makePolygon, True)
|
return self.eachpoint(_makePolygon, True)
|
||||||
|
|
||||||
def polyline(self, listOfXYTuple, forConstruction=False,
|
def polyline(self, listOfXYTuple, forConstruction=False, includeCurrent=False):
|
||||||
includeCurrent=False):
|
|
||||||
"""
|
"""
|
||||||
Create a polyline from a list of points
|
Create a polyline from a list of points
|
||||||
|
|
||||||
@ -2089,11 +2127,11 @@ class Workplane(CQ):
|
|||||||
boreDir = Vector(0, 0, -1)
|
boreDir = Vector(0, 0, -1)
|
||||||
# first make the hole
|
# first make the hole
|
||||||
hole = Solid.makeCylinder(
|
hole = Solid.makeCylinder(
|
||||||
diameter / 2.0, depth, center, boreDir) # local coordianates!
|
diameter / 2.0, depth, center, boreDir
|
||||||
|
) # local coordianates!
|
||||||
|
|
||||||
# add the counter bore
|
# add the counter bore
|
||||||
cbore = Solid.makeCylinder(
|
cbore = Solid.makeCylinder(cboreDiameter / 2.0, cboreDepth, center, boreDir)
|
||||||
cboreDiameter / 2.0, cboreDepth, center, boreDir)
|
|
||||||
r = hole.fuse(cbore)
|
r = hole.fuse(cbore)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
@ -2142,7 +2180,8 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
# first make the hole
|
# first make the hole
|
||||||
hole = Solid.makeCylinder(
|
hole = Solid.makeCylinder(
|
||||||
diameter / 2.0, depth, center, boreDir) # local coords!
|
diameter / 2.0, depth, center, boreDir
|
||||||
|
) # local coords!
|
||||||
r = cskDiameter / 2.0
|
r = cskDiameter / 2.0
|
||||||
h = r / math.tan(math.radians(cskAngle / 2.0))
|
h = r / math.tan(math.radians(cskAngle / 2.0))
|
||||||
csk = Solid.makeCone(r, 0.0, h, center, boreDir)
|
csk = Solid.makeCone(r, 0.0, h, center, boreDir)
|
||||||
@ -2191,7 +2230,8 @@ class Workplane(CQ):
|
|||||||
boreDir = Vector(0, 0, -1)
|
boreDir = Vector(0, 0, -1)
|
||||||
# first make the hole
|
# first make the hole
|
||||||
hole = Solid.makeCylinder(
|
hole = Solid.makeCylinder(
|
||||||
diameter / 2.0, depth, center, boreDir) # local coordinates!
|
diameter / 2.0, depth, center, boreDir
|
||||||
|
) # local coordinates!
|
||||||
return hole
|
return hole
|
||||||
|
|
||||||
return self.cutEach(_makeHole, True, clean)
|
return self.cutEach(_makeHole, True, clean)
|
||||||
@ -2235,8 +2275,9 @@ class Workplane(CQ):
|
|||||||
# are multiple sets
|
# are multiple sets
|
||||||
r = None
|
r = None
|
||||||
for ws in wireSets:
|
for ws in wireSets:
|
||||||
thisObj = Solid.extrudeLinearWithRotation(ws[0], ws[1:], self.plane.origin,
|
thisObj = Solid.extrudeLinearWithRotation(
|
||||||
eDir, angleDegrees)
|
ws[0], ws[1:], self.plane.origin, eDir, angleDegrees
|
||||||
|
)
|
||||||
if r is None:
|
if r is None:
|
||||||
r = thisObj
|
r = thisObj
|
||||||
else:
|
else:
|
||||||
@ -2277,7 +2318,8 @@ class Workplane(CQ):
|
|||||||
selected may not be planar
|
selected may not be planar
|
||||||
"""
|
"""
|
||||||
r = self._extrude(
|
r = self._extrude(
|
||||||
distance, both=both, taper=taper) # returns a Solid (or a compound if there were multiple)
|
distance, both=both, taper=taper
|
||||||
|
) # returns a Solid (or a compound if there were multiple)
|
||||||
|
|
||||||
if combine:
|
if combine:
|
||||||
newS = self._combineWithBase(r)
|
newS = self._combineWithBase(r)
|
||||||
@ -2287,7 +2329,9 @@ class Workplane(CQ):
|
|||||||
newS = newS.clean()
|
newS = newS.clean()
|
||||||
return newS
|
return newS
|
||||||
|
|
||||||
def revolve(self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True, clean=True):
|
def revolve(
|
||||||
|
self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True, clean=True
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Use all un-revolved wires in the parent chain to create a solid.
|
Use all un-revolved wires in the parent chain to create a solid.
|
||||||
|
|
||||||
@ -2344,8 +2388,17 @@ class Workplane(CQ):
|
|||||||
newS = newS.clean()
|
newS = newS.clean()
|
||||||
return newS
|
return newS
|
||||||
|
|
||||||
def sweep(self, path, multisection=False, sweepAlongWires=None, makeSolid=True, isFrenet=False,
|
def sweep(
|
||||||
combine=True, clean=True, transition='right'):
|
self,
|
||||||
|
path,
|
||||||
|
multisection=False,
|
||||||
|
sweepAlongWires=None,
|
||||||
|
makeSolid=True,
|
||||||
|
isFrenet=False,
|
||||||
|
combine=True,
|
||||||
|
clean=True,
|
||||||
|
transition="right",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Use all un-extruded wires in the parent chain to create a swept solid.
|
Use all un-extruded wires in the parent chain to create a swept solid.
|
||||||
|
|
||||||
@ -2360,22 +2413,27 @@ class Workplane(CQ):
|
|||||||
Possible values are {'transformed','round', 'right'} (default: 'right').
|
Possible values are {'transformed','round', 'right'} (default: 'right').
|
||||||
:return: a CQ object with the resulting solid selected.
|
:return: a CQ object with the resulting solid selected.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not sweepAlongWires is None:
|
|
||||||
multisection=sweepAlongWires
|
|
||||||
|
|
||||||
from warnings import warn
|
|
||||||
warn('sweepAlongWires keyword argument is is depracated and will '\
|
|
||||||
'be removed in the next version; use multisection instead',
|
|
||||||
DeprecationWarning)
|
|
||||||
|
|
||||||
r = self._sweep(path.wire(), multisection, makeSolid, isFrenet,
|
if not sweepAlongWires is None:
|
||||||
transition) # returns a Solid (or a compound if there were multiple)
|
multisection = sweepAlongWires
|
||||||
|
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
warn(
|
||||||
|
"sweepAlongWires keyword argument is is depracated and will "
|
||||||
|
"be removed in the next version; use multisection instead",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
|
r = self._sweep(
|
||||||
|
path.wire(), multisection, makeSolid, isFrenet, transition
|
||||||
|
) # returns a Solid (or a compound if there were multiple)
|
||||||
if combine:
|
if combine:
|
||||||
newS = self._combineWithBase(r)
|
newS = self._combineWithBase(r)
|
||||||
else:
|
else:
|
||||||
newS = self.newObject([r])
|
newS = self.newObject([r])
|
||||||
if clean: newS = newS.clean()
|
if clean:
|
||||||
|
newS = newS.clean()
|
||||||
return newS
|
return newS
|
||||||
|
|
||||||
def _combineWithBase(self, obj):
|
def _combineWithBase(self, obj):
|
||||||
@ -2442,7 +2500,8 @@ class Workplane(CQ):
|
|||||||
solids = toUnion.solids().vals()
|
solids = toUnion.solids().vals()
|
||||||
if len(solids) < 1:
|
if len(solids) < 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"CQ object must have at least one solid on the stack to union!")
|
"CQ object must have at least one solid on the stack to union!"
|
||||||
|
)
|
||||||
newS = solids.pop(0)
|
newS = solids.pop(0)
|
||||||
for s in solids:
|
for s in solids:
|
||||||
newS = newS.fuse(s)
|
newS = newS.fuse(s)
|
||||||
@ -2492,7 +2551,7 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
if clean:
|
if clean:
|
||||||
newS = newS.clean()
|
newS = newS.clean()
|
||||||
|
|
||||||
return self.newObject([newS])
|
return self.newObject([newS])
|
||||||
|
|
||||||
def intersect(self, toIntersect, clean=True):
|
def intersect(self, toIntersect, clean=True):
|
||||||
@ -2522,8 +2581,9 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
newS = solidRef.intersect(solidToIntersect)
|
newS = solidRef.intersect(solidToIntersect)
|
||||||
|
|
||||||
if clean: newS = newS.clean()
|
if clean:
|
||||||
|
newS = newS.clean()
|
||||||
|
|
||||||
return self.newObject([newS])
|
return self.newObject([newS])
|
||||||
|
|
||||||
def cutBlind(self, distanceToCut, clean=True, taper=None):
|
def cutBlind(self, distanceToCut, clean=True, taper=None):
|
||||||
@ -2580,19 +2640,18 @@ class Workplane(CQ):
|
|||||||
solidRef = self.findSolid()
|
solidRef = self.findSolid()
|
||||||
faceRef = self.findFace()
|
faceRef = self.findFace()
|
||||||
|
|
||||||
#if no faces on the stack take the nearest face parallel to the plane zDir
|
# if no faces on the stack take the nearest face parallel to the plane zDir
|
||||||
if not faceRef:
|
if not faceRef:
|
||||||
#first select all with faces with good orietation
|
# first select all with faces with good orietation
|
||||||
sel = selectors.PerpendicularDirSelector(self.plane.zDir)
|
sel = selectors.PerpendicularDirSelector(self.plane.zDir)
|
||||||
faces = sel.filter(solidRef.Faces())
|
faces = sel.filter(solidRef.Faces())
|
||||||
#then select the closest
|
# then select the closest
|
||||||
sel = selectors.NearestToPointSelector(self.plane.origin.toTuple())
|
sel = selectors.NearestToPointSelector(self.plane.origin.toTuple())
|
||||||
faceRef = sel.filter(faces)[0]
|
faceRef = sel.filter(faces)[0]
|
||||||
|
|
||||||
rv = []
|
rv = []
|
||||||
for solid in solidRef.Solids():
|
for solid in solidRef.Solids():
|
||||||
s = solid.dprism(faceRef, wires, thruAll=True, additive=False,
|
s = solid.dprism(faceRef, wires, thruAll=True, additive=False, taper=-taper)
|
||||||
taper=-taper)
|
|
||||||
|
|
||||||
if clean:
|
if clean:
|
||||||
s = s.clean()
|
s = s.clean()
|
||||||
@ -2635,9 +2694,8 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
# group wires together into faces based on which ones are inside the others
|
# group wires together into faces based on which ones are inside the others
|
||||||
# result is a list of lists
|
# result is a list of lists
|
||||||
|
|
||||||
wireSets = sortWiresByBuildOrder(
|
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires), [])
|
||||||
list(self.ctx.pendingWires), [])
|
|
||||||
# now all of the wires have been used to create an extrusion
|
# now all of the wires have been used to create an extrusion
|
||||||
self.ctx.pendingWires = []
|
self.ctx.pendingWires = []
|
||||||
|
|
||||||
@ -2655,18 +2713,17 @@ class Workplane(CQ):
|
|||||||
toFuse = []
|
toFuse = []
|
||||||
|
|
||||||
if taper:
|
if taper:
|
||||||
for ws in wireSets:
|
for ws in wireSets:
|
||||||
thisObj = Solid.extrudeLinear(ws[0], [], eDir, taper)
|
thisObj = Solid.extrudeLinear(ws[0], [], eDir, taper)
|
||||||
toFuse.append(thisObj)
|
toFuse.append(thisObj)
|
||||||
else:
|
else:
|
||||||
for ws in wireSets:
|
for ws in wireSets:
|
||||||
thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir)
|
thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir)
|
||||||
toFuse.append(thisObj)
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
if both:
|
if both:
|
||||||
thisObj = Solid.extrudeLinear(
|
thisObj = Solid.extrudeLinear(ws[0], ws[1:], eDir.multiply(-1.0))
|
||||||
ws[0], ws[1:], eDir.multiply(-1.))
|
toFuse.append(thisObj)
|
||||||
toFuse.append(thisObj)
|
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
@ -2685,8 +2742,7 @@ class Workplane(CQ):
|
|||||||
This method is a utility method, primarily for plugin and internal use.
|
This method is a utility method, primarily for plugin and internal use.
|
||||||
"""
|
"""
|
||||||
# We have to gather the wires to be revolved
|
# We have to gather the wires to be revolved
|
||||||
wireSets = sortWiresByBuildOrder(
|
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
|
||||||
list(self.ctx.pendingWires))
|
|
||||||
|
|
||||||
# Mark that all of the wires have been used to create a revolution
|
# Mark that all of the wires have been used to create a revolution
|
||||||
self.ctx.pendingWires = []
|
self.ctx.pendingWires = []
|
||||||
@ -2694,14 +2750,19 @@ class Workplane(CQ):
|
|||||||
# Revolve the wires, make a compound out of them and then fuse them
|
# Revolve the wires, make a compound out of them and then fuse them
|
||||||
toFuse = []
|
toFuse = []
|
||||||
for ws in wireSets:
|
for ws in wireSets:
|
||||||
thisObj = Solid.revolve(
|
thisObj = Solid.revolve(ws[0], ws[1:], angleDegrees, axisStart, axisEnd)
|
||||||
ws[0], ws[1:], angleDegrees, axisStart, axisEnd)
|
|
||||||
toFuse.append(thisObj)
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
def _sweep(self, path, multisection=False, makeSolid=True, isFrenet=False,
|
def _sweep(
|
||||||
transition='right'):
|
self,
|
||||||
|
path,
|
||||||
|
multisection=False,
|
||||||
|
makeSolid=True,
|
||||||
|
isFrenet=False,
|
||||||
|
transition="right",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Makes a swept solid from an existing set of pending wires.
|
Makes a swept solid from an existing set of pending wires.
|
||||||
|
|
||||||
@ -2716,19 +2777,28 @@ class Workplane(CQ):
|
|||||||
if not multisection:
|
if not multisection:
|
||||||
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
|
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
|
||||||
for ws in wireSets:
|
for ws in wireSets:
|
||||||
thisObj = Solid.sweep(ws[0], ws[1:], path.val(), makeSolid,
|
thisObj = Solid.sweep(
|
||||||
isFrenet, transition)
|
ws[0], ws[1:], path.val(), makeSolid, isFrenet, transition
|
||||||
|
)
|
||||||
toFuse.append(thisObj)
|
toFuse.append(thisObj)
|
||||||
else:
|
else:
|
||||||
sections = self.ctx.pendingWires
|
sections = self.ctx.pendingWires
|
||||||
thisObj = Solid.sweep_multi(sections, path.val(), makeSolid, isFrenet)
|
thisObj = Solid.sweep_multi(sections, path.val(), makeSolid, isFrenet)
|
||||||
toFuse.append(thisObj)
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
self.ctx.pendingWires = []
|
self.ctx.pendingWires = []
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
def box(self, length, width, height, centered=(True, True, True), combine=True, clean=True):
|
def box(
|
||||||
|
self,
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
centered=(True, True, True),
|
||||||
|
combine=True,
|
||||||
|
clean=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Return a 3d box with specified dimensions for each object on the stack.
|
Return a 3d box with specified dimensions for each object on the stack.
|
||||||
|
|
||||||
@ -2773,14 +2843,14 @@ class Workplane(CQ):
|
|||||||
|
|
||||||
def _makebox(pnt):
|
def _makebox(pnt):
|
||||||
|
|
||||||
#(xp,yp,zp) = self.plane.toLocalCoords(pnt)
|
# (xp,yp,zp) = self.plane.toLocalCoords(pnt)
|
||||||
(xp, yp, zp) = pnt.toTuple()
|
(xp, yp, zp) = pnt.toTuple()
|
||||||
if centered[0]:
|
if centered[0]:
|
||||||
xp -= (length / 2.0)
|
xp -= length / 2.0
|
||||||
if centered[1]:
|
if centered[1]:
|
||||||
yp -= (width / 2.0)
|
yp -= width / 2.0
|
||||||
if centered[2]:
|
if centered[2]:
|
||||||
zp -= (height / 2.0)
|
zp -= height / 2.0
|
||||||
|
|
||||||
return Solid.makeBox(length, width, height, Vector(xp, yp, zp))
|
return Solid.makeBox(length, width, height, Vector(xp, yp, zp))
|
||||||
|
|
||||||
@ -2793,8 +2863,17 @@ class Workplane(CQ):
|
|||||||
# combine everything
|
# combine everything
|
||||||
return self.union(boxes, clean=clean)
|
return self.union(boxes, clean=clean)
|
||||||
|
|
||||||
def sphere(self, radius, direct=(0, 0, 1), angle1=-90, angle2=90, angle3=360,
|
def sphere(
|
||||||
centered=(True, True, True), combine=True, clean=True):
|
self,
|
||||||
|
radius,
|
||||||
|
direct=(0, 0, 1),
|
||||||
|
angle1=-90,
|
||||||
|
angle2=90,
|
||||||
|
angle3=360,
|
||||||
|
centered=(True, True, True),
|
||||||
|
combine=True,
|
||||||
|
clean=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Returns a 3D sphere with the specified radius for each point on the stack
|
Returns a 3D sphere with the specified radius for each point on the stack
|
||||||
|
|
||||||
@ -2851,7 +2930,9 @@ class Workplane(CQ):
|
|||||||
if not centered[2]:
|
if not centered[2]:
|
||||||
zp += radius
|
zp += radius
|
||||||
|
|
||||||
return Solid.makeSphere(radius, Vector(xp, yp, zp), direct, angle1, angle2, angle3)
|
return Solid.makeSphere(
|
||||||
|
radius, Vector(xp, yp, zp), direct, angle1, angle2, angle3
|
||||||
|
)
|
||||||
|
|
||||||
# We want a sphere for each point on the workplane
|
# We want a sphere for each point on the workplane
|
||||||
spheres = self.eachpoint(_makesphere, True)
|
spheres = self.eachpoint(_makesphere, True)
|
||||||
@ -2862,8 +2943,21 @@ class Workplane(CQ):
|
|||||||
else:
|
else:
|
||||||
return self.union(spheres, clean=clean)
|
return self.union(spheres, clean=clean)
|
||||||
|
|
||||||
def wedge(self, dx, dy, dz, xmin, zmin, xmax, zmax, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1),
|
def wedge(
|
||||||
centered=(True, True, True), combine=True, clean=True):
|
self,
|
||||||
|
dx,
|
||||||
|
dy,
|
||||||
|
dz,
|
||||||
|
xmin,
|
||||||
|
zmin,
|
||||||
|
xmax,
|
||||||
|
zmax,
|
||||||
|
pnt=Vector(0, 0, 0),
|
||||||
|
dir=Vector(0, 0, 1),
|
||||||
|
centered=(True, True, True),
|
||||||
|
combine=True,
|
||||||
|
clean=True,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
:param dx: Distance along the X axis
|
:param dx: Distance along the X axis
|
||||||
:param dy: Distance along the Y axis
|
:param dy: Distance along the Y axis
|
||||||
@ -2901,15 +2995,17 @@ class Workplane(CQ):
|
|||||||
(xp, yp, zp) = pnt.toTuple()
|
(xp, yp, zp) = pnt.toTuple()
|
||||||
|
|
||||||
if not centered[0]:
|
if not centered[0]:
|
||||||
xp += dx / 2.
|
xp += dx / 2.0
|
||||||
|
|
||||||
if not centered[1]:
|
if not centered[1]:
|
||||||
yp += dy / 2.
|
yp += dy / 2.0
|
||||||
|
|
||||||
if not centered[2]:
|
if not centered[2]:
|
||||||
zp += dx / 2.
|
zp += dx / 2.0
|
||||||
|
|
||||||
return Solid.makeWedge(dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir)
|
return Solid.makeWedge(
|
||||||
|
dx, dy, dz, xmin, zmin, xmax, zmax, Vector(xp, yp, zp), dir
|
||||||
|
)
|
||||||
|
|
||||||
# We want a wedge for each point on the workplane
|
# We want a wedge for each point on the workplane
|
||||||
wedges = self.eachpoint(_makewedge)
|
wedges = self.eachpoint(_makewedge)
|
||||||
@ -2945,11 +3041,23 @@ class Workplane(CQ):
|
|||||||
cleanObjects = [obj.clean() for obj in self.objects]
|
cleanObjects = [obj.clean() for obj in self.objects]
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise AttributeError(
|
raise AttributeError(
|
||||||
"%s object doesn't support `clean()` method!" % obj.ShapeType())
|
"%s object doesn't support `clean()` method!" % obj.ShapeType()
|
||||||
|
)
|
||||||
return self.newObject(cleanObjects)
|
return self.newObject(cleanObjects)
|
||||||
|
|
||||||
def text(self, txt, fontsize, distance, cut=True, combine=False, clean=True,
|
def text(
|
||||||
font="Arial", kind='regular',halign='center',valign='center'):
|
self,
|
||||||
|
txt,
|
||||||
|
fontsize,
|
||||||
|
distance,
|
||||||
|
cut=True,
|
||||||
|
combine=False,
|
||||||
|
clean=True,
|
||||||
|
font="Arial",
|
||||||
|
kind="regular",
|
||||||
|
halign="center",
|
||||||
|
valign="center",
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Create a 3D text
|
Create a 3D text
|
||||||
|
|
||||||
@ -2976,8 +3084,16 @@ class Workplane(CQ):
|
|||||||
and the resulting solid becomes the new context solid.
|
and the resulting solid becomes the new context solid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
r = Compound.makeText(txt,fontsize,distance,font=font,kind=kind,
|
r = Compound.makeText(
|
||||||
halign=halign, valign=valign, position=self.plane)
|
txt,
|
||||||
|
fontsize,
|
||||||
|
distance,
|
||||||
|
font=font,
|
||||||
|
kind=kind,
|
||||||
|
halign=halign,
|
||||||
|
valign=valign,
|
||||||
|
position=self.plane,
|
||||||
|
)
|
||||||
|
|
||||||
if cut:
|
if cut:
|
||||||
newS = self._cutFromBase(r)
|
newS = self._cutFromBase(r)
|
||||||
@ -2995,7 +3111,6 @@ class Workplane(CQ):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if type(self.objects[0]) is Vector:
|
if type(self.objects[0]) is Vector:
|
||||||
return '< {} >'.format(self.__repr__()[1:-1])
|
return "< {} >".format(self.__repr__()[1:-1])
|
||||||
else:
|
else:
|
||||||
return Compound.makeCompound(self.objects)._repr_html_()
|
return Compound.makeCompound(self.objects)._repr_html_()
|
||||||
|
|
||||||
|
@ -20,13 +20,22 @@ template = """
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
template_content_indent = ' '
|
template_content_indent = " "
|
||||||
|
|
||||||
|
|
||||||
def cq_directive(name, arguments, options, content, lineno,
|
def cq_directive(
|
||||||
content_offset, block_text, state, state_machine):
|
name,
|
||||||
|
arguments,
|
||||||
|
options,
|
||||||
|
content,
|
||||||
|
lineno,
|
||||||
|
content_offset,
|
||||||
|
block_text,
|
||||||
|
state,
|
||||||
|
state_machine,
|
||||||
|
):
|
||||||
# only consider inline snippets
|
# only consider inline snippets
|
||||||
plot_code = '\n'.join(content)
|
plot_code = "\n".join(content)
|
||||||
|
|
||||||
# Since we don't have a filename, use a hash based on the content
|
# Since we don't have a filename, use a hash based on the content
|
||||||
# the script must define a variable called 'out', which is expected to
|
# the script must define a variable called 'out', which is expected to
|
||||||
@ -52,22 +61,20 @@ def cq_directive(name, arguments, options, content, lineno,
|
|||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
# get rid of new lines
|
# get rid of new lines
|
||||||
out_svg = out_svg.replace('\n', '')
|
out_svg = out_svg.replace("\n", "")
|
||||||
|
|
||||||
txt_align = "left"
|
txt_align = "left"
|
||||||
if "align" in options:
|
if "align" in options:
|
||||||
txt_align = options['align']
|
txt_align = options["align"]
|
||||||
|
|
||||||
lines.extend((template % locals()).split('\n'))
|
lines.extend((template % locals()).split("\n"))
|
||||||
|
|
||||||
lines.extend(['::', ''])
|
lines.extend(["::", ""])
|
||||||
lines.extend([' %s' % row.rstrip()
|
lines.extend([" %s" % row.rstrip() for row in plot_code.split("\n")])
|
||||||
for row in plot_code.split('\n')])
|
lines.append("")
|
||||||
lines.append('')
|
|
||||||
|
|
||||||
if len(lines):
|
if len(lines):
|
||||||
state_machine.insert_input(
|
state_machine.insert_input(lines, state_machine.input_lines.source(0))
|
||||||
lines, state_machine.input_lines.source(0))
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@ -77,9 +84,10 @@ def setup(app):
|
|||||||
setup.config = app.config
|
setup.config = app.config
|
||||||
setup.confdir = app.confdir
|
setup.confdir = app.confdir
|
||||||
|
|
||||||
options = {'height': directives.length_or_unitless,
|
options = {
|
||||||
'width': directives.length_or_percentage_or_unitless,
|
"height": directives.length_or_unitless,
|
||||||
'align': directives.unchanged
|
"width": directives.length_or_percentage_or_unitless,
|
||||||
}
|
"align": directives.unchanged,
|
||||||
|
}
|
||||||
|
|
||||||
app.add_directive('cq_plot', cq_directive, True, (0, 2, 0), **options)
|
app.add_directive("cq_plot", cq_directive, True, (0, 2, 0), **options)
|
||||||
|
142
cadquery/cqgi.py
142
cadquery/cqgi.py
@ -9,6 +9,7 @@ import cadquery
|
|||||||
|
|
||||||
CQSCRIPT = "<cqscript>"
|
CQSCRIPT = "<cqscript>"
|
||||||
|
|
||||||
|
|
||||||
def parse(script_source):
|
def parse(script_source):
|
||||||
"""
|
"""
|
||||||
Parses the script as a model, and returns a model.
|
Parses the script as a model, and returns a model.
|
||||||
@ -34,6 +35,7 @@ class CQModel(object):
|
|||||||
|
|
||||||
the build method can be used to generate a 3d model
|
the build method can be used to generate a 3d model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, script_source):
|
def __init__(self, script_source):
|
||||||
"""
|
"""
|
||||||
Create an object by parsing the supplied python script.
|
Create an object by parsing the supplied python script.
|
||||||
@ -100,16 +102,20 @@ class CQModel(object):
|
|||||||
try:
|
try:
|
||||||
self.set_param_values(build_parameters)
|
self.set_param_values(build_parameters)
|
||||||
collector = ScriptCallback()
|
collector = ScriptCallback()
|
||||||
env = EnvironmentBuilder().with_real_builtins().with_cadquery_objects() \
|
env = (
|
||||||
.add_entry("__name__", "__cqgi__") \
|
EnvironmentBuilder()
|
||||||
.add_entry("show_object", collector.show_object) \
|
.with_real_builtins()
|
||||||
.add_entry("debug", collector.debug) \
|
.with_cadquery_objects()
|
||||||
.add_entry("describe_parameter",collector.describe_parameter) \
|
.add_entry("__name__", "__cqgi__")
|
||||||
|
.add_entry("show_object", collector.show_object)
|
||||||
|
.add_entry("debug", collector.debug)
|
||||||
|
.add_entry("describe_parameter", collector.describe_parameter)
|
||||||
.build()
|
.build()
|
||||||
|
)
|
||||||
|
|
||||||
c = compile(self.ast_tree, CQSCRIPT, 'exec')
|
c = compile(self.ast_tree, CQSCRIPT, "exec")
|
||||||
exec (c, env)
|
exec(c, env)
|
||||||
result.set_debug(collector.debugObjects )
|
result.set_debug(collector.debugObjects)
|
||||||
result.set_success_result(collector.outputObjects)
|
result.set_success_result(collector.outputObjects)
|
||||||
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -124,7 +130,9 @@ class CQModel(object):
|
|||||||
|
|
||||||
for k, v in params.items():
|
for k, v in params.items():
|
||||||
if k not in model_parameters:
|
if k not in model_parameters:
|
||||||
raise InvalidParameterError("Cannot set value '%s': not a parameter of the model." % k)
|
raise InvalidParameterError(
|
||||||
|
"Cannot set value '%s': not a parameter of the model." % k
|
||||||
|
)
|
||||||
|
|
||||||
p = model_parameters[k]
|
p = model_parameters[k]
|
||||||
p.set_value(v)
|
p.set_value(v)
|
||||||
@ -134,10 +142,12 @@ class ShapeResult(object):
|
|||||||
"""
|
"""
|
||||||
An object created by a build, including the user parameters provided
|
An object created by a build, including the user parameters provided
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.shape = None
|
self.shape = None
|
||||||
self.options = None
|
self.options = None
|
||||||
|
|
||||||
|
|
||||||
class BuildResult(object):
|
class BuildResult(object):
|
||||||
"""
|
"""
|
||||||
The result of executing a CadQuery script.
|
The result of executing a CadQuery script.
|
||||||
@ -149,10 +159,11 @@ class BuildResult(object):
|
|||||||
If unsuccessful, the exception property contains a reference to
|
If unsuccessful, the exception property contains a reference to
|
||||||
the stack trace that occurred.
|
the stack trace that occurred.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.buildTime = None
|
self.buildTime = None
|
||||||
self.results = [] #list of ShapeResult
|
self.results = [] # list of ShapeResult
|
||||||
self.debugObjects = [] #list of ShapeResult
|
self.debugObjects = [] # list of ShapeResult
|
||||||
self.first_result = None
|
self.first_result = None
|
||||||
self.success = False
|
self.success = False
|
||||||
self.exception = None
|
self.exception = None
|
||||||
@ -176,13 +187,14 @@ class ScriptMetadata(object):
|
|||||||
Defines the metadata for a parsed CQ Script.
|
Defines the metadata for a parsed CQ Script.
|
||||||
the parameters property is a dict of InputParameter objects.
|
the parameters property is a dict of InputParameter objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.parameters = {}
|
self.parameters = {}
|
||||||
|
|
||||||
def add_script_parameter(self, p):
|
def add_script_parameter(self, p):
|
||||||
self.parameters[p.name] = p
|
self.parameters[p.name] = p
|
||||||
|
|
||||||
def add_parameter_description(self,name,description):
|
def add_parameter_description(self, name, description):
|
||||||
p = self.parameters[name]
|
p = self.parameters[name]
|
||||||
p.desc = description
|
p.desc = description
|
||||||
|
|
||||||
@ -214,6 +226,7 @@ class InputParameter:
|
|||||||
provide additional metadata
|
provide additional metadata
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
#: the default value for the variable.
|
#: the default value for the variable.
|
||||||
@ -234,7 +247,9 @@ class InputParameter:
|
|||||||
self.ast_node = None
|
self.ast_node = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(ast_node, var_name, var_type, default_value, valid_values=None, desc=None):
|
def create(
|
||||||
|
ast_node, var_name, var_type, default_value, valid_values=None, desc=None
|
||||||
|
):
|
||||||
|
|
||||||
if valid_values is None:
|
if valid_values is None:
|
||||||
valid_values = []
|
valid_values = []
|
||||||
@ -251,8 +266,10 @@ class InputParameter:
|
|||||||
def set_value(self, new_value):
|
def set_value(self, new_value):
|
||||||
if len(self.valid_values) > 0 and new_value not in self.valid_values:
|
if len(self.valid_values) > 0 and new_value not in self.valid_values:
|
||||||
raise InvalidParameterError(
|
raise InvalidParameterError(
|
||||||
"Cannot set value '{0:s}' for parameter '{1:s}': not a valid value. Valid values are {2:s} "
|
"Cannot set value '{0:s}' for parameter '{1:s}': not a valid value. Valid values are {2:s} ".format(
|
||||||
.format(str(new_value), self.name, str(self.valid_values)))
|
str(new_value), self.name, str(self.valid_values)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if self.varType == NumberParameterType:
|
if self.varType == NumberParameterType:
|
||||||
try:
|
try:
|
||||||
@ -265,28 +282,33 @@ class InputParameter:
|
|||||||
self.ast_node.n = f
|
self.ast_node.n = f
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidParameterError(
|
raise InvalidParameterError(
|
||||||
"Cannot set value '{0:s}' for parameter '{1:s}': parameter must be numeric."
|
"Cannot set value '{0:s}' for parameter '{1:s}': parameter must be numeric.".format(
|
||||||
.format(str(new_value), self.name))
|
str(new_value), self.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
elif self.varType == StringParameterType:
|
elif self.varType == StringParameterType:
|
||||||
self.ast_node.s = str(new_value)
|
self.ast_node.s = str(new_value)
|
||||||
elif self.varType == BooleanParameterType:
|
elif self.varType == BooleanParameterType:
|
||||||
if new_value:
|
if new_value:
|
||||||
if hasattr(ast, 'NameConstant'):
|
if hasattr(ast, "NameConstant"):
|
||||||
self.ast_node.value = True
|
self.ast_node.value = True
|
||||||
else:
|
else:
|
||||||
self.ast_node.id = 'True'
|
self.ast_node.id = "True"
|
||||||
else:
|
else:
|
||||||
if hasattr(ast, 'NameConstant'):
|
if hasattr(ast, "NameConstant"):
|
||||||
self.ast_node.value = False
|
self.ast_node.value = False
|
||||||
else:
|
else:
|
||||||
self.ast_node.id = 'False'
|
self.ast_node.id = "False"
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unknown Type of var: ", str(self.varType))
|
raise ValueError("Unknown Type of var: ", str(self.varType))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "InputParameter: {name=%s, type=%s, defaultValue=%s" % (
|
return "InputParameter: {name=%s, type=%s, defaultValue=%s" % (
|
||||||
self.name, str(self.varType), str(self.default_value))
|
self.name,
|
||||||
|
str(self.varType),
|
||||||
|
str(self.default_value),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ScriptCallback(object):
|
class ScriptCallback(object):
|
||||||
@ -295,22 +317,23 @@ class ScriptCallback(object):
|
|||||||
the show_object() method is exposed to CQ scripts, to allow them
|
the show_object() method is exposed to CQ scripts, to allow them
|
||||||
to return objects to the execution environment
|
to return objects to the execution environment
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.outputObjects = []
|
self.outputObjects = []
|
||||||
self.debugObjects = []
|
self.debugObjects = []
|
||||||
|
|
||||||
def show_object(self, shape,options={}):
|
def show_object(self, shape, options={}):
|
||||||
"""
|
"""
|
||||||
return an object to the executing environment, with options
|
return an object to the executing environment, with options
|
||||||
:param shape: a cadquery object
|
:param shape: a cadquery object
|
||||||
:param options: a dictionary of options that will be made available to the executing environment
|
:param options: a dictionary of options that will be made available to the executing environment
|
||||||
"""
|
"""
|
||||||
o = ShapeResult()
|
o = ShapeResult()
|
||||||
o.options=options
|
o.options = options
|
||||||
o.shape = shape
|
o.shape = shape
|
||||||
self.outputObjects.append(o)
|
self.outputObjects.append(o)
|
||||||
|
|
||||||
def debug(self,obj,args={}):
|
def debug(self, obj, args={}):
|
||||||
"""
|
"""
|
||||||
Debug print/output an object, with optional arguments.
|
Debug print/output an object, with optional arguments.
|
||||||
"""
|
"""
|
||||||
@ -319,7 +342,7 @@ class ScriptCallback(object):
|
|||||||
s.options = args
|
s.options = args
|
||||||
self.debugObjects.append(s)
|
self.debugObjects.append(s)
|
||||||
|
|
||||||
def describe_parameter(self,var_data ):
|
def describe_parameter(self, var_data):
|
||||||
"""
|
"""
|
||||||
Do Nothing-- we parsed the ast ahead of execution to get what we need.
|
Do Nothing-- we parsed the ast ahead of execution to get what we need.
|
||||||
"""
|
"""
|
||||||
@ -335,12 +358,12 @@ class ScriptCallback(object):
|
|||||||
return len(self.outputObjects) > 0
|
return len(self.outputObjects) > 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidParameterError(Exception):
|
class InvalidParameterError(Exception):
|
||||||
"""
|
"""
|
||||||
Raised when an attempt is made to provide a new parameter value
|
Raised when an attempt is made to provide a new parameter value
|
||||||
that cannot be assigned to the model
|
that cannot be assigned to the model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -349,6 +372,7 @@ class NoOutputError(Exception):
|
|||||||
Raised when the script does not execute the show_object() method to
|
Raised when the script does not execute the show_object() method to
|
||||||
return a solid
|
return a solid
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -386,6 +410,7 @@ class EnvironmentBuilder(object):
|
|||||||
The environment includes the builtins, as well as
|
The environment includes the builtins, as well as
|
||||||
the other methods the script will need.
|
the other methods the script will need.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.env = {}
|
self.env = {}
|
||||||
|
|
||||||
@ -393,12 +418,12 @@ class EnvironmentBuilder(object):
|
|||||||
return self.with_builtins(__builtins__)
|
return self.with_builtins(__builtins__)
|
||||||
|
|
||||||
def with_builtins(self, env_dict):
|
def with_builtins(self, env_dict):
|
||||||
self.env['__builtins__'] = env_dict
|
self.env["__builtins__"] = env_dict
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def with_cadquery_objects(self):
|
def with_cadquery_objects(self):
|
||||||
self.env['cadquery'] = cadquery
|
self.env["cadquery"] = cadquery
|
||||||
self.env['cq'] = cadquery
|
self.env["cq"] = cadquery
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_entry(self, name, value):
|
def add_entry(self, name, value):
|
||||||
@ -408,30 +433,33 @@ class EnvironmentBuilder(object):
|
|||||||
def build(self):
|
def build(self):
|
||||||
return self.env
|
return self.env
|
||||||
|
|
||||||
|
|
||||||
class ParameterDescriptionFinder(ast.NodeTransformer):
|
class ParameterDescriptionFinder(ast.NodeTransformer):
|
||||||
"""
|
"""
|
||||||
Visits a parse tree, looking for function calls to describe_parameter(var, description )
|
Visits a parse tree, looking for function calls to describe_parameter(var, description )
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, cq_model):
|
def __init__(self, cq_model):
|
||||||
self.cqModel = cq_model
|
self.cqModel = cq_model
|
||||||
|
|
||||||
def visit_Call(self,node):
|
def visit_Call(self, node):
|
||||||
"""
|
"""
|
||||||
Called when we see a function call. Is it describe_parameter?
|
Called when we see a function call. Is it describe_parameter?
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if node.func.id == 'describe_parameter':
|
if node.func.id == "describe_parameter":
|
||||||
# looks like we have a call to our function.
|
# looks like we have a call to our function.
|
||||||
# first parameter is the variable,
|
# first parameter is the variable,
|
||||||
# second is the description
|
# second is the description
|
||||||
varname = node.args[0].id
|
varname = node.args[0].id
|
||||||
desc = node.args[1].s
|
desc = node.args[1].s
|
||||||
self.cqModel.add_parameter_description(varname,desc)
|
self.cqModel.add_parameter_description(varname, desc)
|
||||||
|
|
||||||
except:
|
except:
|
||||||
#print "Unable to handle function call"
|
# print "Unable to handle function call"
|
||||||
pass
|
pass
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
class ConstantAssignmentFinder(ast.NodeTransformer):
|
class ConstantAssignmentFinder(ast.NodeTransformer):
|
||||||
"""
|
"""
|
||||||
@ -446,24 +474,42 @@ class ConstantAssignmentFinder(ast.NodeTransformer):
|
|||||||
|
|
||||||
if type(value_node) == ast.Num:
|
if type(value_node) == ast.Num:
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, NumberParameterType, value_node.n))
|
InputParameter.create(
|
||||||
|
value_node, var_name, NumberParameterType, value_node.n
|
||||||
|
)
|
||||||
|
)
|
||||||
elif type(value_node) == ast.Str:
|
elif type(value_node) == ast.Str:
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, StringParameterType, value_node.s))
|
InputParameter.create(
|
||||||
|
value_node, var_name, StringParameterType, value_node.s
|
||||||
|
)
|
||||||
|
)
|
||||||
elif type(value_node) == ast.Name:
|
elif type(value_node) == ast.Name:
|
||||||
if value_node.id == 'True':
|
if value_node.id == "True":
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, BooleanParameterType, True))
|
InputParameter.create(
|
||||||
elif value_node.id == 'False':
|
value_node, var_name, BooleanParameterType, True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif value_node.id == "False":
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, BooleanParameterType, False))
|
InputParameter.create(
|
||||||
elif hasattr(ast, 'NameConstant') and type(value_node) == ast.NameConstant:
|
value_node, var_name, BooleanParameterType, False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif hasattr(ast, "NameConstant") and type(value_node) == ast.NameConstant:
|
||||||
if value_node.value == True:
|
if value_node.value == True:
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, BooleanParameterType, True))
|
InputParameter.create(
|
||||||
|
value_node, var_name, BooleanParameterType, True
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.cqModel.add_script_parameter(
|
self.cqModel.add_script_parameter(
|
||||||
InputParameter.create(value_node, var_name, BooleanParameterType, False))
|
InputParameter.create(
|
||||||
|
value_node, var_name, BooleanParameterType, False
|
||||||
|
)
|
||||||
|
)
|
||||||
except:
|
except:
|
||||||
print("Unable to handle assignment for variable '%s'" % var_name)
|
print("Unable to handle assignment for variable '%s'" % var_name)
|
||||||
pass
|
pass
|
||||||
@ -479,7 +525,7 @@ class ConstantAssignmentFinder(ast.NodeTransformer):
|
|||||||
|
|
||||||
# Handle the NamedConstant type that is only present in Python 3
|
# Handle the NamedConstant type that is only present in Python 3
|
||||||
astTypes = [ast.Num, ast.Str, ast.Name]
|
astTypes = [ast.Num, ast.Str, ast.Name]
|
||||||
if hasattr(ast, 'NameConstant'):
|
if hasattr(ast, "NameConstant"):
|
||||||
astTypes.append(ast.NameConstant)
|
astTypes.append(ast.NameConstant)
|
||||||
|
|
||||||
if type(node.value) in astTypes:
|
if type(node.value) in astTypes:
|
||||||
|
@ -4,6 +4,7 @@ from OCC.Core.Visualization import Tesselator
|
|||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if sys.version_info.major == 2:
|
if sys.version_info.major == 2:
|
||||||
import cStringIO as StringIO
|
import cStringIO as StringIO
|
||||||
else:
|
else:
|
||||||
@ -56,7 +57,7 @@ def exportShape(shape, exportType, fileLike, tolerance=0.1):
|
|||||||
The object should be already open and ready to write. The caller is responsible
|
The object should be already open and ready to write. The caller is responsible
|
||||||
for closing the object
|
for closing the object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ..cq import CQ
|
from ..cq import CQ
|
||||||
|
|
||||||
def tessellate(shape):
|
def tessellate(shape):
|
||||||
@ -116,7 +117,7 @@ def readAndDeleteFile(fileName):
|
|||||||
return the contents as a string
|
return the contents as a string
|
||||||
"""
|
"""
|
||||||
res = ""
|
res = ""
|
||||||
with open(fileName, 'r') as f:
|
with open(fileName, "r") as f:
|
||||||
res = "{}".format(f.read())
|
res = "{}".format(f.read())
|
||||||
|
|
||||||
os.remove(fileName)
|
os.remove(fileName)
|
||||||
@ -152,34 +153,34 @@ class AmfWriter(object):
|
|||||||
self.tessellation = tessellation
|
self.tessellation = tessellation
|
||||||
|
|
||||||
def writeAmf(self, outFile):
|
def writeAmf(self, outFile):
|
||||||
amf = ET.Element('amf', units=self.units)
|
amf = ET.Element("amf", units=self.units)
|
||||||
# TODO: if result is a compound, we need to loop through them
|
# TODO: if result is a compound, we need to loop through them
|
||||||
object = ET.SubElement(amf, 'object', id="0")
|
object = ET.SubElement(amf, "object", id="0")
|
||||||
mesh = ET.SubElement(object, 'mesh')
|
mesh = ET.SubElement(object, "mesh")
|
||||||
vertices = ET.SubElement(mesh, 'vertices')
|
vertices = ET.SubElement(mesh, "vertices")
|
||||||
volume = ET.SubElement(mesh, 'volume')
|
volume = ET.SubElement(mesh, "volume")
|
||||||
|
|
||||||
# add vertices
|
# add vertices
|
||||||
for i_vert in range(self.tessellation.ObjGetVertexCount()):
|
for i_vert in range(self.tessellation.ObjGetVertexCount()):
|
||||||
v = self.tessellation.GetVertex(i_vert)
|
v = self.tessellation.GetVertex(i_vert)
|
||||||
vtx = ET.SubElement(vertices, 'vertex')
|
vtx = ET.SubElement(vertices, "vertex")
|
||||||
coord = ET.SubElement(vtx, 'coordinates')
|
coord = ET.SubElement(vtx, "coordinates")
|
||||||
x = ET.SubElement(coord, 'x')
|
x = ET.SubElement(coord, "x")
|
||||||
x.text = str(v[0])
|
x.text = str(v[0])
|
||||||
y = ET.SubElement(coord, 'y')
|
y = ET.SubElement(coord, "y")
|
||||||
y.text = str(v[1])
|
y.text = str(v[1])
|
||||||
z = ET.SubElement(coord, 'z')
|
z = ET.SubElement(coord, "z")
|
||||||
z.text = str(v[2])
|
z.text = str(v[2])
|
||||||
|
|
||||||
# add triangles
|
# add triangles
|
||||||
for i_tr in range(self.tessellation.ObjGetTriangleCount()):
|
for i_tr in range(self.tessellation.ObjGetTriangleCount()):
|
||||||
t = self.tessellation.GetTriangleIndex(i_tr)
|
t = self.tessellation.GetTriangleIndex(i_tr)
|
||||||
triangle = ET.SubElement(volume, 'triangle')
|
triangle = ET.SubElement(volume, "triangle")
|
||||||
v1 = ET.SubElement(triangle, 'v1')
|
v1 = ET.SubElement(triangle, "v1")
|
||||||
v1.text = str(t[0])
|
v1.text = str(t[0])
|
||||||
v2 = ET.SubElement(triangle, 'v2')
|
v2 = ET.SubElement(triangle, "v2")
|
||||||
v2.text = str(t[1])
|
v2.text = str(t[1])
|
||||||
v3 = ET.SubElement(triangle, 'v3')
|
v3 = ET.SubElement(triangle, "v3")
|
||||||
v3.text = str(t[2])
|
v3.text = str(t[2])
|
||||||
|
|
||||||
amf = ET.ElementTree(amf).write(outFile, xml_declaration=True)
|
amf = ET.ElementTree(amf).write(outFile, xml_declaration=True)
|
||||||
@ -217,11 +218,11 @@ class JsonMesh(object):
|
|||||||
|
|
||||||
def toJson(self):
|
def toJson(self):
|
||||||
return JSON_TEMPLATE % {
|
return JSON_TEMPLATE % {
|
||||||
'vertices': str(self.vertices),
|
"vertices": str(self.vertices),
|
||||||
'faces': str(self.faces),
|
"faces": str(self.faces),
|
||||||
'nVertices': self.nVertices,
|
"nVertices": self.nVertices,
|
||||||
'nFaces': self.nFaces
|
"nFaces": self.nFaces,
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
def makeSVGedge(e):
|
def makeSVGedge(e):
|
||||||
@ -235,20 +236,16 @@ def makeSVGedge(e):
|
|||||||
start = curve.FirstParameter()
|
start = curve.FirstParameter()
|
||||||
end = curve.LastParameter()
|
end = curve.LastParameter()
|
||||||
|
|
||||||
points = GCPnts_QuasiUniformDeflection(curve,
|
points = GCPnts_QuasiUniformDeflection(curve, DISCRETIZATION_TOLERANCE, start, end)
|
||||||
DISCRETIZATION_TOLERANCE,
|
|
||||||
start,
|
|
||||||
end)
|
|
||||||
|
|
||||||
if points.IsDone():
|
if points.IsDone():
|
||||||
point_it = (points.Value(i + 1) for i in
|
point_it = (points.Value(i + 1) for i in range(points.NbPoints()))
|
||||||
range(points.NbPoints()))
|
|
||||||
|
|
||||||
p = next(point_it)
|
p = next(point_it)
|
||||||
cs.write('M{},{} '.format(p.X(), p.Y()))
|
cs.write("M{},{} ".format(p.X(), p.Y()))
|
||||||
|
|
||||||
for p in point_it:
|
for p in point_it:
|
||||||
cs.write('L{},{} '.format(p.X(), p.Y()))
|
cs.write("L{},{} ".format(p.X(), p.Y()))
|
||||||
|
|
||||||
return cs.getvalue()
|
return cs.getvalue()
|
||||||
|
|
||||||
@ -277,7 +274,7 @@ def getSVG(shape, opts=None):
|
|||||||
Export a shape to SVG
|
Export a shape to SVG
|
||||||
"""
|
"""
|
||||||
|
|
||||||
d = {'width': 800, 'height': 240, 'marginLeft': 200, 'marginTop': 20}
|
d = {"width": 800, "height": 240, "marginLeft": 200, "marginTop": 20}
|
||||||
|
|
||||||
if opts:
|
if opts:
|
||||||
d.update(opts)
|
d.update(opts)
|
||||||
@ -285,17 +282,15 @@ def getSVG(shape, opts=None):
|
|||||||
# need to guess the scale and the coordinate center
|
# need to guess the scale and the coordinate center
|
||||||
uom = guessUnitOfMeasure(shape)
|
uom = guessUnitOfMeasure(shape)
|
||||||
|
|
||||||
width = float(d['width'])
|
width = float(d["width"])
|
||||||
height = float(d['height'])
|
height = float(d["height"])
|
||||||
marginLeft = float(d['marginLeft'])
|
marginLeft = float(d["marginLeft"])
|
||||||
marginTop = float(d['marginTop'])
|
marginTop = float(d["marginTop"])
|
||||||
|
|
||||||
hlr = HLRBRep_Algo()
|
hlr = HLRBRep_Algo()
|
||||||
hlr.Add(shape.wrapped)
|
hlr.Add(shape.wrapped)
|
||||||
|
|
||||||
projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(),
|
projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(), DEFAULT_DIR))
|
||||||
DEFAULT_DIR)
|
|
||||||
)
|
|
||||||
|
|
||||||
hlr.Projector(projector)
|
hlr.Projector(projector)
|
||||||
hlr.Update()
|
hlr.Update()
|
||||||
@ -336,8 +331,7 @@ def getSVG(shape, opts=None):
|
|||||||
# convert to native CQ objects
|
# convert to native CQ objects
|
||||||
visible = list(map(Shape, visible))
|
visible = list(map(Shape, visible))
|
||||||
hidden = list(map(Shape, hidden))
|
hidden = list(map(Shape, hidden))
|
||||||
(hiddenPaths, visiblePaths) = getPaths(visible,
|
(hiddenPaths, visiblePaths) = getPaths(visible, hidden)
|
||||||
hidden)
|
|
||||||
|
|
||||||
# get bounding box -- these are all in 2-d space
|
# get bounding box -- these are all in 2-d space
|
||||||
bb = Compound.makeCompound(hidden + visible).BoundingBox()
|
bb = Compound.makeCompound(hidden + visible).BoundingBox()
|
||||||
@ -346,8 +340,10 @@ def getSVG(shape, opts=None):
|
|||||||
unitScale = min(width / bb.xlen * 0.75, height / bb.ylen * 0.75)
|
unitScale = min(width / bb.xlen * 0.75, height / bb.ylen * 0.75)
|
||||||
|
|
||||||
# compute amount to translate-- move the top left into view
|
# compute amount to translate-- move the top left into view
|
||||||
(xTranslate, yTranslate) = ((0 - bb.xmin) + marginLeft /
|
(xTranslate, yTranslate) = (
|
||||||
unitScale, (0 - bb.ymax) - marginTop / unitScale)
|
(0 - bb.xmin) + marginLeft / unitScale,
|
||||||
|
(0 - bb.ymax) - marginTop / unitScale,
|
||||||
|
)
|
||||||
|
|
||||||
# compute paths ( again -- had to strip out freecad crap )
|
# compute paths ( again -- had to strip out freecad crap )
|
||||||
hiddenContent = ""
|
hiddenContent = ""
|
||||||
@ -362,19 +358,19 @@ def getSVG(shape, opts=None):
|
|||||||
{
|
{
|
||||||
"unitScale": str(unitScale),
|
"unitScale": str(unitScale),
|
||||||
"strokeWidth": str(1.0 / unitScale),
|
"strokeWidth": str(1.0 / unitScale),
|
||||||
"hiddenContent": hiddenContent,
|
"hiddenContent": hiddenContent,
|
||||||
"visibleContent": visibleContent,
|
"visibleContent": visibleContent,
|
||||||
"xTranslate": str(xTranslate),
|
"xTranslate": str(xTranslate),
|
||||||
"yTranslate": str(yTranslate),
|
"yTranslate": str(yTranslate),
|
||||||
"width": str(width),
|
"width": str(width),
|
||||||
"height": str(height),
|
"height": str(height),
|
||||||
"textboxY": str(height - 30),
|
"textboxY": str(height - 30),
|
||||||
"uom": str(uom)
|
"uom": str(uom),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
# svg = SVG_TEMPLATE % (
|
# svg = SVG_TEMPLATE % (
|
||||||
# {"content": projectedContent}
|
# {"content": projectedContent}
|
||||||
#)
|
# )
|
||||||
return svg
|
return svg
|
||||||
|
|
||||||
|
|
||||||
@ -386,7 +382,7 @@ def exportSVG(shape, fileName):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
svg = getSVG(shape.val())
|
svg = getSVG(shape.val())
|
||||||
f = open(fileName, 'w')
|
f = open(fileName, "w")
|
||||||
f.write(svg)
|
f.write(svg)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
@ -471,4 +467,4 @@ SVG_TEMPLATE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||||||
</svg>
|
</svg>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
PATHTEMPLATE = "\t\t\t<path d=\"%s\" />\n"
|
PATHTEMPLATE = '\t\t\t<path d="%s" />\n'
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
import math
|
import math
|
||||||
|
|
||||||
from OCC.Core.gp import gp_Vec, gp_Ax1, gp_Ax3, gp_Pnt, gp_Dir, gp_Trsf, gp_GTrsf, gp, gp_XYZ
|
from OCC.Core.gp import (
|
||||||
|
gp_Vec,
|
||||||
|
gp_Ax1,
|
||||||
|
gp_Ax3,
|
||||||
|
gp_Pnt,
|
||||||
|
gp_Dir,
|
||||||
|
gp_Trsf,
|
||||||
|
gp_GTrsf,
|
||||||
|
gp,
|
||||||
|
gp_XYZ,
|
||||||
|
)
|
||||||
from OCC.Core.Bnd import Bnd_Box
|
from OCC.Core.Bnd import Bnd_Box
|
||||||
from OCC.Core.BRepBndLib import brepbndlib_Add # brepbndlib_AddOptimal
|
from OCC.Core.BRepBndLib import brepbndlib_Add # brepbndlib_AddOptimal
|
||||||
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
|
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
|
||||||
@ -27,16 +37,16 @@ class Vector(object):
|
|||||||
if len(args) == 3:
|
if len(args) == 3:
|
||||||
fV = gp_Vec(*args)
|
fV = gp_Vec(*args)
|
||||||
elif len(args) == 2:
|
elif len(args) == 2:
|
||||||
fV = gp_Vec(*args,0)
|
fV = gp_Vec(*args, 0)
|
||||||
elif len(args) == 1:
|
elif len(args) == 1:
|
||||||
if isinstance(args[0], Vector):
|
if isinstance(args[0], Vector):
|
||||||
fV = gp_Vec(args[0].wrapped.XYZ())
|
fV = gp_Vec(args[0].wrapped.XYZ())
|
||||||
elif isinstance(args[0], (tuple, list)):
|
elif isinstance(args[0], (tuple, list)):
|
||||||
arg = args[0]
|
arg = args[0]
|
||||||
if len(arg)==3:
|
if len(arg) == 3:
|
||||||
fV = gp_Vec(*arg)
|
fV = gp_Vec(*arg)
|
||||||
elif len(arg)==2:
|
elif len(arg) == 2:
|
||||||
fV = gp_Vec(*arg,0)
|
fV = gp_Vec(*arg, 0)
|
||||||
elif isinstance(args[0], (gp_Vec, gp_Pnt, gp_Dir)):
|
elif isinstance(args[0], (gp_Vec, gp_Pnt, gp_Dir)):
|
||||||
fV = gp_Vec(args[0].XYZ())
|
fV = gp_Vec(args[0].XYZ())
|
||||||
elif isinstance(args[0], gp_XYZ):
|
elif isinstance(args[0], gp_XYZ):
|
||||||
@ -53,25 +63,25 @@ class Vector(object):
|
|||||||
@property
|
@property
|
||||||
def x(self):
|
def x(self):
|
||||||
return self.wrapped.X()
|
return self.wrapped.X()
|
||||||
|
|
||||||
@x.setter
|
@x.setter
|
||||||
def x(self,value):
|
def x(self, value):
|
||||||
self.wrapped.SetX(value)
|
self.wrapped.SetX(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def y(self):
|
def y(self):
|
||||||
return self.wrapped.Y()
|
return self.wrapped.Y()
|
||||||
|
|
||||||
@y.setter
|
@y.setter
|
||||||
def y(self,value):
|
def y(self, value):
|
||||||
self.wrapped.SetY(value)
|
self.wrapped.SetY(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def z(self):
|
def z(self):
|
||||||
return self.wrapped.Z()
|
return self.wrapped.Z()
|
||||||
|
|
||||||
@z.setter
|
@z.setter
|
||||||
def z(self,value):
|
def z(self, value):
|
||||||
self.wrapped.SetZ(value)
|
self.wrapped.SetZ(value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -132,16 +142,13 @@ class Vector(object):
|
|||||||
return self.wrapped.Angle(v.wrapped)
|
return self.wrapped.Angle(v.wrapped)
|
||||||
|
|
||||||
def distanceToLine(self):
|
def distanceToLine(self):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("Have not needed this yet, but FreeCAD supports it!")
|
||||||
"Have not needed this yet, but FreeCAD supports it!")
|
|
||||||
|
|
||||||
def projectToLine(self):
|
def projectToLine(self):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("Have not needed this yet, but FreeCAD supports it!")
|
||||||
"Have not needed this yet, but FreeCAD supports it!")
|
|
||||||
|
|
||||||
def distanceToPlane(self):
|
def distanceToPlane(self):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("Have not needed this yet, but FreeCAD supports it!")
|
||||||
"Have not needed this yet, but FreeCAD supports it!")
|
|
||||||
|
|
||||||
def projectToPlane(self, plane):
|
def projectToPlane(self, plane):
|
||||||
"""
|
"""
|
||||||
@ -154,7 +161,7 @@ class Vector(object):
|
|||||||
base = plane.origin
|
base = plane.origin
|
||||||
normal = plane.zDir
|
normal = plane.zDir
|
||||||
|
|
||||||
return self-normal*(((self-base).dot(normal))/normal.Length**2)
|
return self - normal * (((self - base).dot(normal)) / normal.Length ** 2)
|
||||||
|
|
||||||
def __neg__(self):
|
def __neg__(self):
|
||||||
return self * -1
|
return self * -1
|
||||||
@ -163,18 +170,19 @@ class Vector(object):
|
|||||||
return self.Length
|
return self.Length
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Vector: ' + str((self.x, self.y, self.z))
|
return "Vector: " + str((self.x, self.y, self.z))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Vector: ' + str((self.x, self.y, self.z))
|
return "Vector: " + str((self.x, self.y, self.z))
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.wrapped.IsEqual(other.wrapped, 0.00001, 0.00001)
|
return self.wrapped.IsEqual(other.wrapped, 0.00001, 0.00001)
|
||||||
'''
|
|
||||||
|
"""
|
||||||
is not implemented in OCC
|
is not implemented in OCC
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return self.wrapped.__ne__(other)
|
return self.wrapped.__ne__(other)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def toPnt(self):
|
def toPnt(self):
|
||||||
|
|
||||||
@ -222,44 +230,48 @@ class Matrix:
|
|||||||
elif isinstance(matrix, (list, tuple)):
|
elif isinstance(matrix, (list, tuple)):
|
||||||
# Validate matrix size & 4x4 last row value
|
# Validate matrix size & 4x4 last row value
|
||||||
valid_sizes = all(
|
valid_sizes = all(
|
||||||
(isinstance(row, (list, tuple)) and (len(row) == 4))
|
(isinstance(row, (list, tuple)) and (len(row) == 4)) for row in matrix
|
||||||
for row in matrix
|
|
||||||
) and len(matrix) in (3, 4)
|
) and len(matrix) in (3, 4)
|
||||||
if not valid_sizes:
|
if not valid_sizes:
|
||||||
raise TypeError("Matrix constructor requires 2d list of 4x3 or 4x4, but got: {!r}".format(matrix))
|
raise TypeError(
|
||||||
elif (len(matrix) == 4) and (tuple(matrix[3]) != (0,0,0,1)):
|
"Matrix constructor requires 2d list of 4x3 or 4x4, but got: {!r}".format(
|
||||||
raise ValueError("Expected the last row to be [0,0,0,1], but got: {!r}".format(matrix[3]))
|
matrix
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif (len(matrix) == 4) and (tuple(matrix[3]) != (0, 0, 0, 1)):
|
||||||
|
raise ValueError(
|
||||||
|
"Expected the last row to be [0,0,0,1], but got: {!r}".format(
|
||||||
|
matrix[3]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Assign values to matrix
|
# Assign values to matrix
|
||||||
self.wrapped = gp_GTrsf()
|
self.wrapped = gp_GTrsf()
|
||||||
[self.wrapped.SetValue(i+1,j+1,e)
|
[
|
||||||
for i,row in enumerate(matrix[:3])
|
self.wrapped.SetValue(i + 1, j + 1, e)
|
||||||
for j,e in enumerate(row)]
|
for i, row in enumerate(matrix[:3])
|
||||||
|
for j, e in enumerate(row)
|
||||||
|
]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError("Invalid param to matrix constructor: {}".format(matrix))
|
||||||
"Invalid param to matrix constructor: {}".format(matrix))
|
|
||||||
|
|
||||||
def rotateX(self, angle):
|
def rotateX(self, angle):
|
||||||
|
|
||||||
self._rotate(gp.OX(),
|
self._rotate(gp.OX(), angle)
|
||||||
angle)
|
|
||||||
|
|
||||||
def rotateY(self, angle):
|
def rotateY(self, angle):
|
||||||
|
|
||||||
self._rotate(gp.OY(),
|
self._rotate(gp.OY(), angle)
|
||||||
angle)
|
|
||||||
|
|
||||||
def rotateZ(self, angle):
|
def rotateZ(self, angle):
|
||||||
|
|
||||||
self._rotate(gp.OZ(),
|
self._rotate(gp.OZ(), angle)
|
||||||
angle)
|
|
||||||
|
|
||||||
def _rotate(self, direction, angle):
|
def _rotate(self, direction, angle):
|
||||||
|
|
||||||
new = gp_Trsf()
|
new = gp_Trsf()
|
||||||
new.SetRotation(direction,
|
new.SetRotation(direction, angle)
|
||||||
angle)
|
|
||||||
|
|
||||||
self.wrapped = self.wrapped * gp_GTrsf(new)
|
self.wrapped = self.wrapped * gp_GTrsf(new)
|
||||||
|
|
||||||
@ -277,11 +289,12 @@ class Matrix:
|
|||||||
def transposed_list(self):
|
def transposed_list(self):
|
||||||
"""Needed by the cqparts gltf exporter
|
"""Needed by the cqparts gltf exporter
|
||||||
"""
|
"""
|
||||||
|
|
||||||
trsf = self.wrapped
|
trsf = self.wrapped
|
||||||
data = [[trsf.Value(i,j) for j in range(1,5)] for i in range(1,4)] + \
|
data = [[trsf.Value(i, j) for j in range(1, 5)] for i in range(1, 4)] + [
|
||||||
[[0.,0.,0.,1.]]
|
[0.0, 0.0, 0.0, 1.0]
|
||||||
|
]
|
||||||
|
|
||||||
return [data[j][i] for i in range(4) for j in range(4)]
|
return [data[j][i] for i in range(4) for j in range(4)]
|
||||||
|
|
||||||
def __getitem__(self, rc):
|
def __getitem__(self, rc):
|
||||||
@ -298,7 +311,7 @@ class Matrix:
|
|||||||
else:
|
else:
|
||||||
# gp_GTrsf doesn't provide access to the 4th row because it has
|
# gp_GTrsf doesn't provide access to the 4th row because it has
|
||||||
# an implied value as below:
|
# an implied value as below:
|
||||||
return [0., 0., 0., 1.][c]
|
return [0.0, 0.0, 0.0, 1.0][c]
|
||||||
else:
|
else:
|
||||||
raise IndexError("Out of bounds access into 4x4 matrix: {!r}".format(rc))
|
raise IndexError("Out of bounds access into 4x4 matrix: {!r}".format(rc))
|
||||||
|
|
||||||
@ -352,95 +365,94 @@ class Plane(object):
|
|||||||
|
|
||||||
namedPlanes = {
|
namedPlanes = {
|
||||||
# origin, xDir, normal
|
# origin, xDir, normal
|
||||||
'XY': Plane(origin, (1, 0, 0), (0, 0, 1)),
|
"XY": Plane(origin, (1, 0, 0), (0, 0, 1)),
|
||||||
'YZ': Plane(origin, (0, 1, 0), (1, 0, 0)),
|
"YZ": Plane(origin, (0, 1, 0), (1, 0, 0)),
|
||||||
'ZX': Plane(origin, (0, 0, 1), (0, 1, 0)),
|
"ZX": Plane(origin, (0, 0, 1), (0, 1, 0)),
|
||||||
'XZ': Plane(origin, (1, 0, 0), (0, -1, 0)),
|
"XZ": Plane(origin, (1, 0, 0), (0, -1, 0)),
|
||||||
'YX': Plane(origin, (0, 1, 0), (0, 0, -1)),
|
"YX": Plane(origin, (0, 1, 0), (0, 0, -1)),
|
||||||
'ZY': Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
"ZY": Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
||||||
'front': Plane(origin, (1, 0, 0), (0, 0, 1)),
|
"front": Plane(origin, (1, 0, 0), (0, 0, 1)),
|
||||||
'back': Plane(origin, (-1, 0, 0), (0, 0, -1)),
|
"back": Plane(origin, (-1, 0, 0), (0, 0, -1)),
|
||||||
'left': Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
"left": Plane(origin, (0, 0, 1), (-1, 0, 0)),
|
||||||
'right': Plane(origin, (0, 0, -1), (1, 0, 0)),
|
"right": Plane(origin, (0, 0, -1), (1, 0, 0)),
|
||||||
'top': Plane(origin, (1, 0, 0), (0, 1, 0)),
|
"top": Plane(origin, (1, 0, 0), (0, 1, 0)),
|
||||||
'bottom': Plane(origin, (1, 0, 0), (0, -1, 0))
|
"bottom": Plane(origin, (1, 0, 0), (0, -1, 0)),
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return namedPlanes[stdName]
|
return namedPlanes[stdName]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError('Supported names are {}'.format(
|
raise ValueError("Supported names are {}".format(list(namedPlanes.keys())))
|
||||||
list(namedPlanes.keys())))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def XY(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
def XY(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||||
plane = Plane.named('XY', origin)
|
plane = Plane.named("XY", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def YZ(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
def YZ(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
||||||
plane = Plane.named('YZ', origin)
|
plane = Plane.named("YZ", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ZX(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
def ZX(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
||||||
plane = Plane.named('ZX', origin)
|
plane = Plane.named("ZX", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def XZ(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
def XZ(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||||
plane = Plane.named('XZ', origin)
|
plane = Plane.named("XZ", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def YX(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
def YX(cls, origin=(0, 0, 0), xDir=Vector(0, 1, 0)):
|
||||||
plane = Plane.named('YX', origin)
|
plane = Plane.named("YX", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ZY(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
def ZY(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
||||||
plane = Plane.named('ZY', origin)
|
plane = Plane.named("ZY", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def front(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
def front(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||||
plane = Plane.named('front', origin)
|
plane = Plane.named("front", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def back(cls, origin=(0, 0, 0), xDir=Vector(-1, 0, 0)):
|
def back(cls, origin=(0, 0, 0), xDir=Vector(-1, 0, 0)):
|
||||||
plane = Plane.named('back', origin)
|
plane = Plane.named("back", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def left(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
def left(cls, origin=(0, 0, 0), xDir=Vector(0, 0, 1)):
|
||||||
plane = Plane.named('left', origin)
|
plane = Plane.named("left", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def right(cls, origin=(0, 0, 0), xDir=Vector(0, 0, -1)):
|
def right(cls, origin=(0, 0, 0), xDir=Vector(0, 0, -1)):
|
||||||
plane = Plane.named('right', origin)
|
plane = Plane.named("right", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def top(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
def top(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||||
plane = Plane.named('top', origin)
|
plane = Plane.named("top", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bottom(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
def bottom(cls, origin=(0, 0, 0), xDir=Vector(1, 0, 0)):
|
||||||
plane = Plane.named('bottom', origin)
|
plane = Plane.named("bottom", origin)
|
||||||
plane._setPlaneDir(xDir)
|
plane._setPlaneDir(xDir)
|
||||||
return plane
|
return plane
|
||||||
|
|
||||||
@ -458,12 +470,12 @@ class Plane(object):
|
|||||||
:return: a plane in the global space, with the xDirection of the plane in the specified direction.
|
:return: a plane in the global space, with the xDirection of the plane in the specified direction.
|
||||||
"""
|
"""
|
||||||
zDir = Vector(normal)
|
zDir = Vector(normal)
|
||||||
if (zDir.Length == 0.0):
|
if zDir.Length == 0.0:
|
||||||
raise ValueError('normal should be non null')
|
raise ValueError("normal should be non null")
|
||||||
|
|
||||||
xDir = Vector(xDir)
|
xDir = Vector(xDir)
|
||||||
if (xDir.Length == 0.0):
|
if xDir.Length == 0.0:
|
||||||
raise ValueError('xDir should be non null')
|
raise ValueError("xDir should be non null")
|
||||||
|
|
||||||
self.zDir = zDir.normalized()
|
self.zDir = zDir.normalized()
|
||||||
self._setPlaneDir(xDir)
|
self._setPlaneDir(xDir)
|
||||||
@ -489,7 +501,8 @@ class Plane(object):
|
|||||||
@property
|
@property
|
||||||
def origin(self):
|
def origin(self):
|
||||||
return self._origin
|
return self._origin
|
||||||
# TODO is this property rly needed -- why not handle this in the constructor
|
|
||||||
|
# TODO is this property rly needed -- why not handle this in the constructor
|
||||||
|
|
||||||
@origin.setter
|
@origin.setter
|
||||||
def origin(self, value):
|
def origin(self, value):
|
||||||
@ -545,7 +558,7 @@ class Plane(object):
|
|||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
'''
|
"""
|
||||||
# TODO: also use a set of points along the wire to test as well.
|
# TODO: also use a set of points along the wire to test as well.
|
||||||
# TODO: would it be more efficient to create objects in the local
|
# TODO: would it be more efficient to create objects in the local
|
||||||
# coordinate system, and then transform to global
|
# coordinate system, and then transform to global
|
||||||
@ -562,7 +575,7 @@ class Plane(object):
|
|||||||
# findOutsideBox actually inspects both ways, here we only want to
|
# findOutsideBox actually inspects both ways, here we only want to
|
||||||
# know if one is inside the other
|
# know if one is inside the other
|
||||||
return bb == BoundBox.findOutsideBox2D(bb, tb)
|
return bb == BoundBox.findOutsideBox2D(bb, tb)
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def toLocalCoords(self, obj):
|
def toLocalCoords(self, obj):
|
||||||
"""Project the provided coordinates onto this plane
|
"""Project the provided coordinates onto this plane
|
||||||
@ -580,7 +593,7 @@ class Plane(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
from .shapes import Shape
|
from .shapes import Shape
|
||||||
|
|
||||||
if isinstance(obj, Vector):
|
if isinstance(obj, Vector):
|
||||||
return obj.transform(self.fG)
|
return obj.transform(self.fG)
|
||||||
elif isinstance(obj, Shape):
|
elif isinstance(obj, Shape):
|
||||||
@ -588,7 +601,9 @@ class Plane(object):
|
|||||||
else:
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Don't know how to convert type {} to local coordinates".format(
|
"Don't know how to convert type {} to local coordinates".format(
|
||||||
type(obj)))
|
type(obj)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def toWorldCoords(self, tuplePoint):
|
def toWorldCoords(self, tuplePoint):
|
||||||
"""Convert a point in local coordinates to global coordinates
|
"""Convert a point in local coordinates to global coordinates
|
||||||
@ -655,7 +670,7 @@ class Plane(object):
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
'''
|
"""
|
||||||
resultWires = []
|
resultWires = []
|
||||||
for w in listOfShapes:
|
for w in listOfShapes:
|
||||||
mirrored = w.transformGeometry(rotationMatrix.wrapped)
|
mirrored = w.transformGeometry(rotationMatrix.wrapped)
|
||||||
@ -681,21 +696,19 @@ class Plane(object):
|
|||||||
|
|
||||||
resultWires.append(cadquery.Shape.cast(mirroredWire))
|
resultWires.append(cadquery.Shape.cast(mirroredWire))
|
||||||
|
|
||||||
return resultWires'''
|
return resultWires"""
|
||||||
|
|
||||||
def mirrorInPlane(self, listOfShapes, axis='X'):
|
def mirrorInPlane(self, listOfShapes, axis="X"):
|
||||||
|
|
||||||
local_coord_system = gp_Ax3(self.origin.toPnt(),
|
local_coord_system = gp_Ax3(
|
||||||
self.zDir.toDir(),
|
self.origin.toPnt(), self.zDir.toDir(), self.xDir.toDir()
|
||||||
self.xDir.toDir())
|
)
|
||||||
T = gp_Trsf()
|
T = gp_Trsf()
|
||||||
|
|
||||||
if axis == 'X':
|
if axis == "X":
|
||||||
T.SetMirror(gp_Ax1(self.origin.toPnt(),
|
T.SetMirror(gp_Ax1(self.origin.toPnt(), local_coord_system.XDirection()))
|
||||||
local_coord_system.XDirection()))
|
elif axis == "Y":
|
||||||
elif axis == 'Y':
|
T.SetMirror(gp_Ax1(self.origin.toPnt(), local_coord_system.YDirection()))
|
||||||
T.SetMirror(gp_Ax1(self.origin.toPnt(),
|
|
||||||
local_coord_system.YDirection()))
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -726,22 +739,21 @@ class Plane(object):
|
|||||||
# the double-inverting is strange, and I don't understand it.
|
# the double-inverting is strange, and I don't understand it.
|
||||||
forward = Matrix()
|
forward = Matrix()
|
||||||
inverse = Matrix()
|
inverse = Matrix()
|
||||||
|
|
||||||
forwardT = gp_Trsf()
|
forwardT = gp_Trsf()
|
||||||
inverseT = gp_Trsf()
|
inverseT = gp_Trsf()
|
||||||
|
|
||||||
global_coord_system = gp_Ax3()
|
global_coord_system = gp_Ax3()
|
||||||
local_coord_system = gp_Ax3(gp_Pnt(*self.origin.toTuple()),
|
local_coord_system = gp_Ax3(
|
||||||
gp_Dir(*self.zDir.toTuple()),
|
gp_Pnt(*self.origin.toTuple()),
|
||||||
gp_Dir(*self.xDir.toTuple())
|
gp_Dir(*self.zDir.toTuple()),
|
||||||
)
|
gp_Dir(*self.xDir.toTuple()),
|
||||||
|
)
|
||||||
|
|
||||||
forwardT.SetTransformation(global_coord_system,
|
forwardT.SetTransformation(global_coord_system, local_coord_system)
|
||||||
local_coord_system)
|
|
||||||
forward.wrapped = gp_GTrsf(forwardT)
|
forward.wrapped = gp_GTrsf(forwardT)
|
||||||
|
|
||||||
inverseT.SetTransformation(local_coord_system,
|
inverseT.SetTransformation(local_coord_system, global_coord_system)
|
||||||
global_coord_system)
|
|
||||||
inverse.wrapped = gp_GTrsf(inverseT)
|
inverse.wrapped = gp_GTrsf(inverseT)
|
||||||
|
|
||||||
# TODO verify if this is OK
|
# TODO verify if this is OK
|
||||||
@ -767,11 +779,9 @@ class BoundBox(object):
|
|||||||
self.zmax = ZMax
|
self.zmax = ZMax
|
||||||
self.zlen = ZMax - ZMin
|
self.zlen = ZMax - ZMin
|
||||||
|
|
||||||
self.center = Vector((XMax + XMin) / 2,
|
self.center = Vector((XMax + XMin) / 2, (YMax + YMin) / 2, (ZMax + ZMin) / 2)
|
||||||
(YMax + YMin) / 2,
|
|
||||||
(ZMax + ZMin) / 2)
|
|
||||||
|
|
||||||
self.DiagonalLength = self.wrapped.SquareExtent()**0.5
|
self.DiagonalLength = self.wrapped.SquareExtent() ** 0.5
|
||||||
|
|
||||||
def add(self, obj, tol=1e-8):
|
def add(self, obj, tol=1e-8):
|
||||||
"""Returns a modified (expanded) bounding box
|
"""Returns a modified (expanded) bounding box
|
||||||
@ -810,25 +820,29 @@ class BoundBox(object):
|
|||||||
the built-in implementation i do not understand.
|
the built-in implementation i do not understand.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if (bb1.XMin < bb2.XMin and
|
if (
|
||||||
bb1.XMax > bb2.XMax and
|
bb1.XMin < bb2.XMin
|
||||||
bb1.YMin < bb2.YMin and
|
and bb1.XMax > bb2.XMax
|
||||||
bb1.YMax > bb2.YMax):
|
and bb1.YMin < bb2.YMin
|
||||||
|
and bb1.YMax > bb2.YMax
|
||||||
|
):
|
||||||
return bb1
|
return bb1
|
||||||
|
|
||||||
if (bb2.XMin < bb1.XMin and
|
if (
|
||||||
bb2.XMax > bb1.XMax and
|
bb2.XMin < bb1.XMin
|
||||||
bb2.YMin < bb1.YMin and
|
and bb2.XMax > bb1.XMax
|
||||||
bb2.YMax > bb1.YMax):
|
and bb2.YMin < bb1.YMin
|
||||||
|
and bb2.YMax > bb1.YMax
|
||||||
|
):
|
||||||
return bb2
|
return bb2
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _fromTopoDS(cls, shape, tol=None, optimal=False):
|
def _fromTopoDS(cls, shape, tol=None, optimal=False):
|
||||||
'''
|
"""
|
||||||
Constructs a bounding box from a TopoDS_Shape
|
Constructs a bounding box from a TopoDS_Shape
|
||||||
'''
|
"""
|
||||||
tol = TOL if tol is None else tol # tol = TOL (by default)
|
tol = TOL if tol is None else tol # tol = TOL (by default)
|
||||||
bbox = Bnd_Box()
|
bbox = Bnd_Box()
|
||||||
bbox.SetGap(tol)
|
bbox.SetGap(tol)
|
||||||
@ -845,12 +859,14 @@ class BoundBox(object):
|
|||||||
|
|
||||||
def isInside(self, b2):
|
def isInside(self, b2):
|
||||||
"""Is the provided bounding box inside this one?"""
|
"""Is the provided bounding box inside this one?"""
|
||||||
if (b2.xmin > self.xmin and
|
if (
|
||||||
b2.ymin > self.ymin and
|
b2.xmin > self.xmin
|
||||||
b2.zmin > self.zmin and
|
and b2.ymin > self.ymin
|
||||||
b2.xmax < self.xmax and
|
and b2.zmin > self.zmin
|
||||||
b2.ymax < self.ymax and
|
and b2.xmax < self.xmax
|
||||||
b2.zmax < self.zmax):
|
and b2.ymax < self.ymax
|
||||||
|
and b2.zmax < self.zmax
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -34,14 +34,14 @@ def importStep(fileName):
|
|||||||
Accepts a file name and loads the STEP file into a cadquery shape
|
Accepts a file name and loads the STEP file into a cadquery shape
|
||||||
:param fileName: The path and name of the STEP file to be imported
|
:param fileName: The path and name of the STEP file to be imported
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Now read and return the shape
|
# Now read and return the shape
|
||||||
reader = STEPControl_Reader()
|
reader = STEPControl_Reader()
|
||||||
readStatus = reader.ReadFile(fileName)
|
readStatus = reader.ReadFile(fileName)
|
||||||
if readStatus != OCC.Core.IFSelect.IFSelect_RetDone:
|
if readStatus != OCC.Core.IFSelect.IFSelect_RetDone:
|
||||||
raise ValueError("STEP File could not be loaded")
|
raise ValueError("STEP File could not be loaded")
|
||||||
for i in range(reader.NbRootsForTransfer()):
|
for i in range(reader.NbRootsForTransfer()):
|
||||||
reader.TransferRoot(i+1)
|
reader.TransferRoot(i + 1)
|
||||||
|
|
||||||
occ_shapes = []
|
occ_shapes = []
|
||||||
for i in range(reader.NbShapes()):
|
for i in range(reader.NbShapes()):
|
||||||
|
@ -6,8 +6,7 @@ from xml.etree import ElementTree
|
|||||||
|
|
||||||
from .geom import BoundBox
|
from .geom import BoundBox
|
||||||
|
|
||||||
BOILERPLATE = \
|
BOILERPLATE = """
|
||||||
'''
|
|
||||||
<link rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'></link>
|
<link rel='stylesheet' type='text/css' href='http://www.x3dom.org/download/x3dom.css'></link>
|
||||||
<div style='height: {height}px; width: 100%;' width='100%' height='{height}px'>
|
<div style='height: {height}px; width: 100%;' width='100%' height='{height}px'>
|
||||||
<x3d style='height: {height}px; width: 100%;' id='{id}' width='100%' height='{height}px'>
|
<x3d style='height: {height}px; width: 100%;' id='{id}' width='100%' height='{height}px'>
|
||||||
@ -35,69 +34,86 @@ BOILERPLATE = \
|
|||||||
|
|
||||||
//document.getElementById('{id}').runtime.fitAll()
|
//document.getElementById('{id}').runtime.fitAll()
|
||||||
</script>
|
</script>
|
||||||
'''
|
"""
|
||||||
|
|
||||||
#https://stackoverflow.com/questions/950087/how-do-i-include-a-javascript-file-in-another-javascript-file
|
# https://stackoverflow.com/questions/950087/how-do-i-include-a-javascript-file-in-another-javascript-file
|
||||||
#better if else
|
# better if else
|
||||||
|
|
||||||
ROT = (0.77,0.3,0.55,1.28)
|
ROT = (0.77, 0.3, 0.55, 1.28)
|
||||||
ROT = (0.,0,0,1.)
|
ROT = (0.0, 0, 0, 1.0)
|
||||||
FOV = 0.2
|
FOV = 0.2
|
||||||
|
|
||||||
def add_x3d_boilerplate(src, height=400, center=(0,0,0), d=(0,0,15), fov=FOV, rot='{} {} {} {} '.format(*ROT)):
|
|
||||||
|
|
||||||
return BOILERPLATE.format(src=src,
|
def add_x3d_boilerplate(
|
||||||
id=uuid4(),
|
src,
|
||||||
height=height,
|
height=400,
|
||||||
x=d[0],
|
center=(0, 0, 0),
|
||||||
y=d[1],
|
d=(0, 0, 15),
|
||||||
z=d[2],
|
fov=FOV,
|
||||||
x0=center[0],
|
rot="{} {} {} {} ".format(*ROT),
|
||||||
y0=center[1],
|
):
|
||||||
z0=center[2],
|
|
||||||
fov=fov,
|
|
||||||
rot=rot)
|
|
||||||
|
|
||||||
def x3d_display(shape,
|
return BOILERPLATE.format(
|
||||||
vertex_shader=None,
|
src=src,
|
||||||
fragment_shader=None,
|
id=uuid4(),
|
||||||
export_edges=True,
|
height=height,
|
||||||
color=(1,1,0),
|
x=d[0],
|
||||||
specular_color=(1,1,1),
|
y=d[1],
|
||||||
shininess=0.4,
|
z=d[2],
|
||||||
transparency=0.4,
|
x0=center[0],
|
||||||
line_color=(0,0,0),
|
y0=center[1],
|
||||||
line_width=2.,
|
z0=center[2],
|
||||||
mesh_quality=.3):
|
fov=fov,
|
||||||
|
rot=rot,
|
||||||
|
)
|
||||||
|
|
||||||
# Export to XML <Scene> tag
|
|
||||||
exporter = X3DExporter(shape,
|
|
||||||
vertex_shader,
|
|
||||||
fragment_shader,
|
|
||||||
export_edges,
|
|
||||||
color,
|
|
||||||
specular_color,
|
|
||||||
shininess,
|
|
||||||
transparency,
|
|
||||||
line_color,
|
|
||||||
line_width,
|
|
||||||
mesh_quality)
|
|
||||||
|
|
||||||
exporter.compute()
|
def x3d_display(
|
||||||
x3d_str = exporter.to_x3dfile_string(shape_id=0)
|
shape,
|
||||||
xml_et = ElementTree.fromstring(x3d_str)
|
vertex_shader=None,
|
||||||
scene_tag = xml_et.find('./Scene')
|
fragment_shader=None,
|
||||||
|
export_edges=True,
|
||||||
|
color=(1, 1, 0),
|
||||||
|
specular_color=(1, 1, 1),
|
||||||
|
shininess=0.4,
|
||||||
|
transparency=0.4,
|
||||||
|
line_color=(0, 0, 0),
|
||||||
|
line_width=2.0,
|
||||||
|
mesh_quality=0.3,
|
||||||
|
):
|
||||||
|
|
||||||
# Viewport Parameters
|
# Export to XML <Scene> tag
|
||||||
bb = BoundBox._fromTopoDS(shape)
|
exporter = X3DExporter(
|
||||||
d = max(bb.xlen,bb.ylen,bb.zlen)
|
shape,
|
||||||
c = bb.center
|
vertex_shader,
|
||||||
|
fragment_shader,
|
||||||
|
export_edges,
|
||||||
|
color,
|
||||||
|
specular_color,
|
||||||
|
shininess,
|
||||||
|
transparency,
|
||||||
|
line_color,
|
||||||
|
line_width,
|
||||||
|
mesh_quality,
|
||||||
|
)
|
||||||
|
|
||||||
vec = gp_Vec(0,0,d/1.5/tan(FOV/2))
|
exporter.compute()
|
||||||
quat = gp_Quaternion(*ROT)
|
x3d_str = exporter.to_x3dfile_string(shape_id=0)
|
||||||
vec = quat*(vec) + c.wrapped
|
xml_et = ElementTree.fromstring(x3d_str)
|
||||||
|
scene_tag = xml_et.find("./Scene")
|
||||||
|
|
||||||
# return boilerplate + Scene
|
# Viewport Parameters
|
||||||
return add_x3d_boilerplate(ElementTree.tostring(scene_tag).decode('utf-8'),
|
bb = BoundBox._fromTopoDS(shape)
|
||||||
d=(vec.X(),vec.Y(),vec.Z()),
|
d = max(bb.xlen, bb.ylen, bb.zlen)
|
||||||
center=(c.x,c.y,c.z))
|
c = bb.center
|
||||||
|
|
||||||
|
vec = gp_Vec(0, 0, d / 1.5 / tan(FOV / 2))
|
||||||
|
quat = gp_Quaternion(*ROT)
|
||||||
|
vec = quat * (vec) + c.wrapped
|
||||||
|
|
||||||
|
# return boilerplate + Scene
|
||||||
|
return add_x3d_boilerplate(
|
||||||
|
ElementTree.tostring(scene_tag).decode("utf-8"),
|
||||||
|
d=(vec.X(), vec.Y(), vec.Z()),
|
||||||
|
center=(c.x, c.y, c.z),
|
||||||
|
)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,9 +21,22 @@ import re
|
|||||||
import math
|
import math
|
||||||
from cadquery import Vector, Edge, Vertex, Face, Solid, Shell, Compound
|
from cadquery import Vector, Edge, Vertex, Face, Solid, Shell, Compound
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from pyparsing import Literal, Word, nums, Optional, Combine, oneOf, upcaseTokens,\
|
from pyparsing import (
|
||||||
CaselessLiteral, Group, infixNotation, opAssoc, Forward,\
|
Literal,
|
||||||
ZeroOrMore, Keyword
|
Word,
|
||||||
|
nums,
|
||||||
|
Optional,
|
||||||
|
Combine,
|
||||||
|
oneOf,
|
||||||
|
upcaseTokens,
|
||||||
|
CaselessLiteral,
|
||||||
|
Group,
|
||||||
|
infixNotation,
|
||||||
|
opAssoc,
|
||||||
|
Forward,
|
||||||
|
ZeroOrMore,
|
||||||
|
Keyword,
|
||||||
|
)
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
|
|
||||||
@ -81,7 +94,6 @@ class NearestToPointSelector(Selector):
|
|||||||
self.pnt = pnt
|
self.pnt = pnt
|
||||||
|
|
||||||
def filter(self, objectList):
|
def filter(self, objectList):
|
||||||
|
|
||||||
def dist(tShape):
|
def dist(tShape):
|
||||||
return tShape.Center().sub(Vector(*self.pnt)).Length
|
return tShape.Center().sub(Vector(*self.pnt)).Length
|
||||||
# if tShape.ShapeType == 'Vertex':
|
# if tShape.ShapeType == 'Vertex':
|
||||||
@ -121,15 +133,18 @@ class BoxSelector(Selector):
|
|||||||
def isInsideBox(p):
|
def isInsideBox(p):
|
||||||
# using XOR for checking if x/y/z is in between regardless
|
# using XOR for checking if x/y/z is in between regardless
|
||||||
# of order of x/y/z0 and x/y/z1
|
# of order of x/y/z0 and x/y/z1
|
||||||
return ((p.x < x0) ^ (p.x < x1)) and \
|
return (
|
||||||
((p.y < y0) ^ (p.y < y1)) and \
|
((p.x < x0) ^ (p.x < x1))
|
||||||
((p.z < z0) ^ (p.z < z1))
|
and ((p.y < y0) ^ (p.y < y1))
|
||||||
|
and ((p.z < z0) ^ (p.z < z1))
|
||||||
|
)
|
||||||
|
|
||||||
for o in objectList:
|
for o in objectList:
|
||||||
if self.test_boundingbox:
|
if self.test_boundingbox:
|
||||||
bb = o.BoundingBox()
|
bb = o.BoundingBox()
|
||||||
if isInsideBox(Vector(bb.xmin, bb.ymin, bb.zmin)) and \
|
if isInsideBox(Vector(bb.xmin, bb.ymin, bb.zmin)) and isInsideBox(
|
||||||
isInsideBox(Vector(bb.xmax, bb.ymax, bb.zmax)):
|
Vector(bb.xmax, bb.ymax, bb.zmax)
|
||||||
|
):
|
||||||
result.append(o)
|
result.append(o)
|
||||||
else:
|
else:
|
||||||
if isInsideBox(o.Center()):
|
if isInsideBox(o.Center()):
|
||||||
@ -168,7 +183,9 @@ class BaseDirSelector(Selector):
|
|||||||
|
|
||||||
if self.test(normal):
|
if self.test(normal):
|
||||||
r.append(o)
|
r.append(o)
|
||||||
elif type(o) == Edge and (o.geomType() == 'LINE' or o.geomType() == 'PLANE'):
|
elif type(o) == Edge and (
|
||||||
|
o.geomType() == "LINE" or o.geomType() == "PLANE"
|
||||||
|
):
|
||||||
# an edge is parallel to a direction if its underlying geometry is plane or line
|
# an edge is parallel to a direction if its underlying geometry is plane or line
|
||||||
tangent = o.tangentAt()
|
tangent = o.tangentAt()
|
||||||
if self.test(tangent):
|
if self.test(tangent):
|
||||||
@ -247,8 +264,7 @@ class PerpendicularDirSelector(BaseDirSelector):
|
|||||||
|
|
||||||
def test(self, vec):
|
def test(self, vec):
|
||||||
angle = self.direction.getAngle(vec)
|
angle = self.direction.getAngle(vec)
|
||||||
r = (abs(angle) < self.TOLERANCE) or (
|
r = (abs(angle) < self.TOLERANCE) or (abs(angle - math.pi) < self.TOLERANCE)
|
||||||
abs(angle - math.pi) < self.TOLERANCE)
|
|
||||||
return not r
|
return not r
|
||||||
|
|
||||||
|
|
||||||
@ -314,17 +330,16 @@ class DirectionMinMaxSelector(Selector):
|
|||||||
self.TOLERANCE = tolerance
|
self.TOLERANCE = tolerance
|
||||||
|
|
||||||
def filter(self, objectList):
|
def filter(self, objectList):
|
||||||
|
|
||||||
def distance(tShape):
|
def distance(tShape):
|
||||||
return tShape.Center().dot(self.vector)
|
return tShape.Center().dot(self.vector)
|
||||||
|
|
||||||
# import OrderedDict
|
# import OrderedDict
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
# make and distance to object dict
|
# make and distance to object dict
|
||||||
objectDict = {distance(el): el for el in objectList}
|
objectDict = {distance(el): el for el in objectList}
|
||||||
# transform it into an ordered dict
|
# transform it into an ordered dict
|
||||||
objectDict = OrderedDict(sorted(list(objectDict.items()),
|
objectDict = OrderedDict(sorted(list(objectDict.items()), key=lambda x: x[0]))
|
||||||
key=lambda x: x[0]))
|
|
||||||
|
|
||||||
# find out the max/min distance
|
# find out the max/min distance
|
||||||
if self.directionMax:
|
if self.directionMax:
|
||||||
@ -370,8 +385,9 @@ class DirectionNthSelector(ParallelDirSelector):
|
|||||||
objectDict[round(distance(el), digits)].append(el)
|
objectDict[round(distance(el), digits)].append(el)
|
||||||
|
|
||||||
# choose the Nth unique rounded distance
|
# choose the Nth unique rounded distance
|
||||||
nth_distance = sorted(list(objectDict.keys()),
|
nth_distance = sorted(list(objectDict.keys()), reverse=not self.directionMax)[
|
||||||
reverse=not self.directionMax)[self.N]
|
self.N
|
||||||
|
]
|
||||||
|
|
||||||
# map back to original objects and return
|
# map back to original objects and return
|
||||||
return objectDict[nth_distance]
|
return objectDict[nth_distance]
|
||||||
@ -388,8 +404,9 @@ class BinarySelector(Selector):
|
|||||||
self.right = right
|
self.right = right
|
||||||
|
|
||||||
def filter(self, objectList):
|
def filter(self, objectList):
|
||||||
return self.filterResults(self.left.filter(objectList),
|
return self.filterResults(
|
||||||
self.right.filter(objectList))
|
self.left.filter(objectList), self.right.filter(objectList)
|
||||||
|
)
|
||||||
|
|
||||||
def filterResults(self, r_left, r_right):
|
def filterResults(self, r_left, r_right):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -445,52 +462,56 @@ def _makeGrammar():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# float definition
|
# float definition
|
||||||
point = Literal('.')
|
point = Literal(".")
|
||||||
plusmin = Literal('+') | Literal('-')
|
plusmin = Literal("+") | Literal("-")
|
||||||
number = Word(nums)
|
number = Word(nums)
|
||||||
integer = Combine(Optional(plusmin) + number)
|
integer = Combine(Optional(plusmin) + number)
|
||||||
floatn = Combine(integer + Optional(point + Optional(number)))
|
floatn = Combine(integer + Optional(point + Optional(number)))
|
||||||
|
|
||||||
# vector definition
|
# vector definition
|
||||||
lbracket = Literal('(')
|
lbracket = Literal("(")
|
||||||
rbracket = Literal(')')
|
rbracket = Literal(")")
|
||||||
comma = Literal(',')
|
comma = Literal(",")
|
||||||
vector = Combine(lbracket + floatn('x') + comma +
|
vector = Combine(
|
||||||
floatn('y') + comma + floatn('z') + rbracket)
|
lbracket + floatn("x") + comma + floatn("y") + comma + floatn("z") + rbracket
|
||||||
|
)
|
||||||
|
|
||||||
# direction definition
|
# direction definition
|
||||||
simple_dir = oneOf(['X', 'Y', 'Z', 'XY', 'XZ', 'YZ'])
|
simple_dir = oneOf(["X", "Y", "Z", "XY", "XZ", "YZ"])
|
||||||
direction = simple_dir('simple_dir') | vector('vector_dir')
|
direction = simple_dir("simple_dir") | vector("vector_dir")
|
||||||
|
|
||||||
# CQ type definition
|
# CQ type definition
|
||||||
cqtype = oneOf(['Plane', 'Cylinder', 'Sphere', 'Cone', 'Line', 'Circle', 'Arc'],
|
cqtype = oneOf(
|
||||||
caseless=True)
|
["Plane", "Cylinder", "Sphere", "Cone", "Line", "Circle", "Arc"], caseless=True
|
||||||
|
)
|
||||||
cqtype = cqtype.setParseAction(upcaseTokens)
|
cqtype = cqtype.setParseAction(upcaseTokens)
|
||||||
|
|
||||||
# type operator
|
# type operator
|
||||||
type_op = Literal('%')
|
type_op = Literal("%")
|
||||||
|
|
||||||
# direction operator
|
# direction operator
|
||||||
direction_op = oneOf(['>', '<'])
|
direction_op = oneOf([">", "<"])
|
||||||
|
|
||||||
# index definition
|
# index definition
|
||||||
ix_number = Group(Optional('-') + Word(nums))
|
ix_number = Group(Optional("-") + Word(nums))
|
||||||
lsqbracket = Literal('[').suppress()
|
lsqbracket = Literal("[").suppress()
|
||||||
rsqbracket = Literal(']').suppress()
|
rsqbracket = Literal("]").suppress()
|
||||||
|
|
||||||
index = lsqbracket + ix_number('index') + rsqbracket
|
index = lsqbracket + ix_number("index") + rsqbracket
|
||||||
|
|
||||||
# other operators
|
# other operators
|
||||||
other_op = oneOf(['|', '#', '+', '-'])
|
other_op = oneOf(["|", "#", "+", "-"])
|
||||||
|
|
||||||
# named view
|
# named view
|
||||||
named_view = oneOf(['front', 'back', 'left', 'right', 'top', 'bottom'])
|
named_view = oneOf(["front", "back", "left", "right", "top", "bottom"])
|
||||||
|
|
||||||
return direction('only_dir') | \
|
return (
|
||||||
(type_op('type_op') + cqtype('cq_type')) | \
|
direction("only_dir")
|
||||||
(direction_op('dir_op') + direction('dir') + Optional(index)) | \
|
| (type_op("type_op") + cqtype("cq_type"))
|
||||||
(other_op('other_op') + direction('dir')) | \
|
| (direction_op("dir_op") + direction("dir") + Optional(index))
|
||||||
named_view('named_view')
|
| (other_op("other_op") + direction("dir"))
|
||||||
|
| named_view("named_view")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
_grammar = _makeGrammar() # make a grammar instance
|
_grammar = _makeGrammar() # make a grammar instance
|
||||||
@ -506,33 +527,34 @@ class _SimpleStringSyntaxSelector(Selector):
|
|||||||
|
|
||||||
# define all token to object mappings
|
# define all token to object mappings
|
||||||
self.axes = {
|
self.axes = {
|
||||||
'X': Vector(1, 0, 0),
|
"X": Vector(1, 0, 0),
|
||||||
'Y': Vector(0, 1, 0),
|
"Y": Vector(0, 1, 0),
|
||||||
'Z': Vector(0, 0, 1),
|
"Z": Vector(0, 0, 1),
|
||||||
'XY': Vector(1, 1, 0),
|
"XY": Vector(1, 1, 0),
|
||||||
'YZ': Vector(0, 1, 1),
|
"YZ": Vector(0, 1, 1),
|
||||||
'XZ': Vector(1, 0, 1)
|
"XZ": Vector(1, 0, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.namedViews = {
|
self.namedViews = {
|
||||||
'front': (Vector(0, 0, 1), True),
|
"front": (Vector(0, 0, 1), True),
|
||||||
'back': (Vector(0, 0, 1), False),
|
"back": (Vector(0, 0, 1), False),
|
||||||
'left': (Vector(1, 0, 0), False),
|
"left": (Vector(1, 0, 0), False),
|
||||||
'right': (Vector(1, 0, 0), True),
|
"right": (Vector(1, 0, 0), True),
|
||||||
'top': (Vector(0, 1, 0), True),
|
"top": (Vector(0, 1, 0), True),
|
||||||
'bottom': (Vector(0, 1, 0), False)
|
"bottom": (Vector(0, 1, 0), False),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operatorMinMax = {
|
self.operatorMinMax = {
|
||||||
'>': True,
|
">": True,
|
||||||
'<': False,
|
"<": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operator = {
|
self.operator = {
|
||||||
'+': DirectionSelector,
|
"+": DirectionSelector,
|
||||||
'-': lambda v: DirectionSelector(-v),
|
"-": lambda v: DirectionSelector(-v),
|
||||||
'#': PerpendicularDirSelector,
|
"#": PerpendicularDirSelector,
|
||||||
'|': ParallelDirSelector}
|
"|": ParallelDirSelector,
|
||||||
|
}
|
||||||
|
|
||||||
self.parseResults = parseResults
|
self.parseResults = parseResults
|
||||||
self.mySelector = self._chooseSelector(parseResults)
|
self.mySelector = self._chooseSelector(parseResults)
|
||||||
@ -541,23 +563,25 @@ class _SimpleStringSyntaxSelector(Selector):
|
|||||||
"""
|
"""
|
||||||
Sets up the underlying filters accordingly
|
Sets up the underlying filters accordingly
|
||||||
"""
|
"""
|
||||||
if 'only_dir' in pr:
|
if "only_dir" in pr:
|
||||||
vec = self._getVector(pr)
|
vec = self._getVector(pr)
|
||||||
return DirectionSelector(vec)
|
return DirectionSelector(vec)
|
||||||
|
|
||||||
elif 'type_op' in pr:
|
elif "type_op" in pr:
|
||||||
return TypeSelector(pr.cq_type)
|
return TypeSelector(pr.cq_type)
|
||||||
|
|
||||||
elif 'dir_op' in pr:
|
elif "dir_op" in pr:
|
||||||
vec = self._getVector(pr)
|
vec = self._getVector(pr)
|
||||||
minmax = self.operatorMinMax[pr.dir_op]
|
minmax = self.operatorMinMax[pr.dir_op]
|
||||||
|
|
||||||
if 'index' in pr:
|
if "index" in pr:
|
||||||
return DirectionNthSelector(vec, int(''.join(pr.index.asList())), minmax)
|
return DirectionNthSelector(
|
||||||
|
vec, int("".join(pr.index.asList())), minmax
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return DirectionMinMaxSelector(vec, minmax)
|
return DirectionMinMaxSelector(vec, minmax)
|
||||||
|
|
||||||
elif 'other_op' in pr:
|
elif "other_op" in pr:
|
||||||
vec = self._getVector(pr)
|
vec = self._getVector(pr)
|
||||||
return self.operator[pr.other_op](vec)
|
return self.operator[pr.other_op](vec)
|
||||||
|
|
||||||
@ -569,7 +593,7 @@ class _SimpleStringSyntaxSelector(Selector):
|
|||||||
"""
|
"""
|
||||||
Translate parsed vector string into a CQ Vector
|
Translate parsed vector string into a CQ Vector
|
||||||
"""
|
"""
|
||||||
if 'vector_dir' in pr:
|
if "vector_dir" in pr:
|
||||||
vec = pr.vector_dir
|
vec = pr.vector_dir
|
||||||
return Vector(float(vec.x), float(vec.y), float(vec.z))
|
return Vector(float(vec.x), float(vec.y), float(vec.z))
|
||||||
else:
|
else:
|
||||||
@ -590,10 +614,10 @@ def _makeExpressionGrammar(atom):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# define operators
|
# define operators
|
||||||
and_op = Literal('and')
|
and_op = Literal("and")
|
||||||
or_op = Literal('or')
|
or_op = Literal("or")
|
||||||
delta_op = oneOf(['exc', 'except'])
|
delta_op = oneOf(["exc", "except"])
|
||||||
not_op = Literal('not')
|
not_op = Literal("not")
|
||||||
|
|
||||||
def atom_callback(res):
|
def atom_callback(res):
|
||||||
return _SimpleStringSyntaxSelector(res)
|
return _SimpleStringSyntaxSelector(res)
|
||||||
@ -622,11 +646,15 @@ def _makeExpressionGrammar(atom):
|
|||||||
return InverseSelector(right)
|
return InverseSelector(right)
|
||||||
|
|
||||||
# construct the final grammar and set all the callbacks
|
# construct the final grammar and set all the callbacks
|
||||||
expr = infixNotation(atom,
|
expr = infixNotation(
|
||||||
[(and_op, 2, opAssoc.LEFT, and_callback),
|
atom,
|
||||||
(or_op, 2, opAssoc.LEFT, or_callback),
|
[
|
||||||
(delta_op, 2, opAssoc.LEFT, exc_callback),
|
(and_op, 2, opAssoc.LEFT, and_callback),
|
||||||
(not_op, 1, opAssoc.RIGHT, not_callback)])
|
(or_op, 2, opAssoc.LEFT, or_callback),
|
||||||
|
(delta_op, 2, opAssoc.LEFT, exc_callback),
|
||||||
|
(not_op, 1, opAssoc.RIGHT, not_callback),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
return expr
|
return expr
|
||||||
|
|
||||||
@ -690,8 +718,7 @@ class StringSyntaxSelector(Selector):
|
|||||||
Feed the input string through the parser and construct an relevant complex selector object
|
Feed the input string through the parser and construct an relevant complex selector object
|
||||||
"""
|
"""
|
||||||
self.selectorString = selectorString
|
self.selectorString = selectorString
|
||||||
parse_result = _expression_grammar.parseString(selectorString,
|
parse_result = _expression_grammar.parseString(selectorString, parseAll=True)
|
||||||
parseAll=True)
|
|
||||||
self.mySelector = parse_result.asList()[0]
|
self.mySelector = parse_result.asList()[0]
|
||||||
|
|
||||||
def filter(self, objectList):
|
def filter(self, objectList):
|
||||||
|
146
doc/conf.py
146
doc/conf.py
@ -13,71 +13,77 @@
|
|||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
import os.path
|
import os.path
|
||||||
#print "working path is %s" % os.getcwd()
|
|
||||||
#sys.path.append("../cadquery")
|
# print "working path is %s" % os.getcwd()
|
||||||
|
# sys.path.append("../cadquery")
|
||||||
import cadquery
|
import cadquery
|
||||||
|
|
||||||
#settings._target = None
|
# settings._target = None
|
||||||
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
#sys.path.insert(0, os.path.abspath('.'))
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
# -- General configuration -----------------------------------------------------
|
||||||
|
|
||||||
# If your documentation needs a minimal Sphinx version, state it here.
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
#needs_sphinx = '1.0'
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.autosummary','cadquery.cq_directive']
|
extensions = [
|
||||||
|
"sphinx.ext.autodoc",
|
||||||
|
"sphinx.ext.viewcode",
|
||||||
|
"sphinx.ext.autosummary",
|
||||||
|
"cadquery.cq_directive",
|
||||||
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix of source filenames.
|
# The suffix of source filenames.
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The encoding of source files.
|
# The encoding of source files.
|
||||||
#source_encoding = 'utf-8-sig'
|
# source_encoding = 'utf-8-sig'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'CadQuery'
|
project = u"CadQuery"
|
||||||
copyright = u'Parametric Products Intellectual Holdings LLC, All Rights Reserved'
|
copyright = u"Parametric Products Intellectual Holdings LLC, All Rights Reserved"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '1.0'
|
version = "1.0"
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '1.0.0'
|
release = "1.0.0"
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#language = None
|
# language = None
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
# There are two options for replacing |today|: either, you set today to some
|
||||||
# non-false value, then it is used:
|
# non-false value, then it is used:
|
||||||
#today = ''
|
# today = ''
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
#today_fmt = '%B %d, %Y'
|
# today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
exclude_patterns = ['_build']
|
exclude_patterns = ["_build"]
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
#default_role = None
|
# default_role = None
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
#add_function_parentheses = True
|
# add_function_parentheses = True
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
# If true, the current module name will be prepended to all description
|
||||||
# unit titles (such as .. function::).
|
# unit titles (such as .. function::).
|
||||||
@ -85,27 +91,27 @@ add_module_names = True
|
|||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
# output. They are ignored by default.
|
# output. They are ignored by default.
|
||||||
#show_authors = False
|
# show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = "sphinx"
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
#modindex_common_prefix = []
|
# modindex_common_prefix = []
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
# -- Options for HTML output ---------------------------------------------------
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#html_theme = 'timlinux-linfiniti-sphinx'
|
# html_theme = 'timlinux-linfiniti-sphinx'
|
||||||
html_theme = 'sphinx_rtd_theme'
|
html_theme = "sphinx_rtd_theme"
|
||||||
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#html_theme_options = {
|
# html_theme_options = {
|
||||||
# "headerfont": "'Open Sans',Arial,sans-serif",
|
# "headerfont": "'Open Sans',Arial,sans-serif",
|
||||||
# #"bodyfont:": "'Open Sans',Arial,sans-serif",
|
# #"bodyfont:": "'Open Sans',Arial,sans-serif",
|
||||||
# #"headerbg" : "{image: url('/img/bg/body.jpg');color:#000000;}",
|
# #"headerbg" : "{image: url('/img/bg/body.jpg');color:#000000;}",
|
||||||
@ -115,9 +121,9 @@ html_theme = 'sphinx_rtd_theme'
|
|||||||
## "headercolor1": "#13171A;",
|
## "headercolor1": "#13171A;",
|
||||||
# "headercolor2": "#444;",
|
# "headercolor2": "#444;",
|
||||||
# "headerlinkcolor" : "#13171A;",
|
# "headerlinkcolor" : "#13171A;",
|
||||||
#}
|
# }
|
||||||
|
|
||||||
#agogo options
|
# agogo options
|
||||||
"""
|
"""
|
||||||
bodyfont (CSS font family): Font for normal text.
|
bodyfont (CSS font family): Font for normal text.
|
||||||
headerfont (CSS font family): Font for headings.
|
headerfont (CSS font family): Font for headings.
|
||||||
@ -133,14 +139,14 @@ html_theme = 'sphinx_rtd_theme'
|
|||||||
textalign (CSS text-align value): Text alignment for the body, default is justify.
|
textalign (CSS text-align value): Text alignment for the body, default is justify.
|
||||||
"""
|
"""
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
#html_theme_path = []
|
# html_theme_path = []
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
html_title = "CadQuery Documentation"
|
html_title = "CadQuery Documentation"
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||||
#html_short_title = None
|
# html_short_title = None
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
# The name of an image file (relative to this directory) to place at the top
|
||||||
# of the sidebar.
|
# of the sidebar.
|
||||||
@ -149,36 +155,36 @@ html_logo = "_static/cqlogo.png"
|
|||||||
# The name of an image file (within the static path) to use as favicon of the
|
# The name of an image file (within the static path) to use as favicon of the
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||||
# pixels large.
|
# pixels large.
|
||||||
#html_favicon = None
|
# html_favicon = None
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||||
# using the given strftime format.
|
# using the given strftime format.
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
# html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
# typographically correct entities.
|
# typographically correct entities.
|
||||||
#html_use_smartypants = True
|
# html_use_smartypants = True
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
# Custom sidebar templates, maps document names to template names.
|
||||||
#html_sidebars = {}
|
# html_sidebars = {}
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
#html_additional_pages = {}
|
# html_additional_pages = {}
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#html_domain_indices = True
|
# html_domain_indices = True
|
||||||
|
|
||||||
# If false, no index is generated.
|
# If false, no index is generated.
|
||||||
#html_use_index = True
|
# html_use_index = True
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
# If true, the index is split into individual pages for each letter.
|
||||||
#html_split_index = False
|
# html_split_index = False
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
# If true, links to the reST sources are added to the pages.
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
@ -187,72 +193,66 @@ html_show_sourcelink = False
|
|||||||
html_show_sphinx = False
|
html_show_sphinx = False
|
||||||
|
|
||||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||||
#html_show_copyright = True
|
# html_show_copyright = True
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
# If true, an OpenSearch description file will be output, and all pages will
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
# contain a <link> tag referring to it. The value of this option must be the
|
||||||
# base URL from which the finished HTML is served.
|
# base URL from which the finished HTML is served.
|
||||||
#html_use_opensearch = ''
|
# html_use_opensearch = ''
|
||||||
|
|
||||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
#html_file_suffix = None
|
# html_file_suffix = None
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'CadQuerydoc'
|
htmlhelp_basename = "CadQuerydoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
# -- Options for LaTeX output --------------------------------------------------
|
||||||
|
|
||||||
latex_elements = {
|
latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#'papersize': 'letterpaper',
|
#'papersize': 'letterpaper',
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
#'pointsize': '10pt',
|
||||||
#'pointsize': '10pt',
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#'preamble': '',
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#'preamble': '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
('index', 'CadQuery.tex', u'CadQuery Documentation',
|
("index", "CadQuery.tex", u"CadQuery Documentation", u"David Cowden", "manual"),
|
||||||
u'David Cowden', 'manual'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
# The name of an image file (relative to this directory) to place at the top of
|
||||||
# the title page.
|
# the title page.
|
||||||
#latex_logo = None
|
# latex_logo = None
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||||
# not chapters.
|
# not chapters.
|
||||||
#latex_use_parts = False
|
# latex_use_parts = False
|
||||||
|
|
||||||
# If true, show page references after internal links.
|
# If true, show page references after internal links.
|
||||||
#latex_show_pagerefs = False
|
# latex_show_pagerefs = False
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#latex_show_urls = False
|
# latex_show_urls = False
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#latex_appendices = []
|
# latex_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#latex_domain_indices = True
|
# latex_domain_indices = True
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output --------------------------------------------
|
# -- Options for manual page output --------------------------------------------
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [("index", "cadquery", u"CadQuery Documentation", [u"David Cowden"], 1)]
|
||||||
('index', 'cadquery', u'CadQuery Documentation',
|
|
||||||
[u'David Cowden'], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
# If true, show URL addresses after external links.
|
# If true, show URL addresses after external links.
|
||||||
#man_show_urls = False
|
# man_show_urls = False
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ------------------------------------------------
|
# -- Options for Texinfo output ------------------------------------------------
|
||||||
@ -261,16 +261,22 @@ man_pages = [
|
|||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
('index', 'CadQuery', u'CadQuery Documentation',
|
(
|
||||||
u'David Cowden', 'CadQuery', 'A Fluent CAD api',
|
"index",
|
||||||
'Miscellaneous'),
|
"CadQuery",
|
||||||
|
u"CadQuery Documentation",
|
||||||
|
u"David Cowden",
|
||||||
|
"CadQuery",
|
||||||
|
"A Fluent CAD api",
|
||||||
|
"Miscellaneous",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
# Documents to append as an appendix to all manuals.
|
||||||
#texinfo_appendices = []
|
# texinfo_appendices = []
|
||||||
|
|
||||||
# If false, no module index is generated.
|
# If false, no module index is generated.
|
||||||
#texinfo_domain_indices = True
|
# texinfo_domain_indices = True
|
||||||
|
|
||||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||||
#texinfo_show_urls = 'footnote'
|
# texinfo_show_urls = 'footnote'
|
||||||
|
@ -9,6 +9,7 @@ dependencies:
|
|||||||
- pyparsing
|
- pyparsing
|
||||||
- sphinx
|
- sphinx
|
||||||
- sphinx_rtd_theme
|
- sphinx_rtd_theme
|
||||||
|
- black
|
||||||
- codecov
|
- codecov
|
||||||
- pytest
|
- pytest
|
||||||
- pytest-cov
|
- pytest-cov
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
length = 80.0 # Length of the block
|
length = 80.0 # Length of the block
|
||||||
height = 60.0 # Height of the block
|
height = 60.0 # Height of the block
|
||||||
thickness = 10.0 # Thickness of the block
|
thickness = 10.0 # Thickness of the block
|
||||||
|
|
||||||
# Create a 3D block based on the dimension variables above.
|
# Create a 3D block based on the dimension variables above.
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
length = 80.0 # Length of the block
|
length = 80.0 # Length of the block
|
||||||
height = 60.0 # Height of the block
|
height = 60.0 # Height of the block
|
||||||
thickness = 10.0 # Thickness of the block
|
thickness = 10.0 # Thickness of the block
|
||||||
center_hole_dia = 22.0 # Diameter of center hole in block
|
center_hole_dia = 22.0 # Diameter of center hole in block
|
||||||
|
|
||||||
# Create a block based on the dimensions above and add a 22mm center hole.
|
# Create a block based on the dimensions above and add a 22mm center hole.
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
@ -13,8 +13,13 @@ center_hole_dia = 22.0 # Diameter of center hole in block
|
|||||||
# 2. The highest (max) Z face is selected and a new workplane is created on it.
|
# 2. The highest (max) Z face is selected and a new workplane is created on it.
|
||||||
# 3. The new workplane is used to drill a hole through the block.
|
# 3. The new workplane is used to drill a hole through the block.
|
||||||
# 3a. The hole is automatically centered in the workplane.
|
# 3a. The hole is automatically centered in the workplane.
|
||||||
result = (cq.Workplane("XY").box(length, height, thickness)
|
result = (
|
||||||
.faces(">Z").workplane().hole(center_hole_dia))
|
cq.Workplane("XY")
|
||||||
|
.box(length, height, thickness)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.hole(center_hole_dia)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
length = 80.0 # Length of the block
|
length = 80.0 # Length of the block
|
||||||
width = 60.0 # Width of the block
|
width = 60.0 # Width of the block
|
||||||
height = 100.0 # Height of the block
|
height = 100.0 # Height of the block
|
||||||
thickness = 10.0 # Thickness of the block
|
thickness = 10.0 # Thickness of the block
|
||||||
center_hole_dia = 22.0 # Diameter of center hole in block
|
center_hole_dia = 22.0 # Diameter of center hole in block
|
||||||
cbore_hole_diameter = 2.4 # Bolt shank/threads clearance hole diameter
|
cbore_hole_diameter = 2.4 # Bolt shank/threads clearance hole diameter
|
||||||
cbore_inset = 12.0 # How far from the edge the cbored holes are set
|
cbore_inset = 12.0 # How far from the edge the cbored holes are set
|
||||||
cbore_diameter = 4.4 # Bolt head pocket hole diameter
|
cbore_diameter = 4.4 # Bolt head pocket hole diameter
|
||||||
cbore_depth = 2.1 # Bolt head pocket hole depth
|
cbore_depth = 2.1 # Bolt head pocket hole depth
|
||||||
|
|
||||||
# Create a 3D block based on the dimensions above and add a 22mm center hold
|
# Create a 3D block based on the dimensions above and add a 22mm center hold
|
||||||
# and 4 counterbored holes for bolts
|
# and 4 counterbored holes for bolts
|
||||||
@ -26,12 +26,20 @@ cbore_depth = 2.1 # Bolt head pocket hole depth
|
|||||||
# do not show up in the final displayed geometry.
|
# do not show up in the final displayed geometry.
|
||||||
# 6. The vertices of the rectangle (corners) are selected, and a counter-bored
|
# 6. The vertices of the rectangle (corners) are selected, and a counter-bored
|
||||||
# hole is placed at each of the vertices (all 4 of them at once).
|
# hole is placed at each of the vertices (all 4 of them at once).
|
||||||
result = (cq.Workplane("XY").box(length, height, thickness)
|
result = (
|
||||||
.faces(">Z").workplane().hole(center_hole_dia)
|
cq.Workplane("XY")
|
||||||
.faces(">Z").workplane()
|
.box(length, height, thickness)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.hole(center_hole_dia)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
.rect(length - cbore_inset, height - cbore_inset, forConstruction=True)
|
.rect(length - cbore_inset, height - cbore_inset, forConstruction=True)
|
||||||
.vertices().cboreHole(cbore_hole_diameter, cbore_diameter, cbore_depth)
|
.vertices()
|
||||||
.edges("|Z").fillet(2.0))
|
.cboreHole(cbore_hole_diameter, cbore_diameter, cbore_depth)
|
||||||
|
.edges("|Z")
|
||||||
|
.fillet(2.0)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
circle_radius = 50.0 # Radius of the plate
|
circle_radius = 50.0 # Radius of the plate
|
||||||
thickness = 13.0 # Thickness of the plate
|
thickness = 13.0 # Thickness of the plate
|
||||||
rectangle_width = 13.0 # Width of rectangular hole in cylindrical plate
|
rectangle_width = 13.0 # Width of rectangular hole in cylindrical plate
|
||||||
rectangle_length = 19.0 # Length of rectangular hole in cylindrical plate
|
rectangle_length = 19.0 # Length of rectangular hole in cylindrical plate
|
||||||
|
|
||||||
# Extrude a cylindrical plate with a rectangular hole in the middle of it.
|
# Extrude a cylindrical plate with a rectangular hole in the middle of it.
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
@ -21,9 +21,12 @@ rectangle_length = 19.0 # Length of rectangular hole in cylindrical plate
|
|||||||
# plate with a rectangular hole in the center.
|
# plate with a rectangular hole in the center.
|
||||||
# 3a. circle() and rect() could be changed to any other shape to completely
|
# 3a. circle() and rect() could be changed to any other shape to completely
|
||||||
# change the resulting plate and/or the hole in it.
|
# change the resulting plate and/or the hole in it.
|
||||||
result = (cq.Workplane("front").circle(circle_radius)
|
result = (
|
||||||
.rect(rectangle_width, rectangle_length)
|
cq.Workplane("front")
|
||||||
.extrude(thickness))
|
.circle(circle_radius)
|
||||||
|
.rect(rectangle_width, rectangle_length)
|
||||||
|
.extrude(thickness)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
width = 2.0 # Overall width of the plate
|
width = 2.0 # Overall width of the plate
|
||||||
thickness = 0.25 # Thickness of the plate
|
thickness = 0.25 # Thickness of the plate
|
||||||
|
|
||||||
# Extrude a plate outline made of lines and an arc
|
# Extrude a plate outline made of lines and an arc
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
@ -34,12 +34,16 @@ thickness = 0.25 # Thickness of the plate
|
|||||||
# 7a. Without the close(), the 2D sketch will be left open and the extrude
|
# 7a. Without the close(), the 2D sketch will be left open and the extrude
|
||||||
# operation will provide unpredictable results.
|
# operation will provide unpredictable results.
|
||||||
# 8. The 2D sketch is extruded into a solid object of the specified thickness.
|
# 8. The 2D sketch is extruded into a solid object of the specified thickness.
|
||||||
result = (cq.Workplane("front").lineTo(width, 0)
|
result = (
|
||||||
.lineTo(width, 1.0)
|
cq.Workplane("front")
|
||||||
.threePointArc((1.0, 1.5), (0.0, 1.0))
|
.lineTo(width, 0)
|
||||||
.sagittaArc((-0.5, 1.0), 0.2)
|
.lineTo(width, 1.0)
|
||||||
.radiusArc((-0.7, -0.2), -1.5)
|
.threePointArc((1.0, 1.5), (0.0, 1.0))
|
||||||
.close().extrude(thickness))
|
.sagittaArc((-0.5, 1.0), 0.2)
|
||||||
|
.radiusArc((-0.7, -0.2), -1.5)
|
||||||
|
.close()
|
||||||
|
.extrude(thickness)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
circle_radius = 3.0 # The outside radius of the plate
|
circle_radius = 3.0 # The outside radius of the plate
|
||||||
thickness = 0.25 # The thickness of the plate
|
thickness = 0.25 # The thickness of the plate
|
||||||
|
|
||||||
# Make a plate with two cutouts in it by moving the workplane center point
|
# Make a plate with two cutouts in it by moving the workplane center point
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
plate_radius = 2.0 # The radius of the plate that will be extruded
|
plate_radius = 2.0 # The radius of the plate that will be extruded
|
||||||
hole_pattern_radius = 0.25 # Radius of circle where the holes will be placed
|
hole_pattern_radius = 0.25 # Radius of circle where the holes will be placed
|
||||||
thickness = 0.125 # The thickness of the plate that will be extruded
|
thickness = 0.125 # The thickness of the plate that will be extruded
|
||||||
|
|
||||||
# Make a plate with 4 holes in it at various points in a polar arrangement from
|
# Make a plate with 4 holes in it at various points in a polar arrangement from
|
||||||
# the center of the workplane.
|
# the center of the workplane.
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# These can be modified rather than hardcoding values for each dimension.
|
# These can be modified rather than hardcoding values for each dimension.
|
||||||
width = 3.0 # The width of the plate
|
width = 3.0 # The width of the plate
|
||||||
height = 4.0 # The height of the plate
|
height = 4.0 # The height of the plate
|
||||||
thickness = 0.25 # The thickness of the plate
|
thickness = 0.25 # The thickness of the plate
|
||||||
polygon_sides = 6 # The number of sides that the polygonal holes should have
|
polygon_sides = 6 # The number of sides that the polygonal holes should have
|
||||||
polygon_dia = 1.0 # The diameter of the circle enclosing the polygon points
|
polygon_dia = 1.0 # The diameter of the circle enclosing the polygon points
|
||||||
|
|
||||||
# Create a plate with two polygons cut through it
|
# Create a plate with two polygons cut through it
|
||||||
# 1. Establishes a workplane that an object can be built on.
|
# 1. Establishes a workplane that an object can be built on.
|
||||||
@ -30,10 +30,13 @@ polygon_dia = 1.0 # The diameter of the circle enclosing the polygon points
|
|||||||
# like cutBlind() assume a positive cut direction, but cutThruAll() assumes
|
# like cutBlind() assume a positive cut direction, but cutThruAll() assumes
|
||||||
# instead that the cut is made from a max direction and cuts downward from
|
# instead that the cut is made from a max direction and cuts downward from
|
||||||
# that max through all objects.
|
# that max through all objects.
|
||||||
result = (cq.Workplane("front").box(width, height, thickness)
|
result = (
|
||||||
.pushPoints([(0, 0.75), (0, -0.75)])
|
cq.Workplane("front")
|
||||||
.polygon(polygon_sides, polygon_dia)
|
.box(width, height, thickness)
|
||||||
.cutThruAll())
|
.pushPoints([(0, 0.75), (0, -0.75)])
|
||||||
|
.polygon(polygon_sides, polygon_dia)
|
||||||
|
.cutThruAll()
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -6,13 +6,13 @@ import cadquery as cq
|
|||||||
|
|
||||||
# Define the points that the polyline will be drawn to/thru
|
# Define the points that the polyline will be drawn to/thru
|
||||||
pts = [
|
pts = [
|
||||||
(W/2.0, H/2.0),
|
(W / 2.0, H / 2.0),
|
||||||
(W/2.0, (H/2.0 - t)),
|
(W / 2.0, (H / 2.0 - t)),
|
||||||
(t/2.0, (H/2.0-t)),
|
(t / 2.0, (H / 2.0 - t)),
|
||||||
(t/2.0, (t - H/2.0)),
|
(t / 2.0, (t - H / 2.0)),
|
||||||
(W/2.0, (t - H/2.0)),
|
(W / 2.0, (t - H / 2.0)),
|
||||||
(W/2.0, H/-2.0),
|
(W / 2.0, H / -2.0),
|
||||||
(0, H/-2.0)
|
(0, H / -2.0),
|
||||||
]
|
]
|
||||||
|
|
||||||
# We generate half of the I-beam outline and then mirror it to create the full
|
# We generate half of the I-beam outline and then mirror it to create the full
|
||||||
@ -30,10 +30,7 @@ pts = [
|
|||||||
# 3. Only half of the I-beam profile has been drawn so far. That half is
|
# 3. Only half of the I-beam profile has been drawn so far. That half is
|
||||||
# mirrored around the Y-axis to create the complete I-beam profile.
|
# mirrored around the Y-axis to create the complete I-beam profile.
|
||||||
# 4. The I-beam profile is extruded to the final length of the beam.
|
# 4. The I-beam profile is extruded to the final length of the beam.
|
||||||
result = (cq.Workplane("front").moveTo(0, H/2.0)
|
result = cq.Workplane("front").moveTo(0, H / 2.0).polyline(pts).mirrorY().extrude(L)
|
||||||
.polyline(pts)
|
|
||||||
.mirrorY()
|
|
||||||
.extrude(L))
|
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -13,7 +13,7 @@ sPnts = [
|
|||||||
(1.5, 1.0),
|
(1.5, 1.0),
|
||||||
(1.0, 1.25),
|
(1.0, 1.25),
|
||||||
(0.5, 1.0),
|
(0.5, 1.0),
|
||||||
(0, 1.0)
|
(0, 1.0),
|
||||||
]
|
]
|
||||||
|
|
||||||
# 2. Generate our plate with the spline feature and make sure it is a
|
# 2. Generate our plate with the spline feature and make sure it is a
|
||||||
|
@ -13,10 +13,16 @@ import cadquery as cq
|
|||||||
# 6. Selects the vertices of the for-construction rectangle.
|
# 6. Selects the vertices of the for-construction rectangle.
|
||||||
# 7. Places holes at the center of each selected vertex.
|
# 7. Places holes at the center of each selected vertex.
|
||||||
# 7a. Since the workplane is rotated, this results in angled holes in the face.
|
# 7a. Since the workplane is rotated, this results in angled holes in the face.
|
||||||
result = (cq.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z")
|
result = (
|
||||||
.workplane()
|
cq.Workplane("front")
|
||||||
.transformed(offset=(0, -1.5, 1.0), rotate=(60, 0, 0))
|
.box(4.0, 4.0, 0.25)
|
||||||
.rect(1.5, 1.5, forConstruction=True).vertices().hole(0.25))
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.transformed(offset=(0, -1.5, 1.0), rotate=(60, 0, 0))
|
||||||
|
.rect(1.5, 1.5, forConstruction=True)
|
||||||
|
.vertices()
|
||||||
|
.hole(0.25)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -12,10 +12,15 @@ import cadquery as cq
|
|||||||
# other geometry.
|
# other geometry.
|
||||||
# 6. Selects the vertices of the for-construction rectangle.
|
# 6. Selects the vertices of the for-construction rectangle.
|
||||||
# 7. Places holes at the center of each selected vertex.
|
# 7. Places holes at the center of each selected vertex.
|
||||||
result = (cq.Workplane("front").box(2, 2, 0.5)
|
result = (
|
||||||
.faces(">Z").workplane()
|
cq.Workplane("front")
|
||||||
.rect(1.5, 1.5, forConstruction=True).vertices()
|
.box(2, 2, 0.5)
|
||||||
.hole(0.125))
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.rect(1.5, 1.5, forConstruction=True)
|
||||||
|
.vertices()
|
||||||
|
.hole(0.125)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -11,10 +11,15 @@ import cadquery as cq
|
|||||||
# 5. Creates a workplane 3 mm above the face the circle was drawn on.
|
# 5. Creates a workplane 3 mm above the face the circle was drawn on.
|
||||||
# 6. Draws a 2D circle on the new, offset workplane.
|
# 6. Draws a 2D circle on the new, offset workplane.
|
||||||
# 7. Creates a loft between the circle and the rectangle.
|
# 7. Creates a loft between the circle and the rectangle.
|
||||||
result = (cq.Workplane("front").box(4.0, 4.0, 0.25).faces(">Z")
|
result = (
|
||||||
.circle(1.5).workplane(offset=3.0)
|
cq.Workplane("front")
|
||||||
.rect(0.75, 0.5)
|
.box(4.0, 4.0, 0.25)
|
||||||
.loft(combine=True))
|
.faces(">Z")
|
||||||
|
.circle(1.5)
|
||||||
|
.workplane(offset=3.0)
|
||||||
|
.rect(0.75, 0.5)
|
||||||
|
.loft(combine=True)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -11,9 +11,15 @@ import cadquery as cq
|
|||||||
# function.
|
# function.
|
||||||
# 5a. When the depth of the counter-sink hole is set to None, the hole will be
|
# 5a. When the depth of the counter-sink hole is set to None, the hole will be
|
||||||
# cut through.
|
# cut through.
|
||||||
result = (cq.Workplane(cq.Plane.XY()).box(4, 2, 0.5).faces(">Z")
|
result = (
|
||||||
.workplane().rect(3.5, 1.5, forConstruction=True)
|
cq.Workplane(cq.Plane.XY())
|
||||||
.vertices().cskHole(0.125, 0.25, 82.0, depth=None))
|
.box(4, 2, 0.5)
|
||||||
|
.faces(">Z")
|
||||||
|
.workplane()
|
||||||
|
.rect(3.5, 1.5, forConstruction=True)
|
||||||
|
.vertices()
|
||||||
|
.cskHole(0.125, 0.25, 82.0, depth=None)
|
||||||
|
)
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -9,8 +9,7 @@ import cadquery as cq
|
|||||||
# that new geometry can be built on.
|
# that new geometry can be built on.
|
||||||
# 4. Draws a 2D circle on the new workplane and then uses it to cut a hole
|
# 4. Draws a 2D circle on the new workplane and then uses it to cut a hole
|
||||||
# all the way through the box.
|
# all the way through the box.
|
||||||
c = (cq.Workplane("XY").box(1, 1, 1).faces(">Z").workplane()
|
c = cq.Workplane("XY").box(1, 1, 1).faces(">Z").workplane().circle(0.25).cutThruAll()
|
||||||
.circle(0.25).cutThruAll())
|
|
||||||
|
|
||||||
# 5. Selects the face furthest away from the origin in the +Y axis direction.
|
# 5. Selects the face furthest away from the origin in the +Y axis direction.
|
||||||
# 6. Creates an offset workplane that is set in the center of the object.
|
# 6. Creates an offset workplane that is set in the center of the object.
|
||||||
|
@ -9,13 +9,13 @@ angle_degrees = 360.0
|
|||||||
# Revolve a cylinder from a rectangle
|
# Revolve a cylinder from a rectangle
|
||||||
# Switch comments around in this section to try the revolve operation with different parameters
|
# Switch comments around in this section to try the revolve operation with different parameters
|
||||||
result = cq.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
|
result = cq.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
|
||||||
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
|
# result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
|
||||||
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
|
# result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
|
||||||
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
|
# result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
|
||||||
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
|
# result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
|
||||||
|
|
||||||
# Revolve a donut with square walls
|
# Revolve a donut with square walls
|
||||||
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
|
# result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
|
||||||
|
|
||||||
# Displays the result of this script
|
# Displays the result of this script
|
||||||
show_object(result)
|
show_object(result)
|
||||||
|
@ -1,11 +1,7 @@
|
|||||||
import cadquery as cq
|
import cadquery as cq
|
||||||
|
|
||||||
# Points we will use to create spline and polyline paths to sweep over
|
# Points we will use to create spline and polyline paths to sweep over
|
||||||
pts = [
|
pts = [(0, 1), (1, 2), (2, 4)]
|
||||||
(0, 1),
|
|
||||||
(1, 2),
|
|
||||||
(2, 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Spline path generated from our list of points (tuples)
|
# Spline path generated from our list of points (tuples)
|
||||||
path = cq.Workplane("XZ").spline(pts)
|
path = cq.Workplane("XZ").spline(pts)
|
||||||
@ -37,4 +33,4 @@ show_object(defaultSweep)
|
|||||||
show_object(frenetShell.translate((5, 0, 0)))
|
show_object(frenetShell.translate((5, 0, 0)))
|
||||||
show_object(defaultRect.translate((10, 0, 0)))
|
show_object(defaultRect.translate((10, 0, 0)))
|
||||||
show_object(plineSweep.translate((15, 0, 0)))
|
show_object(plineSweep.translate((15, 0, 0)))
|
||||||
show_object(arcSweep.translate((20, 0, 0)))
|
show_object(arcSweep.translate((20, 0, 0)))
|
||||||
|
@ -4,37 +4,80 @@ import cadquery as cq
|
|||||||
path = cq.Workplane("XZ").moveTo(-10, 0).lineTo(10, 0)
|
path = cq.Workplane("XZ").moveTo(-10, 0).lineTo(10, 0)
|
||||||
|
|
||||||
# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0
|
# Sweep a circle from diameter 2.0 to diameter 1.0 to diameter 2.0 along X axis length 10.0 + 10.0
|
||||||
defaultSweep = (cq.Workplane("YZ").workplane(offset=-10.0).circle(2.0).
|
defaultSweep = (
|
||||||
workplane(offset=10.0).circle(1.0).
|
cq.Workplane("YZ")
|
||||||
workplane(offset=10.0).circle(2.0).sweep(path, multisection=True))
|
.workplane(offset=-10.0)
|
||||||
|
.circle(2.0)
|
||||||
|
.workplane(offset=10.0)
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=10.0)
|
||||||
|
.circle(2.0)
|
||||||
|
.sweep(path, multisection=True)
|
||||||
|
)
|
||||||
|
|
||||||
# We can sweep thrue different shapes
|
# We can sweep thrue different shapes
|
||||||
recttocircleSweep = (cq.Workplane("YZ").workplane(offset=-10.0).rect(2.0, 2.0).
|
recttocircleSweep = (
|
||||||
workplane(offset=8.0).circle(1.0).workplane(offset=4.0).circle(1.0).
|
cq.Workplane("YZ")
|
||||||
workplane(offset=8.0).rect(2.0, 2.0).sweep(path, multisection=True))
|
.workplane(offset=-10.0)
|
||||||
|
.rect(2.0, 2.0)
|
||||||
|
.workplane(offset=8.0)
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=4.0)
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=8.0)
|
||||||
|
.rect(2.0, 2.0)
|
||||||
|
.sweep(path, multisection=True)
|
||||||
|
)
|
||||||
|
|
||||||
circletorectSweep = (cq.Workplane("YZ").workplane(offset=-10.0).circle(1.0).
|
circletorectSweep = (
|
||||||
workplane(offset=7.0).rect(2.0, 2.0).workplane(offset=6.0).rect(2.0, 2.0).
|
cq.Workplane("YZ")
|
||||||
workplane(offset=7.0).circle(1.0).sweep(path, multisection=True))
|
.workplane(offset=-10.0)
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=7.0)
|
||||||
|
.rect(2.0, 2.0)
|
||||||
|
.workplane(offset=6.0)
|
||||||
|
.rect(2.0, 2.0)
|
||||||
|
.workplane(offset=7.0)
|
||||||
|
.circle(1.0)
|
||||||
|
.sweep(path, multisection=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Placement of the Shape is important otherwise could produce unexpected shape
|
# Placement of the Shape is important otherwise could produce unexpected shape
|
||||||
specialSweep = (cq.Workplane("YZ").circle(1.0).workplane(offset=10.0).rect(2.0, 2.0).
|
specialSweep = (
|
||||||
sweep(path, multisection=True))
|
cq.Workplane("YZ")
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=10.0)
|
||||||
|
.rect(2.0, 2.0)
|
||||||
|
.sweep(path, multisection=True)
|
||||||
|
)
|
||||||
|
|
||||||
# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0
|
# Switch to an arc for the path : line l=5.0 then half circle r=4.0 then line l=5.0
|
||||||
path = (cq.Workplane("XZ").moveTo(-5, 4).lineTo(0, 4).
|
path = (
|
||||||
threePointArc((4, 0), (0, -4)).lineTo(-5, -4))
|
cq.Workplane("XZ")
|
||||||
|
.moveTo(-5, 4)
|
||||||
|
.lineTo(0, 4)
|
||||||
|
.threePointArc((4, 0), (0, -4))
|
||||||
|
.lineTo(-5, -4)
|
||||||
|
)
|
||||||
|
|
||||||
# Placement of different shapes should follow the path
|
# Placement of different shapes should follow the path
|
||||||
# cylinder r=1.5 along first line
|
# cylinder r=1.5 along first line
|
||||||
# then sweep allong arc from r=1.5 to r=1.0
|
# then sweep allong arc from r=1.5 to r=1.0
|
||||||
# then cylinder r=1.0 along last line
|
# then cylinder r=1.0 along last line
|
||||||
arcSweep = (cq.Workplane("YZ").workplane(offset=-5).moveTo(0, 4).circle(1.5).
|
arcSweep = (
|
||||||
workplane(offset=5).circle(1.5).
|
cq.Workplane("YZ")
|
||||||
moveTo(0, -8).circle(1.0).
|
.workplane(offset=-5)
|
||||||
workplane(offset=-5).circle(1.0).
|
.moveTo(0, 4)
|
||||||
sweep(path, multisection=True))
|
.circle(1.5)
|
||||||
|
.workplane(offset=5)
|
||||||
|
.circle(1.5)
|
||||||
|
.moveTo(0, -8)
|
||||||
|
.circle(1.0)
|
||||||
|
.workplane(offset=-5)
|
||||||
|
.circle(1.0)
|
||||||
|
.sweep(path, multisection=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Translate the resulting solids so that they do not overlap and display them left to right
|
# Translate the resulting solids so that they do not overlap and display them left to right
|
||||||
@ -43,5 +86,3 @@ show_object(circletorectSweep.translate((0, 5, 0)))
|
|||||||
show_object(recttocircleSweep.translate((0, 10, 0)))
|
show_object(recttocircleSweep.translate((0, 10, 0)))
|
||||||
show_object(specialSweep.translate((0, 15, 0)))
|
show_object(specialSweep.translate((0, 15, 0)))
|
||||||
show_object(arcSweep.translate((0, -5, 0)))
|
show_object(arcSweep.translate((0, -5, 0)))
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import cadquery as cq
|
|||||||
#####
|
#####
|
||||||
# Inputs
|
# Inputs
|
||||||
######
|
######
|
||||||
lbumps = 1 # number of bumps long
|
lbumps = 1 # number of bumps long
|
||||||
wbumps = 1 # number of bumps wide
|
wbumps = 1 # number of bumps wide
|
||||||
thin = True # True for thin, False for thick
|
thin = True # True for thin, False for thick
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -22,8 +22,8 @@ else:
|
|||||||
|
|
||||||
t = (pitch - (2 * clearance) - bumpDiam) / 2.0
|
t = (pitch - (2 * clearance) - bumpDiam) / 2.0
|
||||||
postDiam = pitch - t # works out to 6.5
|
postDiam = pitch - t # works out to 6.5
|
||||||
total_length = lbumps*pitch - 2.0*clearance
|
total_length = lbumps * pitch - 2.0 * clearance
|
||||||
total_width = wbumps*pitch - 2.0*clearance
|
total_width = wbumps * pitch - 2.0 * clearance
|
||||||
|
|
||||||
# make the base
|
# make the base
|
||||||
s = cq.Workplane("XY").box(total_length, total_width, height)
|
s = cq.Workplane("XY").box(total_length, total_width, height)
|
||||||
@ -32,23 +32,37 @@ s = cq.Workplane("XY").box(total_length, total_width, height)
|
|||||||
s = s.faces("<Z").shell(-1.0 * t)
|
s = s.faces("<Z").shell(-1.0 * t)
|
||||||
|
|
||||||
# make the bumps on the top
|
# make the bumps on the top
|
||||||
s = (s.faces(">Z").workplane().
|
s = (
|
||||||
rarray(pitch, pitch, lbumps, wbumps, True).circle(bumpDiam / 2.0)
|
s.faces(">Z")
|
||||||
.extrude(bumpHeight))
|
.workplane()
|
||||||
|
.rarray(pitch, pitch, lbumps, wbumps, True)
|
||||||
|
.circle(bumpDiam / 2.0)
|
||||||
|
.extrude(bumpHeight)
|
||||||
|
)
|
||||||
|
|
||||||
# add posts on the bottom. posts are different diameter depending on geometry
|
# add posts on the bottom. posts are different diameter depending on geometry
|
||||||
# solid studs for 1 bump, tubes for multiple, none for 1x1
|
# solid studs for 1 bump, tubes for multiple, none for 1x1
|
||||||
tmp = s.faces("<Z").workplane(invert=True)
|
tmp = s.faces("<Z").workplane(invert=True)
|
||||||
|
|
||||||
if lbumps > 1 and wbumps > 1:
|
if lbumps > 1 and wbumps > 1:
|
||||||
tmp = (tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True).
|
tmp = (
|
||||||
circle(postDiam / 2.0).circle(bumpDiam / 2.0).extrude(height - t))
|
tmp.rarray(pitch, pitch, lbumps - 1, wbumps - 1, center=True)
|
||||||
|
.circle(postDiam / 2.0)
|
||||||
|
.circle(bumpDiam / 2.0)
|
||||||
|
.extrude(height - t)
|
||||||
|
)
|
||||||
elif lbumps > 1:
|
elif lbumps > 1:
|
||||||
tmp = (tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True).
|
tmp = (
|
||||||
circle(t).extrude(height - t))
|
tmp.rarray(pitch, pitch, lbumps - 1, 1, center=True)
|
||||||
|
.circle(t)
|
||||||
|
.extrude(height - t)
|
||||||
|
)
|
||||||
elif wbumps > 1:
|
elif wbumps > 1:
|
||||||
tmp = (tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True).
|
tmp = (
|
||||||
circle(t).extrude(height - t))
|
tmp.rarray(pitch, pitch, 1, wbumps - 1, center=True)
|
||||||
|
.circle(t)
|
||||||
|
.extrude(height - t)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
tmp = s
|
tmp = s
|
||||||
|
|
||||||
|
65
setup.py
65
setup.py
@ -15,43 +15,48 @@ import os
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
#if we are building in travis, use the build number as the sub-minor version
|
# if we are building in travis, use the build number as the sub-minor version
|
||||||
version = '0.5-SNAPSHOT'
|
version = "0.5-SNAPSHOT"
|
||||||
if 'TRAVIS_TAG' in os.environ.keys():
|
if "TRAVIS_TAG" in os.environ.keys():
|
||||||
version= os.environ['TRAVIS_TAG']
|
version = os.environ["TRAVIS_TAG"]
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='cadquery',
|
name="cadquery",
|
||||||
version=version,
|
version=version,
|
||||||
url='https://github.com/dcowden/cadquery',
|
url="https://github.com/dcowden/cadquery",
|
||||||
license='Apache Public License 2.0',
|
license="Apache Public License 2.0",
|
||||||
author='David Cowden',
|
author="David Cowden",
|
||||||
author_email='dave.cowden@gmail.com',
|
author_email="dave.cowden@gmail.com",
|
||||||
description='CadQuery is a parametric scripting language for creating and traversing CAD models',
|
description="CadQuery is a parametric scripting language for creating and traversing CAD models",
|
||||||
long_description=open('README.md').read(),
|
long_description=open("README.md").read(),
|
||||||
packages=['cadquery','cadquery.contrib','cadquery.occ_impl','cadquery.plugins','tests'],
|
packages=[
|
||||||
|
"cadquery",
|
||||||
|
"cadquery.contrib",
|
||||||
|
"cadquery.occ_impl",
|
||||||
|
"cadquery.plugins",
|
||||||
|
"tests",
|
||||||
|
],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
platforms='any',
|
platforms="any",
|
||||||
test_suite='tests',
|
test_suite="tests",
|
||||||
|
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
"Development Status :: 5 - Production/Stable",
|
||||||
#'Development Status :: 6 - Mature',
|
#'Development Status :: 6 - Mature',
|
||||||
#'Development Status :: 7 - Inactive',
|
#'Development Status :: 7 - Inactive',
|
||||||
'Intended Audience :: Developers',
|
"Intended Audience :: Developers",
|
||||||
'Intended Audience :: End Users/Desktop',
|
"Intended Audience :: End Users/Desktop",
|
||||||
'Intended Audience :: Information Technology',
|
"Intended Audience :: Information Technology",
|
||||||
'Intended Audience :: Science/Research',
|
"Intended Audience :: Science/Research",
|
||||||
'Intended Audience :: System Administrators',
|
"Intended Audience :: System Administrators",
|
||||||
'License :: OSI Approved :: Apache Software License',
|
"License :: OSI Approved :: Apache Software License",
|
||||||
'Operating System :: POSIX',
|
"Operating System :: POSIX",
|
||||||
'Operating System :: MacOS',
|
"Operating System :: MacOS",
|
||||||
'Operating System :: Unix',
|
"Operating System :: Unix",
|
||||||
'Programming Language :: Python',
|
"Programming Language :: Python",
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||||
'Topic :: Internet',
|
"Topic :: Internet",
|
||||||
'Topic :: Scientific/Engineering'
|
"Topic :: Scientific/Engineering",
|
||||||
]
|
],
|
||||||
)
|
)
|
||||||
|
@ -6,21 +6,23 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
def readFileAsString(fileName):
|
def readFileAsString(fileName):
|
||||||
f = open(fileName, 'r')
|
f = open(fileName, "r")
|
||||||
s = f.read()
|
s = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def writeStringToFile(strToWrite, fileName):
|
def writeStringToFile(strToWrite, fileName):
|
||||||
f = open(fileName, 'w')
|
f = open(fileName, "w")
|
||||||
f.write(strToWrite)
|
f.write(strToWrite)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
def makeUnitSquareWire():
|
def makeUnitSquareWire():
|
||||||
V = Vector
|
V = Vector
|
||||||
return Wire.makePolygon([V(0, 0, 0), V(1, 0, 0), V(1, 1, 0), V(0, 1, 0), V(0, 0, 0)])
|
return Wire.makePolygon(
|
||||||
|
[V(0, 0, 0), V(1, 0, 0), V(1, 1, 0), V(0, 1, 0), V(0, 0, 0)]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def makeUnitCube():
|
def makeUnitCube():
|
||||||
@ -38,26 +40,24 @@ def toTuple(v):
|
|||||||
elif type(v) == Vector:
|
elif type(v) == Vector:
|
||||||
return v.toTuple()
|
return v.toTuple()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError("dont know how to convert type %s to tuple" % str(type(v)))
|
||||||
"dont know how to convert type %s to tuple" % str(type(v)))
|
|
||||||
|
|
||||||
|
|
||||||
class BaseTest(unittest.TestCase):
|
class BaseTest(unittest.TestCase):
|
||||||
|
|
||||||
def assertTupleAlmostEquals(self, expected, actual, places):
|
def assertTupleAlmostEquals(self, expected, actual, places):
|
||||||
for i, j in zip(actual, expected):
|
for i, j in zip(actual, expected):
|
||||||
self.assertAlmostEqual(i, j, places)
|
self.assertAlmostEqual(i, j, places)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'TestCadObjects',
|
"TestCadObjects",
|
||||||
'TestCadQuery',
|
"TestCadQuery",
|
||||||
'TestCQGI',
|
"TestCQGI",
|
||||||
'TestCQSelectors',
|
"TestCQSelectors",
|
||||||
'TestCQSelectors',
|
"TestCQSelectors",
|
||||||
'TestExporters',
|
"TestExporters",
|
||||||
'TestImporters',
|
"TestImporters",
|
||||||
'TestJupyter',
|
"TestJupyter",
|
||||||
'TestWorkplanes',
|
"TestWorkplanes",
|
||||||
'TestAssembleEdges',
|
"TestAssembleEdges",
|
||||||
]
|
]
|
||||||
|
@ -3,9 +3,11 @@ import sys
|
|||||||
import unittest
|
import unittest
|
||||||
from tests import BaseTest
|
from tests import BaseTest
|
||||||
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_DZ, gp_XYZ
|
from OCC.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_DZ, gp_XYZ
|
||||||
from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeVertex,
|
from OCC.BRepBuilderAPI import (
|
||||||
BRepBuilderAPI_MakeEdge,
|
BRepBuilderAPI_MakeVertex,
|
||||||
BRepBuilderAPI_MakeFace)
|
BRepBuilderAPI_MakeEdge,
|
||||||
|
BRepBuilderAPI_MakeFace,
|
||||||
|
)
|
||||||
|
|
||||||
from OCC.GC import GC_MakeCircle
|
from OCC.GC import GC_MakeCircle
|
||||||
|
|
||||||
@ -13,36 +15,34 @@ from cadquery import *
|
|||||||
|
|
||||||
|
|
||||||
class TestCadObjects(BaseTest):
|
class TestCadObjects(BaseTest):
|
||||||
|
|
||||||
def _make_circle(self):
|
def _make_circle(self):
|
||||||
|
|
||||||
circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp_DZ()),
|
circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp_DZ()), 2.0)
|
||||||
2.)
|
|
||||||
return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
|
return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
|
||||||
|
|
||||||
def testVectorConstructors(self):
|
def testVectorConstructors(self):
|
||||||
v1 = Vector(1, 2, 3)
|
v1 = Vector(1, 2, 3)
|
||||||
v2 = Vector((1, 2, 3))
|
v2 = Vector((1, 2, 3))
|
||||||
v3 = Vector(gp_Vec(1, 2, 3))
|
v3 = Vector(gp_Vec(1, 2, 3))
|
||||||
v4 = Vector([1,2,3])
|
v4 = Vector([1, 2, 3])
|
||||||
v5 = Vector(gp_XYZ(1,2,3))
|
v5 = Vector(gp_XYZ(1, 2, 3))
|
||||||
|
|
||||||
for v in [v1, v2, v3, v4, v5]:
|
for v in [v1, v2, v3, v4, v5]:
|
||||||
self.assertTupleAlmostEquals((1, 2, 3), v.toTuple(), 4)
|
self.assertTupleAlmostEquals((1, 2, 3), v.toTuple(), 4)
|
||||||
|
|
||||||
v6 = Vector((1,2))
|
v6 = Vector((1, 2))
|
||||||
v7 = Vector([1,2])
|
v7 = Vector([1, 2])
|
||||||
v8 = Vector(1,2)
|
v8 = Vector(1, 2)
|
||||||
|
|
||||||
for v in [v6, v7, v8]:
|
for v in [v6, v7, v8]:
|
||||||
self.assertTupleAlmostEquals((1, 2, 0), v.toTuple(), 4)
|
self.assertTupleAlmostEquals((1, 2, 0), v.toTuple(), 4)
|
||||||
|
|
||||||
v9 = Vector()
|
v9 = Vector()
|
||||||
self.assertTupleAlmostEquals((0, 0, 0), v9.toTuple(), 4)
|
self.assertTupleAlmostEquals((0, 0, 0), v9.toTuple(), 4)
|
||||||
|
|
||||||
v9.x = 1.
|
v9.x = 1.0
|
||||||
v9.y = 2.
|
v9.y = 2.0
|
||||||
v9.z = 3.
|
v9.z = 3.0
|
||||||
self.assertTupleAlmostEquals((1, 2, 3), (v9.x, v9.y, v9.z), 4)
|
self.assertTupleAlmostEquals((1, 2, 3), (v9.x, v9.y, v9.z), 4)
|
||||||
|
|
||||||
def testVertex(self):
|
def testVertex(self):
|
||||||
@ -70,20 +70,22 @@ class TestCadObjects(BaseTest):
|
|||||||
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
|
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
|
||||||
|
|
||||||
def testEdgeWrapperMakeCircle(self):
|
def testEdgeWrapperMakeCircle(self):
|
||||||
halfCircleEdge = Edge.makeCircle(radius=10, pnt=(
|
halfCircleEdge = Edge.makeCircle(
|
||||||
0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180)
|
radius=10, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180
|
||||||
|
)
|
||||||
|
|
||||||
#self.assertTupleAlmostEquals((0.0, 5.0, 0.0), halfCircleEdge.CenterOfBoundBox(0.0001).toTuple(),3)
|
# self.assertTupleAlmostEquals((0.0, 5.0, 0.0), halfCircleEdge.CenterOfBoundBox(0.0001).toTuple(),3)
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(10.0, 0.0, 0.0), halfCircleEdge.startPoint().toTuple(), 3)
|
(10.0, 0.0, 0.0), halfCircleEdge.startPoint().toTuple(), 3
|
||||||
|
)
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(-10.0, 0.0, 0.0), halfCircleEdge.endPoint().toTuple(), 3)
|
(-10.0, 0.0, 0.0), halfCircleEdge.endPoint().toTuple(), 3
|
||||||
|
)
|
||||||
|
|
||||||
def testFaceWrapperMakePlane(self):
|
def testFaceWrapperMakePlane(self):
|
||||||
mplane = Face.makePlane(10, 10)
|
mplane = Face.makePlane(10, 10)
|
||||||
|
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0.0, 0.0, 1.0), mplane.normalAt().toTuple(), 3)
|
||||||
(0.0, 0.0, 1.0), mplane.normalAt().toTuple(), 3)
|
|
||||||
|
|
||||||
def testCenterOfBoundBox(self):
|
def testCenterOfBoundBox(self):
|
||||||
pass
|
pass
|
||||||
@ -109,12 +111,15 @@ class TestCadObjects(BaseTest):
|
|||||||
Workplane.cyl = cylinders
|
Workplane.cyl = cylinders
|
||||||
|
|
||||||
# Now test. here we want weird workplane to see if the objects are transformed right
|
# Now test. here we want weird workplane to see if the objects are transformed right
|
||||||
s = Workplane("XY").rect(
|
s = (
|
||||||
2.0, 3.0, forConstruction=True).vertices().cyl(0.25, 0.5)
|
Workplane("XY")
|
||||||
|
.rect(2.0, 3.0, forConstruction=True)
|
||||||
|
.vertices()
|
||||||
|
.cyl(0.25, 0.5)
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEqual(4, len(s.val().Solids()))
|
self.assertEqual(4, len(s.val().Solids()))
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0.0, 0.0, 0.25), s.val().Center().toTuple(), 3)
|
||||||
(0.0, 0.0, 0.25), s.val().Center().toTuple(), 3)
|
|
||||||
|
|
||||||
def testDot(self):
|
def testDot(self):
|
||||||
v1 = Vector(2, 2, 2)
|
v1 = Vector(2, 2, 2)
|
||||||
@ -142,7 +147,7 @@ class TestCadObjects(BaseTest):
|
|||||||
|
|
||||||
self.assertEqual(0, abs(Vector(0, 0, 0)))
|
self.assertEqual(0, abs(Vector(0, 0, 0)))
|
||||||
self.assertEqual(1, abs(Vector(1, 0, 0)))
|
self.assertEqual(1, abs(Vector(1, 0, 0)))
|
||||||
self.assertEqual((1+4+9)**0.5, abs(Vector(1, 2, 3)))
|
self.assertEqual((1 + 4 + 9) ** 0.5, abs(Vector(1, 2, 3)))
|
||||||
|
|
||||||
def testVectorEquals(self):
|
def testVectorEquals(self):
|
||||||
a = Vector(1, 2, 3)
|
a = Vector(1, 2, 3)
|
||||||
@ -163,25 +168,31 @@ class TestCadObjects(BaseTest):
|
|||||||
|
|
||||||
# test passing Plane object
|
# test passing Plane object
|
||||||
point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal))
|
point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal))
|
||||||
self.assertTupleAlmostEquals(point.toTuple(), (59/7, 55/7, 51/7),
|
self.assertTupleAlmostEquals(
|
||||||
decimal_places)
|
point.toTuple(), (59 / 7, 55 / 7, 51 / 7), decimal_places
|
||||||
|
)
|
||||||
|
|
||||||
def testMatrixCreationAndAccess(self):
|
def testMatrixCreationAndAccess(self):
|
||||||
def matrix_vals(m):
|
def matrix_vals(m):
|
||||||
return [[m[r,c] for c in range(4)] for r in range(4)]
|
return [[m[r, c] for c in range(4)] for r in range(4)]
|
||||||
|
|
||||||
# default constructor creates a 4x4 identity matrix
|
# default constructor creates a 4x4 identity matrix
|
||||||
m = Matrix()
|
m = Matrix()
|
||||||
identity = [[1., 0., 0., 0.],
|
identity = [
|
||||||
[0., 1., 0., 0.],
|
[1.0, 0.0, 0.0, 0.0],
|
||||||
[0., 0., 1., 0.],
|
[0.0, 1.0, 0.0, 0.0],
|
||||||
[0., 0., 0., 1.]]
|
[0.0, 0.0, 1.0, 0.0],
|
||||||
|
[0.0, 0.0, 0.0, 1.0],
|
||||||
|
]
|
||||||
self.assertEqual(identity, matrix_vals(m))
|
self.assertEqual(identity, matrix_vals(m))
|
||||||
|
|
||||||
vals4x4 = [[1., 0., 0., 1.],
|
vals4x4 = [
|
||||||
[0., 1., 0., 2.],
|
[1.0, 0.0, 0.0, 1.0],
|
||||||
[0., 0., 1., 3.],
|
[0.0, 1.0, 0.0, 2.0],
|
||||||
[0., 0., 0., 1.]]
|
[0.0, 0.0, 1.0, 3.0],
|
||||||
vals4x4_tuple = tuple(tuple(r) for r in vals4x4)
|
[0.0, 0.0, 0.0, 1.0],
|
||||||
|
]
|
||||||
|
vals4x4_tuple = tuple(tuple(r) for r in vals4x4)
|
||||||
|
|
||||||
# test constructor with 16-value input
|
# test constructor with 16-value input
|
||||||
m = Matrix(vals4x4)
|
m = Matrix(vals4x4)
|
||||||
@ -197,10 +208,12 @@ class TestCadObjects(BaseTest):
|
|||||||
self.assertEqual(vals4x4, matrix_vals(m))
|
self.assertEqual(vals4x4, matrix_vals(m))
|
||||||
|
|
||||||
# Test 16-value input with invalid values for the last 4
|
# Test 16-value input with invalid values for the last 4
|
||||||
invalid = [[1., 0., 0., 1.],
|
invalid = [
|
||||||
[0., 1., 0., 2.],
|
[1.0, 0.0, 0.0, 1.0],
|
||||||
[0., 0., 1., 3.],
|
[0.0, 1.0, 0.0, 2.0],
|
||||||
[1., 2., 3., 4.]]
|
[0.0, 0.0, 1.0, 3.0],
|
||||||
|
[1.0, 2.0, 3.0, 4.0],
|
||||||
|
]
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
Matrix(invalid)
|
Matrix(invalid)
|
||||||
|
|
||||||
@ -208,11 +221,11 @@ class TestCadObjects(BaseTest):
|
|||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Matrix([[1, 2, 3, 4], [1, 2, 3], [1, 2, 3, 4]])
|
Matrix([[1, 2, 3, 4], [1, 2, 3], [1, 2, 3, 4]])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Matrix([1,2,3])
|
Matrix([1, 2, 3])
|
||||||
|
|
||||||
# Invalid sub-type
|
# Invalid sub-type
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Matrix([[1, 2, 3, 4], 'abc', [1, 2, 3, 4]])
|
Matrix([[1, 2, 3, 4], "abc", [1, 2, 3, 4]])
|
||||||
|
|
||||||
# test out-of-bounds access
|
# test out-of-bounds access
|
||||||
m = Matrix()
|
m = Matrix()
|
||||||
@ -221,8 +234,7 @@ class TestCadObjects(BaseTest):
|
|||||||
with self.assertRaises(IndexError):
|
with self.assertRaises(IndexError):
|
||||||
m[4, 0]
|
m[4, 0]
|
||||||
with self.assertRaises(IndexError):
|
with self.assertRaises(IndexError):
|
||||||
m['ab']
|
m["ab"]
|
||||||
|
|
||||||
|
|
||||||
def testTranslate(self):
|
def testTranslate(self):
|
||||||
e = Edge.makeCircle(2, (1, 2, 3))
|
e = Edge.makeCircle(2, (1, 2, 3))
|
||||||
@ -231,54 +243,53 @@ class TestCadObjects(BaseTest):
|
|||||||
self.assertTupleAlmostEquals((1.0, 2.0, 4.0), e2.Center().toTuple(), 3)
|
self.assertTupleAlmostEquals((1.0, 2.0, 4.0), e2.Center().toTuple(), 3)
|
||||||
|
|
||||||
def testVertices(self):
|
def testVertices(self):
|
||||||
e = Shape.cast(BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0),
|
e = Shape.cast(BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 1, 0)).Edge())
|
||||||
gp_Pnt(1, 1, 0)).Edge())
|
|
||||||
self.assertEqual(2, len(e.Vertices()))
|
self.assertEqual(2, len(e.Vertices()))
|
||||||
|
|
||||||
def testPlaneEqual(self):
|
def testPlaneEqual(self):
|
||||||
# default orientation
|
# default orientation
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1))
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
)
|
)
|
||||||
# moved origin
|
# moved origin
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Plane(origin=(2,1,-1), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(2, 1, -1), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(2,1,-1), xDir=(1,0,0), normal=(0,0,1))
|
Plane(origin=(2, 1, -1), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
)
|
)
|
||||||
# moved x-axis
|
# moved x-axis
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1))
|
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
|
||||||
)
|
)
|
||||||
# moved z-axis
|
# moved z-axis
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1))
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
|
||||||
)
|
)
|
||||||
|
|
||||||
def testPlaneNotEqual(self):
|
def testPlaneNotEqual(self):
|
||||||
# type difference
|
# type difference
|
||||||
for value in [None, 0, 1, 'abc']:
|
for value in [None, 0, 1, "abc"]:
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)), value
|
||||||
value
|
|
||||||
)
|
)
|
||||||
# origin difference
|
# origin difference
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(0,0,1), xDir=(1,0,0), normal=(0,0,1))
|
Plane(origin=(0, 0, 1), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
)
|
)
|
||||||
# x-axis difference
|
# x-axis difference
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(0,0,0), xDir=(1,1,0), normal=(0,0,1))
|
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
|
||||||
)
|
)
|
||||||
# z-axis difference
|
# z-axis difference
|
||||||
self.assertNotEqual(
|
self.assertNotEqual(
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)),
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
|
||||||
Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,1,1))
|
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -42,8 +42,9 @@ class TestCQGI(BaseTest):
|
|||||||
model = cqgi.CQModel(TESTSCRIPT)
|
model = cqgi.CQModel(TESTSCRIPT)
|
||||||
metadata = model.metadata
|
metadata = model.metadata
|
||||||
|
|
||||||
self.assertEqual(set(metadata.parameters.keys()), {
|
self.assertEqual(
|
||||||
'height', 'width', 'a', 'b', 'foo'})
|
set(metadata.parameters.keys()), {"height", "width", "a", "b", "foo"}
|
||||||
|
)
|
||||||
|
|
||||||
def test_build_with_debug(self):
|
def test_build_with_debug(self):
|
||||||
model = cqgi.CQModel(TEST_DEBUG_SCRIPT)
|
model = cqgi.CQModel(TEST_DEBUG_SCRIPT)
|
||||||
@ -51,7 +52,7 @@ class TestCQGI(BaseTest):
|
|||||||
debugItems = result.debugObjects
|
debugItems = result.debugObjects
|
||||||
self.assertTrue(len(debugItems) == 2)
|
self.assertTrue(len(debugItems) == 2)
|
||||||
self.assertTrue(debugItems[0].shape == "bar")
|
self.assertTrue(debugItems[0].shape == "bar")
|
||||||
self.assertTrue(debugItems[0].options == {"color": 'yellow'})
|
self.assertTrue(debugItems[0].options == {"color": "yellow"})
|
||||||
self.assertTrue(debugItems[1].shape == 2.0)
|
self.assertTrue(debugItems[1].shape == 2.0)
|
||||||
self.assertTrue(debugItems[1].options == {})
|
self.assertTrue(debugItems[1].options == {})
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ class TestCQGI(BaseTest):
|
|||||||
|
|
||||||
def test_build_with_different_params(self):
|
def test_build_with_different_params(self):
|
||||||
model = cqgi.CQModel(TESTSCRIPT)
|
model = cqgi.CQModel(TESTSCRIPT)
|
||||||
result = model.build({'height': 3.0})
|
result = model.build({"height": 3.0})
|
||||||
self.assertTrue(result.results[0].shape == "3.0|3.0|bar|1.0")
|
self.assertTrue(result.results[0].shape == "3.0|3.0|bar|1.0")
|
||||||
|
|
||||||
def test_describe_parameters(self):
|
def test_describe_parameters(self):
|
||||||
@ -76,9 +77,9 @@ class TestCQGI(BaseTest):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
model = cqgi.CQModel(script)
|
model = cqgi.CQModel(script)
|
||||||
a_param = model.metadata.parameters['a']
|
a_param = model.metadata.parameters["a"]
|
||||||
self.assertTrue(a_param.default_value == 2.0)
|
self.assertTrue(a_param.default_value == 2.0)
|
||||||
self.assertTrue(a_param.desc == 'FirstLetter')
|
self.assertTrue(a_param.desc == "FirstLetter")
|
||||||
self.assertTrue(a_param.varType == cqgi.NumberParameterType)
|
self.assertTrue(a_param.varType == cqgi.NumberParameterType)
|
||||||
|
|
||||||
def test_describe_parameter_invalid_doesnt_fail_script(self):
|
def test_describe_parameter_invalid_doesnt_fail_script(self):
|
||||||
@ -89,8 +90,8 @@ class TestCQGI(BaseTest):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
model = cqgi.CQModel(script)
|
model = cqgi.CQModel(script)
|
||||||
a_param = model.metadata.parameters['a']
|
a_param = model.metadata.parameters["a"]
|
||||||
self.assertTrue(a_param.name == 'a')
|
self.assertTrue(a_param.name == "a")
|
||||||
|
|
||||||
def test_build_with_exception(self):
|
def test_build_with_exception(self):
|
||||||
badscript = textwrap.dedent(
|
badscript = textwrap.dedent(
|
||||||
@ -115,7 +116,7 @@ class TestCQGI(BaseTest):
|
|||||||
with self.assertRaises(Exception) as context:
|
with self.assertRaises(Exception) as context:
|
||||||
model = cqgi.CQModel(badscript)
|
model = cqgi.CQModel(badscript)
|
||||||
|
|
||||||
self.assertTrue('invalid syntax' in context.exception.args)
|
self.assertTrue("invalid syntax" in context.exception.args)
|
||||||
|
|
||||||
def test_that_two_results_are_returned(self):
|
def test_that_two_results_are_returned(self):
|
||||||
script = textwrap.dedent(
|
script = textwrap.dedent(
|
||||||
@ -140,7 +141,7 @@ class TestCQGI(BaseTest):
|
|||||||
show_object(h)
|
show_object(h)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = cqgi.parse(script).build({'h': 33.33})
|
result = cqgi.parse(script).build({"h": 33.33})
|
||||||
self.assertEqual(result.results[0].shape, "33.33")
|
self.assertEqual(result.results[0].shape, "33.33")
|
||||||
|
|
||||||
def test_that_assigning_string_to_number_fails(self):
|
def test_that_assigning_string_to_number_fails(self):
|
||||||
@ -150,9 +151,8 @@ class TestCQGI(BaseTest):
|
|||||||
show_object(h)
|
show_object(h)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = cqgi.parse(script).build({'h': "a string"})
|
result = cqgi.parse(script).build({"h": "a string"})
|
||||||
self.assertTrue(isinstance(result.exception,
|
self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
|
||||||
cqgi.InvalidParameterError))
|
|
||||||
|
|
||||||
def test_that_assigning_unknown_var_fails(self):
|
def test_that_assigning_unknown_var_fails(self):
|
||||||
script = textwrap.dedent(
|
script = textwrap.dedent(
|
||||||
@ -162,9 +162,8 @@ class TestCQGI(BaseTest):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
result = cqgi.parse(script).build({'w': "var is not there"})
|
result = cqgi.parse(script).build({"w": "var is not there"})
|
||||||
self.assertTrue(isinstance(result.exception,
|
self.assertTrue(isinstance(result.exception, cqgi.InvalidParameterError))
|
||||||
cqgi.InvalidParameterError))
|
|
||||||
|
|
||||||
def test_that_cq_objects_are_visible(self):
|
def test_that_cq_objects_are_visible(self):
|
||||||
script = textwrap.dedent(
|
script = textwrap.dedent(
|
||||||
@ -198,10 +197,10 @@ class TestCQGI(BaseTest):
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
result = cqgi.parse(script).build({'h': False})
|
result = cqgi.parse(script).build({"h": False})
|
||||||
|
|
||||||
self.assertTrue(result.success)
|
self.assertTrue(result.success)
|
||||||
self.assertEqual(result.first_result.shape, '*False*')
|
self.assertEqual(result.first_result.shape, "*False*")
|
||||||
|
|
||||||
def test_that_only_top_level_vars_are_detected(self):
|
def test_that_only_top_level_vars_are_detected(self):
|
||||||
script = textwrap.dedent(
|
script = textwrap.dedent(
|
||||||
|
@ -12,7 +12,6 @@ from tests import BaseTest
|
|||||||
|
|
||||||
|
|
||||||
class TestExporters(BaseTest):
|
class TestExporters(BaseTest):
|
||||||
|
|
||||||
def _exportBox(self, eType, stringsToFind):
|
def _exportBox(self, eType, stringsToFind):
|
||||||
"""
|
"""
|
||||||
Exports a test object, and then looks for
|
Exports a test object, and then looks for
|
||||||
@ -21,31 +20,32 @@ class TestExporters(BaseTest):
|
|||||||
"""
|
"""
|
||||||
p = Workplane("XY").box(1, 2, 3)
|
p = Workplane("XY").box(1, 2, 3)
|
||||||
|
|
||||||
if eType == exporters.ExportTypes.AMF:
|
if eType == exporters.ExportTypes.AMF:
|
||||||
s = io.BytesIO()
|
s = io.BytesIO()
|
||||||
else:
|
else:
|
||||||
s = io.StringIO()
|
s = io.StringIO()
|
||||||
|
|
||||||
exporters.exportShape(p, eType, s, 0.1)
|
exporters.exportShape(p, eType, s, 0.1)
|
||||||
|
|
||||||
result = '{}'.format(s.getvalue())
|
result = "{}".format(s.getvalue())
|
||||||
|
|
||||||
for q in stringsToFind:
|
for q in stringsToFind:
|
||||||
self.assertTrue(result.find(q) > -1)
|
self.assertTrue(result.find(q) > -1)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def testSTL(self):
|
def testSTL(self):
|
||||||
self._exportBox(exporters.ExportTypes.STL, ['facet normal'])
|
self._exportBox(exporters.ExportTypes.STL, ["facet normal"])
|
||||||
|
|
||||||
def testSVG(self):
|
def testSVG(self):
|
||||||
self._exportBox(exporters.ExportTypes.SVG, ['<svg', '<g transform'])
|
self._exportBox(exporters.ExportTypes.SVG, ["<svg", "<g transform"])
|
||||||
|
|
||||||
def testAMF(self):
|
def testAMF(self):
|
||||||
self._exportBox(exporters.ExportTypes.AMF, ['<amf units', '</object>'])
|
self._exportBox(exporters.ExportTypes.AMF, ["<amf units", "</object>"])
|
||||||
|
|
||||||
def testSTEP(self):
|
def testSTEP(self):
|
||||||
self._exportBox(exporters.ExportTypes.STEP, ['FILE_SCHEMA'])
|
self._exportBox(exporters.ExportTypes.STEP, ["FILE_SCHEMA"])
|
||||||
|
|
||||||
def testTJS(self):
|
def testTJS(self):
|
||||||
self._exportBox(exporters.ExportTypes.TJS, [
|
self._exportBox(
|
||||||
'vertices', 'formatVersion', 'faces'])
|
exporters.ExportTypes.TJS, ["vertices", "formatVersion", "faces"]
|
||||||
|
)
|
||||||
|
@ -36,12 +36,18 @@ class TestImporters(BaseTest):
|
|||||||
self.assertTrue(importedShape.val().ShapeType() == "Solid")
|
self.assertTrue(importedShape.val().ShapeType() == "Solid")
|
||||||
|
|
||||||
# Check the number of faces and vertices per face to make sure we have a box shape
|
# Check the number of faces and vertices per face to make sure we have a box shape
|
||||||
self.assertTrue(importedShape.faces("+X").size() ==
|
self.assertTrue(
|
||||||
1 and importedShape.faces("+X").vertices().size() == 4)
|
importedShape.faces("+X").size() == 1
|
||||||
self.assertTrue(importedShape.faces("+Y").size() ==
|
and importedShape.faces("+X").vertices().size() == 4
|
||||||
1 and importedShape.faces("+Y").vertices().size() == 4)
|
)
|
||||||
self.assertTrue(importedShape.faces("+Z").size() ==
|
self.assertTrue(
|
||||||
1 and importedShape.faces("+Z").vertices().size() == 4)
|
importedShape.faces("+Y").size() == 1
|
||||||
|
and importedShape.faces("+Y").vertices().size() == 4
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
importedShape.faces("+Z").size() == 1
|
||||||
|
and importedShape.faces("+Z").vertices().size() == 4
|
||||||
|
)
|
||||||
|
|
||||||
def testSTEP(self):
|
def testSTEP(self):
|
||||||
"""
|
"""
|
||||||
@ -55,7 +61,8 @@ class TestImporters(BaseTest):
|
|||||||
not segfault.
|
not segfault.
|
||||||
"""
|
"""
|
||||||
tmpfile = OUTDIR + "/badSTEP.step"
|
tmpfile = OUTDIR + "/badSTEP.step"
|
||||||
with open(tmpfile, 'w') as f: f.write("invalid STEP file")
|
with open(tmpfile, "w") as f:
|
||||||
|
f.write("invalid STEP file")
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
importers.importShape(importers.ImportTypes.STEP, tmpfile)
|
importers.importShape(importers.ImportTypes.STEP, tmpfile)
|
||||||
|
|
||||||
@ -69,6 +76,8 @@ class TestImporters(BaseTest):
|
|||||||
objs = importers.importShape(importers.ImportTypes.STEP, filename)
|
objs = importers.importShape(importers.ImportTypes.STEP, filename)
|
||||||
self.assertEqual(2, len(objs.all()))
|
self.assertEqual(2, len(objs.all()))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -2,9 +2,10 @@ from tests import BaseTest
|
|||||||
|
|
||||||
import cadquery
|
import cadquery
|
||||||
|
|
||||||
|
|
||||||
class TestJupyter(BaseTest):
|
class TestJupyter(BaseTest):
|
||||||
def test_repr_html(self):
|
def test_repr_html(self):
|
||||||
cube = cadquery.Workplane('XY').box(1, 1, 1)
|
cube = cadquery.Workplane("XY").box(1, 1, 1)
|
||||||
shape = cube.val()
|
shape = cube.val()
|
||||||
self.assertIsInstance(shape, cadquery.occ_impl.shapes.Solid)
|
self.assertIsInstance(shape, cadquery.occ_impl.shapes.Solid)
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
__author__ = 'dcowden'
|
__author__ = "dcowden"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Tests for CadQuery Selectors
|
Tests for CadQuery Selectors
|
||||||
@ -20,22 +20,19 @@ from cadquery import selectors
|
|||||||
|
|
||||||
|
|
||||||
class TestCQSelectors(BaseTest):
|
class TestCQSelectors(BaseTest):
|
||||||
|
|
||||||
def testWorkplaneCenter(self):
|
def testWorkplaneCenter(self):
|
||||||
"Test Moving workplane center"
|
"Test Moving workplane center"
|
||||||
s = Workplane(Plane.XY())
|
s = Workplane(Plane.XY())
|
||||||
|
|
||||||
# current point and world point should be equal
|
# current point and world point should be equal
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0.0, 0.0, 0.0), s.plane.origin.toTuple(), 3)
|
||||||
(0.0, 0.0, 0.0), s.plane.origin.toTuple(), 3)
|
|
||||||
|
|
||||||
# move origin and confirm center moves
|
# move origin and confirm center moves
|
||||||
s.center(-2.0, -2.0)
|
s.center(-2.0, -2.0)
|
||||||
|
|
||||||
# current point should be 0,0, but
|
# current point should be 0,0, but
|
||||||
|
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((-2.0, -2.0, 0.0), s.plane.origin.toTuple(), 3)
|
||||||
(-2.0, -2.0, 0.0), s.plane.origin.toTuple(), 3)
|
|
||||||
|
|
||||||
def testVertices(self):
|
def testVertices(self):
|
||||||
t = makeUnitSquareWire() # square box
|
t = makeUnitSquareWire() # square box
|
||||||
@ -43,8 +40,7 @@ class TestCQSelectors(BaseTest):
|
|||||||
|
|
||||||
self.assertEqual(4, c.vertices().size())
|
self.assertEqual(4, c.vertices().size())
|
||||||
self.assertEqual(4, c.edges().size())
|
self.assertEqual(4, c.edges().size())
|
||||||
self.assertEqual(0, c.vertices().edges().size()
|
self.assertEqual(0, c.vertices().edges().size()) # no edges on any vertices
|
||||||
) # no edges on any vertices
|
|
||||||
# but selecting all edges still yields all vertices
|
# but selecting all edges still yields all vertices
|
||||||
self.assertEqual(4, c.edges().vertices().size())
|
self.assertEqual(4, c.edges().vertices().size())
|
||||||
self.assertEqual(1, c.wires().size()) # just one wire
|
self.assertEqual(1, c.wires().size()) # just one wire
|
||||||
@ -71,8 +67,7 @@ class TestCQSelectors(BaseTest):
|
|||||||
def testFirst(self):
|
def testFirst(self):
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
self.assertEqual(type(c.vertices().first().val()), Vertex)
|
self.assertEqual(type(c.vertices().first().val()), Vertex)
|
||||||
self.assertEqual(
|
self.assertEqual(type(c.vertices().first().first().first().val()), Vertex)
|
||||||
type(c.vertices().first().first().first().val()), Vertex)
|
|
||||||
|
|
||||||
def testCompounds(self):
|
def testCompounds(self):
|
||||||
c = CQ(makeUnitSquareWire())
|
c = CQ(makeUnitSquareWire())
|
||||||
@ -99,11 +94,11 @@ class TestCQSelectors(BaseTest):
|
|||||||
def testFaceTypesFilter(self):
|
def testFaceTypesFilter(self):
|
||||||
"Filters by face type"
|
"Filters by face type"
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
self.assertEqual(c.faces().size(), c.faces('%PLANE').size())
|
self.assertEqual(c.faces().size(), c.faces("%PLANE").size())
|
||||||
self.assertEqual(c.faces().size(), c.faces('%plane').size())
|
self.assertEqual(c.faces().size(), c.faces("%plane").size())
|
||||||
self.assertEqual(0, c.faces('%sphere').size())
|
self.assertEqual(0, c.faces("%sphere").size())
|
||||||
self.assertEqual(0, c.faces('%cone').size())
|
self.assertEqual(0, c.faces("%cone").size())
|
||||||
self.assertEqual(0, c.faces('%SPHERE').size())
|
self.assertEqual(0, c.faces("%SPHERE").size())
|
||||||
|
|
||||||
def testPerpendicularDirFilter(self):
|
def testPerpendicularDirFilter(self):
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
@ -131,10 +126,12 @@ class TestCQSelectors(BaseTest):
|
|||||||
# faces parallel to Z axis
|
# faces parallel to Z axis
|
||||||
self.assertEqual(2, c.faces("|Z").size())
|
self.assertEqual(2, c.faces("|Z").size())
|
||||||
# TODO: provide short names for ParallelDirSelector
|
# TODO: provide short names for ParallelDirSelector
|
||||||
self.assertEqual(2, c.faces(selectors.ParallelDirSelector(
|
self.assertEqual(
|
||||||
Vector((0, 0, 1)))).size()) # same thing as above
|
2, c.faces(selectors.ParallelDirSelector(Vector((0, 0, 1)))).size()
|
||||||
self.assertEqual(2, c.faces(selectors.ParallelDirSelector(
|
) # same thing as above
|
||||||
Vector((0, 0, -1)))).size()) # same thing as above
|
self.assertEqual(
|
||||||
|
2, c.faces(selectors.ParallelDirSelector(Vector((0, 0, -1)))).size()
|
||||||
|
) # same thing as above
|
||||||
|
|
||||||
# just for fun, vertices on faces parallel to z
|
# just for fun, vertices on faces parallel to z
|
||||||
self.assertEqual(8, c.faces("|Z").vertices().size())
|
self.assertEqual(8, c.faces("|Z").vertices().size())
|
||||||
@ -178,97 +175,96 @@ class TestCQSelectors(BaseTest):
|
|||||||
self.assertEqual(4, len(el))
|
self.assertEqual(4, len(el))
|
||||||
|
|
||||||
def testNthDistance(self):
|
def testNthDistance(self):
|
||||||
c = Workplane('XY').pushPoints([(-2, 0), (2, 0)]).box(1, 1, 1)
|
c = Workplane("XY").pushPoints([(-2, 0), (2, 0)]).box(1, 1, 1)
|
||||||
|
|
||||||
# 2nd face
|
# 2nd face
|
||||||
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), 1)).val()
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), 1)).val()
|
||||||
self.assertAlmostEqual(val.Center().x, -1.5)
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
||||||
|
|
||||||
# 2nd face with inversed selection vector
|
# 2nd face with inversed selection vector
|
||||||
val = c.faces(selectors.DirectionNthSelector(
|
val = c.faces(selectors.DirectionNthSelector(Vector(-1, 0, 0), 1)).val()
|
||||||
Vector(-1, 0, 0), 1)).val()
|
|
||||||
self.assertAlmostEqual(val.Center().x, 1.5)
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
||||||
|
|
||||||
# 2nd last face
|
# 2nd last face
|
||||||
val = c.faces(selectors.DirectionNthSelector(
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), -2)).val()
|
||||||
Vector(1, 0, 0), -2)).val()
|
|
||||||
self.assertAlmostEqual(val.Center().x, 1.5)
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
||||||
|
|
||||||
# Last face
|
# Last face
|
||||||
val = c.faces(selectors.DirectionNthSelector(
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), -1)).val()
|
||||||
Vector(1, 0, 0), -1)).val()
|
|
||||||
self.assertAlmostEqual(val.Center().x, 2.5)
|
self.assertAlmostEqual(val.Center().x, 2.5)
|
||||||
|
|
||||||
# check if the selected face if normal to the specified Vector
|
# check if the selected face if normal to the specified Vector
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
||||||
val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
|
||||||
|
|
||||||
# repeat the test using string based selector
|
# repeat the test using string based selector
|
||||||
|
|
||||||
# 2nd face
|
# 2nd face
|
||||||
val = c.faces('>(1,0,0)[1]').val()
|
val = c.faces(">(1,0,0)[1]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, -1.5)
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
||||||
val = c.faces('>X[1]').val()
|
val = c.faces(">X[1]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, -1.5)
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
||||||
|
|
||||||
# 2nd face with inversed selection vector
|
# 2nd face with inversed selection vector
|
||||||
val = c.faces('>(-1,0,0)[1]').val()
|
val = c.faces(">(-1,0,0)[1]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, 1.5)
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
||||||
val = c.faces('<X[1]').val()
|
val = c.faces("<X[1]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, 1.5)
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
||||||
|
|
||||||
# 2nd last face
|
# 2nd last face
|
||||||
val = c.faces('>X[-2]').val()
|
val = c.faces(">X[-2]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, 1.5)
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
||||||
|
|
||||||
# Last face
|
# Last face
|
||||||
val = c.faces('>X[-1]').val()
|
val = c.faces(">X[-1]").val()
|
||||||
self.assertAlmostEqual(val.Center().x, 2.5)
|
self.assertAlmostEqual(val.Center().x, 2.5)
|
||||||
|
|
||||||
# check if the selected face if normal to the specified Vector
|
# check if the selected face if normal to the specified Vector
|
||||||
self.assertAlmostEqual(
|
self.assertAlmostEqual(val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
||||||
val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
|
||||||
|
|
||||||
# test selection of multiple faces with the same distance
|
# test selection of multiple faces with the same distance
|
||||||
c = Workplane('XY')\
|
c = (
|
||||||
.box(1, 4, 1, centered=(False, True, False)).faces('<Z')\
|
Workplane("XY")
|
||||||
.box(2, 2, 2, centered=(True, True, False)).faces('>Z')\
|
.box(1, 4, 1, centered=(False, True, False))
|
||||||
|
.faces("<Z")
|
||||||
|
.box(2, 2, 2, centered=(True, True, False))
|
||||||
|
.faces(">Z")
|
||||||
.box(1, 1, 1, centered=(True, True, False))
|
.box(1, 1, 1, centered=(True, True, False))
|
||||||
|
)
|
||||||
|
|
||||||
# select 2nd from the bottom (NB python indexing is 0-based)
|
# select 2nd from the bottom (NB python indexing is 0-based)
|
||||||
vals = c.faces('>Z[1]').vals()
|
vals = c.faces(">Z[1]").vals()
|
||||||
self.assertEqual(len(vals), 2)
|
self.assertEqual(len(vals), 2)
|
||||||
|
|
||||||
val = c.faces('>Z[1]').val()
|
val = c.faces(">Z[1]").val()
|
||||||
self.assertAlmostEqual(val.Center().z, 1)
|
self.assertAlmostEqual(val.Center().z, 1)
|
||||||
|
|
||||||
# do the same but by selecting 3rd from the top
|
# do the same but by selecting 3rd from the top
|
||||||
vals = c.faces('<Z[2]').vals()
|
vals = c.faces("<Z[2]").vals()
|
||||||
self.assertEqual(len(vals), 2)
|
self.assertEqual(len(vals), 2)
|
||||||
|
|
||||||
val = c.faces('<Z[2]').val()
|
val = c.faces("<Z[2]").val()
|
||||||
self.assertAlmostEqual(val.Center().z, 1)
|
self.assertAlmostEqual(val.Center().z, 1)
|
||||||
|
|
||||||
# do the same but by selecting 2nd last from the bottom
|
# do the same but by selecting 2nd last from the bottom
|
||||||
vals = c.faces('<Z[-2]').vals()
|
vals = c.faces("<Z[-2]").vals()
|
||||||
self.assertEqual(len(vals), 2)
|
self.assertEqual(len(vals), 2)
|
||||||
|
|
||||||
val = c.faces('<Z[-2]').val()
|
val = c.faces("<Z[-2]").val()
|
||||||
self.assertAlmostEqual(val.Center().z, 1)
|
self.assertAlmostEqual(val.Center().z, 1)
|
||||||
|
|
||||||
# verify that <Z[-1] is equivalent to <Z
|
# verify that <Z[-1] is equivalent to <Z
|
||||||
val1 = c.faces('<Z[-1]').val()
|
val1 = c.faces("<Z[-1]").val()
|
||||||
val2 = c.faces('<Z').val()
|
val2 = c.faces("<Z").val()
|
||||||
self.assertTupleAlmostEquals(val1.Center().toTuple(),
|
self.assertTupleAlmostEquals(
|
||||||
val2.Center().toTuple(),
|
val1.Center().toTuple(), val2.Center().toTuple(), 3
|
||||||
3)
|
)
|
||||||
|
|
||||||
# verify that >Z[-1] is equivalent to >Z
|
# verify that >Z[-1] is equivalent to >Z
|
||||||
val1 = c.faces('>Z[-1]').val()
|
val1 = c.faces(">Z[-1]").val()
|
||||||
val2 = c.faces('>Z').val()
|
val2 = c.faces(">Z").val()
|
||||||
self.assertTupleAlmostEquals(val1.Center().toTuple(),
|
self.assertTupleAlmostEquals(
|
||||||
val2.Center().toTuple(),
|
val1.Center().toTuple(), val2.Center().toTuple(), 3
|
||||||
3)
|
)
|
||||||
|
|
||||||
def testNearestTo(self):
|
def testNearestTo(self):
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
@ -302,7 +298,7 @@ class TestCQSelectors(BaseTest):
|
|||||||
((0.9, -0.1, -0.1), (1.1, 0.1, 0.1), (1.0, 0.0, 0.0)),
|
((0.9, -0.1, -0.1), (1.1, 0.1, 0.1), (1.0, 0.0, 0.0)),
|
||||||
((0.9, 0.9, -0.1), (1.1, 1.1, 0.1), (1.0, 1.0, 0.0)),
|
((0.9, 0.9, -0.1), (1.1, 1.1, 0.1), (1.0, 1.0, 0.0)),
|
||||||
((-0.1, 0.9, -0.1), (0.1, 1.1, 0.1), (0.0, 1.0, 0.0)),
|
((-0.1, 0.9, -0.1), (0.1, 1.1, 0.1), (0.0, 1.0, 0.0)),
|
||||||
((0.9, -0.1, 0.9), (1.1, 0.1, 1.1), (1.0, 0.0, 1.0))
|
((0.9, -0.1, 0.9), (1.1, 0.1, 1.1), (1.0, 0.0, 1.0)),
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in test_data_vertices:
|
for d in test_data_vertices:
|
||||||
@ -318,11 +314,13 @@ class TestCQSelectors(BaseTest):
|
|||||||
self.assertTupleAlmostEquals(d[2], (v.X, v.Y, v.Z), 3)
|
self.assertTupleAlmostEquals(d[2], (v.X, v.Y, v.Z), 3)
|
||||||
|
|
||||||
# test multiple vertices selection
|
# test multiple vertices selection
|
||||||
vl = c.vertices(selectors.BoxSelector(
|
vl = c.vertices(
|
||||||
(-0.1, -0.1, 0.9), (0.1, 1.1, 1.1))).vals()
|
selectors.BoxSelector((-0.1, -0.1, 0.9), (0.1, 1.1, 1.1))
|
||||||
|
).vals()
|
||||||
self.assertEqual(2, len(vl))
|
self.assertEqual(2, len(vl))
|
||||||
vl = c.vertices(selectors.BoxSelector(
|
vl = c.vertices(
|
||||||
(-0.1, -0.1, -0.1), (0.1, 1.1, 1.1))).vals()
|
selectors.BoxSelector((-0.1, -0.1, -0.1), (0.1, 1.1, 1.1))
|
||||||
|
).vals()
|
||||||
self.assertEqual(4, len(vl))
|
self.assertEqual(4, len(vl))
|
||||||
|
|
||||||
# test edge selection
|
# test edge selection
|
||||||
@ -331,7 +329,7 @@ class TestCQSelectors(BaseTest):
|
|||||||
((0.4, -0.1, -0.1), (0.6, 0.1, 0.1), (0.5, 0.0, 0.0)),
|
((0.4, -0.1, -0.1), (0.6, 0.1, 0.1), (0.5, 0.0, 0.0)),
|
||||||
((-0.1, -0.1, 0.4), (0.1, 0.1, 0.6), (0.0, 0.0, 0.5)),
|
((-0.1, -0.1, 0.4), (0.1, 0.1, 0.6), (0.0, 0.0, 0.5)),
|
||||||
((0.9, 0.9, 0.4), (1.1, 1.1, 0.6), (1.0, 1.0, 0.5)),
|
((0.9, 0.9, 0.4), (1.1, 1.1, 0.6), (1.0, 1.0, 0.5)),
|
||||||
((0.4, 0.9, 0.9), (0.6, 1.1, 1.1,), (0.5, 1.0, 1.0))
|
((0.4, 0.9, 0.9), (0.6, 1.1, 1.1,), (0.5, 1.0, 1.0)),
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in test_data_edges:
|
for d in test_data_edges:
|
||||||
@ -347,11 +345,9 @@ class TestCQSelectors(BaseTest):
|
|||||||
self.assertTupleAlmostEquals(d[2], (ec.x, ec.y, ec.z), 3)
|
self.assertTupleAlmostEquals(d[2], (ec.x, ec.y, ec.z), 3)
|
||||||
|
|
||||||
# test multiple edge selection
|
# test multiple edge selection
|
||||||
el = c.edges(selectors.BoxSelector(
|
el = c.edges(selectors.BoxSelector((-0.1, -0.1, -0.1), (0.6, 0.1, 0.6))).vals()
|
||||||
(-0.1, -0.1, -0.1), (0.6, 0.1, 0.6))).vals()
|
|
||||||
self.assertEqual(2, len(el))
|
self.assertEqual(2, len(el))
|
||||||
el = c.edges(selectors.BoxSelector(
|
el = c.edges(selectors.BoxSelector((-0.1, -0.1, -0.1), (1.1, 0.1, 0.6))).vals()
|
||||||
(-0.1, -0.1, -0.1), (1.1, 0.1, 0.6))).vals()
|
|
||||||
self.assertEqual(3, len(el))
|
self.assertEqual(3, len(el))
|
||||||
|
|
||||||
# test face selection
|
# test face selection
|
||||||
@ -360,7 +356,7 @@ class TestCQSelectors(BaseTest):
|
|||||||
((0.4, -0.1, 0.4), (0.6, 0.1, 0.6), (0.5, 0.0, 0.5)),
|
((0.4, -0.1, 0.4), (0.6, 0.1, 0.6), (0.5, 0.0, 0.5)),
|
||||||
((0.9, 0.4, 0.4), (1.1, 0.6, 0.6), (1.0, 0.5, 0.5)),
|
((0.9, 0.4, 0.4), (1.1, 0.6, 0.6), (1.0, 0.5, 0.5)),
|
||||||
((0.4, 0.4, 0.9), (0.6, 0.6, 1.1), (0.5, 0.5, 1.0)),
|
((0.4, 0.4, 0.9), (0.6, 0.6, 1.1), (0.5, 0.5, 1.0)),
|
||||||
((0.4, 0.4, -0.1), (0.6, 0.6, 0.1), (0.5, 0.5, 0.0))
|
((0.4, 0.4, -0.1), (0.6, 0.6, 0.1), (0.5, 0.5, 0.0)),
|
||||||
]
|
]
|
||||||
|
|
||||||
for d in test_data_faces:
|
for d in test_data_faces:
|
||||||
@ -376,22 +372,23 @@ class TestCQSelectors(BaseTest):
|
|||||||
self.assertTupleAlmostEquals(d[2], (fc.x, fc.y, fc.z), 3)
|
self.assertTupleAlmostEquals(d[2], (fc.x, fc.y, fc.z), 3)
|
||||||
|
|
||||||
# test multiple face selection
|
# test multiple face selection
|
||||||
fl = c.faces(selectors.BoxSelector(
|
fl = c.faces(selectors.BoxSelector((0.4, 0.4, 0.4), (0.6, 1.1, 1.1))).vals()
|
||||||
(0.4, 0.4, 0.4), (0.6, 1.1, 1.1))).vals()
|
|
||||||
self.assertEqual(2, len(fl))
|
self.assertEqual(2, len(fl))
|
||||||
fl = c.faces(selectors.BoxSelector(
|
fl = c.faces(selectors.BoxSelector((0.4, 0.4, 0.4), (1.1, 1.1, 1.1))).vals()
|
||||||
(0.4, 0.4, 0.4), (1.1, 1.1, 1.1))).vals()
|
|
||||||
self.assertEqual(3, len(fl))
|
self.assertEqual(3, len(fl))
|
||||||
|
|
||||||
# test boundingbox option
|
# test boundingbox option
|
||||||
el = c.edges(selectors.BoxSelector(
|
el = c.edges(
|
||||||
(-0.1, -0.1, -0.1), (1.1, 0.1, 0.6), True)).vals()
|
selectors.BoxSelector((-0.1, -0.1, -0.1), (1.1, 0.1, 0.6), True)
|
||||||
|
).vals()
|
||||||
self.assertEqual(1, len(el))
|
self.assertEqual(1, len(el))
|
||||||
fl = c.faces(selectors.BoxSelector(
|
fl = c.faces(
|
||||||
(0.4, 0.4, 0.4), (1.1, 1.1, 1.1), True)).vals()
|
selectors.BoxSelector((0.4, 0.4, 0.4), (1.1, 1.1, 1.1), True)
|
||||||
|
).vals()
|
||||||
self.assertEqual(0, len(fl))
|
self.assertEqual(0, len(fl))
|
||||||
fl = c.faces(selectors.BoxSelector(
|
fl = c.faces(
|
||||||
(-0.1, 0.4, -0.1), (1.1, 1.1, 1.1), True)).vals()
|
selectors.BoxSelector((-0.1, 0.4, -0.1), (1.1, 1.1, 1.1), True)
|
||||||
|
).vals()
|
||||||
self.assertEqual(1, len(fl))
|
self.assertEqual(1, len(fl))
|
||||||
|
|
||||||
def testAndSelector(self):
|
def testAndSelector(self):
|
||||||
@ -400,12 +397,13 @@ class TestCQSelectors(BaseTest):
|
|||||||
S = selectors.StringSyntaxSelector
|
S = selectors.StringSyntaxSelector
|
||||||
BS = selectors.BoxSelector
|
BS = selectors.BoxSelector
|
||||||
|
|
||||||
el = c.edges(selectors.AndSelector(
|
el = c.edges(
|
||||||
S('|X'), BS((-2, -2, 0.1), (2, 2, 2)))).vals()
|
selectors.AndSelector(S("|X"), BS((-2, -2, 0.1), (2, 2, 2)))
|
||||||
|
).vals()
|
||||||
self.assertEqual(2, len(el))
|
self.assertEqual(2, len(el))
|
||||||
|
|
||||||
# test 'and' (intersection) operator
|
# test 'and' (intersection) operator
|
||||||
el = c.edges(S('|X') & BS((-2, -2, 0.1), (2, 2, 2))).vals()
|
el = c.edges(S("|X") & BS((-2, -2, 0.1), (2, 2, 2))).vals()
|
||||||
self.assertEqual(2, len(el))
|
self.assertEqual(2, len(el))
|
||||||
|
|
||||||
# test using extended string syntax
|
# test using extended string syntax
|
||||||
@ -455,27 +453,27 @@ class TestCQSelectors(BaseTest):
|
|||||||
|
|
||||||
S = selectors.StringSyntaxSelector
|
S = selectors.StringSyntaxSelector
|
||||||
|
|
||||||
fl = c.faces(selectors.InverseSelector(S('>Z'))).vals()
|
fl = c.faces(selectors.InverseSelector(S(">Z"))).vals()
|
||||||
self.assertEqual(5, len(fl))
|
self.assertEqual(5, len(fl))
|
||||||
el = c.faces('>Z').edges(selectors.InverseSelector(S('>X'))).vals()
|
el = c.faces(">Z").edges(selectors.InverseSelector(S(">X"))).vals()
|
||||||
self.assertEqual(3, len(el))
|
self.assertEqual(3, len(el))
|
||||||
|
|
||||||
# test invert operator
|
# test invert operator
|
||||||
fl = c.faces(-S('>Z')).vals()
|
fl = c.faces(-S(">Z")).vals()
|
||||||
self.assertEqual(5, len(fl))
|
self.assertEqual(5, len(fl))
|
||||||
el = c.faces('>Z').edges(-S('>X')).vals()
|
el = c.faces(">Z").edges(-S(">X")).vals()
|
||||||
self.assertEqual(3, len(el))
|
self.assertEqual(3, len(el))
|
||||||
|
|
||||||
# test using extended string syntax
|
# test using extended string syntax
|
||||||
fl = c.faces('not >Z').vals()
|
fl = c.faces("not >Z").vals()
|
||||||
self.assertEqual(5, len(fl))
|
self.assertEqual(5, len(fl))
|
||||||
el = c.faces('>Z').edges('not >X').vals()
|
el = c.faces(">Z").edges("not >X").vals()
|
||||||
self.assertEqual(3, len(el))
|
self.assertEqual(3, len(el))
|
||||||
|
|
||||||
def testComplexStringSelector(self):
|
def testComplexStringSelector(self):
|
||||||
c = CQ(makeUnitCube())
|
c = CQ(makeUnitCube())
|
||||||
|
|
||||||
v = c.vertices('(>X and >Y) or (<X and <Y)').vals()
|
v = c.vertices("(>X and >Y) or (<X and <Y)").vals()
|
||||||
self.assertEqual(4, len(v))
|
self.assertEqual(4, len(v))
|
||||||
|
|
||||||
def testFaceCount(self):
|
def testFaceCount(self):
|
||||||
@ -503,25 +501,27 @@ class TestCQSelectors(BaseTest):
|
|||||||
|
|
||||||
gram = selectors._expression_grammar
|
gram = selectors._expression_grammar
|
||||||
|
|
||||||
expressions = ['+X ',
|
expressions = [
|
||||||
'-Y',
|
"+X ",
|
||||||
'|(1,0,0)',
|
"-Y",
|
||||||
'#(1.,1.4114,-0.532)',
|
"|(1,0,0)",
|
||||||
'%Plane',
|
"#(1.,1.4114,-0.532)",
|
||||||
'>XZ',
|
"%Plane",
|
||||||
'<Z[-2]',
|
">XZ",
|
||||||
'>(1,4,55.)[20]',
|
"<Z[-2]",
|
||||||
'|XY',
|
">(1,4,55.)[20]",
|
||||||
'<YZ[0]',
|
"|XY",
|
||||||
'front',
|
"<YZ[0]",
|
||||||
'back',
|
"front",
|
||||||
'left',
|
"back",
|
||||||
'right',
|
"left",
|
||||||
'top',
|
"right",
|
||||||
'bottom',
|
"top",
|
||||||
'not |(1,1,0) and >(0,0,1) or XY except >(1,1,1)[-1]',
|
"bottom",
|
||||||
'(not |(1,1,0) and >(0,0,1)) exc XY and (Z or X)',
|
"not |(1,1,0) and >(0,0,1) or XY except >(1,1,1)[-1]",
|
||||||
'not ( <X or >X or <Y or >Y )']
|
"(not |(1,1,0) and >(0,0,1)) exc XY and (Z or X)",
|
||||||
|
"not ( <X or >X or <Y or >Y )",
|
||||||
|
]
|
||||||
|
|
||||||
for e in expressions:
|
for e in expressions:
|
||||||
gram.parseString(e, parseAll=True)
|
gram.parseString(e, parseAll=True)
|
||||||
|
@ -16,65 +16,70 @@ zInvAxis_ = Vector(0, 0, -1)
|
|||||||
|
|
||||||
|
|
||||||
class TestWorkplanes(BaseTest):
|
class TestWorkplanes(BaseTest):
|
||||||
|
|
||||||
def testYZPlaneOrigins(self):
|
def testYZPlaneOrigins(self):
|
||||||
# xy plane-- with origin at x=0.25
|
# xy plane-- with origin at x=0.25
|
||||||
base = Vector(0.25, 0, 0)
|
base = Vector(0.25, 0, 0)
|
||||||
p = Plane(base, Vector(0, 1, 0), Vector(1, 0, 0))
|
p = Plane(base, Vector(0, 1, 0), Vector(1, 0, 0))
|
||||||
|
|
||||||
# origin is always (0,0,0) in local coordinates
|
# origin is always (0,0,0) in local coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
||||||
(0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
|
||||||
|
|
||||||
#(0,0,0) is always the original base in global coordinates
|
# (0,0,0) is always the original base in global coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
base.toTuple(), p.toWorldCoords((0, 0)).toTuple(), 2)
|
base.toTuple(), p.toWorldCoords((0, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
def testXYPlaneOrigins(self):
|
def testXYPlaneOrigins(self):
|
||||||
base = Vector(0, 0, 0.25)
|
base = Vector(0, 0, 0.25)
|
||||||
p = Plane(base, Vector(1, 0, 0), Vector(0, 0, 1))
|
p = Plane(base, Vector(1, 0, 0), Vector(0, 0, 1))
|
||||||
|
|
||||||
# origin is always (0,0,0) in local coordinates
|
# origin is always (0,0,0) in local coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
||||||
(0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
|
||||||
|
|
||||||
#(0,0,0) is always the original base in global coordinates
|
# (0,0,0) is always the original base in global coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2)
|
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
def testXZPlaneOrigins(self):
|
def testXZPlaneOrigins(self):
|
||||||
base = Vector(0, 0.25, 0)
|
base = Vector(0, 0.25, 0)
|
||||||
p = Plane(base, Vector(0, 0, 1), Vector(0, 1, 0))
|
p = Plane(base, Vector(0, 0, 1), Vector(0, 1, 0))
|
||||||
|
|
||||||
#(0,0,0) is always the original base in global coordinates
|
# (0,0,0) is always the original base in global coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2)
|
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
# origin is always (0,0,0) in local coordinates
|
# origin is always (0,0,0) in local coordinates
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
||||||
(0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
|
|
||||||
|
|
||||||
def testPlaneBasics(self):
|
def testPlaneBasics(self):
|
||||||
p = Plane.XY()
|
p = Plane.XY()
|
||||||
# local to world
|
# local to world
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(1.0, 1.0, 0), p.toWorldCoords((1, 1)).toTuple(), 2)
|
(1.0, 1.0, 0), p.toWorldCoords((1, 1)).toTuple(), 2
|
||||||
|
)
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(-1.0, -1.0, 0), p.toWorldCoords((-1, -1)).toTuple(), 2)
|
(-1.0, -1.0, 0), p.toWorldCoords((-1, -1)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
# world to local
|
# world to local
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(-1.0, -1.0), p.toLocalCoords(Vector(-1, -1, 0)).toTuple(), 2)
|
(-1.0, -1.0), p.toLocalCoords(Vector(-1, -1, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(1.0, 1.0), p.toLocalCoords(Vector(1, 1, 0)).toTuple(), 2)
|
(1.0, 1.0), p.toLocalCoords(Vector(1, 1, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
p = Plane.YZ()
|
p = Plane.YZ()
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(0, 1.0, 1.0), p.toWorldCoords((1, 1)).toTuple(), 2)
|
(0, 1.0, 1.0), p.toWorldCoords((1, 1)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
# world to local
|
# world to local
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(1.0, 1.0), p.toLocalCoords(Vector(0, 1, 1)).toTuple(), 2)
|
(1.0, 1.0), p.toLocalCoords(Vector(0, 1, 1)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
p = Plane.XZ()
|
p = Plane.XZ()
|
||||||
r = p.toWorldCoords((1, 1)).toTuple()
|
r = p.toWorldCoords((1, 1)).toTuple()
|
||||||
@ -82,62 +87,68 @@ class TestWorkplanes(BaseTest):
|
|||||||
|
|
||||||
# world to local
|
# world to local
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(1.0, 1.0), p.toLocalCoords(Vector(1, 0, 1)).toTuple(), 2)
|
(1.0, 1.0), p.toLocalCoords(Vector(1, 0, 1)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
def testOffsetPlanes(self):
|
def testOffsetPlanes(self):
|
||||||
"Tests that a plane offset from the origin works ok too"
|
"Tests that a plane offset from the origin works ok too"
|
||||||
p = Plane.XY(origin=(10.0, 10.0, 0))
|
p = Plane.XY(origin=(10.0, 10.0, 0))
|
||||||
|
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(11.0, 11.0, 0.0), p.toWorldCoords((1.0, 1.0)).toTuple(), 2)
|
(11.0, 11.0, 0.0), p.toWorldCoords((1.0, 1.0)).toTuple(), 2
|
||||||
self.assertTupleAlmostEquals((2.0, 2.0), p.toLocalCoords(
|
)
|
||||||
Vector(12.0, 12.0, 0)).toTuple(), 2)
|
self.assertTupleAlmostEquals(
|
||||||
|
(2.0, 2.0), p.toLocalCoords(Vector(12.0, 12.0, 0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
# TODO test these offsets in the other dimensions too
|
# TODO test these offsets in the other dimensions too
|
||||||
p = Plane.YZ(origin=(0, 2, 2))
|
p = Plane.YZ(origin=(0, 2, 2))
|
||||||
self.assertTupleAlmostEquals(
|
self.assertTupleAlmostEquals(
|
||||||
(0.0, 5.0, 5.0), p.toWorldCoords((3.0, 3.0)).toTuple(), 2)
|
(0.0, 5.0, 5.0), p.toWorldCoords((3.0, 3.0)).toTuple(), 2
|
||||||
self.assertTupleAlmostEquals((10, 10.0, 0.0), p.toLocalCoords(
|
)
|
||||||
Vector(0.0, 12.0, 12.0)).toTuple(), 2)
|
self.assertTupleAlmostEquals(
|
||||||
|
(10, 10.0, 0.0), p.toLocalCoords(Vector(0.0, 12.0, 12.0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
p = Plane.XZ(origin=(2, 0, 2))
|
p = Plane.XZ(origin=(2, 0, 2))
|
||||||
r = p.toWorldCoords((1.0, 1.0)).toTuple()
|
r = p.toWorldCoords((1.0, 1.0)).toTuple()
|
||||||
self.assertTupleAlmostEquals((3.0, 0.0, 3.0), r, 2)
|
self.assertTupleAlmostEquals((3.0, 0.0, 3.0), r, 2)
|
||||||
self.assertTupleAlmostEquals((10.0, 10.0), p.toLocalCoords(
|
self.assertTupleAlmostEquals(
|
||||||
Vector(12.0, 0.0, 12.0)).toTuple(), 2)
|
(10.0, 10.0), p.toLocalCoords(Vector(12.0, 0.0, 12.0)).toTuple(), 2
|
||||||
|
)
|
||||||
|
|
||||||
def testXYPlaneBasics(self):
|
def testXYPlaneBasics(self):
|
||||||
p = Plane.named('XY')
|
p = Plane.named("XY")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), zAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), zAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
|
||||||
|
|
||||||
def testYZPlaneBasics(self):
|
def testYZPlaneBasics(self):
|
||||||
p = Plane.named('YZ')
|
p = Plane.named("YZ")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), xAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), xAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
|
||||||
|
|
||||||
def testZXPlaneBasics(self):
|
def testZXPlaneBasics(self):
|
||||||
p = Plane.named('ZX')
|
p = Plane.named("ZX")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), yAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), yAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
|
||||||
|
|
||||||
def testXZPlaneBasics(self):
|
def testXZPlaneBasics(self):
|
||||||
p = Plane.named('XZ')
|
p = Plane.named("XZ")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), yInvAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), yInvAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
|
||||||
|
|
||||||
def testYXPlaneBasics(self):
|
def testYXPlaneBasics(self):
|
||||||
p = Plane.named('YX')
|
p = Plane.named("YX")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), zInvAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), zInvAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
|
||||||
|
|
||||||
def testZYPlaneBasics(self):
|
def testZYPlaneBasics(self):
|
||||||
p = Plane.named('ZY')
|
p = Plane.named("ZY")
|
||||||
self.assertTupleAlmostEquals(p.zDir.toTuple(), xInvAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.zDir.toTuple(), xInvAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
|
||||||
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
|
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
|
||||||
|
Reference in New Issue
Block a user