Created
June 9, 2022 21:29
-
-
Save Jojain/87b3e1b27bd287d8dceb9973b08578a1 to your computer and use it in GitHub Desktop.
CQ refactor ideas
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Below is presented some real code lacking implementation. The main purpose is to expose my thought about what a better API of | |
CadQuery could be. I have not thought to much in depth of the problematic such an API could bring but its up to be discussed. | |
My thoughts are that currently the CQ fluent API through the Workplane class (and the Sketch one) try to do too much different things at | |
the same time. This is great because it mostly succeed in this task but it's also wrong because there is a lot of edgy cases where it just doesnt work | |
well. | |
In order to tackle down this problem I think a strongly structured OOP API would help in debugging and adding new features. | |
Some ideas "implemented" below are : | |
- Providing different modelling contextes that only allow one type of modelling | |
and enforce it, so if we are working on a 2DModellingContext, operations run on it could only work in a plan, with panar faces, | |
planar wires, planar edges, etc. | |
- Class based operations, that are reusable and store information that can later be retrieve for further modelling | |
- Class based entities like Patterns, it's easy to derive from it to create new ones. | |
The pros of such an API is that it structure the project better, it helps extending the code, adding new Patterns or Operations | |
is as simple as deriving from it and coding the required logic. | |
At this moment Selectors use this kind of logic and are probably the best structured part of CQ and the easiest to extend. | |
""" | |
from abc import ABCMeta, abstractmethod | |
from cadquery import Solid | |
############################################################ | |
# Definitions of ModellingContext classes | |
class ModellingContext: | |
def __init__(self) -> None: | |
self._name = None | |
self._shape = None | |
# add there all the attributes that might be relevant and be used by the ops | |
# It might be useful to derive the ModellingContext to have different kinds of modelling context | |
# i.e 2DModellingContext, 3DModellingContext, etc. | |
# Then we could have a fluent API on top of each context (Sketch, Part, SurfacePart, etc) | |
class ModellingContext3D(ModellingContext): | |
def __init__(self) -> None: | |
super().__init__() | |
@property | |
def shape(self): | |
return self._shape | |
@property.setter | |
def shape(self, value): | |
if not isinstance(value, Solid): | |
raise ValueError( | |
f"Shapes of type {type(value)} are not allowed in {self.__class__}" | |
) | |
else: | |
self._shape = value | |
############################################################ | |
# Definitions of Operations classes | |
class Operation(ABCMeta): | |
""" | |
The base class from which every operation must derive | |
All the Operation must derive from this class | |
""" | |
def __init__(self, ctx): | |
self._id = None # Each operation can have an id for finding it in the context, or a name, or a tag ... | |
# Maybe each operation should hold a reference of the ctx before and after the execution of the operation | |
# in this case it may be easier to find features that have been created by the op | |
self.ctx_before = None | |
self.ctx_after = None | |
@abstractmethod | |
def perform(self): | |
""" | |
All operation must implement this method | |
""" | |
pass | |
class TransformOperation(Operation): | |
# The MoveOperation could be a base class for all the transformed operations | |
# i.e we could have derived classes for MoveOp, RotateOp, ScaleOp etc. | |
pass | |
class CompoundOperation(Operation): | |
def __init__(self, ctx): | |
super().__init__(ctx) | |
# A compound operation holds reference to its child operations | |
# it helps concatenate several simple Operation into one more complex | |
# Ideally the CompoundOperation can offuscate a lot of complexity and do complex stuff while using | |
# basics op that are easy to debug if something goes wrong at one point. | |
self.childs_op = [] | |
def perform(self): | |
return super().perform() | |
class FuseOperation(Operation): | |
def __init__(self, ctx): | |
super().__init__(ctx) | |
def perform(self): | |
pass | |
class CylinderOperation(Operation): | |
def __init__(self, ctx): | |
super().__init__(ctx) | |
def perform(self): | |
pass | |
class BoltOperation(CompoundOperation): | |
def __init__(self) -> None: | |
super().__init__() | |
self.childs_op.append(CylinderOperation()) # First cylinder R = 10, H = 3 | |
self.childs_op.append(CylinderOperation()) # Second cylinder R = 3, H = 30 | |
self.childs_op.append(FuseOperation()) # Fuse both the cylinder to make a bolt | |
def perform(self): | |
for op in self.child_op: | |
op.perform() | |
class ExtrudeOperation(Operation): | |
def __init__(self, ctx, sketch, direction): | |
super().__init__(ctx) | |
self.sketch = sketch | |
self.direction = direction | |
def perform(self): | |
# Here we can mix OCP and cq.occ_impl API | |
Solid.makeExtrusion(self.sketch, self.direction) | |
############################################################ | |
# Definitions of Selectors classes | |
# Similarly as currently Selector takes a shape as an input and give back another shape | |
class Selector: | |
pass | |
class FaceGeneratedByOp(Selector): | |
def __init__(self, ctx, operation_id) -> None: | |
pass | |
def filter(self): | |
# Given the operation and the context we can retrieve what have been created by the op | |
pass | |
############################################################ | |
# Definitions of Patterns classes | |
# Like operations one can derive Pattern to create all sort of pattern objects that can be reused | |
class Pattern: | |
""" | |
Patterns are lists of Locations in 3D, like polarArray and stuff | |
""" | |
pass | |
class PolarPatter(Pattern): | |
""" | |
Represent a polar pattern of location | |
""" | |
pass # the implementation is needed there | |
class HelixPattern(Pattern): | |
""" | |
Represent a helix pattern of location | |
""" | |
pass # the implementation is needed there | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment