Part
Build Cycle¶
A Part
is just a container for a cadquery.Workplane
instance.
So the “building” of a part is more to help placement in the world, and to facilitate changes made to that object.
For more information on how to create solids as a cadquery.Workplane
,
read more in the cadquery
documentation.
Instantiation¶
When a Part
is created, it initializes its parameters.
Most of the work here is done by its parent class
ParametricObject
.
This is covered in detail in Parametric Object.
Note
make()
is not called when instantiating a part; it’s
called later.
import cadquery
import cqparts
from cqparts.params import *
class Box(cqparts.Part):
x = Int(10)
def initialize_parameters(self):
print('initializing parameters...')
print(' x = %r' % self.x)
def make(self):
print('running make()...')
return cadquery.Workplane('XY').box(self.x,10,10)
So then when we simply create an instance…
>>> box = Box(x=20)
initializing parameters...
x = 20
Getting local_obj
¶
When a part’s local_obj
is requested, the instance’s
make()
result is returned.
Note that in the above test, the print
statement in make()
doesn’t show.
However, when we request local_obj
…
>>> box = Box(x=15)
initializing parameters...
x = 15
>>> obj1 = box.local_obj
running make()...
>>> obj2 = box.local_obj
However nothing is printed on the 2nd call. This is because the result is
buffered, so when local_obj
is requested a second time, it is
not re-made.
Most uses of a Part
will get the local object by referencing
local_obj
, but you can forcefully remake the object in 2 ways:
>>> box = Box()
initializing parameters...
x = 10
>>> obj = box.local_obj
running make()...
>>> # 1) set local_obj to None
>>> box.local_obj = None
>>> obj = box.local_obj
running make()...
>>> # 2) call make() explicitly
>>> obj = box.make()
running make()...
However you shouldn’t need to do this.
world_coords
and world_obj
¶
If world_coords
has been set, getting
world_obj
will create a copy of
local_obj
that has been transformed to
world_coords
.
That is to say that it is translated, and rotated so the object’s local coordinates are equal to the world coordinates, relative to the object itself.
Let’s re-define Box
without those print statements…
from cqparts.utils.geometry import CoordSystem
class Box(cqparts.Part):
x = Int(10)
def make(self):
return cadquery.Workplane('XY').box(self.x,10,10)
Now let’s create a box, then set its location in the world by
setting world_coords
.
>>> box = Box()
>>> box.local_obj.val().BoundingBox().ymin
-5.0
>>> # world_obj is None when the part does not have its world_coords
>>> box.world_obj is None
True
>>> # let's translate across the y-axis
>>> box.world_coords = CoordSystem(origin=(0,20,0))
>>> # Now world_obj exists, and has been translated
>>> isinstance(box.world_obj, cadquery.Workplane)
True
>>> box.world_obj.val().BoundingBox().ymin
15.0
Changing local_obj
or world_coords
¶
If local_obj
or
world_coords
is changed,
world_obj
is reset.
Then when world_obj
is requested again,
local_obj
is copied and moved,
just as it is explained above.
So the obvious thing to do now is to drill a hole through the box… right?
>>> box = Box()
>>> box.world_coords = CoordSystem(origin=(0,20,0))
>>> len(box.world_obj.val().Faces())
6
>>> box.local_obj = box.local_obj.faces(">Z").hole(2)
>>> len(box.world_obj.val().Faces())
7
Note that we changed local_obj
, but we tested the number of faces on
world_obj
. So we can conclude from this that when any changes are
made to the local_obj
, the
world_obj
is re-created using local_obj
as reference.
Let’s try the same thing by changing world_obj
:
>>> box = Box()
>>> box.world_coords = CoordSystem(origin=(0,20,0))
>>> box.world_obj = box.world_obj.faces(">Z").hole(2)
ValueError: can't set world_obj directly, set local_obj instead
We get an exception instead
But Why?: This is to avoid bad practices that encourage accumulated errors; if the part can only be modified in its native coordinates, there is no possibility of accumulated numerical error.
Note
Remember: when changing the local_obj
, if your
alterations are based on world coordinates, you must convert back to the
object’s local coordinates before the changes will match your expectations.
Also remember that the world_obj
is likely rotated
to fit into an assembly, so using queries like
faces(">Z")
(for example) may not give you
the information you’re expecting.