cqparts.utils package¶
Submodules¶
cqparts.utils.geometry module¶
-
class
cqparts.utils.geometry.
CoordSystem
(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1))¶ Bases:
cadquery.freecad_impl.geom.Plane
Defines the location, and rotation of an orthogonal 3 dimensional coordinate system.
-
__add__
(other)¶ For
A
+B
. WhereA
is this coordinate system, andB
isother
.Raises: TypeError – if addition for the given type is not supported Supported types:
A
(CoordSystem
) +B
(CoordSystem
):Returns: world coordinates of B
inA
’s coordinatesReturn type: CoordSystem
A
(CoordSystem
) +B
(cadquery.Vector
):Returns: world coordinates of B
represented inA
’s coordinate systemReturn type: cadquery.Vector
A
(CoordSystem
) +B
(cadquery.CQ
):remember:
cadquery.Workplane
inherits fromcadquery.CQ
Returns: content of B
moved toA
’s coordinate systemReturn type: cadquery.Workplane
-
__sub__
(other)¶ For
A
-B
. WhereA
is this coordinate system, andB
isother
.Raises: TypeError – if subtraction for the given type is not supported Supported types:
A
(CoordSystem
) +B
(CoordSystem
):Returns: local coordinate system of A
fromB
’s coordinate systemReturn type: CoordSystem
-
classmethod
from_plane
(plane)¶ Parameters: plane ( cadquery.Plane
) – cadquery plane instance to base coordinate system onReturns: duplicate of the given plane, in this class Return type: CoordSystem
usage example:
>>> import cadquery >>> from cqparts.utils.geometry import CoordSystem >>> obj = cadquery.Workplane('XY').circle(1).extrude(5) >>> plane = obj.faces(">Z").workplane().plane >>> isinstance(plane, cadquery.Plane) True >>> coord_sys = CoordSystem.from_plane(plane) >>> isinstance(coord_sys, CoordSystem) True >>> coord_sys.origin.z 5.0
-
classmethod
from_transform
(matrix)¶ Parameters: matrix ( FreeCAD.Matrix
) – 4x4 3d affine transform matrixReturns: a unit, zero offset coordinate system transformed by the given matrix Return type: CoordSystem
Individual rotation & translation matricies are:
\[\begin{split}R_z & = \begin{bmatrix} cos(\alpha) & -sin(\alpha) & 0 & 0 \\ sin(\alpha) & cos(\alpha) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \qquad & R_y & = \begin{bmatrix} cos(\beta) & 0 & sin(\beta) & 0 \\ 0 & 1 & 0 & 0 \\ -sin(\beta) & 0 & cos(\beta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \\ \\ R_x & = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos(\gamma) & -sin(\gamma) & 0 \\ 0 & sin(\gamma) & cos(\gamma) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \qquad & T_{\text{xyz}} & = \begin{bmatrix} 1 & 0 & 0 & \delta x \\ 0 & 1 & 0 & \delta y \\ 0 & 0 & 1 & \delta z \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{split}\]The
transform
is the combination of these:\[\begin{split}transform = T_{\text{xyz}} \cdot R_z \cdot R_y \cdot R_x = \begin{bmatrix} a & b & c & \delta x \\ d & e & f & \delta y \\ g & h & i & \delta z \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{split}\]Where:
\[\begin{split}a & = cos(\alpha) cos(\beta) \\ b & = cos(\alpha) sin(\beta) sin(\gamma) - sin(\alpha) cos(\gamma) \\ c & = cos(\alpha) sin(\beta) cos(\gamma) + sin(\alpha) sin(\gamma) \\ d & = sin(\alpha) cos(\beta) \\ e & = sin(\alpha) sin(\beta) sin(\gamma) + cos(\alpha) cos(\gamma) \\ f & = sin(\alpha) sin(\beta) cos(\gamma) - cos(\alpha) sin(\gamma) \\ g & = -sin(\beta) \\ h & = cos(\beta) sin(\gamma) \\ i & = cos(\beta) cos(\gamma)\end{split}\]
-
local_to_world_transform
¶ Returns: 3d affine transform matrix to convert local coordinates to world coordinates. Return type: cadquery.Matrix
For matrix structure, see
from_transform()
.
-
classmethod
random
(span=1, seed=None)¶ Creates a randomized coordinate system.
Useful for confirming that an assembly does not rely on its origin coordinate system to remain intact.
For example, the
CoordSysIndicator
assembly aligns 3 boxes along each of the \(XYZ\) axes. Positioning it randomly by setting itsworld_coords
shows that each box is always positioned orthogonally to the other two.from cqparts_misc.basic.indicators import CoordSysIndicator from cqparts.display import display from cqparts.utils import CoordSystem cs = CoordSysIndicator() cs.world_coords = CoordSystem.random() display(cs)
Parameters: - span – origin of return will be \(\pm span\) per axis
- seed (hashable object) – if supplied, return is psudorandom (repeatable)
Returns: randomized coordinate system
Return type:
-
world_to_local_transform
¶ Returns: 3d affine transform matrix to convert world coordinates to local coordinates. Return type: cadquery.Matrix
For matrix structure, see
from_transform()
.
-
cqparts.utils.misc module¶
-
cqparts.utils.misc.
indicate_last
(items)¶ iterate through list and indicate which item is the last, intended to assist tree displays of hierarchical content.
Returns: yielding (<bool>, <item>) where bool is True only on last entry Return type: generator
-
cqparts.utils.misc.
measure_time
(*args, **kwds)¶
-
class
cqparts.utils.misc.
property_buffered
(getter, name=None)¶ Bases:
object
Buffer the result of a method on the class instance, similar to python builtin
@property
, but the result is kept in memory until it’s explicitly deleted.>>> from cqparts.utils.misc import property_buffered >>> class A(object): ... @property_buffered ... def x(self): ... print("x called") ... return 100 >>> a = A() >>> a.x x called 100 >>> a.x 100 >>> del a.x >>> a.x x called 100
Basis of class was sourced from the funkybob/antfarm project. thanks to @funkybob.
-
cqparts.utils.misc.
working_dir
(*args, **kwds)¶ Change working directory within a context:
>>> import os >>> from cqparts.utils import working_dir >>> print(os.getcwd()) /home/myuser/temp >>> with working_dir('..'): ... print(os.getcwd()) ... /home/myuser
Parameters: path ( str
) – working path to use while in context
cqparts.utils.sphinx module¶
This module is only to be referenced from your project’s sphinx autodoc configuration.
http://www.sphinx-doc.org/en/stable/ext/autodoc.html
-
cqparts.utils.sphinx.
add_parametric_object_params
(prepend=False, hide_private=True)¶ Add
ParametricObject
parameters in a list to the docstring.This is only intended to be used with sphinx autodoc.
In your sphinx
config.py
file:from cqparts.utils.sphinx import add_parametric_object_params def setup(app): app.connect("autodoc-process-docstring", add_parametric_object_params())
Then, when documenting your
Part
orAssembly
theParametricObject
parameters will also be documented in the output.Parameters:
-
cqparts.utils.sphinx.
add_search_index_criteria
(prepend=False)¶ Add the search criteria used when calling
register()
on aComponent
as a table to the docstring.This is only intended to be used with sphinx autodoc.
In your sphinx
config.py
file:from cqparts.utils.sphinx import add_search_index_criteria def setup(app): app.connect("autodoc-process-docstring", add_search_index_criteria())
Then, when documenting your
Part
orAssembly
the search criteria will also be documented in the output.Parameters: prepend ( bool
) – if True, table is added to the beginning of the docstring. otherwise, it’s appended at the end.
-
cqparts.utils.sphinx.
skip_class_parameters
()¶ Can be used with
add_parametric_object_params()
, this removes duplicate variables cluttering the sphinx docs.This is only intended to be used with sphinx autodoc
In your sphinx
config.py
file:from cqparts.utils.sphinx import skip_class_parameters def setup(app): app.connect("autodoc-skip-member", skip_class_parameters())
cqparts.utils.test module¶
-
class
cqparts.utils.test.
CatalogueTest
(methodName='runTest')¶ Bases:
cqparts.utils.test.ComponentTest
-
catalogue
= None¶
-
classmethod
create_from
(catalogue, add_to={}, id_mangler=None, include_cond=None, exclude_cond=None)¶ Create a testcase class that will run generic tests on each item in the given
Catalogue
.Parameters: - catalogue (
Catalogue
) – catalogue to generatea tests from - add_to (
dict
) – dict to add resulting class to (usuallyglobals()
. - id_mangler (
function
) – convert item id to a valid python method name - include_cond (
function
) – returns true if item should be tested - exclude_cond (
function
) – returns true if item should not be tested
Returns: a testcase class to be discovered and run by
unittest
Return type: unittest.TestCase
sub-class (a class, not an instance)To create a test-case, and add the class with the catalogue’s name to the
globals()
namespace:from cqparts.utils.test import CatalogueTest from cqparts.catalogue import JSONCatalogue catalogue = JSONCatalogue('my_catalogue.json') CatalogueTest.create_from(catalogue, add_to=globals())
Alternatively, to control your class name a bit more traditionally:
# alternatively MyTestCase = CatalogueTest.create_from(catalogue)
Test Names / Catalogue IDs
Each test is named for its item’s
id
. By default, to translate the ids into valid python method names, this is done by replacing any non-alpha-numeric characters with a_
.To illustrate with some examples:
id mangled id test name abc123
abc123
(same)test_abc123
3.14159
3_14159
test_3_14159
%$#*_yeah!
_____yeah_
test______yeah_
_(@@)yeah&
_____yeah_
test______yeah_
So you can see why a python method name of
test_%$#*_yeah!
might be a problem, which is why this is done. But you may also spot that the last 2, although their IDs are unique, the test method names are the same.To change the way ID’s are mangled into test method names, set the
id_mangler
parameter:def mangle_id(id_str): return id_str.replace('a', 'X') CatalogueTest.create_from( catalogue, # as defined in the previous example add_to=globals(), id_mangler=mangle_id, )
That would change the first test name to
test_Xbc123
.Include / Exclude Items
If you intend on including or excluding certain items from the testlist, you can employ the
include_cond
and/orexclude_cond
parameters:def include_item(item): # include item if it has a specific id return item.get('id') in ['a', 'b', 'c'] def exclude_item(item): # exclude everything with a width > 100 return item.get('obj').get('params').get('width', 0) > 100 CatalogueTest.create_from( catalogue, # as defined in the previous example add_to=globals(), include_cond=include_item, exclude_cond=exclude_item, )
Tests will be created if the following conditions are met:
excluded included test case generated? n/a n/a Yes : tests are generated if no include/exclude methods are set n/a True
Yes n/a False
No True
n/a No False
n/a Yes False
False
No : inclusion take precedence (or lack thereof) False
True
Yes True
False
No True
True
Yes : inclusion take precedence - catalogue (
-
-
class
cqparts.utils.test.
ComponentTest
(methodName='runTest')¶ Bases:
unittest.case.TestCase
Generic testcase with utilities for testing
Part
andAssembly
instances.For example:
import cqparts import cadquery from cqparts.utils.test import ComponentTest class Box(cqparts.Part): def make(self): return cadquery.Workplane('XY').box(1,1,1) class BoxTest(ComponentTest): def test_box(self): box = Box() self.assertComponent(box)
-
assertAssembly
(obj)¶ Assert criteria common to any fully formed Assembly.
Parameters: obj ( Assembly
) – assembly under test
-
assertAssembyHasComponents
(obj)¶
-
assertComponent
(obj, recursive=True, _depth=0)¶ Assert criteria common to any fully formed Component.
Parameters:
-
assertPart
(obj)¶ Assert criteria common to any fully formed Part.
Parameters: obj ( Part
) – part under test
-
assertPartBoundingBox
(obj)¶
-
assertPartHasVolume
(obj)¶
-
cqparts.utils.wrappers module¶
-
cqparts.utils.wrappers.
as_part
(func)¶ Converts a function to a
Part
instance.So the conventionally defined part:
import cadquery from cqparts import Part from cqparts.params import Float class Box(Part): x = Float(1) y = Float(2) z = Float(4) def make(self): return cadquery.Workplane('XY').box(self.x, self.y, self.z) box = Box(x=6, y=3, z=1)
May also be written as:
import cadquery from cqparts.utils.wrappers import as_part @as_part def make_box(x=1, y=2, z=4): return cadquery.Workplane('XY').box(x, y, z) box = make_box(x=6, y=3, z=1)
In both cases,
box
is aPart
instance.
Module contents¶
-
class
cqparts.utils.
CoordSystem
(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1))¶ Bases:
cadquery.freecad_impl.geom.Plane
Defines the location, and rotation of an orthogonal 3 dimensional coordinate system.
-
__add__
(other)¶ For
A
+B
. WhereA
is this coordinate system, andB
isother
.Raises: TypeError – if addition for the given type is not supported Supported types:
A
(CoordSystem
) +B
(CoordSystem
):Returns: world coordinates of B
inA
’s coordinatesReturn type: CoordSystem
A
(CoordSystem
) +B
(cadquery.Vector
):Returns: world coordinates of B
represented inA
’s coordinate systemReturn type: cadquery.Vector
A
(CoordSystem
) +B
(cadquery.CQ
):remember:
cadquery.Workplane
inherits fromcadquery.CQ
Returns: content of B
moved toA
’s coordinate systemReturn type: cadquery.Workplane
-
__sub__
(other)¶ For
A
-B
. WhereA
is this coordinate system, andB
isother
.Raises: TypeError – if subtraction for the given type is not supported Supported types:
A
(CoordSystem
) +B
(CoordSystem
):Returns: local coordinate system of A
fromB
’s coordinate systemReturn type: CoordSystem
-
classmethod
from_plane
(plane)¶ Parameters: plane ( cadquery.Plane
) – cadquery plane instance to base coordinate system onReturns: duplicate of the given plane, in this class Return type: CoordSystem
usage example:
>>> import cadquery >>> from cqparts.utils.geometry import CoordSystem >>> obj = cadquery.Workplane('XY').circle(1).extrude(5) >>> plane = obj.faces(">Z").workplane().plane >>> isinstance(plane, cadquery.Plane) True >>> coord_sys = CoordSystem.from_plane(plane) >>> isinstance(coord_sys, CoordSystem) True >>> coord_sys.origin.z 5.0
-
classmethod
from_transform
(matrix)¶ Parameters: matrix ( FreeCAD.Matrix
) – 4x4 3d affine transform matrixReturns: a unit, zero offset coordinate system transformed by the given matrix Return type: CoordSystem
Individual rotation & translation matricies are:
\[\begin{split}R_z & = \begin{bmatrix} cos(\alpha) & -sin(\alpha) & 0 & 0 \\ sin(\alpha) & cos(\alpha) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \qquad & R_y & = \begin{bmatrix} cos(\beta) & 0 & sin(\beta) & 0 \\ 0 & 1 & 0 & 0 \\ -sin(\beta) & 0 & cos(\beta) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \\ \\ R_x & = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & cos(\gamma) & -sin(\gamma) & 0 \\ 0 & sin(\gamma) & cos(\gamma) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \qquad & T_{\text{xyz}} & = \begin{bmatrix} 1 & 0 & 0 & \delta x \\ 0 & 1 & 0 & \delta y \\ 0 & 0 & 1 & \delta z \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{split}\]The
transform
is the combination of these:\[\begin{split}transform = T_{\text{xyz}} \cdot R_z \cdot R_y \cdot R_x = \begin{bmatrix} a & b & c & \delta x \\ d & e & f & \delta y \\ g & h & i & \delta z \\ 0 & 0 & 0 & 1 \end{bmatrix}\end{split}\]Where:
\[\begin{split}a & = cos(\alpha) cos(\beta) \\ b & = cos(\alpha) sin(\beta) sin(\gamma) - sin(\alpha) cos(\gamma) \\ c & = cos(\alpha) sin(\beta) cos(\gamma) + sin(\alpha) sin(\gamma) \\ d & = sin(\alpha) cos(\beta) \\ e & = sin(\alpha) sin(\beta) sin(\gamma) + cos(\alpha) cos(\gamma) \\ f & = sin(\alpha) sin(\beta) cos(\gamma) - cos(\alpha) sin(\gamma) \\ g & = -sin(\beta) \\ h & = cos(\beta) sin(\gamma) \\ i & = cos(\beta) cos(\gamma)\end{split}\]
-
local_to_world_transform
¶ Returns: 3d affine transform matrix to convert local coordinates to world coordinates. Return type: cadquery.Matrix
For matrix structure, see
from_transform()
.
-
classmethod
random
(span=1, seed=None)¶ Creates a randomized coordinate system.
Useful for confirming that an assembly does not rely on its origin coordinate system to remain intact.
For example, the
CoordSysIndicator
assembly aligns 3 boxes along each of the \(XYZ\) axes. Positioning it randomly by setting itsworld_coords
shows that each box is always positioned orthogonally to the other two.from cqparts_misc.basic.indicators import CoordSysIndicator from cqparts.display import display from cqparts.utils import CoordSystem cs = CoordSysIndicator() cs.world_coords = CoordSystem.random() display(cs)
Parameters: - span – origin of return will be \(\pm span\) per axis
- seed (hashable object) – if supplied, return is psudorandom (repeatable)
Returns: randomized coordinate system
Return type:
-
world_to_local_transform
¶ Returns: 3d affine transform matrix to convert world coordinates to local coordinates. Return type: cadquery.Matrix
For matrix structure, see
from_transform()
.
-
-
class
cqparts.utils.
property_buffered
(getter, name=None)¶ Bases:
object
Buffer the result of a method on the class instance, similar to python builtin
@property
, but the result is kept in memory until it’s explicitly deleted.>>> from cqparts.utils.misc import property_buffered >>> class A(object): ... @property_buffered ... def x(self): ... print("x called") ... return 100 >>> a = A() >>> a.x x called 100 >>> a.x 100 >>> del a.x >>> a.x x called 100
Basis of class was sourced from the funkybob/antfarm project. thanks to @funkybob.
-
cqparts.utils.
indicate_last
(items)¶ iterate through list and indicate which item is the last, intended to assist tree displays of hierarchical content.
Returns: yielding (<bool>, <item>) where bool is True only on last entry Return type: generator
-
cqparts.utils.
working_dir
(*args, **kwds)¶ Change working directory within a context:
>>> import os >>> from cqparts.utils import working_dir >>> print(os.getcwd()) /home/myuser/temp >>> with working_dir('..'): ... print(os.getcwd()) ... /home/myuser
Parameters: path ( str
) – working path to use while in context
-
cqparts.utils.
measure_time
(*args, **kwds)¶
-
cqparts.utils.
as_part
(func)¶ Converts a function to a
Part
instance.So the conventionally defined part:
import cadquery from cqparts import Part from cqparts.params import Float class Box(Part): x = Float(1) y = Float(2) z = Float(4) def make(self): return cadquery.Workplane('XY').box(self.x, self.y, self.z) box = Box(x=6, y=3, z=1)
May also be written as:
import cadquery from cqparts.utils.wrappers import as_part @as_part def make_box(x=1, y=2, z=4): return cadquery.Workplane('XY').box(x, y, z) box = make_box(x=6, y=3, z=1)
In both cases,
box
is aPart
instance.