Skip to content

Commit e35f4b3

Browse files
Wider edge operators support (#348)
* Wider edge operators support * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5876b34 commit e35f4b3

File tree

4 files changed

+126
-18
lines changed

4 files changed

+126
-18
lines changed

examples/Notebook.ipynb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,26 @@
6666
"doc.add_cylinder(radius=0.5, position=[0, 0, 0.8]).fuse();"
6767
]
6868
},
69+
{
70+
"cell_type": "code",
71+
"execution_count": null,
72+
"id": "58b50fb3-e95a-409c-b12d-3b630bae7f27",
73+
"metadata": {},
74+
"outputs": [],
75+
"source": [
76+
"doc = CadDocument()"
77+
]
78+
},
79+
{
80+
"cell_type": "code",
81+
"execution_count": null,
82+
"id": "9f4c257e-7093-4724-8474-435fdfaa70fe",
83+
"metadata": {},
84+
"outputs": [],
85+
"source": [
86+
"doc.add_box().chamfer(edge=11, dist=0.3).fillet(radius=0.2, edge=8)"
87+
]
88+
},
6989
{
7090
"cell_type": "markdown",
7191
"id": "e072569b-21b8-474c-b7b4-5bf042101e9f",
@@ -269,7 +289,7 @@
269289
"name": "python",
270290
"nbconvert_exporter": "python",
271291
"pygments_lexer": "ipython3",
272-
"version": "3.10.13"
292+
"version": "3.12.2"
273293
}
274294
},
275295
"nbformat": 4,

