Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ dependencies = [
"compas>=2.15",
"compas_cgal>=0.9",
"loguru>=0.7",
"networkx>=3.6",
"networkx>=3.2",
"numpy>=2.0",
"progressbar2>=4.5",
"pyclipper>=1.4",
"rdp>=0.8",
"scipy>=1.16",
"pyclipper>=1.3",
"scipy>=1.10",
"tomli>=2.0; python_version < '3.11'",
]

Expand Down
8 changes: 4 additions & 4 deletions src/compas_slicer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import compas_slicer

if __name__ == '__main__':
logger.info(f'COMPAS: {compas.__version__}')
logger.info(f'COMPAS Slicer: {compas_slicer.__version__}')
logger.info('Awesome! Your installation worked! :)')
if __name__ == "__main__":
logger.info(f"COMPAS: {compas.__version__}")
logger.info(f"COMPAS Slicer: {compas_slicer.__version__}")
logger.info("Awesome! Your installation worked! :)")
10 changes: 4 additions & 6 deletions src/compas_slicer/_numpy_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,7 @@ def face_gradient_from_scalar_field(
cross2 = np.cross(v1 - v0, face_normals) # (F, 3)

# Compute gradient
grad = (
(u1 - u0)[:, np.newaxis] * cross1 + (u2 - u0)[:, np.newaxis] * cross2
) / (2 * face_areas[:, np.newaxis])
grad = ((u1 - u0)[:, np.newaxis] * cross1 + (u2 - u0)[:, np.newaxis] * cross2) / (2 * face_areas[:, np.newaxis])

return grad

Expand Down Expand Up @@ -187,9 +185,9 @@ def per_vertex_divergence(
e2 = v0 - v1 # edge opposite to v2

# Compute dot products with gradient
dot0 = np.einsum('ij,ij->i', X, e0) # (F,)
dot1 = np.einsum('ij,ij->i', X, e1) # (F,)
dot2 = np.einsum('ij,ij->i', X, e2) # (F,)
dot0 = np.einsum("ij,ij->i", X, e0) # (F,)
dot1 = np.einsum("ij,ij->i", X, e1) # (F,)
dot2 = np.einsum("ij,ij->i", X, e2) # (F,)

# Cotangent contributions (cotans[f, i] is cotan of angle at vertex i)
# For vertex i: contrib = cotan[k] * dot(X, e_i) + cotan[j] * dot(X, -e_k)
Expand Down
8 changes: 6 additions & 2 deletions src/compas_slicer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,14 @@ class InterpolationConfig(Data):
default_factory=lambda: _interpolation_defaults().get("vertical_layers_max_centroid_dist", 25.0)
)
target_low_geodesics_method: GeodesicsMethod = field(
default_factory=lambda: GeodesicsMethod(_interpolation_defaults().get("target_low_geodesics_method", "heat_igl"))
default_factory=lambda: GeodesicsMethod(
_interpolation_defaults().get("target_low_geodesics_method", "heat_igl")
)
)
target_high_geodesics_method: GeodesicsMethod = field(
default_factory=lambda: GeodesicsMethod(_interpolation_defaults().get("target_high_geodesics_method", "heat_igl"))
default_factory=lambda: GeodesicsMethod(
_interpolation_defaults().get("target_high_geodesics_method", "heat_igl")
)
)
target_high_union_method: UnionMethod = field(
default_factory=lambda: UnionMethod(_interpolation_defaults().get("target_high_union_method", "min"))
Expand Down
2 changes: 1 addition & 1 deletion src/compas_slicer/geometry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
from .print_point import * # noqa: F401 E402 F403
from .printpoints_collection import * # noqa: F401 E402 F403

__all__ = [name for name in dir() if not name.startswith('_')]
__all__ = [name for name in dir() if not name.startswith("_")]
5 changes: 1 addition & 4 deletions src/compas_slicer/geometry/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ def __from_data__(cls, data: dict[str, Any]) -> Path:
points_data = data["points"]
# Handle both list format and legacy dict format
if isinstance(points_data, dict):
pts = [
Point.__from_data__(points_data[key])
for key in sorted(points_data.keys(), key=lambda x: int(x))
]
pts = [Point.__from_data__(points_data[key]) for key in sorted(points_data.keys(), key=lambda x: int(x))]
else:
pts = [Point.__from_data__(p) for p in points_data]
return cls(points=pts, is_closed=data["is_closed"])
Expand Down
2 changes: 1 addition & 1 deletion src/compas_slicer/post_processing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@
from .unify_paths_orientation import * # noqa: F401 E402 F403
from .zig_zag_open_paths import * # noqa: F401 E402 F403

__all__ = [name for name in dir() if not name.startswith('_')]
__all__ = [name for name in dir() if not name.startswith("_")]
18 changes: 6 additions & 12 deletions src/compas_slicer/post_processing/generate_brim.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
try:
from compas_cgal.straight_skeleton_2 import offset_polygon as _cgal_offset
from compas_cgal.straight_skeleton_2 import offset_polygon_with_holes as _cgal_offset_with_holes

_USE_CGAL = True
except ImportError:
_cgal_offset = None
Expand All @@ -23,7 +24,7 @@
from compas_slicer.slicers import BaseSlicer


__all__ = ['generate_brim', 'offset_polygon', 'offset_polygon_with_holes']
__all__ = ["generate_brim", "offset_polygon", "offset_polygon_with_holes"]


def _offset_polygon_cgal(points: list[Point], offset: float, z: float) -> list[Point]:
Expand Down Expand Up @@ -83,16 +84,12 @@ def _offset_polygon_pyclipper(points: list[Point], offset: float, z: float) -> l
import pyclipper
from pyclipper import scale_from_clipper, scale_to_clipper

SCALING_FACTOR = 2 ** 32
SCALING_FACTOR = 2**32

xy_coords = [[p[0], p[1]] for p in points]

pco = pyclipper.PyclipperOffset()
pco.AddPath(
scale_to_clipper(xy_coords, SCALING_FACTOR),
pyclipper.JT_MITER,
pyclipper.ET_CLOSEDPOLYGON
)
pco.AddPath(scale_to_clipper(xy_coords, SCALING_FACTOR), pyclipper.JT_MITER, pyclipper.ET_CLOSEDPOLYGON)

result = scale_from_clipper(pco.Execute(offset * SCALING_FACTOR), SCALING_FACTOR)

Expand Down Expand Up @@ -132,10 +129,7 @@ def offset_polygon(points: list[Point], offset: float, z: float) -> list[Point]:


def offset_polygon_with_holes(
outer: list[Point],
holes: list[list[Point]],
offset: float,
z: float
outer: list[Point], holes: list[list[Point]], offset: float, z: float
) -> list[tuple[list[Point], list[list[Point]]]]:
"""Offset a polygon with holes using CGAL straight skeleton.

Expand Down Expand Up @@ -224,7 +218,7 @@ def generate_brim(slicer: BaseSlicer, layer_width: float, number_of_brim_offsets
has_vertical_layers = False

if len(paths_to_offset) == 0:
raise ValueError('Brim generator did not find any path on the base. Please check the paths of your slicer.')
raise ValueError("Brim generator did not find any path on the base. Please check the paths of your slicer.")

# (2) --- create new empty brim_layer
brim_layer = Layer(paths=[])
Expand Down
50 changes: 31 additions & 19 deletions src/compas_slicer/post_processing/generate_raft.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@
import compas_slicer
from compas_slicer.geometry import Layer, Path

__all__ = ['generate_raft']
__all__ = ["generate_raft"]


def generate_raft(slicer,
raft_offset=10,
distance_between_paths=10,
direction="xy_diagonal",
raft_layers=1,
raft_layer_height=None):
def generate_raft(
slicer, raft_offset=10, distance_between_paths=10, direction="xy_diagonal", raft_layers=1, raft_layer_height=None
):
"""Creates a raft.

Parameters
Expand Down Expand Up @@ -76,7 +73,7 @@ def generate_raft(slicer,

# create starting line for diagonal direction
if direction == "xy_diagonal":
c = math.sqrt(2*(distance_between_paths**2))
c = math.sqrt(2 * (distance_between_paths**2))

pt1 = Point(raft_start_pt[0] + c, raft_start_pt[1], raft_start_pt[2])
pt2 = Point(pt1[0] - y_range, pt1[1] + y_range, pt1[2])
Expand All @@ -86,10 +83,9 @@ def generate_raft(slicer,
for i, layer in enumerate(slicer.layers):
for j, path in enumerate(layer.paths):
for k, pt in enumerate(path.points):
slicer.layers[i].paths[j].points[k] = Point(pt[0], pt[1], pt[2] + (raft_layers)*raft_layer_height)
slicer.layers[i].paths[j].points[k] = Point(pt[0], pt[1], pt[2] + (raft_layers) * raft_layer_height)

for i in range(raft_layers):

iter = 0
raft_points = []

Expand All @@ -99,8 +95,16 @@ def generate_raft(slicer,
# VERTICAL RAFT
# ===============
if direction == "y_axis":
raft_pt1 = Point(raft_start_pt[0] + iter*distance_between_paths, raft_start_pt[1], raft_start_pt[2] + i*raft_layer_height)
raft_pt2 = Point(raft_start_pt[0] + iter*distance_between_paths, raft_start_pt[1] + y_range, raft_start_pt[2] + i*raft_layer_height)
raft_pt1 = Point(
raft_start_pt[0] + iter * distance_between_paths,
raft_start_pt[1],
raft_start_pt[2] + i * raft_layer_height,
)
raft_pt2 = Point(
raft_start_pt[0] + iter * distance_between_paths,
raft_start_pt[1] + y_range,
raft_start_pt[2] + i * raft_layer_height,
)

if raft_pt2[0] > bb_max_x_right or raft_pt1[0] > bb_max_x_right:
break
Expand All @@ -109,8 +113,16 @@ def generate_raft(slicer,
# HORIZONTAL RAFT
# ===============
elif direction == "x_axis":
raft_pt1 = Point(raft_start_pt[0], raft_start_pt[1] + iter*distance_between_paths, raft_start_pt[2] + i*raft_layer_height)
raft_pt2 = Point(raft_start_pt[0] + x_range, raft_start_pt[1] + iter*distance_between_paths, raft_start_pt[2] + i*raft_layer_height)
raft_pt1 = Point(
raft_start_pt[0],
raft_start_pt[1] + iter * distance_between_paths,
raft_start_pt[2] + i * raft_layer_height,
)
raft_pt2 = Point(
raft_start_pt[0] + x_range,
raft_start_pt[1] + iter * distance_between_paths,
raft_start_pt[2] + i * raft_layer_height,
)

if raft_pt2[1] > bb_max_y_top or raft_pt1[1] > bb_max_y_top:
break
Expand All @@ -120,21 +132,21 @@ def generate_raft(slicer,
# ===============
elif direction == "xy_diagonal":
# create offset of the initial diagonal line
offset_l = offset_line(line, iter*distance_between_paths, Vector(0, 0, -1))
offset_l = offset_line(line, iter * distance_between_paths, Vector(0, 0, -1))

# get intersections for the initial diagonal line with the left and bottom of the bb
int_left = intersection_line_line(offset_l, [bb_xy_offset[0], bb_xy_offset[3]])
int_bottom = intersection_line_line(offset_l, [bb_xy_offset[0], bb_xy_offset[1]])

# get the points at the intersections
raft_pt1 = Point(int_left[0][0], int_left[0][1], int_left[0][2] + i*raft_layer_height)
raft_pt2 = Point(int_bottom[0][0], int_bottom[0][1], int_bottom[0][2] + i*raft_layer_height)
raft_pt1 = Point(int_left[0][0], int_left[0][1], int_left[0][2] + i * raft_layer_height)
raft_pt2 = Point(int_bottom[0][0], int_bottom[0][1], int_bottom[0][2] + i * raft_layer_height)

# if the intersection goes beyond the height of the left side of the bounding box:
if int_left[0][1] > bb_max_y_top:
# create intersection with the top side
int_top = intersection_line_line(offset_l, [bb_xy_offset[3], bb_xy_offset[2]])
raft_pt1 = Point(int_top[0][0], int_top[0][1], int_top[0][2] + i*raft_layer_height)
raft_pt1 = Point(int_top[0][0], int_top[0][1], int_top[0][2] + i * raft_layer_height)

# if intersection goes beyond the length of the top side, break
if raft_pt1[0] > bb_max_x_right:
Expand All @@ -144,7 +156,7 @@ def generate_raft(slicer,
if int_bottom[0][0] > bb_max_x_right:
# create intersection with the right side
int_right = intersection_line_line(offset_l, [bb_xy_offset[1], bb_xy_offset[2]])
raft_pt2 = Point(int_right[0][0], int_right[0][1], int_right[0][2] + i*raft_layer_height)
raft_pt2 = Point(int_right[0][0], int_right[0][1], int_right[0][2] + i * raft_layer_height)

# if intersection goes beyond the height of the right side, break
if raft_pt2[1] > bb_xy_offset[2][1]:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Medial axis based infill generation using CGAL straight skeleton."""

from __future__ import annotations

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -64,9 +65,7 @@ def generate_medial_axis_infill(
continue

# Extract skeleton edges as paths
skeleton_paths = _skeleton_to_paths(
graph, z_height, min_length, include_bisectors
)
skeleton_paths = _skeleton_to_paths(graph, z_height, min_length, include_bisectors)
infill_paths.extend(skeleton_paths)

# Add infill paths to layer
Expand Down
10 changes: 6 additions & 4 deletions src/compas_slicer/post_processing/reorder_vertical_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from compas_slicer.slicers import BaseSlicer


__all__ = ['reorder_vertical_layers']
__all__ = ["reorder_vertical_layers"]

AlignWith = Literal["x_axis", "y_axis"]

Expand All @@ -29,9 +29,9 @@ def reorder_vertical_layers(slicer: BaseSlicer, align_with: AlignWith | Point) -
"""

if align_with == "x_axis":
align_pt = Point(2 ** 32, 0, 0)
align_pt = Point(2**32, 0, 0)
elif align_with == "y_axis":
align_pt = Point(0, 2 ** 32, 0)
align_pt = Point(0, 2**32, 0)
elif isinstance(align_with, Point):
align_pt = align_with
else:
Expand All @@ -56,7 +56,9 @@ def reorder_vertical_layers(slicer: BaseSlicer, align_with: AlignWith | Point) -
distances = []
for vert_layer in grouped_layers:
# recreate head_centroid_pt as compas.Point
head_centroid_pt = Point(vert_layer.head_centroid[0], vert_layer.head_centroid[1], vert_layer.head_centroid[2])
head_centroid_pt = Point(
vert_layer.head_centroid[0], vert_layer.head_centroid[1], vert_layer.head_centroid[2]
)
# measure distance
distances.append(distance_point_point(head_centroid_pt, align_pt))

Expand Down
7 changes: 3 additions & 4 deletions src/compas_slicer/post_processing/seams_align.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from compas_slicer.slicers import BaseSlicer


__all__ = ['seams_align']
__all__ = ["seams_align"]

AlignWith = Literal["next_path", "origin", "x_axis", "y_axis"]

Expand All @@ -36,7 +36,6 @@ def seams_align(slicer: BaseSlicer, align_with: AlignWith | Point = "next_path")

for i, layer in enumerate(slicer.layers):
for j, path in enumerate(layer.paths):

if align_with == "next_path":
pt_to_align_with = None # make sure aligning point is cleared

Expand Down Expand Up @@ -67,9 +66,9 @@ def seams_align(slicer: BaseSlicer, align_with: AlignWith | Point = "next_path")
elif align_with == "origin":
pt_to_align_with = Point(0, 0, 0)
elif align_with == "x_axis":
pt_to_align_with = Point(2 ** 32, 0, 0)
pt_to_align_with = Point(2**32, 0, 0)
elif align_with == "y_axis":
pt_to_align_with = Point(0, 2 ** 32, 0)
pt_to_align_with = Point(0, 2**32, 0)
elif isinstance(align_with, Point):
pt_to_align_with = align_with
else:
Expand Down
4 changes: 2 additions & 2 deletions src/compas_slicer/post_processing/seams_smooth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from compas_slicer.slicers import BaseSlicer


__all__ = ['seams_smooth']
__all__ = ["seams_smooth"]


def seams_smooth(slicer: BaseSlicer, smooth_distance: float) -> None:
Expand All @@ -34,7 +34,7 @@ def seams_smooth(slicer: BaseSlicer, smooth_distance: float) -> None:
if path.is_closed: # only for closed paths
pt0 = path.points[0]
# only points in the first half of a path should be evaluated
half_of_path = path.points[:int(len(path.points)/2)]
half_of_path = path.points[: int(len(path.points) / 2)]
for point in half_of_path:
if distance_point_point(pt0, point) < smooth_distance:
# remove points if within smooth_distance
Expand Down
Loading
Loading