Download src/Mod/CAM/Path/Tool/library/models/library.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 7.76 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/library/models/library.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/CAM/Path/Tool/library/models/library.py
-
curl -L -o library.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/CAM/Path/Tool/library/models/library.py
7.76 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2025 Samuel Abels <knipknap@gmail.com> * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * This program 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 Library General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with this program; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| import uuid | |
| import pathlib | |
| from typing import Mapping, Union, Optional, List, Dict, cast | |
| import Path | |
| from ...assets import Asset, AssetUri | |
| from ...toolbit import ToolBit | |
| class Library(Asset): | |
| asset_type: str = "toolbitlibrary" | |
| API_VERSION = 1 | |
| def __init__(self, label, id=None): | |
| self.id = id if id is not None else str(uuid.uuid4()) | |
| self._label = label | |
| self._bits: List[ToolBit] = [] | |
| self._bit_nos: Dict[int, ToolBit] = {} | |
| self._bit_urls: Dict[AssetUri, ToolBit] = {} | |
| def label(self) -> str: | |
| return self._label | |
| def get_id(self) -> str: | |
| """Returns the unique identifier for the Library instance.""" | |
| return self.id | |
| def resolve_name(cls, identifier: Union[str, AssetUri, pathlib.Path]) -> AssetUri: | |
| """ | |
| Resolves various forms of library identifiers to a canonical AssetUri string. | |
| Handles direct AssetUri objects, URI strings, asset IDs, or legacy filenames. | |
| Returns the canonical URI string or None if resolution fails. | |
| """ | |
| if isinstance(identifier, AssetUri): | |
| return identifier | |
| if isinstance(identifier, str) and AssetUri.is_uri(identifier): | |
| return AssetUri(identifier) | |
| if isinstance(identifier, pathlib.Path): # Handle direct Path objects (legacy filenames) | |
| identifier = identifier.stem # Use the filename stem as potential ID | |
| if not isinstance(identifier, str): | |
| raise ValueError("Failed to resolve {identifier} to a Uri") | |
| return AssetUri.build(asset_type=Library.asset_type, asset_id=identifier) | |
| def to_dict(self) -> dict: | |
| """Returns a dictionary representation of the Library in the specified format.""" | |
| tools_list = [] | |
| for tool_no, tool in self._bit_nos.items(): | |
| tools_list.append( | |
| {"nr": tool_no, "path": f"{tool.get_id()}.fctb"} # Tool ID with .fctb extension | |
| ) | |
| return {"label": self.label, "tools": tools_list, "version": self.API_VERSION} | |
| def from_dict( | |
| cls, | |
| data_dict: dict, | |
| id: str, | |
| dependencies: Optional[Mapping[AssetUri, Asset]], | |
| ) -> "Library": | |
| """ | |
| Creates a Library instance from a dictionary and resolved dependencies. | |
| If dependencies is None, it's a shallow load, and tools are not populated. | |
| """ | |
| library = cls(data_dict.get("label", id or "Unnamed Library"), id=id) | |
| if dependencies is None: | |
| Path.Log.debug( | |
| f"Library.from_dict: Shallow load for library '{library.label}' (id: {id}). Tools not populated." | |
| ) | |
| return library # Only process tools if dependencies were resolved | |
| tools_list = data_dict.get("tools", []) | |
| for tool_data in tools_list: | |
| tool_no = tool_data["nr"] | |
| tool_id = pathlib.Path(tool_data["path"]).stem # Extract tool ID | |
| tool_uri = AssetUri(f"toolbit://{tool_id}") | |
| bit = cast(ToolBit, dependencies.get(tool_uri)) | |
| if bit: | |
| library.add_bit(bit, bit_no=tool_no) | |
| else: | |
| raise ValueError(f"Tool with id {tool_id} not found in dependencies") | |
| return library | |
| def __str__(self): | |
| return '{} "{}"'.format(self.id, self.label) | |
| def __eq__(self, other): | |
| return self.id == other.id | |
| def __iter__(self): | |
| return self._bits.__iter__() | |
| def get_next_bit_no(self): | |
| bit_nolist = sorted(self._bit_nos, reverse=True) | |
| return bit_nolist[0] + 1 if bit_nolist else 1 | |
| def get_bit_no_from_bit(self, bit: ToolBit) -> Optional[int]: | |
| for bit_no, thebit in self._bit_nos.items(): | |
| if bit == thebit: | |
| return bit_no | |
| return None | |
| def get_tool_by_uri(self, uri: AssetUri) -> Optional[ToolBit]: | |
| for tool in self._bit_nos.values(): | |
| if tool.get_uri() == uri: | |
| return tool | |
| return None | |
| def assign_new_bit_no(self, bit: ToolBit, bit_no: Optional[int] = None) -> int: | |
| if bit not in self._bits: | |
| raise ValueError(f"given bit {bit} not in library; cannot assign tool number") | |
| # If no specific bit_no was requested, assign a new one. | |
| if bit_no is None: | |
| bit_no = self.get_next_bit_no() | |
| elif self._bit_nos.get(bit_no) == bit: | |
| return bit_no | |
| # Otherwise, add the bit. Since the requested bit_no may already | |
| # be in use, we need to account for that. In this case, we will | |
| # add the removed bit into a new bit_no. | |
| old_bit = self._bit_nos.pop(bit_no, None) | |
| old_bit_no = self.get_bit_no_from_bit(bit) | |
| if old_bit_no: | |
| del self._bit_nos[old_bit_no] | |
| self._bit_nos[bit_no] = bit | |
| if old_bit: | |
| self.assign_new_bit_no(old_bit) | |
| return bit_no | |
| def add_bit(self, bit: ToolBit, bit_no: Optional[int] = None) -> int: | |
| if bit not in self._bits: | |
| self._bits.append(bit) | |
| return self.assign_new_bit_no(bit, bit_no) | |
| def get_bits(self) -> List[ToolBit]: | |
| return self._bits | |
| def has_bit(self, bit: ToolBit) -> bool: | |
| for t in self._bits: | |
| if bit.id == t.id: | |
| return True | |
| return False | |
| def remove_bit(self, bit: ToolBit): | |
| self._bits = [t for t in self._bits if t.id != bit.id] | |
| self._bit_nos = {k: v for (k, v) in self._bit_nos.items() if v.id != bit.id} | |
| def remove_bit_by_uri(self, uri: AssetUri | str): | |
| if isinstance(uri, str): | |
| uri = AssetUri(uri) | |
| self._bits = [t for t in self._bits if t.get_uri() != uri] | |
| self._bit_nos = {k: v for (k, v) in self._bit_nos.items() if v.get_uri() != uri} | |
| def dump(self, summarize: bool = False): | |
| title = 'Library "{}" ({}) (instance {})'.format(self.label, self.id, id(self)) | |
| print("-" * len(title)) | |
| print(title) | |
| print("-" * len(title)) | |
| for bit in self._bits: | |
| print(f"- {bit.label} ({bit.get_id()})") | |
| print() | |