from __future__ import annotations import os import re from typing import Any import bpy from ..slice import Pather from ...infra.blender.list_utils import flatten_list, is_list_data def paths_to_gcode(p: Any, intr: Any, use_header: bool = True) -> str: if not p: return "" pather: Pather = Pather(p) if hasattr(p, "layer_height"): pather.layer_height = getattr(p, "layer_height") if hasattr(p, "first_layer_height"): pather.first_layer_height = getattr(p, "first_layer_height") if intr: pather.bed_center_x = intr.bed_x / 2.0 pather.bed_center_y = intr.bed_y / 2.0 pather.feedrate = intr.feedrate pather.travel_feedrate = intr.travel_feedrate pather.extrusion_rate = intr.extrusion_rate pather.retraction = intr.retraction pather.retraction_feedrate = getattr(intr, "retraction_feedrate", 0.0) pather.lift = intr.lift return pather.to_gcode(include_header=use_header) def format_gcode_metadata(name: Any, filament_type: Any, printer_model: Any) -> str: lines = [] if name: lines.append(f'; objects_info = {{"objects": [{{"name": "{name}", "polygon": []}}]}}') if filament_type: lines.append(f'; filament_type = {filament_type}') if printer_model: lines.append(f'; printer_model = {printer_model}') return '\n'.join(lines) def regex_replace_gcode(gc: Any, find_pattern: Any, replace_pattern: Any, node_name: str = "") -> str: gc_str = str(gc or "") f_str = str(find_pattern or "") r_str = str(replace_pattern or "") if not f_str or not gc_str: return gc_str try: def subst(match): if match.group(1): return '$' elif match.group(2): return '\\g<' + match.group(2) + '>' elif match.group(3): return '\\g<' + match.group(3) + '>' return match.group(0) rep = re.sub(r'(\$\$)|(?:\$\{([^}]+)\})|(?:\$([a-zA-Z0-9_]+))', subst, r_str) return re.sub(f_str, rep, gc_str) except Exception as e: if node_name: print(f"Regex error in node {node_name}: {e}") return gc_str def format_gcode_command(command: str, parameters: str = "") -> str: cmd = command params = parameters.strip() if params: if cmd == 'T': return f"{cmd}{params}" return f"{cmd} {params}" return cmd def join_gcode_inputs(raw_inputs: list[Any]) -> Any: has_list = any(is_list_data(inp) for inp in raw_inputs) if not has_list: flattened = flatten_list(raw_inputs) gcode_input: list[str] = [str(gc) for gc in flattened if gc] return "\n".join(gcode_input) # There is at least one list input list_lengths = [len(inp) for inp in raw_inputs if is_list_data(inp)] if len(set(list_lengths)) > 1: raise ValueError( f"Join Gcode: all input lists must have the same length, got lengths {list_lengths}" ) n_elements = list_lengths[0] if list_lengths else 0 result: list[str] = [] for i in range(n_elements): row_parts: list[str] = [] for inp in raw_inputs: if is_list_data(inp): val = inp[i] else: val = inp if is_list_data(val): val_str = "\n".join(str(s) for s in flatten_list(val) if s) if val_str: row_parts.append(val_str) elif val: row_parts.append(str(val)) result.append("\n".join(row_parts)) return result def load_gcode_file(path: str) -> str: if not path: return "" abs_path = bpy.path.abspath(path) if os.path.exists(abs_path): with open(abs_path, 'r', encoding='utf-8') as f: return f.read() return ""