Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add chamfer tool #1495

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
bf9dda7
implement chamfer and test
jonahrb Oct 18, 2024
5e2710a
chore: adding changelog file 1495.added.md [dependabot-skip]
pyansys-ci-bot Oct 18, 2024
6fcbec1
cleanup
jonahrb Oct 21, 2024
282b43c
bump api geometry
jonahrb Oct 22, 2024
645c8a8
more cleanup
jonahrb Oct 22, 2024
359cf8e
Merge branch 'main' into feat/chamfer
RobPasMue Oct 22, 2024
7b4c94c
final cleanup, add example
jonahrb Oct 23, 2024
c00e7be
Merge branch 'feat/chamfer' of https://github.com/ansys/pyansys-geome…
jonahrb Oct 23, 2024
22d114c
Update doc/source/examples/03_modeling/chamfer.mystnb
jonahrb Oct 28, 2024
7ea2daa
Update doc/source/examples/03_modeling/chamfer.mystnb
jonahrb Oct 28, 2024
ac4b9d1
Update src/ansys/geometry/core/tools/pull_tools.py
jonahrb Oct 28, 2024
4205a17
Update src/ansys/geometry/core/tools/pull_tools.py
jonahrb Oct 28, 2024
8786d55
Update src/ansys/geometry/core/tools/pull_tools.py
jonahrb Oct 28, 2024
5905606
Update src/ansys/geometry/core/tools/pull_tools.py
jonahrb Oct 28, 2024
5ac0815
Merge branch 'main' into feat/chamfer
jonahrb Oct 28, 2024
45116ea
Merge branch 'main' into feat/chamfer
RobPasMue Oct 31, 2024
e442584
Merge branch 'main' into feat/chamfer
RobPasMue Nov 5, 2024
e00614d
Merge branch 'main' into feat/chamfer
RobPasMue Nov 20, 2024
dd34aae
fix: various typehint issues and proper ID
RobPasMue Nov 20, 2024
af23732
Update src/ansys/geometry/core/tools/pull_tools.py
RobPasMue Nov 20, 2024
a01dbbf
Merge branch 'main' into feat/chamfer
RobPasMue Nov 20, 2024
67941f8
Merge branch 'main' into feat/chamfer
RobPasMue Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/1495.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add chamfer tool
Binary file added doc/source/_static/thumbnails/chamfer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ def intersphinx_pyansys_geometry(switcher_version: str):
"examples/03_modeling/design_tree": "_static/thumbnails/design_tree.png",
"examples/03_modeling/service_colors": "_static/thumbnails/service_colors.png",
"examples/03_modeling/surface_bodies": "_static/thumbnails/quarter_sphere.png",
"examples/03_modeling/chamfer": "_static/thumbnails/chamfer.png",
"examples/04_applied/01_naca_airfoils": "_static/thumbnails/naca_airfoils.png",
"examples/04_applied/02_naca_fluent": "_static/thumbnails/naca_fluent.png",
}
Expand Down
1 change: 1 addition & 0 deletions doc/source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ These examples demonstrate service-based modeling operations.
examples/03_modeling/design_tree.mystnb
examples/03_modeling/service_colors.mystnb
examples/03_modeling/surface_bodies.mystnb
examples/03_modeling/chamfer.mystnb

Applied examples
----------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ output list is sorted according to the picking order.
pl = GeometryPlotter(allow_picking=True)
pl.plot(design.bodies)
pl.show()
bodies: List[Body] = GeometryPlotter(allow_picking=True).show(design.bodies)
bodies: list[Body] = GeometryPlotter(allow_picking=True).show(design.bodies)
```

Otherwise, you can select bodies from the design directly.
Expand Down
63 changes: 63 additions & 0 deletions doc/source/examples/03_modeling/chamfer.mystnb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
jupytext:
text_representation:
extension: .mystnb
format_name: myst
format_version: 0.13
jupytext_version: 1.16.4
kernelspec:
display_name: Python 3 (ipykernel)
language: python
name: python3
---

# Modeling: Chamfer edges and faces
A chamfer is an angled cut on an edge. Chamfers can be created using the ``Modeler.pull_tools`` module.

+++

## Create a block
Launch the modeler and create a block.

```{code-cell} ipython3
from ansys.geometry.core import launch_modeler, Modeler