python/jupytercad_app/src/app/plugins/mainmenu/menuWidget.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ export class MainMenu extends MenuBar {
127127
type: 'command',
128128
command: CommandIDs.union
129129
});
130+
operatorMenu.addItem({
131+
type: 'command',
132+
command: CommandIDs.chamfer
133+
});
134+
operatorMenu.addItem({
135+
type: 'command',
136+
command: CommandIDs.fillet
137+
});
130138
menu.addItem({
131139
type: 'submenu',
132140
submenu: operatorMenu

python/jupytercad_lab/jupytercad_lab/notebook/cad_document.py

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
IFuse,
2424
IIntersection,
2525
ISphere,
26+
IChamfer,
27+
IFillet,
2628
ITorus,
2729
Parts,
2830
ShapeMetadata,
@@ -593,29 +595,101 @@ def intersect(
593595
self.set_visible(shape2, False)
594596
return self.add_object(OBJECT_FACTORY.create_object(data, self))
595597

596-
def _get_boolean_operands(self, shape1: str | int | None, shape2: str | int | None):
597-
objects = self.objects
598+
def chamfer(
599+
self,
600+
name: str = "",
601+
shape: str | int = None,
602+
edge: int = 0,
603+
dist: float = 0.1,
604+
position: List[float] = [0, 0, 0],
605+
rotation_axis: List[float] = [0, 0, 1],
606+
rotation_angle: float = 0,
607+
) -> CadDocument:
608+
"""
609+
Apply a chamfer operation on an object.
610+
If no objects are provided as input, the last created object will be used as operand.
598611
612+
:param name: The name that will be used for the object in the document.
613+
:param shape: The input object used for the chamfer. Can be the name of the object or its index in the objects list.
614+
:param edge: The edge index where to apply chamfer.
615+
:param dist: The distance of the chamfer.
616+
:param position: The shape 3D position.
617+
:param rotation_axis: The 3D axis used for the rotation.
618+
:param rotation_angle: The shape rotation angle, in degrees.
619+
:return: The document itself.
620+
""" # noqa E501
621+
shape = self._get_operand(shape)
622+
623+
data = {
624+
"shape": Parts.Part__Chamfer.value,
625+
"name": name if name else self._new_name("Chamfer"),
626+
"parameters": {
627+
"Base": shape,
628+
"Edge": edge,
629+
"Dist": dist,
630+
"Placement": {"Position": [0, 0, 0], "Axis": [0, 0, 1], "Angle": 0},
631+
},
632+
}
633+
self.set_visible(shape, False)
634+
return self.add_object(OBJECT_FACTORY.create_object(data, self))
635+
636+
def fillet(
637+
self,
638+
name: str = "",
639+
shape: str | int = None,
640+
edge: int = 0,
641+
radius: float = 0.1,
642+
position: List[float] = [0, 0, 0],
643+
rotation_axis: List[float] = [0, 0, 1],
644+
rotation_angle: float = 0,
645+
) -> CadDocument:
646+
"""
647+
Apply a fillet operation on an object.
648+
If no objects are provided as input, the last created object will be used as operand.
649+
650+
:param name: The name that will be used for the object in the document.
651+
:param shape: The input object used for the fillet. Can be the name of the object or its index in the objects list.
652+
:param edge: The edge index where to apply fillet.
653+
:param radius: The radius of the fillet.
654+
:param position: The shape 3D position.
655+
:param rotation_axis: The 3D axis used for the rotation.
656+
:param rotation_angle: The shape rotation angle, in degrees.
657+
:return: The document itself.
658+
""" # noqa E501
659+
shape = self._get_operand(shape)
660+
661+
data = {
662+
"shape": Parts.Part__Fillet.value,
663+
"name": name if name else self._new_name("Fillet"),
664+
"parameters": {
665+
"Base": shape,
666+
"Edge": edge,
667+
"Radius": radius,
668+
"Placement": {"Position": [0, 0, 0], "Axis": [0, 0, 1], "Angle": 0},
669+
},
670+
}
671+
self.set_visible(shape, False)
672+
return self.add_object(OBJECT_FACTORY.create_object(data, self))
673+
674+
def _get_operand(self, shape: str | int | None, default_idx: int = -1):
675+
if isinstance(shape, str):
676+
if shape not in self.objects:
677+
raise ValueError(f"Unknown object {shape}")
678+
elif isinstance(shape, int):
679+
shape = self.objects[shape]
680+
else:
681+
shape = self.objects[default_idx]
682+
683+
return shape
684+
685+
def _get_boolean_operands(self, shape1: str | int | None, shape2: str | int | None):
599686
if len(self.objects) < 2:
600687
raise ValueError(
601688
"Cannot apply boolean operator if there are less than two objects in the document." # noqa E501
602689
)
603690

604-
if isinstance(shape1, str):
605-
if shape1 not in objects:
606-
raise ValueError(f"Unknown object {shape1}")
607-
elif isinstance(shape1, int):
608-
shape1 = objects[shape1]
609-
else:
610-
shape1 = objects[-2]
611-
612-
if isinstance(shape2, str):
613-
if shape2 not in objects:
614-
raise ValueError(f"Unknown object {shape2}")
615-
elif isinstance(shape2, int):
616-
shape2 = objects[shape2]
617-
else:
618-
shape2 = objects[-1]
691+
shape1 = self._get_operand(shape1, -2)
692+
shape2 = self._get_operand(shape2, -1)
619693

620694
return shape1, shape2
621695

@@ -676,6 +750,8 @@ class Config:
676750
IFuse,
677751
ISphere,
678752
ITorus,
753+
IFillet,
754+
IChamfer,
679755
]
680756
metadata: Optional[ShapeMetadata]
681757
_caddoc = Optional[CadDocument]
@@ -742,3 +818,5 @@ def create_object(
742818
OBJECT_FACTORY.register_factory(Parts.Part__MultiFuse.value, IFuse)
743819
OBJECT_FACTORY.register_factory(Parts.Part__Sphere.value, ISphere)
744820
OBJECT_FACTORY.register_factory(Parts.Part__Torus.value, ITorus)
821+
OBJECT_FACTORY.register_factory(Parts.Part__Chamfer.value, IChamfer)
822+
OBJECT_FACTORY.register_factory(Parts.Part__Fillet.value, IFillet)

python/jupytercad_lab/jupytercad_lab/notebook/objects/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@
88
from ._schema.jcad import Parts, ShapeMetadata # noqa
99
from ._schema.sphere import ISphere # noqa
1010
from ._schema.torus import ITorus # noqa
11+
from ._schema.chamfer import IChamfer # noqa
12+
from ._schema.fillet import IFillet # noqa

0 commit comments

Comments
 (0)