Skip to content

Commit

Permalink
Check if circle (#21)
Browse files Browse the repository at this point in the history
* check if circle

* add new test models

* upgrade deps

* check if there's a common point

* create models for tests with cadquery

* Add method to combine same arcs in Shape class

* Refactor shape.py and add new tests
  • Loading branch information
yuichiroaoki authored Dec 29, 2023
1 parent 21e2f53 commit 2ced600
Show file tree
Hide file tree
Showing 11 changed files with 621 additions and 70 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ jobs:
- name: Lint with ruff
run: |
# stop the build if there are Python syntax errors or undefined names
poetry run ruff --format=github --select=E9,F63,F7,F82 --target-version=py37 .
poetry run ruff --output-format=github --select=E9,F63,F7,F82 --target-version=py37 .
# default set of ruff rules with GitHub Annotations
poetry run ruff --format=github --target-version=py37 .
poetry run ruff --output-format=github --target-version=py37 .
- name: Run the automated tests
run: poetry run pytest -v --cov=cnceye tests
run: poetry run pytest -v --cov=cnceye --cov-report=term-missing tests

blender:
name: Blender
Expand Down
2 changes: 1 addition & 1 deletion cnceye/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .shape import Shape # noqa: F401
from .shape import Shape # noqa: F401
17 changes: 16 additions & 1 deletion cnceye/arc.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ def get_arc_info(arc_points: np.ndarray, decimal_places: int = 3):
Radius of arc
center : np.array
Center of arc
is_circle : bool
True if arc is a circle
"""
center_x, center_y, radius = fit_circle(arc_points[:, :2])
center = np.array([center_x, center_y, arc_points[0, 2]])
center = np.round(center, decimal_places)
radius = round(radius, decimal_places)
return radius, center
return radius, center, is_circle(arc_points)


def fit_circle(points):
Expand All @@ -68,3 +70,16 @@ def fit_circle(points):
def circle_residuals(params, x, y):
cx, cy, r = params
return (x - cx) ** 2 + (y - cy) ** 2 - r**2


def is_circle(points, tolerance=1.0):
"""
Check if points are a circle within a tolerance
If they are a circle, distances between points should be within tolerance.
If not, they are an arc.
"""
x = points[:, 0]
y = points[:, 1]
distances = np.sqrt((x - np.mean(x)) ** 2 + (y - np.mean(y)) ** 2)
return np.allclose(distances, distances[0], atol=tolerance)
94 changes: 82 additions & 12 deletions cnceye/shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,65 @@ def group_by_coplanar_facets(self, facet_indices: np.ndarray):

return coplanar_facets

def get_lines_and_arcs(self, decimal_places: int = 3, arc_threshold: int = 1):
def get_common_point(self, prev_points, points):
"""
Get the common point between two lists of points
"""
if prev_points is None:
return None
for prev_point in prev_points:
for point in points:
if np.array_equal(prev_point, point):
return point

def line_length_similar(self, points0, points1):
line_length0 = np.linalg.norm(points0[0] - points0[1])
line_length1 = np.linalg.norm(points1[0] - points1[1])
return np.isclose(
line_length0,
line_length1,
atol=1e-3,
)

def combine_same_arc(self, arc_group):
"""
Combine arcs that are the same
"""
if len(arc_group) < 2:
return arc_group
new_arc_group = []
prev_points = arc_group[0]
for i in range(1, len(arc_group)):
arc_points = arc_group[i]
if np.array_equal(
prev_points[-1], arc_points[0]
) and self.line_length_similar(prev_points, arc_points):
# merge prev_points and arc_points
prev_points = np.vstack((prev_points, arc_points[1:]))
elif np.array_equal(
prev_points[-1], arc_points[-1]
) and self.line_length_similar(prev_points, arc_points):
# reverse arc_points and merge with prev_points
prev_points = np.vstack((prev_points, arc_points[-2::-1]))
elif np.array_equal(
prev_points[0], arc_points[0]
) and self.line_length_similar(prev_points, arc_points):
# reverse prev_points and merge with arc_points
prev_points = np.vstack((arc_points[-2::-1], prev_points))
elif np.array_equal(
prev_points[0], arc_points[-1]
) and self.line_length_similar(prev_points, arc_points):
# merge prev_points and reverse arc_points
prev_points = np.vstack((arc_points, prev_points[1:]))
else:
new_arc_group.append(prev_points)
prev_points = arc_points

if i == len(arc_group) - 1:
new_arc_group.append(prev_points)
return new_arc_group

def get_lines_and_arcs(self, arc_threshold: int = 1):
"""
Extract lines and arcs from an STL file \n
If the line length is less than 1, it is considered an arc.
Expand All @@ -90,8 +148,6 @@ def get_lines_and_arcs(self, decimal_places: int = 3, arc_threshold: int = 1):
Parameters
----------
decimal_places : int
Number of decimal places to round to
arc_threshold : int
Length threshold to determine if a line is an arc
Expand All @@ -107,6 +163,7 @@ def get_lines_and_arcs(self, decimal_places: int = 3, arc_threshold: int = 1):
arcs = []

previous_length = 0
previous_arc_points = None
for coplanar_shapes in shapes:
line_group = []
arc_group = []
Expand All @@ -120,22 +177,28 @@ def get_lines_and_arcs(self, decimal_places: int = 3, arc_threshold: int = 1):
line_group.append(point)
else:
# arc
# if close to previous length, add to previous arc
if np.isclose(line_length, previous_length, atol=1e-3):
arc_group[-1] = np.vstack((arc_group[-1], point1))
# if there is a common point and the length is
# close to previous length, add to previous arc
common_point = self.get_common_point(previous_arc_points, point)
if common_point is not None and np.isclose(
line_length, previous_length, atol=1e-3
):
mask = np.any(point != common_point, axis=1)
new_point = point[mask]
if np.array_equal(arc_group[-1][-1], common_point):
arc_group[-1] = np.vstack((arc_group[-1], new_point))
else:
arc_group[-1] = np.vstack((new_point, arc_group[-1]))
else:
# new arc
arc_group.append(point)

previous_arc_points = point
previous_length = line_length

# round to decimal places
line_group = round_shape_values(line_group, decimal_places)
arc_group = round_shape_values(arc_group, decimal_places)

if line_group:
lines.append(line_group)
if arc_group:
arcs.append(arc_group)
arcs.append(self.combine_same_arc(arc_group))

return lines, arcs

Expand Down Expand Up @@ -184,9 +247,16 @@ def get_arc_info(self, arc_points: np.ndarray, decimal_places: int = 3):
Radius of arc
center : np.array
Center of arc
is_circle : bool
True if arc is a circle
"""
return get_arc_info(arc_points, decimal_places=decimal_places)

def analyze(self):
lines, arcs = self.get_lines_and_arcs()
self.lines = lines
self.arcs = arcs


def round_shape_values(shapes: np.ndarray, decimal_places: int = 3):
for i in range(len(shapes)):
Expand Down
2 changes: 1 addition & 1 deletion lint.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash

poetry run black .
poetry run ruff check .
poetry run ruff check --fix .
Loading

0 comments on commit 2ced600

Please sign in to comment.