modeler = Modeler()
print(modeler)
```

```{code-cell} ipython3
from ansys.geometry.core.sketch import Sketch
from ansys.geometry.core.math import Point2D

design = modeler.create_design("chamfer_block")
body = design.extrude_sketch("block", Sketch().box(Point2D([0, 0]), 1, 1), 1)

body.plot()
```

## Chamfer edges
Create a uniform chamfer on all edges of the block.

```{code-cell} ipython3
modeler.pull_tools.chamfer(body.edges, distance=0.1)

body.plot()
```

## Chamfer faces
The chamfer of a face can also be modified. Create a chamfer on a single edge and then modify the chamfer distance value by providing the newly created face that represents the chamfer.

```{code-cell} ipython3
body = design.extrude_sketch("box", Sketch().box(Point2D([0,0]), 1, 1), 1)

modeler.pull_tools.chamfer(body.edges[0], distance=0.1)

body.plot()
```

```{code-cell} ipython3
modeler.pull_tools.chamfer(body.faces[-1], distance=0.3)

body.plot()
```
4 changes: 2 additions & 2 deletions doc/source/examples/04_applied/01_naca_airfoils.mystnb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import numpy as np

from ansys.geometry.core.math import Point2D

def naca_airfoil_4digits(number: Union[int, str], n_points: int = 200) -> List[Point2D]:
def naca_airfoil_4digits(number: Union[int, str], n_points: int = 200) -> list[Point2D]:
"""
Generate a NACA 4-digits airfoil.

Expand All @@ -58,7 +58,7 @@ def naca_airfoil_4digits(number: Union[int, str], n_points: int = 200) -> List[P

Returns
-------
List[Point2D]
list[Point2D]
List of points that define the airfoil.
"""
# Check if the number is a string
Expand Down
6 changes: 5 additions & 1 deletion src/ansys/geometry/core/designer/body.py
Original file line number Diff line number Diff line change
Expand Up @@ -1207,11 +1207,15 @@ def reset_tessellation_cache(func): # noqa: N805

@wraps(func)
def wrapper(self: "Body", *args, **kwargs):
self._template._tessellation = None
self._reset_tessellation_cache()
return func(self, *args, **kwargs)

return wrapper

def _reset_tessellation_cache(self): # noqa: N805
"""Reset the cached tessellation for a body."""
self._template._tessellation = None

jonahrb marked this conversation as resolved.
Show resolved Hide resolved
@property
def id(self) -> str: # noqa: D102
return self._id
Expand Down
5 changes: 5 additions & 0 deletions src/ansys/geometry/core/designer/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ def _grpc_id(self) -> EntityIdentifier:
"""Entity ID of this edge on the server side."""
return EntityIdentifier(id=self._id)

@property
def body(self) -> "Body":
"""Body of the edge."""
return self._body
jonahrb marked this conversation as resolved.
Show resolved Hide resolved

@property
def is_reversed(self) -> bool:
"""Flag indicating if the edge is reversed."""
Expand Down
9 changes: 8 additions & 1 deletion src/ansys/geometry/core/modeler.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from ansys.geometry.core.misc.options import ImportOptions
from ansys.geometry.core.tools.measurement_tools import MeasurementTools
from ansys.geometry.core.tools.prepare_tools import PrepareTools
from ansys.geometry.core.tools.pull_tools import PullTools
from ansys.geometry.core.tools.repair_tools import RepairTools
from ansys.geometry.core.typing import Real

Expand All @@ -59,7 +60,7 @@ class Modeler:
----------
host : str, default: DEFAULT_HOST
Host where the server is running.
port : Union[str, int], default: DEFAULT_PORT
port : str | int, default: DEFAULT_PORT
Port number where the server is running.
channel : ~grpc.Channel, default: None
gRPC channel for server communication.
Expand Down Expand Up @@ -128,6 +129,7 @@ def __init__(
self._repair_tools = RepairTools(self._grpc_client)
self._prepare_tools = PrepareTools(self._grpc_client)
self._measurement_tools = MeasurementTools(self._grpc_client)
self._pull_tools = PullTools(self._grpc_client)

# Maintaining references to all designs within the modeler workspace
self._designs: dict[str, "Design"] = {}
Expand Down Expand Up @@ -498,6 +500,11 @@ def measurement_tools(self) -> MeasurementTools:
"""Access to measurement tools."""
return self._measurement_tools

@property
def pull_tools(self) -> PullTools:
"""Access to pull tools."""
return self._pull_tools

@min_backend_version(25, 1, 0)
def get_service_logs(
self,
Expand Down
1 change: 1 addition & 0 deletions src/ansys/geometry/core/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
ExtraEdgeProblemAreas,
InexactEdgeProblemAreas,
)
from ansys.geometry.core.tools.pull_tools import PullTools
from ansys.geometry.core.tools.repair_tool_message import RepairToolMessage
from ansys.geometry.core.tools.repair_tools import RepairTools
81 changes: 81 additions & 0 deletions src/ansys/geometry/core/tools/pull_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Provides tools for pulling geometry."""

from typing import TYPE_CHECKING, List, Union

from ansys.api.geometry.v0.commands_pb2 import ChamferRequest
from ansys.api.geometry.v0.commands_pb2_grpc import CommandsStub
from ansys.geometry.core.connection import GrpcClient
from ansys.geometry.core.errors import protect_grpc
from ansys.geometry.core.misc.checks import min_backend_version
from ansys.geometry.core.typing import Real

if TYPE_CHECKING: # pragma: no cover
from ansys.geometry.core.designer.edge import Edge
from ansys.geometry.core.designer.face import Face


class PullTools:
"""Provides pull tools for PyAnsys Geometry.

Parameters
----------
grpc_client : GrpcClient
gRPC client to use for the measurement tools.
"""

@protect_grpc
def __init__(self, grpc_client: GrpcClient):
"""Initialize an instance of the ``PullTools`` class."""
self._grpc_client = grpc_client
self._commands_stub = CommandsStub(self._grpc_client.channel)

@protect_grpc
@min_backend_version(25, 2, 0)
def chamfer(
self, selection: Union["Edge", List["Edge"], "Face", List["Face"]], distance: Real
) -> bool:
"""Create a chamfer on an edge, or adjust the chamfer of a face.

Parameters
----------
edges_or_faces : Edge | list[Edge] | Face | list[Face]
One or more edges or faces to act on.
distance : Real
Chamfer distance.

Returns
-------
bool
``True`` when successful, ``False`` when failed.
"""
selection: list[Edge | Face] = selection if isinstance(selection, list) else [selection]

for ef in selection:
ef.body._reset_tessellation_cache()

result = self._commands_stub.Chamfer(
ChamferRequest(ids=[ef._grpc_id for ef in selection], distance=distance)
)

return result.success
63 changes: 63 additions & 0 deletions tests/integration/test_pull_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (C) 2023 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
""" "Testing of repair tools."""

from pint import Quantity
import pytest

from ansys.geometry.core.math.point import Point2D
from ansys.geometry.core.misc import UNITS
from ansys.geometry.core.modeler import Modeler
from ansys.geometry.core.sketch.sketch import Sketch


def test_chamfer(modeler: Modeler):
"""Test chamfer on edge and face."""
design = modeler.create_design("chamfer")

body = design.extrude_sketch("box", Sketch().box(Point2D([0, 0]), 1, 1), 1)
assert len(body.faces) == 6
assert len(body.edges) == 12
assert body.volume.m == pytest.approx(Quantity(1, UNITS.m**3).m, rel=1e-6, abs=1e-8)

modeler.pull_tools.chamfer(body.edges[0], 0.1)
assert len(body.faces) == 7
assert len(body.edges) == 15
assert body.volume.m == pytest.approx(Quantity(0.995, UNITS.m**3).m, rel=1e-6, abs=1e-8)

modeler.pull_tools.chamfer(body.faces[-1], 0.5)
assert len(body.faces) == 7
assert len(body.edges) == 15
assert body.volume.m == pytest.approx(Quantity(0.875, UNITS.m**3).m, rel=1e-6, abs=1e-8)

# multiple edges
body2 = design.extrude_sketch("box2", Sketch().box(Point2D([0, 0]), 1, 1), 1)
assert len(body2.faces) == 6
assert len(body2.edges) == 12
assert body2.volume.m == pytest.approx(Quantity(1, UNITS.m**3).m, rel=1e-6, abs=1e-8)

modeler.pull_tools.chamfer(body2.edges, 0.1)
assert len(body2.faces) == 26
assert len(body2.edges) == 48
assert body2.volume.m == pytest.approx(
Quantity(0.945333333333333333, UNITS.m**3).m, rel=1e-6, abs=1e-8
)
Loading