"""Frozen runtime library embedded into generated build123d scripts. These lines are appended after the per-model header (imports plus source volume/area constants) in every generated script. The library is stable: safe boolean wrappers, selector-based edge matching, and owned-face cutters. Changes here affect every translator-generated rebuild. """ from __future__ import annotations RUNTIME_LIB_LINES: list[str] = [ "", "def _dist(a, b):", " return math.sqrt(sum((a[i] - b[i]) ** 2 for i in range(3)))", "", "def _owned_face_match_score(shape, expected_faces):", " if not expected_faces:", " return 0.0", " try:", " available = list(shape.faces())", " except Exception:", " return 1e99", " total = 0.0", " for expected in expected_faces:", " bbox_m = expected.get('box_m')", " if not bbox_m or len(bbox_m) < 6 or not available:", " total += 1e6", " continue", " target_box = [float(v) * 1000 for v in bbox_m[:6]]", " surface = expected.get('surface') or {}", " target_type = next((name for name in ('plane', 'cylinder', 'cone', 'sphere', 'torus') if surface.get('is_' + name)), '')", " target_area = float(expected.get('area_m2') or 0) * 1_000_000", " ranked = []", " for index, face in enumerate(available):", " try:", " fb = face.bounding_box()", " face_box = [fb.min.X, fb.min.Y, fb.min.Z, fb.max.X, fb.max.Y, fb.max.Z]", " geom = face.geom_type() if callable(face.geom_type) else face.geom_type", " geom_name = getattr(geom, 'name', str(geom)).lower()", " type_penalty = 0.0 if not target_type or target_type in geom_name else 1000.0", " bbox_penalty = sum(abs(face_box[i] - target_box[i]) for i in range(6))", " area_penalty = abs(float(face.area) - target_area) / max(math.sqrt(abs(target_area)), 1.0) if target_area else 0.0", " ranked.append((type_penalty + bbox_penalty + area_penalty, index))", " except Exception:", " continue", " if not ranked:", " total += 1e6", " continue", " best, index = min(ranked, key=lambda item: item[0])", " total += best", " available.pop(index)", " return total / max(len(expected_faces), 1)", "", "def _candidate_score(shape, expected_faces=None):", " # Owned faces describe this exact SW history step. Final-part mass properties", " # must not be used to choose an intermediate feature candidate.", " if expected_faces:", " return _owned_face_match_score(shape, expected_faces)", " score = 0", " if SOURCE_VOLUME_MM3 is not None:", " try:", " score += abs(float(shape.volume) - SOURCE_VOLUME_MM3)", " except Exception:", " score += 1e99", " if SOURCE_AREA_MM2 is not None:", " try:", " score += abs(float(shape.area) - SOURCE_AREA_MM2) * 0.01", " except Exception:", " score += 1e99", " score += _owned_face_match_score(shape, expected_faces)", " return score", "", "def _edge_endpoints(edge):", " vertices = [v.to_tuple() for v in edge.vertices()]", " if len(vertices) != 2:", " center = edge.center().to_tuple()", " return center, center", " return vertices[0], vertices[1]", "", "def _edge_match_score(edge, start, end):", " a, b = _edge_endpoints(edge)", " endpoint_score = min(_dist(a, start) + _dist(b, end), _dist(a, end) + _dist(b, start))", " containment_score = edge.distance_to(start) + edge.distance_to(end)", " return min(endpoint_score, containment_score)", "", "def select_edges_by_endpoints(part, selector_points, tolerance=0.5):", " edges = list(part.edges())", " selected = []", " used = set()", " for selector in selector_points:", " start, end = selector", " ranked = sorted(((_edge_match_score(edge, start, end), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " score, index, edge = ranked[0]", " if score > tolerance:", " raise ValueError(f\"No edge matched selector {selector}; best score={score:.4f} mm\")", " if index not in used:", " selected.append(edge)", " used.add(index)", " return selected", "", "def _bbox_match_score(edge, bbox_mm):", " if not bbox_mm or len(bbox_mm) < 6:", " return float('inf')", " try:", " a, b = _edge_endpoints(edge)", " mid = tuple((a[i] + b[i]) / 2 for i in range(3))", " mins = tuple(float(bbox_mm[i]) for i in range(3))", " maxs = tuple(float(bbox_mm[i + 3]) for i in range(3))", " diag = math.sqrt(sum((maxs[i] - mins[i]) ** 2 for i in range(3)))", " pad = max(0.25, diag * 0.15)", " def point_score(point):", " total = 0.0", " for axis in range(3):", " if point[axis] < mins[axis] - pad:", " total += mins[axis] - pad - point[axis]", " elif point[axis] > maxs[axis] + pad:", " total += point[axis] - maxs[axis] - pad", " return total", " return min(point_score(mid), (point_score(a) + point_score(b)) / 2)", " except Exception:", " return float('inf')", "", "def _circle_match_score(edge, circle_params):", " if not circle_params or len(circle_params) < 7:", " return float('inf')", " try:", " geom_type = edge.geom_type() if callable(edge.geom_type) else edge.geom_type", " geom_name = getattr(geom_type, 'name', str(geom_type))", " if 'CIRCLE' not in geom_name:", " return float('inf')", " target_center = tuple(float(v) * 1000 for v in circle_params[:3])", " target_radius = float(circle_params[6]) * 1000", " edge_center = edge.arc_center.to_tuple()", " return _dist(edge_center, target_center) + abs(edge.radius - target_radius)", " except Exception:", " return float('inf')", "", "def _line_match_score(edge, line_params):", " if not line_params or len(line_params) < 6:", " return float('inf')", " try:", " geom_type = edge.geom_type() if callable(edge.geom_type) else edge.geom_type", " geom_name = getattr(geom_type, 'name', str(geom_type))", " if 'LINE' not in geom_name:", " return float('inf')", " target_point = tuple(float(v) * 1000 for v in line_params[:3])", " target_dir = tuple(float(v) for v in line_params[3:6])", " a, b = _edge_endpoints(edge)", " edge_dir_raw = tuple(b[i] - a[i] for i in range(3))", " length = math.sqrt(sum(v * v for v in edge_dir_raw))", " if length <= 0:", " return float('inf')", " edge_dir = tuple(v / length for v in edge_dir_raw)", " parallel = 1 - abs(sum(edge_dir[i] * target_dir[i] for i in range(3)))", " distance = edge.distance_to(target_point)", " return distance + parallel * 10", " except Exception:", " return float('inf')", "", "def select_edges_by_selectors(part, selectors, tolerance=0.5):", " if part is None:", " return []", " edges = list(part.edges())", " selected = []", " used = set()", " for selector in selectors or []:", " geometry = selector.get('geometry', {})", " start_vertex = geometry.get('start_vertex')", " end_vertex = geometry.get('end_vertex')", " start = start_vertex.get('point_m') if start_vertex else None", " end = end_vertex.get('point_m') if end_vertex else None", " bbox_mm = geometry.get('bbox_mm')", " if start and end:", " start_mm = tuple(float(v) * 1000 for v in start)", " end_mm = tuple(float(v) * 1000 for v in end)", " line_params = geometry.get('curve', {}).get('line_params')", " if line_params:", " ranked = sorted(((min(_edge_match_score(edge, start_mm, end_mm), _line_match_score(edge, line_params)) + (_bbox_match_score(edge, bbox_mm) if bbox_mm else 0), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " else:", " ranked = sorted(((_edge_match_score(edge, start_mm, end_mm) + (_bbox_match_score(edge, bbox_mm) if bbox_mm else 0), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " else:", " line_params = geometry.get('curve', {}).get('line_params')", " circle_params = geometry.get('curve', {}).get('circle_params')", " if line_params:", " ranked = sorted(((_line_match_score(edge, line_params) + (_bbox_match_score(edge, bbox_mm) if bbox_mm else 0), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " elif bbox_mm:", " ranked = sorted(((_bbox_match_score(edge, bbox_mm), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " else:", " ranked = sorted(((_circle_match_score(edge, circle_params), i, edge) for i, edge in enumerate(edges)), key=lambda item: item[0])", " score, index, edge = ranked[0]", " selector_tolerance = float(selector.get('tolerance_mm') or tolerance)", " if score > selector_tolerance:", " # Skip edges that don't match well enough", " continue", " if index not in used:", " selected.append(edge)", " used.add(index)", " return selected", "", "def _point_inside_bbox(point, bbox_mm, pad=0.25):", " return all(float(bbox_mm[i]) - pad <= point[i] <= float(bbox_mm[i + 3]) + pad for i in range(3))", "", "def fillet_edges_from_owned_surface_bbox(part, selectors):", " if part is None:", " return []", " boxes = []", " seen_boxes = set()", " for selector in selectors or []:", " if selector.get('source') not in ('owned_cylindrical_face_axis', 'owned_face_bbox'):", " continue", " bbox = (selector.get('geometry') or {}).get('bbox_mm')", " if bbox and len(bbox) >= 6:", " normalized = [float(v) for v in bbox[:6]]", " key = tuple(round(v, 6) for v in normalized)", " if key not in seen_boxes:", " seen_boxes.add(key)", " boxes.append(normalized)", " if len(boxes) < 2:", " return []", " selected = []", " used_keys = set()", " for box in boxes:", " diag = math.sqrt(sum((box[i + 3] - box[i]) ** 2 for i in range(3)))", " pad = max(0.25, diag * 0.08)", " sizes = [abs(box[i + 3] - box[i]) for i in range(3)]", " thin_axes = [i for i, size in enumerate(sizes) if size <= max(1.5, diag * 0.08)]", " circle_candidates = []", " if thin_axes:", " thin_axis = thin_axes[0]", " for edge in part.edges():", " try:", " geom_type = edge.geom_type() if callable(edge.geom_type) else edge.geom_type", " geom_name = getattr(geom_type, 'name', str(geom_type))", " if 'CIRCLE' not in geom_name:", " continue", " eb = edge.bounding_box()", " edge_box = [eb.min.X, eb.min.Y, eb.min.Z, eb.max.X, eb.max.Y, eb.max.Z]", " ok = True", " score = 0.0", " for axis in range(3):", " if axis == thin_axis:", " plane_delta = min(abs(edge_box[axis] - box[axis]), abs(edge_box[axis] - box[axis + 3]), abs(edge_box[axis + 3] - box[axis]), abs(edge_box[axis + 3] - box[axis + 3]))", " if plane_delta > pad:", " ok = False", " break", " score += plane_delta", " else:", " if edge_box[axis] < box[axis] - pad or edge_box[axis + 3] > box[axis + 3] + pad:", " ok = False", " break", " score += abs(edge_box[axis] - box[axis]) + abs(edge_box[axis + 3] - box[axis + 3])", " if not ok:", " continue", " key = tuple(round(v, 5) for v in edge_box)", " circle_candidates.append((score, key, edge))", " except Exception:", " continue", " if circle_candidates:", " circle_candidates.sort(key=lambda item: item[0])", " for _, key, edge in circle_candidates:", " if key in used_keys:", " continue", " used_keys.add(key)", " selected.append(edge)", " break", " continue", " box_candidates = []", " for edge in part.edges():", " try:", " geom_type = edge.geom_type() if callable(edge.geom_type) else edge.geom_type", " geom_name = getattr(geom_type, 'name', str(geom_type))", " if 'LINE' not in geom_name:", " continue", " a, b = _edge_endpoints(edge)", " mid = tuple((a[i] + b[i]) / 2 for i in range(3))", " if not (_point_inside_bbox(a, box, pad) and _point_inside_bbox(b, box, pad) and _point_inside_bbox(mid, box, pad)):", " continue", " key = tuple(round(v, 5) for point in (a, b) for v in point)", " box_candidates.append((float(edge.length), key, edge))", " except Exception:", " continue", " if not box_candidates:", " continue", " box_candidates.sort(key=lambda item: item[0], reverse=True)", " for _, key, edge in box_candidates:", " reverse_key = key[3:] + key[:3]", " if key in used_keys or reverse_key in used_keys:", " continue", " used_keys.add(key)", " selected.append(edge)", " break", " if selected:", " return selected", " union_bbox = [", " min(box[i] for box in boxes) if i < 3 else max(box[i] for box in boxes)", " for i in range(6)", " ]", " diag = math.sqrt(sum((union_bbox[i + 3] - union_bbox[i]) ** 2 for i in range(3)))", " pad = max(0.25, diag * 0.05)", " candidates = []", " for edge in part.edges():", " try:", " geom_type = edge.geom_type() if callable(edge.geom_type) else edge.geom_type", " geom_name = getattr(geom_type, 'name', str(geom_type))", " if 'LINE' not in geom_name:", " continue", " a, b = _edge_endpoints(edge)", " mid = tuple((a[i] + b[i]) / 2 for i in range(3))", " if not (_point_inside_bbox(a, union_bbox, pad) and _point_inside_bbox(b, union_bbox, pad) and _point_inside_bbox(mid, union_bbox, pad)):", " continue", " candidates.append((float(edge.length), edge))", " except Exception:", " continue", " if not candidates:", " return []", " candidates.sort(key=lambda item: item[0], reverse=True)", " return [candidates[0][1]]", "", "def fillet_with_tolerance(edges, radius):", " radii = [float(radius)]", " shrink = max(0.001, abs(float(radius)) * 0.001)", " if float(radius) > shrink:", " radii.append(float(radius) - shrink)", " radii.append(float(radius) * 0.99)", " last_error = None", " for candidate_radius in radii:", " if candidate_radius <= 0:", " continue", " try:", " return fillet(edges, radius=candidate_radius)", " except Exception as exc:", " last_error = exc", " continue", " if last_error:", " raise last_error", " return fillet(edges, radius=radius)", "", "def fillet_selected(part, radius, selectors, owned_faces=None):", " if part is None:", " return part", " if not selectors:", " # No edge selectors - skip fillet to avoid failing on all edges", " return part", " candidates = []", " owned_edges = fillet_edges_from_owned_surface_bbox(part, selectors)", " if owned_edges:", " try:", " candidates.append(fillet_with_tolerance(owned_edges, radius))", " except Exception:", " pass", " try:", " target_edges = select_edges_by_selectors(part, selectors)", " if target_edges:", " candidates.append(fillet_with_tolerance(target_edges, radius))", " except Exception:", " pass", " result = part", " applied_any = False", " for selector in selectors:", " edges = select_edges_by_selectors(result, [selector])", " if not edges:", " continue # Skip selectors that don't match any edge", " try:", " result = fillet_with_tolerance([edges[0]], radius)", " applied_any = True", " except Exception:", " # OCC fillets are fragile: one invalid edge/radius should not abort the whole rebuild.", " continue", " if applied_any:", " candidates.append(result)", " variants = []", " for selector in selectors:", " edges = select_edges_by_selectors(part, [selector])", " if not edges:", " continue", " try:", " variants.append(fillet_with_tolerance([edges[0]], radius))", " except Exception:", " continue", " if variants:", " try:", " union_result = part", " for variant in variants:", " union_result = union_result + variant", " candidates.append(union_result)", " except Exception:", " pass", " try:", " intersection_result = part", " for variant in variants:", " intersection_result = intersection_result & variant", " candidates.append(intersection_result)", " except Exception:", " pass", " if candidates:", " return sorted(candidates, key=lambda shape: _candidate_score(shape, owned_faces))[0]", " return part", "", "def chamfer_selected(part, distance, selectors, owned_faces=None):", " if part is None:", " return part", " if not selectors:", " # No edge selectors available - chamfer would fail on all edges", " return part", " candidates = []", " owned_edges = fillet_edges_from_owned_surface_bbox(part, selectors)", " if owned_edges:", " try:", " candidates.append(chamfer(owned_edges, length=distance))", " except Exception:", " pass", " target_edges = select_edges_by_selectors(part, selectors)", " if target_edges:", " try:", " candidates.append(chamfer(target_edges, length=distance))", " except Exception:", " pass", " result = part", " applied_any = False", " for selector in selectors:", " edges = select_edges_by_selectors(result, [selector])", " if not edges:", " continue", " try:", " result = chamfer([edges[0]], length=distance)", " applied_any = True", " except Exception:", " continue", " if applied_any:", " candidates.append(result)", " if candidates:", " return sorted(candidates, key=lambda shape: _candidate_score(shape, owned_faces))[0]", " return part", "", "def is_internal_cone_face(face, part):", " try:", " bbox_m = face.get('box_m')", " surface = face.get('surface') or {}", " if not (bbox_m and len(bbox_m) >= 6 and surface.get('is_cone')):", " return False", " params = surface.get('cone_params')", " if not params or len(params) < 6:", " return False", " direction = tuple(float(v) for v in params[3:6])", " axis = max(range(3), key=lambda i: abs(direction[i]))", " radial_axes = tuple(i for i in range(3) if i != axis)", " part_bbox = part.bounding_box()", " part_min = part_bbox.min.to_tuple()", " part_max = part_bbox.max.to_tuple()", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " tol = 0.25", " touches_outer = any(", " abs(mins[i] - part_min[i]) <= tol or abs(maxs[i] - part_max[i]) <= tol", " for i in radial_axes", " )", " return not touches_outer", " except Exception:", " return False", "", "def is_external_cone_face(face, part):", " try:", " bbox_m = face.get('box_m')", " surface = face.get('surface') or {}", " if not (bbox_m and len(bbox_m) >= 6 and surface.get('is_cone')):", " return False", " params = surface.get('cone_params')", " if not params or len(params) < 6:", " return False", " direction = tuple(float(v) for v in params[3:6])", " axis = max(range(3), key=lambda i: abs(direction[i]))", " radial_axes = tuple(i for i in range(3) if i != axis)", " part_bbox = part.bounding_box()", " part_min = part_bbox.min.to_tuple()", " part_max = part_bbox.max.to_tuple()", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " tol = 0.25", " return any(", " abs(mins[i] - part_min[i]) <= tol or abs(maxs[i] - part_max[i]) <= tol", " for i in radial_axes", " )", " except Exception:", " return False", "", "def make_owned_external_cone_chamfer_cutter(face):", " surface = face.get('surface') or {}", " params = surface.get('cone_params')", " bbox_m = face.get('box_m')", " if not params or len(params) < 8 or not bbox_m or len(bbox_m) < 6:", " return None", " origin = tuple(float(v) * 1000 for v in params[:3])", " direction = tuple(float(v) for v in params[3:6])", " norm = math.sqrt(sum(v * v for v in direction))", " base_radius = abs(float(params[6]) * 1000)", " half_angle = abs(float(params[7]))", " if norm <= 1e-9 or base_radius <= 1e-9 or half_angle <= 1e-9:", " return None", " direction = tuple(v / norm for v in direction)", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " projections = []", " for x in (mins[0], maxs[0]):", " for y in (mins[1], maxs[1]):", " for z in (mins[2], maxs[2]):", " delta = (x - origin[0], y - origin[1], z - origin[2])", " axial = sum(delta[i] * direction[i] for i in range(3))", " projections.append(axial)", " start = min(projections)", " end = max(projections)", " height = max(0.001, end - start)", " r1 = max(0.0, base_radius - math.tan(half_angle) * start)", " r2 = max(0.0, base_radius - math.tan(half_angle) * end)", " outer_radius = max(r1, r2) + 0.001", " center_offset = (start + end) / 2", " center = tuple(origin[i] + direction[i] * center_offset for i in range(3))", " if r1 <= 1e-9:", " r1 = 1e-6", " if r2 <= 1e-9:", " r2 = 1e-6", " with BuildPart(Plane(origin=center, z_dir=direction)) as cutter_part:", " Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.CENTER))", " Cone(r1, r2, height + 0.002, align=(Align.CENTER, Align.CENTER, Align.CENTER), mode=Mode.SUBTRACT)", " return cutter_part.part", "", "def chamfer_owned_external_cones(part, faces):", " if part is None:", " return part, False", " result = part", " applied = False", " for face in faces or []:", " if not is_external_cone_face(face, result):", " continue", " cutter = make_owned_external_cone_chamfer_cutter(face)", " new_result = safe_subtract(result, cutter)", " if new_result is not result:", " result = new_result", " applied = True", " return result, applied", "", "def chamfer_owned_internal_cones(part, faces):", " if part is None:", " return part, False", " result = part", " applied = False", " for face in faces or []:", " if not is_internal_cone_face(face, result):", " continue", " cutter = make_owned_cone_cutter(face)", " new_result = safe_subtract(result, cutter)", " if new_result is not result:", " result = new_result", " applied = True", " return result, applied", "", "def chamfer_selected_with_owned_faces(part, distance, selectors, owned_faces):", " cone_faces = [face for face in (owned_faces or []) if (face.get('surface') or {}).get('is_cone')]", " if len(cone_faces) == 1 and is_internal_cone_face(cone_faces[0], part):", " result, applied = chamfer_owned_internal_cones(part, cone_faces)", " if applied:", " return result", " if len(cone_faces) == 1 and is_external_cone_face(cone_faces[0], part):", " result, applied = chamfer_owned_external_cones(part, cone_faces)", " if applied:", " return result", " return chamfer_selected(part, distance, selectors, owned_faces)", "", "def safe_subtract(part, cutter):", " if part is None or cutter is None:", " return part", " try:", " vol_before = float(part.volume)", " except Exception:", " vol_before = -1", " try:", " cut = part - cutter", " if cut is None:", " print(f' SUBTRACT: cutter resulted in None, keeping original (vol={vol_before:.0f})')", " return part", " # Accept the cut even when solids() reports 0 – can happen", " # for valid boolean results with non-standard structures.", " try:", " nb_solids = len(list(cut.solids()))", " if nb_solids == 0:", " print(f' SUBTRACT: cut produced 0 solids (still accepting) vol={vol_before:.0f}')", " except Exception:", " pass", " return cut", " except Exception as e:", " print(f' SUBTRACT: exception {type(e).__name__}: {e}, keeping original (vol={vol_before:.0f})')", " return part", "", "def _project_bbox_along_direction(bbox, origin, direction):", " mins = tuple(float(bbox[i]) for i in range(3))", " maxs = tuple(float(bbox[i + 3]) for i in range(3))", " projections = []", " for x in (mins[0], maxs[0]):", " for y in (mins[1], maxs[1]):", " for z in (mins[2], maxs[2]):", " projections.append(sum(((x, y, z)[i] - origin[i]) * direction[i] for i in range(3)))", " return min(projections), max(projections)", "", "def make_owned_cylinder_cutter(face, target_part=None):", " surface = face.get('surface') or {}", " params = surface.get('cylinder_params')", " bbox_m = face.get('box_m')", " if not params or len(params) < 7 or not bbox_m or len(bbox_m) < 6:", " return None", " origin = tuple(float(v) * 1000 for v in params[:3])", " direction = tuple(float(v) for v in params[3:6])", " norm = math.sqrt(sum(v * v for v in direction))", " radius = abs(float(params[6]) * 1000)", " if norm <= 1e-9 or radius <= 1e-9:", " return None", " direction = tuple(v / norm for v in direction)", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " start, end = _project_bbox_along_direction((*mins, *maxs), origin, direction)", " if target_part is not None:", " try:", " part_bbox = target_part.bounding_box()", " part_box = (*part_bbox.min.to_tuple(), *part_bbox.max.to_tuple())", " part_start, part_end = _project_bbox_along_direction(part_box, origin, direction)", " through_tolerance = max(1.0, radius * 0.12)", " if abs(start - part_start) <= through_tolerance:", " start = part_start", " if abs(end - part_end) <= through_tolerance:", " end = part_end", " except Exception:", " pass", " height = max(0.001, end - start)", " center_offset = (start + end) / 2", " center = tuple(origin[i] + direction[i] * center_offset for i in range(3))", " with BuildPart(Plane(origin=center, z_dir=direction)) as cutter_part:", " Cylinder(radius, height, align=(Align.CENTER, Align.CENTER, Align.CENTER))", " return cutter_part.part", "", "def make_owned_cone_cutter(face):", " surface = face.get('surface') or {}", " params = surface.get('cone_params')", " bbox_m = face.get('box_m')", " if not params or len(params) < 8 or not bbox_m or len(bbox_m) < 6:", " return None", " origin = tuple(float(v) * 1000 for v in params[:3])", " direction = tuple(float(v) for v in params[3:6])", " norm = math.sqrt(sum(v * v for v in direction))", " base_radius = abs(float(params[6]) * 1000)", " half_angle = abs(float(params[7]))", " if norm <= 1e-9 or base_radius <= 1e-9 or half_angle <= 1e-9:", " return None", " direction = tuple(v / norm for v in direction)", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " projections = []", " for x in (mins[0], maxs[0]):", " for y in (mins[1], maxs[1]):", " for z in (mins[2], maxs[2]):", " projections.append(sum(((x, y, z)[i] - origin[i]) * direction[i] for i in range(3)))", " start = min(projections)", " end = max(projections)", " # Keep a tiny overlap for the boolean while preserving blind-hole depth.", " height = max(0.001, end - start) + 0.001", " # SolidWorks ConeParams stores the radius at the cone origin; along the axis", " # direction the radius tapers rather than expands for hole drill tips.", " r1 = max(0.0, base_radius - math.tan(half_angle) * start)", " r2 = max(0.0, base_radius - math.tan(half_angle) * end)", " if max(r1, r2) <= 1e-9:", " return None", " if r1 <= 1e-9:", " r1 = 1e-6", " if r2 <= 1e-9:", " r2 = 1e-6", " center_offset = (start + end) / 2", " center = tuple(origin[i] + direction[i] * center_offset for i in range(3))", " with BuildPart(Plane(origin=center, z_dir=direction)) as cutter_part:", " Cone(r1, r2, height, align=(Align.CENTER, Align.CENTER, Align.CENTER))", " return cutter_part.part", "", "def make_owned_face_cutter(face, target_part=None):", " surface = face.get('surface') or {}", " if surface.get('is_cylinder'):", " return make_owned_cylinder_cutter(face, target_part)", " if surface.get('is_cone'):", " return make_owned_cone_cutter(face)", " return None", "", "def cut_owned_cylindrical_faces(part, faces):", " result = part", " for face in faces or []:", " cutter = make_owned_face_cutter(face, result)", " result = safe_subtract(result, cutter)", " return result", "", "def make_owned_flip_side_ring_cutter(face, target_part):", " surface = face.get('surface') or {}", " params = surface.get('cylinder_params')", " bbox_m = face.get('box_m')", " if target_part is None or not params or len(params) < 7 or not bbox_m or len(bbox_m) < 6:", " return None", " origin = tuple(float(v) * 1000 for v in params[:3])", " direction = tuple(float(v) for v in params[3:6])", " norm = math.sqrt(sum(v * v for v in direction))", " inner_radius = abs(float(params[6]) * 1000)", " if norm <= 1e-9 or inner_radius <= 1e-9:", " return None", " direction = tuple(v / norm for v in direction)", " mins = tuple(float(bbox_m[i]) * 1000 for i in range(3))", " maxs = tuple(float(bbox_m[i + 3]) * 1000 for i in range(3))", " start, end = _project_bbox_along_direction((*mins, *maxs), origin, direction)", " height = max(0.001, end - start)", " center_offset = (start + end) / 2", " center = tuple(origin[i] + direction[i] * center_offset for i in range(3))", " try:", " part_bbox = target_part.bounding_box()", " part_min = part_bbox.min.to_tuple()", " part_max = part_bbox.max.to_tuple()", " radial = []", " for x in (part_min[0], part_max[0]):", " for y in (part_min[1], part_max[1]):", " for z in (part_min[2], part_max[2]):", " delta = (x - origin[0], y - origin[1], z - origin[2])", " axial = sum(delta[i] * direction[i] for i in range(3))", " perp = tuple(delta[i] - axial * direction[i] for i in range(3))", " radial.append(math.sqrt(sum(v * v for v in perp)))", " outer_radius = max(radial) + max(1.0, inner_radius * 0.05)", " except Exception:", " outer_radius = inner_radius + 100.0", " if outer_radius <= inner_radius + 1e-6:", " return None", " with BuildPart(Plane(origin=center, z_dir=direction)) as cutter_part:", " Cylinder(outer_radius, height, align=(Align.CENTER, Align.CENTER, Align.CENTER))", " Cylinder(inner_radius, height + 0.002, align=(Align.CENTER, Align.CENTER, Align.CENTER), mode=Mode.SUBTRACT)", " return cutter_part.part", "", "def cut_owned_flip_side_cylindrical_faces(part, faces):", " result = part", " for face in faces or []:", " cutter = make_owned_flip_side_ring_cutter(face, result)", " result = safe_subtract(result, cutter)", " return result", "", "def cut_owned_bbox(part, bbox_mm):", " if part is None or not bbox_mm or len(bbox_mm) < 6:", " return part", " mins = tuple(float(bbox_mm[i]) for i in range(3))", " maxs = tuple(float(bbox_mm[i + 3]) for i in range(3))", " size = tuple(max(0.001, maxs[i] - mins[i]) for i in range(3))", " center = tuple((mins[i] + maxs[i]) / 2 for i in range(3))", " cutter = Pos(center) * Box(size[0], size[1], size[2])", " return safe_subtract(part, cutter)", "", "def shape_face_count(shape):", " if shape is None:", " return 0", " try:", " return len(list(shape.faces()))", " except Exception:", " return 0", "", "def safe_union(part, solid, preserve_visible=False):", " if part is None:", " return solid", " if solid is None:", " return part", " try:", " fused = part + solid", " # OCCT fuse succeeded; always return the fused result.", " # is_valid() can return False for edge cases where the geometry", " # is actually correct (e.g. touching-at-faces). Accept it.", " return fused", " except Exception as e:", " print(f' UNION: fuse threw {type(e).__name__}: {e}')", " pass", " try:", " compound = Compound.make_composite([part, solid])", " fused = compound.fuse()", " try:", " if len(list(fused.solids())) > 0:", " print(f' UNION: compound.fuse() worked, {len(list(fused.solids()))} solids')", " return fused", " except Exception:", " pass", " except Exception as e:", " print(f' UNION: compound.fuse() threw {type(e).__name__}: {e}')", " pass", " shapes = []", " try:", " shapes.extend(list(part.solids()))", " except Exception:", " shapes.append(part)", " try:", " shapes.extend(list(solid.solids()))", " except Exception:", " shapes.append(solid)", " return Compound.make_composite(shapes)", "", "def sw_inverted_profile_cut(part, profile_solid, normal):", " if part is None or profile_solid is None:", " return part", " try:", " part_bbox = part.bounding_box()", " profile_bbox = profile_solid.bounding_box()", " n = tuple(float(v) for v in normal)", " axis = max(range(3), key=lambda i: abs(n[i]))", " part_min = part_bbox.min.to_tuple()", " part_max = part_bbox.max.to_tuple()", " prof_min = profile_bbox.min.to_tuple()", " prof_max = profile_bbox.max.to_tuple()", " margin = 5.0", " mins = [part_min[i] - margin for i in range(3)]", " maxs = [part_max[i] + margin for i in range(3)]", " mins[axis] = prof_min[axis] - margin * 0.05", " maxs[axis] = prof_max[axis] + margin * 0.05", " center = tuple((mins[i] + maxs[i]) / 2 for i in range(3))", " size = tuple(max(0.001, maxs[i] - mins[i]) for i in range(3))", " envelope = Pos(center) * Box(size[0], size[1], size[2])", " outside_profile = safe_subtract(envelope, profile_solid)", " return safe_subtract(part, outside_profile)", " except Exception:", " return part", "", "def sw_flip_side_step_cut(part, profile_solid, normal, outer_radius_mm, inner_radius_mm):", " part = sw_inverted_profile_cut(part, profile_solid, normal)", " if part is None or profile_solid is None:", " return part", " try:", " outer_radius = abs(float(outer_radius_mm))", " inner_radius = abs(float(inner_radius_mm))", " except Exception:", " return part", " if outer_radius <= inner_radius + 1e-6:", " return part", " try:", " profile_bbox = profile_solid.bounding_box()", " prof_min = profile_bbox.min.to_tuple()", " prof_max = profile_bbox.max.to_tuple()", " center = tuple((prof_min[i] + prof_max[i]) / 2 for i in range(3))", " n = tuple(float(v) for v in normal)", " axis = max(range(3), key=lambda i: abs(n[i]))", " span_xy = max(prof_max[0] - prof_min[0], prof_max[1] - prof_min[1])", " margin_xy = max(2.0, span_xy * 0.05)", " margin_z = 0.1", " size = tuple(", " max(0.001, prof_max[i] - prof_min[i] + (margin_xy if i < 2 else margin_z))", " for i in range(3)", " )", " plane = Plane(", " origin=center,", " x_dir=(1.0, 0.0, 0.0) if axis != 0 else (0.0, 1.0, 0.0),", " z_dir=n,", " )", " cut_extent = prof_max[axis] - prof_min[axis]", " cut_amount = -abs(cut_extent) if n[axis] < 0 else abs(cut_extent)", " with BuildSketch(plane) as ring_sketch:", " Circle(outer_radius)", " Circle(inner_radius, mode=Mode.SUBTRACT)", " ring = extrude(ring_sketch.sketch, amount=cut_amount)", " return safe_union(part, ring)", " except Exception:", " return part", "", "def sw_cut_holes(part, positions, host_face, diameter, depth, drill_angle=0, include_drill_tip=False, countersink_diameter=0, countersink_angle=0, counterbore_diameter=0, counterbore_depth=0):", " if part is None:", " return part", " if not positions or diameter <= 0 or depth <= 0:", " return part", " plane = host_face.get('surface', {}).get('plane_params') or [0, 0, 1, 0, 0, 0]", " frame = host_face.get('frame') or {}", " normal = tuple(float(v) for v in plane[:3])", " plane_point = tuple(float(v) * 1000 for v in plane[3:6])", " origin = tuple(float(v) for v in frame.get('origin_mm', plane_point))", " x_dir = tuple(float(v) for v in frame.get('x_dir', (0, 0, 0)))", " y_dir = tuple(float(v) for v in frame.get('y_dir', (0, 0, 0)))", " has_frame = sum(abs(v) for v in x_dir) > 0 and sum(abs(v) for v in y_dir) > 0", " bbox = part.bounding_box()", " part_center = tuple((bbox.min.to_tuple()[i] + bbox.max.to_tuple()[i]) / 2 for i in range(3))", " toward_center = tuple(part_center[i] - plane_point[i] for i in range(3))", " dot = sum(toward_center[i] * normal[i] for i in range(3))", " inward = normal if dot >= 0 else tuple(-v for v in normal)", " axis = max(range(3), key=lambda i: abs(inward[i]))", " rotation = (0, 0, 0)", " if axis == 0:", " rotation = (0, 90, 0) if inward[0] >= 0 else (0, -90, 0)", " elif axis == 1:", " rotation = (-90, 0, 0) if inward[1] >= 0 else (90, 0, 0)", " elif inward[2] < 0:", " rotation = (180, 0, 0)", " tip_depth = 0", " if include_drill_tip and drill_angle > 0:", " tip_depth = (diameter / 2) / math.tan(drill_angle / 2)", " countersink_depth = 0", " if countersink_diameter > diameter and countersink_angle > 0:", " countersink_depth = ((countersink_diameter - diameter) / 2) / math.tan(countersink_angle / 2)", " result = part", " for pos in positions:", " x, y = float(pos[0]), float(pos[1])", " if has_frame:", " start = tuple(origin[i] + x_dir[i] * x + y_dir[i] * y for i in range(3))", " elif axis == 0:", " start = (plane_point[0], x, y)", " elif axis == 1:", " start = (x, plane_point[1], -y)", " else:", " start = (x, y, plane_point[2])", " cut_depth = depth", " if depth >= 199:", " part_min = bbox.min.to_tuple()", " part_max = bbox.max.to_tuple()", " corners = []", " for ci in range(2):", " for cj in range(2):", " for ck in range(2):", " corners.append((", " part_min[0] if ci else part_max[0],", " part_min[1] if cj else part_max[1],", " part_min[2] if ck else part_max[2],", " ))", " cut_depth = max(", " sum((corner[i] - start[i]) * inward[i] for i in range(3))", " for corner in corners", " ) + 2.0", " cutters = []", " cb_depth = counterbore_depth if counterbore_diameter > diameter and counterbore_depth > 0 else 0", " cs_depth = countersink_depth if countersink_depth > 0 else 0", " hole_start = cs_depth", " hole_depth = max(0.001, cut_depth - hole_start - cb_depth)", " if hole_depth > 0:", " hole_center = tuple(start[i] + inward[i] * (hole_start + cb_depth + hole_depth / 2) for i in range(3))", " cutters.append(Pos(hole_center) * Cylinder(diameter / 2, hole_depth, rotation=rotation))", " if cb_depth > 0:", " cb_center = tuple(start[i] + inward[i] * (hole_start + cb_depth / 2) for i in range(3))", " cutters.append(Pos(cb_center) * Cylinder(counterbore_diameter / 2, cb_depth, rotation=rotation))", " if cs_depth > 0:", " cs_center = tuple(start[i] + inward[i] * cs_depth / 2 for i in range(3))", " cs = Pos(cs_center) * Cone(countersink_diameter / 2, diameter / 2, cs_depth, rotation=rotation)", " cutters.append(cs)", " if tip_depth > 0:", " base = tuple(start[i] + inward[i] * cut_depth for i in range(3))", " tip_center = tuple(base[i] + inward[i] * tip_depth / 2 for i in range(3))", " tip = Pos(tip_center) * Cone(diameter / 2, 0, tip_depth, rotation=rotation)", " cutters.append(tip)", " if len(cutters) == 1:", " cutter = cutters[0]", " else:", " cutter = Compound.make_composite(cutters)", " result = safe_subtract(result, cutter)", " return result", "", ]