AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * *
# * Copyright (c) 2025 Billy Huddleston <billy@ivdc.com> *
# * *
# * This file is part of FreeCAD. *
# * *
# * FreeCAD is free software: you can redistribute it and/or modify it *
# * under the terms of the GNU Lesser General Public License as *
# * published by the Free Software Foundation, either version 2.1 of the *
# * License, or (at your option) any later version. *
# * *
# * FreeCAD is distributed in the hope that it will be useful, but *
# * WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# * Lesser General Public License for more details. *
# * *
# * You should have received a copy of the GNU Lesser General Public *
# * License along with FreeCAD. If not, see *
# * <https://www.gnu.org/licenses/>. *
# * *
# ***************************************************************************
import FreeCAD
import Path
from typing import Dict, Any, Optional, Union
from .util import units_from_json
class ParameterAccessor:
"""
Unified accessor for dicts and FreeCAD objects for migration logic.
"""
def __init__(self, target):
self.target = target
self.is_dict = isinstance(target, dict)
def has(self, key):
if self.is_dict:
# For dicts, check in nested 'parameter' dict
param = self.target.get("parameter", {})
return key in param if isinstance(param, dict) else False
else:
return key in getattr(self.target, "PropertiesList", [])
def get(self, key):
if self.is_dict:
# For dicts, get from nested 'parameter' dict
param = self.target.get("parameter", {})
return param.get(key) if isinstance(param, dict) else None
else:
return self.target.getPropertyByName(key)
def set(self, key, value):
if self.is_dict:
# For dicts, set in nested 'parameter' dict
if "parameter" not in self.target:
self.target["parameter"] = {}
self.target["parameter"][key] = value
else:
setattr(self.target, key, value)
def add_property(self, prop_type, key, group, doc):
if self.is_dict:
# For dicts, just set the value
pass # No-op, handled by set()
else:
self.target.addProperty(prop_type, key, group, doc)
def set_editor_mode(self, key, mode):
if self.is_dict:
pass # No-op
else:
self.target.setEditorMode(key, mode)
def name(self):
if self.is_dict:
return self.target.get("name", "toolbit")
else:
return getattr(self.target, "Label", "unknown toolbit")
def get_shape_type(self):
if self.is_dict:
# For dicts, shape-type is at top level of attrs dict
return self.target.get("shape-type")
else:
# For FreeCAD objects, use ShapeType attribute
return getattr(self.target, "ShapeType", None)
if False:
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
Path.Log.trackModule(Path.Log.thisModule())
else:
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
def migrate_parameters(accessor: ParameterAccessor) -> bool:
"""
Migrates legacy parameters using a unified accessor.
Currently handles:
- TorusRadius → CornerRadius
- FlatRadius/Diameter → CornerRadius
- Infers Units from parameter strings if not set
Args:
accessor: ParameterAccessor instance wrapping dict or FreeCAD object
Returns:
True if migration occurred, False otherwise
"""
migrated = False
has_torus = accessor.has("TorusRadius")
has_flat = accessor.has("FlatRadius")
has_diam = accessor.has("Diameter")
has_corner = accessor.has("CornerRadius")
has_units = accessor.has("Units")
name = accessor.name()
shape_type = accessor.get_shape_type()
# Infer Units from parameter strings if not set
if not has_units:
# Gather all parameters to check for units
params = {}
if accessor.is_dict:
params = accessor.target.get("parameter", {})
inferred_units = units_from_json(params)
if inferred_units:
accessor.set("Units", inferred_units)
Path.Log.info(f"Adding Units as '{inferred_units}' for {name}")
migrated = True
# Only run migration logic if shape type == 'Bullnose'
if shape_type and str(shape_type).lower() == "bullnose":
# Case 1: TorusRadius exists, copy to CornerRadius
if has_torus and not has_corner:
value = accessor.get("TorusRadius")
accessor.add_property(
"App::PropertyLength",
"CornerRadius",
"Shape",
"Corner radius copied from TorusRadius",
)
accessor.set_editor_mode("CornerRadius", 0)
accessor.set("CornerRadius", value)
Path.Log.info(f"Copied TorusRadius to CornerRadius={value} for {name}")
migrated = True
# Case 2: FlatRadius and Diameter exist, calculate CornerRadius
if has_flat and has_diam and not has_corner and not has_torus:
try:
diam_raw = accessor.get("Diameter")
flat_raw = accessor.get("FlatRadius")
diameter = FreeCAD.Units.Quantity(diam_raw)
flat_radius = FreeCAD.Units.Quantity(flat_raw)
corner_radius = (float(diameter) / 2.0) - float(flat_radius)
# Convert to correct unit
if isinstance(diam_raw, str) and diam_raw.strip().endswith("in"):
cr_in = FreeCAD.Units.Quantity(f"{corner_radius} mm").getValueAs("in")
value = f"{float(cr_in):.4f} in"
else:
if isinstance(diam_raw, str) and not diam_raw.strip().endswith("mm"):
cr_mm = FreeCAD.Units.Quantity(f"{corner_radius} in").getValueAs("mm")
value = f"{float(cr_mm):.4f} mm"
else:
value = f"{float(corner_radius):.4f} mm"
accessor.add_property(
"App::PropertyLength",
"CornerRadius",
"Shape",
"Corner radius migrated from FlatRadius/Diameter",
)
accessor.set_editor_mode("CornerRadius", 0)
accessor.set("CornerRadius", value)
Path.Log.info(f"Migrated FlatRadius/Diameter to CornerRadius={value} for {name}")
migrated = True
except Exception as e:
Path.Log.error(f"Failed to migrate FlatRadius for toolbit {name}: {e}")
return migrated