From a4fe7d49339810457ac9d612b69919e52bf59fb9 Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Sun, 7 Jan 2024 14:30:01 +0900 Subject: [PATCH 1/7] Add group_by_common_point method to Shape class --- cnceye/shape.py | 27 +++++++++++++++++++++++++++ tests/test_shape.py | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/cnceye/shape.py b/cnceye/shape.py index 628c7dc..a92af2d 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -138,6 +138,33 @@ def combine_same_arc(self, arc_group): new_arc_group.append(prev_points) return new_arc_group + def group_by_common_point(self, coplanar_shapes): + """ + Group coplanar shapes by common point + """ + + def _get_point(_coplanar_shape): + point0 = self.mesh.vertices[_coplanar_shape[0]] + point1 = self.mesh.vertices[_coplanar_shape[1]] + return np.array([point0, point1]) + + previous_points = _get_point(coplanar_shapes[0]) + point_groups = [previous_points] + for i in range(1, len(coplanar_shapes)): + point = _get_point(coplanar_shapes[i]) + common_point = self.get_common_point(previous_points, point) + if common_point is not None: + mask = np.any(point != common_point, axis=1) + new_point = point[mask] + if np.array_equal(point_groups[-1][-1], common_point): + point_groups[-1] = np.vstack((point_groups[-1], new_point)) + else: + point_groups[-1] = np.vstack((new_point, point_groups[-1])) + else: + point_groups.append(point) + previous_points = point + return point_groups + def get_lines_and_arcs(self, arc_threshold: int = 1): """ Extract lines and arcs from an STL file \n diff --git a/tests/test_shape.py b/tests/test_shape.py index f395310..98263ef 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -174,3 +174,28 @@ def test_cadquery_models_filleting(): # assert is_circle is True print(radius, center, is_circle) # assert radius == small_hole_diameter / 2 or radius == diameter / 2 + + +def test_group_by_common_point(): + height = 20.0 + width = 30.0 + thickness = 10.0 + radius = 82.0 + + result = cq.Workplane("front").circle(radius).rect(height, width).extrude(thickness) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + shapes = shape.get_shapes() + point_groups = shape.group_by_common_point(shapes[0]) + assert len(point_groups) == 2 + assert len(point_groups[0]) == 127 + assert len(point_groups[1]) == 5 + + +def test_group_by_common_point_with_line_and_arc(): + shape = Shape("tests/fixtures/stl/sample.stl") + shapes = shape.get_shapes() + point_groups = shape.group_by_common_point(shapes[0]) + assert len(point_groups) == 3 From 56613d5f7a625103eaedb7a5a91209533dd5454b Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Sun, 7 Jan 2024 22:30:55 +0900 Subject: [PATCH 2/7] Add tests for grouping shapes by common point and creating cadquery models --- cnceye/shape.py | 146 ++++++++++++++++++++++++++++++++------------ tests/test_shape.py | 101 ++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 40 deletions(-) diff --git a/cnceye/shape.py b/cnceye/shape.py index a92af2d..f4c9798 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -148,24 +148,86 @@ def _get_point(_coplanar_shape): point1 = self.mesh.vertices[_coplanar_shape[1]] return np.array([point0, point1]) - previous_points = _get_point(coplanar_shapes[0]) - point_groups = [previous_points] + point_groups = [_get_point(coplanar_shapes[0])] for i in range(1, len(coplanar_shapes)): point = _get_point(coplanar_shapes[i]) - common_point = self.get_common_point(previous_points, point) - if common_point is not None: - mask = np.any(point != common_point, axis=1) - new_point = point[mask] - if np.array_equal(point_groups[-1][-1], common_point): - point_groups[-1] = np.vstack((point_groups[-1], new_point)) - else: - point_groups[-1] = np.vstack((new_point, point_groups[-1])) - else: + + duplicate = False + for i in range(len(point_groups)): + point_group = point_groups[i] + first_and_last_point = np.array([point_group[0], point_group[-1]]) + common_point = self.get_common_point(first_and_last_point, point) + if common_point is not None: + mask = np.any(point != common_point, axis=1) + new_point = point[mask] + if np.array_equal(point_groups[i][-1], common_point): + point_groups[i] = np.vstack((point_groups[i], new_point)) + else: + point_groups[i] = np.vstack((new_point, point_groups[i])) + duplicate = True + break + if not duplicate: point_groups.append(point) - previous_points = point - return point_groups - def get_lines_and_arcs(self, arc_threshold: int = 1): + groups = [] + missing_point_groups = [] + for point_group in point_groups: + if np.array_equal(point_group[0], point_group[-1]): + groups.append(point_group) + else: + for missing_point_group in missing_point_groups: + if np.array_equal(missing_point_group[0], point_group[-1]): + combined_group = np.vstack((point_group, missing_point_group)) + groups.append(combined_group) + continue + elif np.array_equal(missing_point_group[-1], point_group[0]): + combined_group = np.vstack((missing_point_group, point_group)) + groups.append(combined_group) + continue + elif np.array_equal(missing_point_group[0], point_group[0]): + combined_group = np.vstack( + (point_group[::-1], missing_point_group) + ) + groups.append(combined_group) + continue + elif np.array_equal(missing_point_group[-1], point_group[-1]): + combined_group = np.vstack( + (missing_point_group, point_group[::-1]) + ) + groups.append(combined_group) + continue + missing_point_groups.append(point_group) + + # first and last point should be the same + for group in groups: + assert np.array_equal(group[0], group[-1]) + return groups + + def get_line_angle(self, line0: np.array, line1: np.array): + """ + Get the angle between two lines + """ + line0 = line0[1] - line0[0] + line1 = line1[1] - line1[0] + return np.arccos( + np.dot(line0, line1) / (np.linalg.norm(line0) * np.linalg.norm(line1)) + ) + + def get_line_length(self, line: np.array): + """ + Get the length of a line + """ + return np.linalg.norm(line[1] - line[0]) + + def get_line_diff_percentage(self, line0: np.array, line1: np.array): + """ + Get the difference in length between two lines + """ + line_length0 = self.get_line_length(line0) + line_length1 = self.get_line_length(line1) + return abs(line_length0 - line_length1) / line_length0 + + def get_lines_and_arcs(self, angle_threshold: int = 0.1): """ Extract lines and arcs from an STL file \n If the line length is less than 1, it is considered an arc. @@ -189,38 +251,42 @@ def get_lines_and_arcs(self, arc_threshold: int = 1): lines = [] arcs = [] - previous_length = 0 - previous_arc_points = None for coplanar_shapes in shapes: + point_groups = self.group_by_common_point(coplanar_shapes) line_group = [] arc_group = [] - for i in range(len(coplanar_shapes)): - point0 = self.mesh.vertices[coplanar_shapes[i][0]] - point1 = self.mesh.vertices[coplanar_shapes[i][1]] - point = np.array([point0, point1]) - line_length = np.linalg.norm(point0 - point1) - if line_length > arc_threshold: - # line - line_group.append(point) - else: - # arc - # 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 + + for point_group in point_groups: + arc_start_idx = None + for i in range(len(point_group) - 2): + line0 = np.array([point_group[i], point_group[i + 1]]) + line1 = np.array([point_group[i + 1], point_group[i + 2]]) + line_angle = self.get_line_angle(line0, line1) + if ( + abs(line_angle) < angle_threshold + and self.get_line_diff_percentage(line0, line1) < 0.01 ): - 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)) + # arc + if arc_start_idx is None: + arc_start_idx = i + elif i == len(point_group) - 3: + arc_points = point_group[arc_start_idx:] + arc_group.append(arc_points) + arc_start_idx = None else: - arc_group[-1] = np.vstack((new_point, arc_group[-1])) + continue else: - # new arc - arc_group.append(point) - previous_arc_points = point - previous_length = line_length + # line + if arc_start_idx is not None: + arc_points = point_group[arc_start_idx : i + 1] + arc_group.append(arc_points) + arc_start_idx = None + if i != len(point_group) - 3: + line_group.append(line1) + else: + line_group.append(line0) + if i == len(point_group) - 3: + line_group.append(line1) if line_group: lines.append(line_group) diff --git a/tests/test_shape.py b/tests/test_shape.py index 98263ef..6d73e3e 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -137,6 +137,38 @@ def test_cadquery_models_more_holes(): assert radius == small_hole_diameter / 2 or radius == diameter / 2 +def test_group_by_common_point_cadquery_models_filleting(): + height = 60.0 + width = 80.0 + thickness = 10.0 + diameter = 22.0 + padding = 12.0 + small_hole_diameter = 4.4 + + result = ( + cq.Workplane("XY") + .box(height, width, thickness) + .faces(">Z") + .workplane() + .hole(diameter) + .faces(">Z") + .workplane() + .rect(height - padding, width - padding, forConstruction=True) + .vertices() + .cboreHole(2.4, small_hole_diameter, 2.1) + .edges("|Z") + .fillet(2.0) + ) + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + shapes = shape.get_shapes() + point_groups = shape.group_by_common_point(shapes[0]) + assert len(point_groups) == 6 + point_groups = shape.group_by_common_point(shapes[1]) + assert len(point_groups) == 8 + + def test_cadquery_models_filleting(): height = 60.0 width = 80.0 @@ -199,3 +231,72 @@ def test_group_by_common_point_with_line_and_arc(): shapes = shape.get_shapes() point_groups = shape.group_by_common_point(shapes[0]) assert len(point_groups) == 3 + + +def test_cadquery_model_rectangle_inside_circle(): + height = 20.0 + width = 30.0 + thickness = 10.0 + radius = 82.0 + + result = cq.Workplane("front").circle(radius).rect(height, width).extrude(thickness) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 4 + assert len(arcs[0]) == 1 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + # assert is_circle is True + assert radius == _radius + + +def test_with_a_large_model(): + height = 200.0 + width = 300.0 + thickness = 100.0 + radius = 802.0 + + result = cq.Workplane("front").circle(radius).rect(height, width).extrude(thickness) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 4 + assert len(arcs[0]) == 1 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + # assert is_circle is True + assert radius == _radius + + +def test_with_a_small_model(): + height = 0.02 + width = 0.03 + thickness = 0.1 + radius = 0.8 + + result = cq.Workplane("front").circle(radius).rect(height, width).extrude(thickness) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 4 + assert len(arcs[0]) == 1 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + # assert is_circle is True + assert radius == _radius From c67387b64ba5a4545bdc6caa9e7db11b1fc44dd7 Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:31:34 +0900 Subject: [PATCH 3/7] remove duplicates --- cnceye/shape.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cnceye/shape.py b/cnceye/shape.py index f4c9798..881240e 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -177,22 +177,26 @@ def _get_point(_coplanar_shape): else: for missing_point_group in missing_point_groups: if np.array_equal(missing_point_group[0], point_group[-1]): - combined_group = np.vstack((point_group, missing_point_group)) + combined_group = np.vstack( + (point_group, missing_point_group[1:]) + ) groups.append(combined_group) continue elif np.array_equal(missing_point_group[-1], point_group[0]): - combined_group = np.vstack((missing_point_group, point_group)) + combined_group = np.vstack( + (missing_point_group, point_group[1:]) + ) groups.append(combined_group) continue elif np.array_equal(missing_point_group[0], point_group[0]): combined_group = np.vstack( - (point_group[::-1], missing_point_group) + (point_group[::-1], missing_point_group[1:]) ) groups.append(combined_group) continue elif np.array_equal(missing_point_group[-1], point_group[-1]): combined_group = np.vstack( - (missing_point_group, point_group[::-1]) + (missing_point_group[:-1], point_group[::-1]) ) groups.append(combined_group) continue @@ -281,8 +285,6 @@ def get_lines_and_arcs(self, angle_threshold: int = 0.1): arc_points = point_group[arc_start_idx : i + 1] arc_group.append(arc_points) arc_start_idx = None - if i != len(point_group) - 3: - line_group.append(line1) else: line_group.append(line0) if i == len(point_group) - 3: From 1d622d4d3d9db677c0236c8af6ea69c323a984f7 Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Mon, 8 Jan 2024 09:59:09 +0900 Subject: [PATCH 4/7] more examples --- tests/test_shape.py | 165 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/tests/test_shape.py b/tests/test_shape.py index 6d73e3e..ef6110e 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -1,5 +1,6 @@ from cnceye import Shape import cadquery as cq +import pytest def test_are_facets_on_same_plane(): @@ -300,3 +301,167 @@ def test_with_a_small_model(): _radius, center, is_circle = shape.get_arc_info(arc_points) # assert is_circle is True assert radius == _radius + + +def test_with_a_prismatic_solid(): + result = ( + cq.Workplane("front") + .lineTo(2.0, 0) + .lineTo(2.0, 1.0) + .threePointArc((1.0, 1.5), (0.0, 1.0)) + .close() + .extrude(0.25) + ) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 3 + assert len(arcs[0]) == 1 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + print(_radius, center, is_circle) + + +def test_multiple_holes_inside_circle(): + result = cq.Workplane("front").circle( + 3.0 + ) # current point is the center of the circle, at (0, 0) + result = result.center(1.5, 0.0).rect(0.5, 0.5) # new work center is (1.5, 0.0) + + result = result.center(-1.5, 1.5).circle(0.25) # new work center is (0.0, 1.5). + # The new center is specified relative to the previous center, not global coordinates! + + result = result.extrude(0.25) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 4 + assert len(arcs[0]) == 2 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + print(_radius, center, is_circle) + + +def test_using_join_list(): + r = cq.Workplane("front").circle(2.0) # make base + r = r.pushPoints( + [(1.5, 0), (0, 1.5), (-1.5, 0), (0, -1.5)] + ) # now four points are on the stack + r = r.circle(0.25) # circle will operate on all four points + result = r.extrude(0.125) # make prism + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 0 + assert len(arcs) == 1 + assert len(arcs[0]) == 5 + + for arc_points in arcs[0]: + _radius, center, is_circle = shape.get_arc_info(arc_points) + print(_radius, center, is_circle) + + +def test_polygons(): + result = ( + cq.Workplane("front") + .box(3.0, 4.0, 0.25) + .pushPoints([(0, 0.75), (0, -0.75)]) + .polygon(6, 1.0) + .cutThruAll() + ) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 0 + assert len(lines[0]) == 16 + + +def test_polylines(): + (L, H, W, t) = (100.0, 20.0, 20.0, 1.0) + pts = [ + (0, H / 2.0), + (W / 2.0, H / 2.0), + (W / 2.0, (H / 2.0 - t)), + (t / 2.0, (H / 2.0 - t)), + (t / 2.0, (t - H / 2.0)), + (W / 2.0, (t - H / 2.0)), + (W / 2.0, H / -2.0), + (0, H / -2.0), + ] + result = cq.Workplane("front").polyline(pts).mirrorY().extrude(L) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 0 + assert len(lines[0]) == 12 + + +@pytest.mark.skip(reason="Not implemented yet") +def test_spline(): + s = cq.Workplane("XY") + sPnts = [ + (2.75, 1.5), + (2.5, 1.75), + (2.0, 1.5), + (1.5, 1.0), + (1.0, 1.25), + (0.5, 1.0), + (0, 1.0), + ] + r = s.lineTo(3.0, 0).lineTo(3.0, 1.0).spline(sPnts, includeCurrent=True).close() + result = r.extrude(0.5) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) > 0 + assert len(lines[0]) == 3 + + +def test_mirror_2D_geometry(): + r = cq.Workplane("front").hLine(1.0) # 1.0 is the distance, not coordinate + r = ( + r.vLine(0.5).hLine(-0.25).vLine(-0.25).hLineTo(0.0) + ) # hLineTo allows using xCoordinate not distance + result = r.mirrorY().extrude(0.25) # mirror the geometry and extrude + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 0 + assert len(lines[0]) == 8 + + +def test_mirror_from_faces(): + result = cq.Workplane("XY").line(0, 1).line(1, 0).line(0, -0.5).close().extrude(1) + result = result.mirror(result.faces(">X"), union=True) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 1 + assert len(arcs) == 0 + assert len(lines[0]) == 5 From ae7fa252679784bcad8c6b390c9ebefadfaab1a2 Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Mon, 8 Jan 2024 10:42:35 +0900 Subject: [PATCH 5/7] upgrade version --- cnceye/shape.py | 65 ++++++++++++++++++++++++--------------------- pyproject.toml | 2 +- tests/test_shape.py | 17 ++++++++++++ 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/cnceye/shape.py b/cnceye/shape.py index 881240e..fb9d670 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -138,37 +138,7 @@ def combine_same_arc(self, arc_group): new_arc_group.append(prev_points) return new_arc_group - def group_by_common_point(self, coplanar_shapes): - """ - Group coplanar shapes by common point - """ - - def _get_point(_coplanar_shape): - point0 = self.mesh.vertices[_coplanar_shape[0]] - point1 = self.mesh.vertices[_coplanar_shape[1]] - return np.array([point0, point1]) - - point_groups = [_get_point(coplanar_shapes[0])] - for i in range(1, len(coplanar_shapes)): - point = _get_point(coplanar_shapes[i]) - - duplicate = False - for i in range(len(point_groups)): - point_group = point_groups[i] - first_and_last_point = np.array([point_group[0], point_group[-1]]) - common_point = self.get_common_point(first_and_last_point, point) - if common_point is not None: - mask = np.any(point != common_point, axis=1) - new_point = point[mask] - if np.array_equal(point_groups[i][-1], common_point): - point_groups[i] = np.vstack((point_groups[i], new_point)) - else: - point_groups[i] = np.vstack((new_point, point_groups[i])) - duplicate = True - break - if not duplicate: - point_groups.append(point) - + def connect_same_group(self, point_groups): groups = [] missing_point_groups = [] for point_group in point_groups: @@ -207,6 +177,39 @@ def _get_point(_coplanar_shape): assert np.array_equal(group[0], group[-1]) return groups + def group_by_common_point(self, coplanar_shapes): + """ + Group coplanar shapes by common point + """ + + def _get_point(_coplanar_shape): + point0 = self.mesh.vertices[_coplanar_shape[0]] + point1 = self.mesh.vertices[_coplanar_shape[1]] + return np.array([point0, point1]) + + point_groups = [_get_point(coplanar_shapes[0])] + for i in range(1, len(coplanar_shapes)): + point = _get_point(coplanar_shapes[i]) + + duplicate = False + for i in range(len(point_groups)): + point_group = point_groups[i] + first_and_last_point = np.array([point_group[0], point_group[-1]]) + common_point = self.get_common_point(first_and_last_point, point) + if common_point is not None: + mask = np.any(point != common_point, axis=1) + new_point = point[mask] + if np.array_equal(point_groups[i][-1], common_point): + point_groups[i] = np.vstack((point_groups[i], new_point)) + else: + point_groups[i] = np.vstack((new_point, point_groups[i])) + duplicate = True + break + if not duplicate: + point_groups.append(point) + + return self.connect_same_group(point_groups) + def get_line_angle(self, line0: np.array, line1: np.array): """ Get the angle between two lines diff --git a/pyproject.toml b/pyproject.toml index 23bff6b..8e17ed3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cnceye" -version = "0.5.0" +version = "0.5.1" description = "CMM python library" license = "MIT" authors = ["yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com>"] diff --git a/tests/test_shape.py b/tests/test_shape.py index ef6110e..bf32078 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -465,3 +465,20 @@ def test_mirror_from_faces(): assert len(lines) == 1 assert len(arcs) == 0 assert len(lines[0]) == 5 + + +# def test_cut_a_corner_out(): +# result = cq.Workplane("front").box(10, 6, 2.0) # make a basic prism +# result = ( +# result.faces(">Z").vertices(" Date: Mon, 8 Jan 2024 11:13:54 +0900 Subject: [PATCH 6/7] update comments --- cnceye/shape.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/cnceye/shape.py b/cnceye/shape.py index fb9d670..b37f17f 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -139,6 +139,10 @@ def combine_same_arc(self, arc_group): return new_arc_group def connect_same_group(self, point_groups): + """ + Connect point groups that are the same \n + Check if the first and last point are the same \n + """ groups = [] missing_point_groups = [] for point_group in point_groups: @@ -150,26 +154,21 @@ def connect_same_group(self, point_groups): combined_group = np.vstack( (point_group, missing_point_group[1:]) ) - groups.append(combined_group) - continue elif np.array_equal(missing_point_group[-1], point_group[0]): combined_group = np.vstack( (missing_point_group, point_group[1:]) ) - groups.append(combined_group) - continue elif np.array_equal(missing_point_group[0], point_group[0]): combined_group = np.vstack( (point_group[::-1], missing_point_group[1:]) ) - groups.append(combined_group) - continue elif np.array_equal(missing_point_group[-1], point_group[-1]): combined_group = np.vstack( (missing_point_group[:-1], point_group[::-1]) ) - groups.append(combined_group) + else: continue + groups.append(combined_group) missing_point_groups.append(point_group) # first and last point should be the same @@ -234,18 +233,18 @@ def get_line_diff_percentage(self, line0: np.array, line1: np.array): line_length1 = self.get_line_length(line1) return abs(line_length0 - line_length1) / line_length0 - def get_lines_and_arcs(self, angle_threshold: int = 0.1): + def get_lines_and_arcs(self, angle_threshold: float = 0.1): """ Extract lines and arcs from an STL file \n - If the line length is less than 1, it is considered an arc. - If the line length for an arc is close to the previous arc length, - it is considered part of the previous arc. \n + If the line angle between two lines is close to 0 and + the line length is close to the previous line length, + it is considered an arc. \n Note: This is not a robust algorithm. Parameters ---------- - arc_threshold : int - Length threshold to determine if a line is an arc + angle_threshold : int + Angle threshold for arc Returns ------- From 6e41bf90fc5a842601b89fb022c43d80b0fb4f82 Mon Sep 17 00:00:00 2001 From: yuichiroaoki <45054071+yuichiroaoki@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:35:48 +0900 Subject: [PATCH 7/7] more examples --- cnceye/shape.py | 16 +++++++-- tests/test_shape.py | 81 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 18 deletions(-) diff --git a/cnceye/shape.py b/cnceye/shape.py index b37f17f..ca273fe 100644 --- a/cnceye/shape.py +++ b/cnceye/shape.py @@ -149,6 +149,7 @@ def connect_same_group(self, point_groups): if np.array_equal(point_group[0], point_group[-1]): groups.append(point_group) else: + found_pair = False for missing_point_group in missing_point_groups: if np.array_equal(missing_point_group[0], point_group[-1]): combined_group = np.vstack( @@ -168,8 +169,17 @@ def connect_same_group(self, point_groups): ) else: continue - groups.append(combined_group) - missing_point_groups.append(point_group) + if np.array_equal(combined_group[0], combined_group[-1]): + groups.append(combined_group) + missing_point_groups.remove(missing_point_group) + else: + # replace missing_point_group with combined_group + missing_point_groups.remove(missing_point_group) + missing_point_groups.append(combined_group) + found_pair = True + break + if not found_pair: + missing_point_groups.append(point_group) # first and last point should be the same for group in groups: @@ -236,7 +246,7 @@ def get_line_diff_percentage(self, line0: np.array, line1: np.array): def get_lines_and_arcs(self, angle_threshold: float = 0.1): """ Extract lines and arcs from an STL file \n - If the line angle between two lines is close to 0 and + If the line angle between two lines is close to 0 and the line length is close to the previous line length, it is considered an arc. \n Note: This is not a robust algorithm. diff --git a/tests/test_shape.py b/tests/test_shape.py index bf32078..a0c4f4d 100644 --- a/tests/test_shape.py +++ b/tests/test_shape.py @@ -467,18 +467,69 @@ def test_mirror_from_faces(): assert len(lines[0]) == 5 -# def test_cut_a_corner_out(): -# result = cq.Workplane("front").box(10, 6, 2.0) # make a basic prism -# result = ( -# result.faces(">Z").vertices("Z").vertices("Z") + .workplane() + .rect(1.5, 1.5, forConstruction=True) + .vertices() + .hole(0.125) + ) + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs(0.3) + assert len(lines) == 1 + assert len(arcs) == 1 + assert len(lines[0]) == 4 + assert len(arcs[0]) == 4 + + +def test_shelling(): + result = cq.Workplane("front").box(2, 2, 2).faces("+Z or -X or +X").shell(0.1) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs() + assert len(lines) == 2 + assert len(arcs) == 0 + assert len(lines[0]) == 4 + assert len(lines[1]) == 8 + + +@pytest.mark.skip(reason="Not implemented yet") +def test_making_lofts(): + result = ( + cq.Workplane("front") + .box(4.0, 4.0, 0.25) + .faces(">Z") + .circle(1.5) + .workplane(offset=3.0) + .rect(0.75, 0.5) + .loft(combine=True) + ) + + stl_filename = "tests/fixtures/stl/cq/cadquery_model.stl" + cq.exporters.export(result, stl_filename) + shape = Shape(stl_filename) + lines, arcs = shape.get_lines_and_arcs()