Discussion: Parts, Assemblies and Instances
See original GitHub issueRight now we have two kinds of components Parts and Assemblies. A part is an atomic component and an Assembly is a non-atomic component consisting of Parts and other assemblies.
This is inconvenient since if I am creating a table with four legs I need create four leg parts and enter the same dimensions four times for each leg and create four separate objects that should be identical. Not very DRY and not very memory efficient. This is also true when exporting for example GLTF.
To solve this I would like to introduce “Instances” to cqparts.
The four legs in the table assembly should be four instances of the same part. I want to enter the dimensions and create the part only once. The part should define the dimensions, shape, and color of the part. The instance of the part should just define the position of each part-instance in the world and a reference to the defining part.
This means that an assembly should always consist of only Instances and not Parts and Assemblies. Parts are instantiated when added to the assembly and assemblies are also instantiated when added to an assembly.
Some pseudo code for how this could look in practice for a four legged table. The interesting part is in the class Table() make_components()
definition
import cadquery
import cqparts
from cqparts.component import Mate
class Leg(cqparts.Part):
def make(self):
return cadquery.Workplane('XY').box(1, 1, 10, centered=(True, True, False))
@property
def mate_top(self):
return Mate(self, CoordSystem((0, 0, 10)))
class Tabletop(cqparts.Part):
def make(self):
return cadquery.Workplane('XY').box(10, 10, 1, centered=(True, True, False))
@property
def mate_leg_0(self):
return Mate(self, CoordSystem((5, 5, 10)))
@property
def mate_leg_1(self):
return Mate(self, CoordSystem((5, -5, 10)))
@property
def mate_leg_2(self):
return Mate(self, CoordSystem((-5, -5, 10)))
@property
def mate_leg_3(self):
return Mate(self, CoordSystem((-5, 5, 10)))
class Table(cqparts.Assembly):
def make_components(self):
# Only create the individual parts once
leg = Leg()
tabletop = Tabletop()
# The components are instances of the parts, this would be the same for sub-assemblies as well
components = dict()
components["tabletop_instance_0"] = cqparts.Instance(tabletop)
components["leg_instance_0"] = cqparts.Instance(leg)
components["leg_instance_1"] = cqparts.Instance(leg)
components["leg_instance_2"] = cqparts.Instance(leg)
components["leg_instance_3"] = cqparts.Instance(leg)
return components
def make_constraints(self):
constraints = [
Fixed(self.components['tabletop_instance_0'].mate_origin,
CoordSystem((0,0,10), (1,0,0), (0,0,1))),
Coincident(
self.components['leg_instance_0'].mate_top,
self.components['tabletop_instance_0'].mate_leg_0
),
Coincident(
self.components['leg_instance_1'].mate_top,
self.components['tabletop_instance_0'].mate_leg_1
),
Coincident(
self.components['leg_instance_2'].mate_top,
self.components['tabletop_instance_0'].mate_leg_2
),
Coincident(
self.components['leg_instance_3'].mate_top,
self.components['tabletop_instance_0'].mate_leg_3
),
]
return constraints
table = Table()
A sub-assembly is also always instantiated when inserted into an assembly.
Take for example your car-assembly example in the documentation. With this notion of Parts, Assemblies and Instances the structure would look something like this.
Parts
- Chassis
- Axle
- Wheel
Assemblies
- Wheel-axle
- Wheel-instance-1
- Wheel-instance-2
- Axle-instance-1
- Car
- Chassis-instance-1
- Wheel-axle-instance-1
- Wheel-axle-instance-2
This has the added benefit as well that I can compare two instances to see if they are identical. Identical in this context is “based on the same part”.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:2
- Comments:44 (27 by maintainers)
Top GitHub Comments
I’d like to make a table of all these terms at some point. It makes transitioning from one language/CAD-tool to another much easier.
@dcowden I think this is a smaller issue for
cqparts
than it is forcadquery
. Incqparts
one explicitly creates aPart
instance and all subsequent methods called on that instance just modify this one instance (as one would expect in OO I presume).In cadquery one implicitly creates (or modifies?) shapes and I think that is where the confusion came from. And since cqparts does not use method chaining, the return value
c1.cut_a_hole_out_of_it()
can be really anything. I think that forcadquery
it is the best to always return the new geometry/shape to allow both - saving the return value for future reference and chaining the operations. I don’t see any disadvantage to returning the new shape for each call (but I don’t have that much experience so I might be easily missing something).