autoforge 1.4.1__tar.gz → 1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {autoforge-1.4.1 → autoforge-1.4.2}/PKG-INFO +1 -1
- {autoforge-1.4.1 → autoforge-1.4.2}/pyproject.toml +1 -1
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/PKG-INFO +1 -1
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/ImageHelper.py +8 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/OutputHelper.py +93 -65
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/auto_forge.py +48 -18
- {autoforge-1.4.1 → autoforge-1.4.2}/README.md +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/setup.cfg +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/SOURCES.txt +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/dependency_links.txt +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/entry_points.txt +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/requires.txt +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/AutoForge.egg-info/top_level.txt +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/Views/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/Views/backend_database.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/Views/filament_editor.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/app.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Gui/main.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/FilamentHelper.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/OptimizerHelper.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/PruningHelper.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Helper/filamentcolors_library.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Loss/LossFunctions.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Loss/PerceptionLoss.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Loss/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Misc/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Misc/color_fit_all.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Modules/Optimizer.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/Modules/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/__init__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/__main__.py +0 -0
- {autoforge-1.4.1 → autoforge-1.4.2}/src/autoforge/old_jax_files.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "AutoForge"
|
|
7
|
-
version="1.4.
|
|
7
|
+
version="1.4.2"
|
|
8
8
|
description = "AutoForge is a Python tool for generating 3D printed layered models from an input image."
|
|
9
9
|
authors = [{ name = "Hendric Voss", email = "hvoss@techfak.uni-bielefeld.de" }]
|
|
10
10
|
readme = "README.md"
|
|
@@ -23,6 +23,14 @@ def resize_image(img, max_size):
|
|
|
23
23
|
return img_out
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def resize_image_exact(img, new_w, new_h):
|
|
27
|
+
img_out = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_CUBIC)
|
|
28
|
+
# INTER_CUBIC is actually not a good choice for shrinking images. Except in our case.
|
|
29
|
+
# As we shrink the image, we lose information and the image becomes blurry with INTER_AREA. This is detrimental for the solver. If you want to try it out be my guest.
|
|
30
|
+
# INTER_AREA actually destroys a lot of the colors of our solver.
|
|
31
|
+
return img_out
|
|
32
|
+
|
|
33
|
+
|
|
26
34
|
def srgb_to_lab(srgb, eps=1e-6):
|
|
27
35
|
"""
|
|
28
36
|
Converts an sRGB image (values in [0, 255]) to the CIELAB color space.
|
|
@@ -186,37 +186,50 @@ def generate_project_file(
|
|
|
186
186
|
json.dump(project_data, f, indent=4)
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
import numpy as np
|
|
190
|
+
import struct
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def generate_stl(height_map, filename, background_height, maximum_x_y_size, alpha_mask=None):
|
|
190
194
|
"""
|
|
191
|
-
Generate a binary STL file from a height map with
|
|
192
|
-
|
|
193
|
-
|
|
195
|
+
Generate a binary STL file from a height map with an optional alpha mask.
|
|
196
|
+
If alpha_mask is provided, vertices where alpha < 128 are omitted.
|
|
197
|
+
This function builds a manifold mesh consisting of:
|
|
198
|
+
- a top surface (only quads whose four vertices are valid),
|
|
199
|
+
- side walls along the boundary edges of the top surface, and
|
|
200
|
+
- a bottom face covering the valid region.
|
|
194
201
|
|
|
195
202
|
Args:
|
|
196
203
|
height_map (np.ndarray): 2D array representing the height map.
|
|
197
204
|
filename (str): The name of the output STL file.
|
|
198
205
|
background_height (float): The height of the background in the STL model.
|
|
199
206
|
maximum_x_y_size (float): Maximum size (in millimeters) for the x and y dimensions.
|
|
207
|
+
alpha_mask (np.ndarray): Optional alpha mask (same shape as height_map).
|
|
208
|
+
A pixel is “valid” only if its alpha is ≥ 128.
|
|
200
209
|
"""
|
|
201
210
|
H, W = height_map.shape
|
|
202
211
|
|
|
203
|
-
#
|
|
204
|
-
|
|
212
|
+
# Compute valid mask: True if no alpha mask or alpha >= 128.
|
|
213
|
+
if alpha_mask is None:
|
|
214
|
+
valid_mask = np.ones((H, W), dtype=bool)
|
|
215
|
+
else:
|
|
216
|
+
valid_mask = (alpha_mask >= 128)
|
|
217
|
+
|
|
218
|
+
# --- Create vertices for the top surface ---
|
|
219
|
+
# We compute a grid of vertices (even for masked-out regions) but later only use those that are valid.
|
|
205
220
|
top_vertices = np.zeros((H, W, 3), dtype=np.float32)
|
|
206
221
|
for i in range(H):
|
|
207
222
|
for j in range(W):
|
|
208
223
|
top_vertices[i, j, 0] = j
|
|
209
|
-
|
|
224
|
+
# Note: y coordinate is set so that row 0 is at the top.
|
|
225
|
+
top_vertices[i, j, 1] = H - 1 - i
|
|
210
226
|
top_vertices[i, j, 2] = height_map[i, j] + background_height
|
|
211
227
|
|
|
212
|
-
# Create
|
|
213
|
-
# but setting z to 0.
|
|
228
|
+
# Create corresponding bottom vertices (same x,y but z = 0)
|
|
214
229
|
bottom_vertices = top_vertices.copy()
|
|
215
230
|
bottom_vertices[:, :, 2] = 0
|
|
216
231
|
|
|
217
232
|
# --- Scale vertices so that the maximum x or y dimension equals maximum_x_y_size ---
|
|
218
|
-
# The current extents in x and y are 0 to (W - 1) and 0 to (H - 1), respectively.
|
|
219
|
-
# We use the maximum of these as our original size.
|
|
220
233
|
original_max = max(W - 1, H - 1)
|
|
221
234
|
scale = maximum_x_y_size / original_max
|
|
222
235
|
top_vertices[:, :, 0] *= scale
|
|
@@ -224,81 +237,96 @@ def generate_stl(height_map, filename, background_height, maximum_x_y_size):
|
|
|
224
237
|
bottom_vertices[:, :, 0] *= scale
|
|
225
238
|
bottom_vertices[:, :, 1] *= scale
|
|
226
239
|
|
|
227
|
-
triangles = []
|
|
240
|
+
triangles = [] # List to collect all triangles (each as a tuple of three vertices)
|
|
228
241
|
|
|
229
242
|
def add_triangle(v1, v2, v3):
|
|
230
|
-
"""
|
|
243
|
+
"""Append a triangle (defined by 3 vertices) to the triangle list."""
|
|
231
244
|
triangles.append((v1, v2, v3))
|
|
232
245
|
|
|
233
246
|
# --- Top Surface ---
|
|
247
|
+
# For the top surface, we iterate over each cell (quad) in the grid.
|
|
248
|
+
# Only if all 4 corners of the quad are valid do we add two triangles.
|
|
249
|
+
# We also store the grid indices (i,j) of the vertices used so we can later extract the boundary edges.
|
|
250
|
+
top_triangles_indices = [] # Each element is a tuple of 3 (i,j) indices.
|
|
234
251
|
for i in range(H - 1):
|
|
235
252
|
for j in range(W - 1):
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
253
|
+
if valid_mask[i, j] and valid_mask[i, j + 1] and valid_mask[i + 1, j + 1] and valid_mask[i + 1, j]:
|
|
254
|
+
# Define the four corners of the quad.
|
|
255
|
+
v0 = top_vertices[i, j]
|
|
256
|
+
v1 = top_vertices[i, j + 1]
|
|
257
|
+
v2 = top_vertices[i + 1, j + 1]
|
|
258
|
+
v3 = top_vertices[i + 1, j]
|
|
259
|
+
# For the top surface, use a reversed order so that the computed normal (via cross product) faces upward.
|
|
260
|
+
add_triangle(v2, v1, v0)
|
|
261
|
+
add_triangle(v3, v2, v0)
|
|
262
|
+
# Save the corresponding grid indices (for later boundary detection)
|
|
263
|
+
top_triangles_indices.append(((i, j), (i, j + 1), (i + 1, j + 1)))
|
|
264
|
+
top_triangles_indices.append(((i + 1, j), (i + 1, j + 1), (i, j)))
|
|
243
265
|
|
|
244
266
|
# --- Side Walls ---
|
|
245
|
-
#
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
add_triangle(
|
|
267
|
+
# Instead of simply using the rectangular border, we compute the boundary of the top surface.
|
|
268
|
+
# We count each undirected edge from the top surface triangles and select those that appear only once.
|
|
269
|
+
edge_count = {}
|
|
270
|
+
|
|
271
|
+
def add_edge(idx1, idx2):
|
|
272
|
+
# Store an undirected edge as a sorted tuple of the grid indices.
|
|
273
|
+
key = tuple(sorted((idx1, idx2)))
|
|
274
|
+
edge_count[key] = edge_count.get(key, 0) + 1
|
|
275
|
+
|
|
276
|
+
for tri in top_triangles_indices:
|
|
277
|
+
add_edge(tri[0], tri[1])
|
|
278
|
+
add_edge(tri[1], tri[2])
|
|
279
|
+
add_edge(tri[2], tri[0])
|
|
280
|
+
|
|
281
|
+
# A boundary edge appears only once.
|
|
282
|
+
boundary_edges = [edge for edge, count in edge_count.items() if count == 1]
|
|
283
|
+
|
|
284
|
+
# Helper functions to get the coordinate for a given grid index.
|
|
285
|
+
def get_top_vertex(idx):
|
|
286
|
+
i, j = idx
|
|
287
|
+
return top_vertices[i, j]
|
|
288
|
+
|
|
289
|
+
def get_bottom_vertex(idx):
|
|
290
|
+
i, j = idx
|
|
291
|
+
return bottom_vertices[i, j]
|
|
292
|
+
|
|
293
|
+
# For each boundary edge, create a vertical wall.
|
|
294
|
+
for edge in boundary_edges:
|
|
295
|
+
idx1, idx2 = edge
|
|
296
|
+
v_top1 = get_top_vertex(idx1)
|
|
297
|
+
v_top2 = get_top_vertex(idx2)
|
|
298
|
+
v_bottom1 = get_bottom_vertex(idx1)
|
|
299
|
+
v_bottom2 = get_bottom_vertex(idx2)
|
|
300
|
+
# Create a quad (two triangles) connecting the top edge to the bottom.
|
|
301
|
+
add_triangle(v_top1, v_top2, v_bottom2)
|
|
302
|
+
add_triangle(v_top1, v_bottom2, v_bottom1)
|
|
280
303
|
|
|
281
304
|
# --- Bottom Face ---
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
305
|
+
# For the bottom face, we mimic the top-surface logic but using the bottom vertices.
|
|
306
|
+
# The ordering is reversed (relative to the top face) so that the normals face downward.
|
|
307
|
+
for i in range(H - 1):
|
|
308
|
+
for j in range(W - 1):
|
|
309
|
+
if valid_mask[i, j] and valid_mask[i, j + 1] and valid_mask[i + 1, j + 1] and valid_mask[i + 1, j]:
|
|
310
|
+
v0 = bottom_vertices[i, j]
|
|
311
|
+
v1 = bottom_vertices[i, j + 1]
|
|
312
|
+
v2 = bottom_vertices[i + 1, j + 1]
|
|
313
|
+
v3 = bottom_vertices[i + 1, j]
|
|
314
|
+
# Order so that the normal will point downward.
|
|
315
|
+
add_triangle(v0, v1, v2)
|
|
316
|
+
add_triangle(v0, v2, v3)
|
|
290
317
|
|
|
291
318
|
num_triangles = len(triangles)
|
|
292
319
|
|
|
293
|
-
# --- Write Binary STL ---
|
|
320
|
+
# --- Write Binary STL File ---
|
|
294
321
|
with open(filename, "wb") as f:
|
|
295
|
-
header_str = "Binary STL generated from heightmap"
|
|
322
|
+
header_str = "Binary STL generated from heightmap with alpha mask"
|
|
296
323
|
header = header_str.encode("utf-8")
|
|
297
324
|
header = header.ljust(80, b" ")
|
|
298
325
|
f.write(header)
|
|
299
326
|
f.write(struct.pack("<I", num_triangles))
|
|
300
327
|
for tri in triangles:
|
|
301
328
|
v1, v2, v3 = tri
|
|
329
|
+
# Compute the normal as the normalized cross product of (v2-v1) and (v3-v1).
|
|
302
330
|
normal = np.cross(v2 - v1, v3 - v1)
|
|
303
331
|
norm = np.linalg.norm(normal)
|
|
304
332
|
if norm == 0:
|
|
@@ -7,7 +7,7 @@ import numpy as np
|
|
|
7
7
|
from tqdm import tqdm
|
|
8
8
|
|
|
9
9
|
from autoforge.Helper.FilamentHelper import hex_to_rgb, load_materials
|
|
10
|
-
from autoforge.Helper.ImageHelper import resize_image
|
|
10
|
+
from autoforge.Helper.ImageHelper import resize_image, resize_image_exact
|
|
11
11
|
from autoforge.Helper.OptimizerHelper import (
|
|
12
12
|
init_height_map,
|
|
13
13
|
composite_image,
|
|
@@ -86,10 +86,16 @@ def main():
|
|
|
86
86
|
help="Maximum dimension for solver (fast) image",
|
|
87
87
|
)
|
|
88
88
|
parser.add_argument(
|
|
89
|
-
"--init_tau",
|
|
89
|
+
"--init_tau",
|
|
90
|
+
type=float,
|
|
91
|
+
default=1.0,
|
|
92
|
+
help="Initial tau value for Gumbel-Softmax",
|
|
90
93
|
)
|
|
91
94
|
parser.add_argument(
|
|
92
|
-
"--final_tau",
|
|
95
|
+
"--final_tau",
|
|
96
|
+
type=float,
|
|
97
|
+
default=0.01,
|
|
98
|
+
help="Final tau value for Gumbel-Softmax",
|
|
93
99
|
)
|
|
94
100
|
|
|
95
101
|
parser.add_argument(
|
|
@@ -178,14 +184,10 @@ def main():
|
|
|
178
184
|
help="Use the Metal Performance Shaders (MPS) backend, if available.",
|
|
179
185
|
)
|
|
180
186
|
parser.add_argument(
|
|
181
|
-
"--run-name",
|
|
182
|
-
type=str,
|
|
183
|
-
help="Name of the run used for TensorBoard logging"
|
|
187
|
+
"--run-name", type=str, help="Name of the run used for TensorBoard logging"
|
|
184
188
|
)
|
|
185
189
|
parser.add_argument(
|
|
186
|
-
"--tensorboard",
|
|
187
|
-
action="store_true",
|
|
188
|
-
help="Enable TensorBoard logging"
|
|
190
|
+
"--tensorboard", action="store_true", help="Enable TensorBoard logging"
|
|
189
191
|
)
|
|
190
192
|
|
|
191
193
|
args = parser.parse_args()
|
|
@@ -225,7 +227,18 @@ def main():
|
|
|
225
227
|
material_TDs = torch.tensor(material_TDs_np, dtype=torch.float32, device=device)
|
|
226
228
|
|
|
227
229
|
# Read input image
|
|
228
|
-
img = cv2.imread(args.input_image)
|
|
230
|
+
img = cv2.imread(args.input_image, cv2.IMREAD_UNCHANGED)
|
|
231
|
+
|
|
232
|
+
alpha = None
|
|
233
|
+
# check for alpha mask
|
|
234
|
+
if img.shape[2] == 4:
|
|
235
|
+
# Extract the alpha channel
|
|
236
|
+
alpha = img[:, :, 3]
|
|
237
|
+
alpha = alpha[..., None]
|
|
238
|
+
alpha = resize_image(alpha, args.output_size)
|
|
239
|
+
# Convert the image from BGRA to BGR
|
|
240
|
+
img = img[:, :, :3]
|
|
241
|
+
|
|
229
242
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
230
243
|
|
|
231
244
|
# This smaller 'target' is the solver resolution
|
|
@@ -254,11 +267,15 @@ def main():
|
|
|
254
267
|
output_img_np, args.max_layers, args.layer_height, random_seed=random_seed
|
|
255
268
|
)
|
|
256
269
|
|
|
270
|
+
# if we have an alpha mask we set the height for thos pixels to -13.815512 (the lowest init sigmoid value)
|
|
271
|
+
if alpha is not None:
|
|
272
|
+
pixel_height_logits_init[alpha < 128] = -13.815512
|
|
273
|
+
|
|
257
274
|
# But we will solve at the smaller resolution => resize that init to solver size
|
|
258
275
|
# Add a dummy channel so that cv2.resize can handle it
|
|
259
276
|
tmp_3d = pixel_height_logits_init[..., None] # shape (H, W, 1)
|
|
260
|
-
phl_solver_np =
|
|
261
|
-
tmp_3d,
|
|
277
|
+
phl_solver_np = resize_image_exact(
|
|
278
|
+
tmp_3d, target_solver.shape[1], target_solver.shape[0]
|
|
262
279
|
) # shape (H', W') cause of weird opencv quirk
|
|
263
280
|
|
|
264
281
|
# VGG Perceptual Loss
|
|
@@ -305,7 +322,9 @@ def main():
|
|
|
305
322
|
optimizer.best_discrete_loss = new_loss
|
|
306
323
|
print(f"New best seed found: {new_rng_seed} with loss: {new_loss}")
|
|
307
324
|
|
|
308
|
-
optimizer.log_to_tensorboard(
|
|
325
|
+
optimizer.log_to_tensorboard(
|
|
326
|
+
interval=1, namespace="post_opt", step=(post_opt_step := post_opt_step + 1)
|
|
327
|
+
)
|
|
309
328
|
|
|
310
329
|
# Optionally prune
|
|
311
330
|
if args.perform_pruning:
|
|
@@ -317,14 +336,18 @@ def main():
|
|
|
317
336
|
tau_g=optimizer.best_tau,
|
|
318
337
|
)
|
|
319
338
|
|
|
320
|
-
optimizer.log_to_tensorboard(
|
|
339
|
+
optimizer.log_to_tensorboard(
|
|
340
|
+
interval=1, namespace="post_opt", step=(post_opt_step := post_opt_step + 1)
|
|
341
|
+
)
|
|
321
342
|
|
|
322
343
|
# Do a quick search for a better rng seed
|
|
323
344
|
new_rng_seed, new_loss = optimizer.rng_seed_search(
|
|
324
345
|
optimizer.best_discrete_loss, 500
|
|
325
346
|
)
|
|
326
347
|
|
|
327
|
-
optimizer.log_to_tensorboard(
|
|
348
|
+
optimizer.log_to_tensorboard(
|
|
349
|
+
interval=1, namespace="post_opt", step=(post_opt_step := post_opt_step + 1)
|
|
350
|
+
)
|
|
328
351
|
|
|
329
352
|
if new_loss < optimizer.best_discrete_loss:
|
|
330
353
|
optimizer.best_seed = new_rng_seed
|
|
@@ -378,8 +401,9 @@ def main():
|
|
|
378
401
|
rng_seed=optimizer.best_seed,
|
|
379
402
|
)
|
|
380
403
|
|
|
381
|
-
optimizer.log_to_tensorboard(
|
|
382
|
-
|
|
404
|
+
optimizer.log_to_tensorboard(
|
|
405
|
+
interval=1, namespace="post_opt", step=(post_opt_step := post_opt_step + 1)
|
|
406
|
+
)
|
|
383
407
|
|
|
384
408
|
comp_disc_np = comp_disc.cpu().numpy().astype(np.uint8)
|
|
385
409
|
comp_disc_np = cv2.cvtColor(comp_disc_np, cv2.COLOR_RGB2BGR)
|
|
@@ -389,7 +413,13 @@ def main():
|
|
|
389
413
|
height_map_mm = (
|
|
390
414
|
disc_height_image.cpu().numpy().astype(np.float32)
|
|
391
415
|
) * args.layer_height
|
|
392
|
-
generate_stl(
|
|
416
|
+
generate_stl(
|
|
417
|
+
height_map_mm,
|
|
418
|
+
stl_filename,
|
|
419
|
+
args.background_height,
|
|
420
|
+
maximum_x_y_size=args.stl_output_size,
|
|
421
|
+
alpha_mask=alpha,
|
|
422
|
+
)
|
|
393
423
|
|
|
394
424
|
# Swap instructions
|
|
395
425
|
background_layers = int(args.background_height // args.layer_height)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|