symcad.core.CAD.ScriptedCad
1#!/usr/bin/env python3 2# Copyright (C) 2022, Will Hedgecock 3# 4# This program is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17from __future__ import annotations 18from typing import Callable, Dict, Literal, Optional, Tuple 19from PyFreeCAD.FreeCAD import FreeCAD, Part 20from .CadGeneral import is_symbolic 21from . import CadGeneral 22from pathlib import Path 23 24TESSELATION_VALUE = 1.0 25 26class ScriptedCad(object): 27 """Private helper class to generate a CAD representation from a `SymPart`.""" 28 29 # Public attributes ---------------------------------------------------------------------------- 30 31 creation_callback: Callable[[Dict[str, float], bool], Part.Solid] 32 """Creation callback for a `SymPart` to generate a concrete OpenCascade model.""" 33 34 35 # Constructor ---------------------------------------------------------------------------------- 36 37 def __init__(self, creation_callback: Callable[[Dict[str, float], bool], Part.Solid]) -> None: 38 """Initializes a `ScriptedCad` object, where `creation_callback` is a shape-specific 39 method that uses a dictionary of `geometry: value` entries to create an OpenCascade 40 `Part.Solid` for a given `SymPart`. 41 """ 42 self.creation_callback = creation_callback 43 44 45 # Built-in method implementations -------------------------------------------------------------- 46 47 def __eq__(self, other: ScriptedCad) -> bool: 48 return self.creation_callback.__qualname__ == other.creation_callback.__qualname__ 49 50 51 # Public methods ------------------------------------------------------------------------------- 52 53 def add_to_assembly(self, model_name: str, 54 assembly: FreeCAD.Document, 55 concrete_parameters: Dict[str, float], 56 placement_point: Tuple[float, float, float], 57 placement_m: Tuple[float, float, float], 58 yaw_pitch_roll_deg: Tuple[float, float, float], 59 fully_displace: Optional[bool] = False) -> None: 60 """Adds a CAD model representation of the `SymPart` to the specified assembly. 61 62 Parameters 63 ---------- 64 model_name : `str` 65 Desired name for the CAD model. 66 assembly : `FreeCAD.Document` 67 FreeCAD assembly document to which the CAD model should be added. 68 concrete_parameters : `Dict[str, float]` 69 Dictionary of free variables along with their desired concrete values. 70 placement_point : `Tuple[float, float, float]` 71 Local coordinate (in percent length) to be used for the center of rotation and placement 72 of the CAD model. 73 placement_m : `Tuple[float, float, float]` 74 Global xyz-placement (in `m`) of the `placement_point` of the CAD object in the assembly. 75 yaw_pitch_roll_deg : `Tuple[float, float, float]` 76 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 77 fully_displace : `bool` 78 Whether to create a fully solid CAD model for displacement purposes. 79 """ 80 81 # Verify that all parameters are concrete 82 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 83 is_symbolic(yaw_pitch_roll_deg[2]): 84 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to add it ' 85 'to a CAD assembly'.format(yaw_pitch_roll_deg)) 86 for key, val in concrete_parameters.items(): 87 if key != 'name' and is_symbolic(val): 88 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 89 'add it to a CAD assembly'.format(key)) 90 91 # Create and add a new CAD model to the assembly 92 model = assembly.addObject(CadGeneral.PART_FEATURE_STRING, model_name) 93 model.Shape = self.creation_callback(concrete_parameters, fully_displace) 94 model.Shape.tessellate(TESSELATION_VALUE) 95 assembly.recompute() 96 97 # Properly place and orient the CAD model in the assembly 98 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 99 placement = FreeCAD.Vector((1000.0 * placement_m[0]) - rotation_point.x, 100 (1000.0 * placement_m[1]) - rotation_point.y, 101 (1000.0 * placement_m[2]) - rotation_point.z) 102 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 103 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 104 model.Shape.tessellate(TESSELATION_VALUE) 105 assembly.recompute() 106 107 108 def get_physical_properties(self, concrete_parameters: Dict[str, float], 109 placement_point: Tuple[float, float, float], 110 yaw_pitch_roll_deg: Tuple[float, float, float], 111 material_density_kg_m3: float, 112 normalize_origin: bool) -> Dict[str, float]: 113 """Returns all physical properties of the CAD model. 114 115 Mass properties will be computed assuming a uniform material density as specified in 116 the `material_density_kg_m3` parameter. 117 118 All free parameters within the CAD model must be concretized by passing in an appropriate 119 `concrete_parameters` dictionary containing the names of the free parameters as its keys, 120 along with their corresponding concrete floating-point values. 121 122 Parameters 123 ---------- 124 concrete_parameters : `Dict[str, float]` 125 Dictionary of free variables along with their desired concrete values. 126 placement_point : `Tuple[float, float, float]` 127 Local coordinate (in percent length) to be used for the center of placement 128 of the CAD model. 129 yaw_pitch_roll_deg : `Tuple[float, float, float]` 130 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 131 material_density_kg_m3 : `float` 132 Uniform material density to be used in mass property calculations (in `kg/m^3`). 133 normalize_origin : `bool` 134 Return physical properties with respect to the front, left, bottom corner of the 135 underlying CAD model. 136 137 Returns 138 ------- 139 `Dict[str, float]` 140 A dictionary containing all physical `SymPart` properties as computed using the 141 underlying CAD model. 142 """ 143 144 # Verify that all parameters are concrete 145 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 146 is_symbolic(yaw_pitch_roll_deg[2]): 147 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to calculate ' 148 'its physical properties from CAD'.format(yaw_pitch_roll_deg)) 149 for key, val in concrete_parameters.items(): 150 if key != 'name' and is_symbolic(val): 151 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 152 'calculate its physical properties from CAD'.format(key)) 153 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 154 155 # Create the scripted CAD model 156 doc = FreeCAD.newDocument() 157 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 158 model.Shape = self.creation_callback(concrete_parameters, False) 159 model.Shape.tessellate(TESSELATION_VALUE) 160 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 161 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 162 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 163 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 164 model.Shape.tessellate(TESSELATION_VALUE) 165 166 # Create a separate displacement model 167 displaced_model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'DisplacedModel') 168 displaced_model.Shape = self.creation_callback(concrete_parameters, True) 169 displaced_model.Shape.tessellate(TESSELATION_VALUE) 170 displaced_model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 171 displaced_model.Shape.tessellate(TESSELATION_VALUE) 172 173 # Retrieve all physical model properties 174 doc.recompute() 175 properties = CadGeneral.fetch_model_physical_properties(model.Shape, 176 displaced_model.Shape, 177 material_density_kg_m3, 178 normalize_origin) 179 FreeCAD.closeDocument(doc.Name) 180 return properties 181 182 183 def export_model(self, file_save_path: str, 184 model_type: Literal['freecad', 'step', 'stl'], 185 concrete_parameters: Dict[str, float], 186 placement_point: Tuple[float, float, float], 187 yaw_pitch_roll_deg: Tuple[float, float, float]) -> None: 188 """Creates a CAD model of the `SymPart` in the specified format. 189 190 Parameters 191 ---------- 192 file_save_path : `str` 193 Output file path at which to store the generated CAD model. 194 model_type : {'freecad', 'step', 'stl'} 195 Format of the CAD model to export. 196 concrete_parameters : `Dict[str, float]` 197 Dictionary of free variables along with their desired concrete values. 198 placement_point : `Tuple[float, float, float]` 199 Local coordinate (in percent length) to be used for the center of placement 200 of the CAD model. 201 yaw_pitch_roll_deg : `Tuple[float, float, float]` 202 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 203 """ 204 205 # Verify that all parameters have concrete representations 206 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 207 is_symbolic(yaw_pitch_roll_deg[2]): 208 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to export ' 209 'it as a CAD model'.format(yaw_pitch_roll_deg)) 210 for key, val in concrete_parameters.items(): 211 if key != 'name' and is_symbolic(val): 212 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 213 'export it as a CAD model'.format(key)) 214 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 215 216 # Create any necessary path directories 217 file_path = Path(file_save_path).absolute().resolve() 218 if not file_path.parent.exists(): 219 file_path.parent.mkdir() 220 221 # Create and tessellate the scripted CAD model 222 doc = FreeCAD.newDocument() 223 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 224 model.Shape = self.creation_callback(concrete_parameters, False) 225 model.Shape.tessellate(TESSELATION_VALUE) 226 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 227 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 228 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 229 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 230 model.Shape.tessellate(TESSELATION_VALUE) 231 doc.recompute() 232 233 # Create the requested CAD format of the model 234 CadGeneral.save_model(file_path, model_type, model) 235 FreeCAD.closeDocument(doc.Name)
27class ScriptedCad(object): 28 """Private helper class to generate a CAD representation from a `SymPart`.""" 29 30 # Public attributes ---------------------------------------------------------------------------- 31 32 creation_callback: Callable[[Dict[str, float], bool], Part.Solid] 33 """Creation callback for a `SymPart` to generate a concrete OpenCascade model.""" 34 35 36 # Constructor ---------------------------------------------------------------------------------- 37 38 def __init__(self, creation_callback: Callable[[Dict[str, float], bool], Part.Solid]) -> None: 39 """Initializes a `ScriptedCad` object, where `creation_callback` is a shape-specific 40 method that uses a dictionary of `geometry: value` entries to create an OpenCascade 41 `Part.Solid` for a given `SymPart`. 42 """ 43 self.creation_callback = creation_callback 44 45 46 # Built-in method implementations -------------------------------------------------------------- 47 48 def __eq__(self, other: ScriptedCad) -> bool: 49 return self.creation_callback.__qualname__ == other.creation_callback.__qualname__ 50 51 52 # Public methods ------------------------------------------------------------------------------- 53 54 def add_to_assembly(self, model_name: str, 55 assembly: FreeCAD.Document, 56 concrete_parameters: Dict[str, float], 57 placement_point: Tuple[float, float, float], 58 placement_m: Tuple[float, float, float], 59 yaw_pitch_roll_deg: Tuple[float, float, float], 60 fully_displace: Optional[bool] = False) -> None: 61 """Adds a CAD model representation of the `SymPart` to the specified assembly. 62 63 Parameters 64 ---------- 65 model_name : `str` 66 Desired name for the CAD model. 67 assembly : `FreeCAD.Document` 68 FreeCAD assembly document to which the CAD model should be added. 69 concrete_parameters : `Dict[str, float]` 70 Dictionary of free variables along with their desired concrete values. 71 placement_point : `Tuple[float, float, float]` 72 Local coordinate (in percent length) to be used for the center of rotation and placement 73 of the CAD model. 74 placement_m : `Tuple[float, float, float]` 75 Global xyz-placement (in `m`) of the `placement_point` of the CAD object in the assembly. 76 yaw_pitch_roll_deg : `Tuple[float, float, float]` 77 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 78 fully_displace : `bool` 79 Whether to create a fully solid CAD model for displacement purposes. 80 """ 81 82 # Verify that all parameters are concrete 83 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 84 is_symbolic(yaw_pitch_roll_deg[2]): 85 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to add it ' 86 'to a CAD assembly'.format(yaw_pitch_roll_deg)) 87 for key, val in concrete_parameters.items(): 88 if key != 'name' and is_symbolic(val): 89 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 90 'add it to a CAD assembly'.format(key)) 91 92 # Create and add a new CAD model to the assembly 93 model = assembly.addObject(CadGeneral.PART_FEATURE_STRING, model_name) 94 model.Shape = self.creation_callback(concrete_parameters, fully_displace) 95 model.Shape.tessellate(TESSELATION_VALUE) 96 assembly.recompute() 97 98 # Properly place and orient the CAD model in the assembly 99 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 100 placement = FreeCAD.Vector((1000.0 * placement_m[0]) - rotation_point.x, 101 (1000.0 * placement_m[1]) - rotation_point.y, 102 (1000.0 * placement_m[2]) - rotation_point.z) 103 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 104 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 105 model.Shape.tessellate(TESSELATION_VALUE) 106 assembly.recompute() 107 108 109 def get_physical_properties(self, concrete_parameters: Dict[str, float], 110 placement_point: Tuple[float, float, float], 111 yaw_pitch_roll_deg: Tuple[float, float, float], 112 material_density_kg_m3: float, 113 normalize_origin: bool) -> Dict[str, float]: 114 """Returns all physical properties of the CAD model. 115 116 Mass properties will be computed assuming a uniform material density as specified in 117 the `material_density_kg_m3` parameter. 118 119 All free parameters within the CAD model must be concretized by passing in an appropriate 120 `concrete_parameters` dictionary containing the names of the free parameters as its keys, 121 along with their corresponding concrete floating-point values. 122 123 Parameters 124 ---------- 125 concrete_parameters : `Dict[str, float]` 126 Dictionary of free variables along with their desired concrete values. 127 placement_point : `Tuple[float, float, float]` 128 Local coordinate (in percent length) to be used for the center of placement 129 of the CAD model. 130 yaw_pitch_roll_deg : `Tuple[float, float, float]` 131 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 132 material_density_kg_m3 : `float` 133 Uniform material density to be used in mass property calculations (in `kg/m^3`). 134 normalize_origin : `bool` 135 Return physical properties with respect to the front, left, bottom corner of the 136 underlying CAD model. 137 138 Returns 139 ------- 140 `Dict[str, float]` 141 A dictionary containing all physical `SymPart` properties as computed using the 142 underlying CAD model. 143 """ 144 145 # Verify that all parameters are concrete 146 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 147 is_symbolic(yaw_pitch_roll_deg[2]): 148 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to calculate ' 149 'its physical properties from CAD'.format(yaw_pitch_roll_deg)) 150 for key, val in concrete_parameters.items(): 151 if key != 'name' and is_symbolic(val): 152 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 153 'calculate its physical properties from CAD'.format(key)) 154 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 155 156 # Create the scripted CAD model 157 doc = FreeCAD.newDocument() 158 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 159 model.Shape = self.creation_callback(concrete_parameters, False) 160 model.Shape.tessellate(TESSELATION_VALUE) 161 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 162 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 163 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 164 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 165 model.Shape.tessellate(TESSELATION_VALUE) 166 167 # Create a separate displacement model 168 displaced_model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'DisplacedModel') 169 displaced_model.Shape = self.creation_callback(concrete_parameters, True) 170 displaced_model.Shape.tessellate(TESSELATION_VALUE) 171 displaced_model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 172 displaced_model.Shape.tessellate(TESSELATION_VALUE) 173 174 # Retrieve all physical model properties 175 doc.recompute() 176 properties = CadGeneral.fetch_model_physical_properties(model.Shape, 177 displaced_model.Shape, 178 material_density_kg_m3, 179 normalize_origin) 180 FreeCAD.closeDocument(doc.Name) 181 return properties 182 183 184 def export_model(self, file_save_path: str, 185 model_type: Literal['freecad', 'step', 'stl'], 186 concrete_parameters: Dict[str, float], 187 placement_point: Tuple[float, float, float], 188 yaw_pitch_roll_deg: Tuple[float, float, float]) -> None: 189 """Creates a CAD model of the `SymPart` in the specified format. 190 191 Parameters 192 ---------- 193 file_save_path : `str` 194 Output file path at which to store the generated CAD model. 195 model_type : {'freecad', 'step', 'stl'} 196 Format of the CAD model to export. 197 concrete_parameters : `Dict[str, float]` 198 Dictionary of free variables along with their desired concrete values. 199 placement_point : `Tuple[float, float, float]` 200 Local coordinate (in percent length) to be used for the center of placement 201 of the CAD model. 202 yaw_pitch_roll_deg : `Tuple[float, float, float]` 203 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 204 """ 205 206 # Verify that all parameters have concrete representations 207 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 208 is_symbolic(yaw_pitch_roll_deg[2]): 209 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to export ' 210 'it as a CAD model'.format(yaw_pitch_roll_deg)) 211 for key, val in concrete_parameters.items(): 212 if key != 'name' and is_symbolic(val): 213 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 214 'export it as a CAD model'.format(key)) 215 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 216 217 # Create any necessary path directories 218 file_path = Path(file_save_path).absolute().resolve() 219 if not file_path.parent.exists(): 220 file_path.parent.mkdir() 221 222 # Create and tessellate the scripted CAD model 223 doc = FreeCAD.newDocument() 224 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 225 model.Shape = self.creation_callback(concrete_parameters, False) 226 model.Shape.tessellate(TESSELATION_VALUE) 227 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 228 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 229 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 230 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 231 model.Shape.tessellate(TESSELATION_VALUE) 232 doc.recompute() 233 234 # Create the requested CAD format of the model 235 CadGeneral.save_model(file_path, model_type, model) 236 FreeCAD.closeDocument(doc.Name)
Private helper class to generate a CAD representation from a SymPart.
38 def __init__(self, creation_callback: Callable[[Dict[str, float], bool], Part.Solid]) -> None: 39 """Initializes a `ScriptedCad` object, where `creation_callback` is a shape-specific 40 method that uses a dictionary of `geometry: value` entries to create an OpenCascade 41 `Part.Solid` for a given `SymPart`. 42 """ 43 self.creation_callback = creation_callback
Initializes a ScriptedCad object, where creation_callback is a shape-specific
method that uses a dictionary of geometry: value entries to create an OpenCascade
Part.Solid for a given SymPart.
Creation callback for a SymPart to generate a concrete OpenCascade model.
54 def add_to_assembly(self, model_name: str, 55 assembly: FreeCAD.Document, 56 concrete_parameters: Dict[str, float], 57 placement_point: Tuple[float, float, float], 58 placement_m: Tuple[float, float, float], 59 yaw_pitch_roll_deg: Tuple[float, float, float], 60 fully_displace: Optional[bool] = False) -> None: 61 """Adds a CAD model representation of the `SymPart` to the specified assembly. 62 63 Parameters 64 ---------- 65 model_name : `str` 66 Desired name for the CAD model. 67 assembly : `FreeCAD.Document` 68 FreeCAD assembly document to which the CAD model should be added. 69 concrete_parameters : `Dict[str, float]` 70 Dictionary of free variables along with their desired concrete values. 71 placement_point : `Tuple[float, float, float]` 72 Local coordinate (in percent length) to be used for the center of rotation and placement 73 of the CAD model. 74 placement_m : `Tuple[float, float, float]` 75 Global xyz-placement (in `m`) of the `placement_point` of the CAD object in the assembly. 76 yaw_pitch_roll_deg : `Tuple[float, float, float]` 77 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 78 fully_displace : `bool` 79 Whether to create a fully solid CAD model for displacement purposes. 80 """ 81 82 # Verify that all parameters are concrete 83 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 84 is_symbolic(yaw_pitch_roll_deg[2]): 85 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to add it ' 86 'to a CAD assembly'.format(yaw_pitch_roll_deg)) 87 for key, val in concrete_parameters.items(): 88 if key != 'name' and is_symbolic(val): 89 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 90 'add it to a CAD assembly'.format(key)) 91 92 # Create and add a new CAD model to the assembly 93 model = assembly.addObject(CadGeneral.PART_FEATURE_STRING, model_name) 94 model.Shape = self.creation_callback(concrete_parameters, fully_displace) 95 model.Shape.tessellate(TESSELATION_VALUE) 96 assembly.recompute() 97 98 # Properly place and orient the CAD model in the assembly 99 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 100 placement = FreeCAD.Vector((1000.0 * placement_m[0]) - rotation_point.x, 101 (1000.0 * placement_m[1]) - rotation_point.y, 102 (1000.0 * placement_m[2]) - rotation_point.z) 103 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 104 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 105 model.Shape.tessellate(TESSELATION_VALUE) 106 assembly.recompute()
Adds a CAD model representation of the SymPart to the specified assembly.
Parameters
- model_name (
str): Desired name for the CAD model. - assembly (
FreeCAD.Document): FreeCAD assembly document to which the CAD model should be added. - concrete_parameters (
Dict[str, float]): Dictionary of free variables along with their desired concrete values. - placement_point (
Tuple[float, float, float]): Local coordinate (in percent length) to be used for the center of rotation and placement of the CAD model. - placement_m (
Tuple[float, float, float]): Global xyz-placement (inm) of theplacement_pointof the CAD object in the assembly. - yaw_pitch_roll_deg (
Tuple[float, float, float]): Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. - fully_displace (
bool): Whether to create a fully solid CAD model for displacement purposes.
109 def get_physical_properties(self, concrete_parameters: Dict[str, float], 110 placement_point: Tuple[float, float, float], 111 yaw_pitch_roll_deg: Tuple[float, float, float], 112 material_density_kg_m3: float, 113 normalize_origin: bool) -> Dict[str, float]: 114 """Returns all physical properties of the CAD model. 115 116 Mass properties will be computed assuming a uniform material density as specified in 117 the `material_density_kg_m3` parameter. 118 119 All free parameters within the CAD model must be concretized by passing in an appropriate 120 `concrete_parameters` dictionary containing the names of the free parameters as its keys, 121 along with their corresponding concrete floating-point values. 122 123 Parameters 124 ---------- 125 concrete_parameters : `Dict[str, float]` 126 Dictionary of free variables along with their desired concrete values. 127 placement_point : `Tuple[float, float, float]` 128 Local coordinate (in percent length) to be used for the center of placement 129 of the CAD model. 130 yaw_pitch_roll_deg : `Tuple[float, float, float]` 131 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 132 material_density_kg_m3 : `float` 133 Uniform material density to be used in mass property calculations (in `kg/m^3`). 134 normalize_origin : `bool` 135 Return physical properties with respect to the front, left, bottom corner of the 136 underlying CAD model. 137 138 Returns 139 ------- 140 `Dict[str, float]` 141 A dictionary containing all physical `SymPart` properties as computed using the 142 underlying CAD model. 143 """ 144 145 # Verify that all parameters are concrete 146 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 147 is_symbolic(yaw_pitch_roll_deg[2]): 148 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to calculate ' 149 'its physical properties from CAD'.format(yaw_pitch_roll_deg)) 150 for key, val in concrete_parameters.items(): 151 if key != 'name' and is_symbolic(val): 152 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 153 'calculate its physical properties from CAD'.format(key)) 154 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 155 156 # Create the scripted CAD model 157 doc = FreeCAD.newDocument() 158 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 159 model.Shape = self.creation_callback(concrete_parameters, False) 160 model.Shape.tessellate(TESSELATION_VALUE) 161 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 162 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 163 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 164 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 165 model.Shape.tessellate(TESSELATION_VALUE) 166 167 # Create a separate displacement model 168 displaced_model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'DisplacedModel') 169 displaced_model.Shape = self.creation_callback(concrete_parameters, True) 170 displaced_model.Shape.tessellate(TESSELATION_VALUE) 171 displaced_model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 172 displaced_model.Shape.tessellate(TESSELATION_VALUE) 173 174 # Retrieve all physical model properties 175 doc.recompute() 176 properties = CadGeneral.fetch_model_physical_properties(model.Shape, 177 displaced_model.Shape, 178 material_density_kg_m3, 179 normalize_origin) 180 FreeCAD.closeDocument(doc.Name) 181 return properties
Returns all physical properties of the CAD model.
Mass properties will be computed assuming a uniform material density as specified in
the material_density_kg_m3 parameter.
All free parameters within the CAD model must be concretized by passing in an appropriate
concrete_parameters dictionary containing the names of the free parameters as its keys,
along with their corresponding concrete floating-point values.
Parameters
- concrete_parameters (
Dict[str, float]): Dictionary of free variables along with their desired concrete values. - placement_point (
Tuple[float, float, float]): Local coordinate (in percent length) to be used for the center of placement of the CAD model. - yaw_pitch_roll_deg (
Tuple[float, float, float]): Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. - material_density_kg_m3 (
float): Uniform material density to be used in mass property calculations (inkg/m^3). - normalize_origin (
bool): Return physical properties with respect to the front, left, bottom corner of the underlying CAD model.
Returns
Dict[str, float]: A dictionary containing all physicalSymPartproperties as computed using the underlying CAD model.
184 def export_model(self, file_save_path: str, 185 model_type: Literal['freecad', 'step', 'stl'], 186 concrete_parameters: Dict[str, float], 187 placement_point: Tuple[float, float, float], 188 yaw_pitch_roll_deg: Tuple[float, float, float]) -> None: 189 """Creates a CAD model of the `SymPart` in the specified format. 190 191 Parameters 192 ---------- 193 file_save_path : `str` 194 Output file path at which to store the generated CAD model. 195 model_type : {'freecad', 'step', 'stl'} 196 Format of the CAD model to export. 197 concrete_parameters : `Dict[str, float]` 198 Dictionary of free variables along with their desired concrete values. 199 placement_point : `Tuple[float, float, float]` 200 Local coordinate (in percent length) to be used for the center of placement 201 of the CAD model. 202 yaw_pitch_roll_deg : `Tuple[float, float, float]` 203 Global yaw-, pitch-, and roll-orientation in degrees of the CAD object. 204 """ 205 206 # Verify that all parameters have concrete representations 207 if is_symbolic(yaw_pitch_roll_deg[0]) or is_symbolic(yaw_pitch_roll_deg[1]) or \ 208 is_symbolic(yaw_pitch_roll_deg[2]): 209 raise RuntimeError('The orientation of the part ("{}") must not be symbolic to export ' 210 'it as a CAD model'.format(yaw_pitch_roll_deg)) 211 for key, val in concrete_parameters.items(): 212 if key != 'name' and is_symbolic(val): 213 raise RuntimeError('The geometric parameter "{}" of the part must not be symbolic to ' 214 'export it as a CAD model'.format(key)) 215 placement_point = [0.0 if is_symbolic(p) else float(p) for p in placement_point] 216 217 # Create any necessary path directories 218 file_path = Path(file_save_path).absolute().resolve() 219 if not file_path.parent.exists(): 220 file_path.parent.mkdir() 221 222 # Create and tessellate the scripted CAD model 223 doc = FreeCAD.newDocument() 224 model = doc.addObject(CadGeneral.PART_FEATURE_STRING, 'Model') 225 model.Shape = self.creation_callback(concrete_parameters, False) 226 model.Shape.tessellate(TESSELATION_VALUE) 227 rotation_point = CadGeneral.compute_placement_point(model.Shape, placement_point) 228 placement = FreeCAD.Vector(-rotation_point.x, -rotation_point.y, -rotation_point.z) 229 rotation = FreeCAD.Rotation(*yaw_pitch_roll_deg) 230 model.Placement = FreeCAD.Placement(placement, rotation, rotation_point) 231 model.Shape.tessellate(TESSELATION_VALUE) 232 doc.recompute() 233 234 # Create the requested CAD format of the model 235 CadGeneral.save_model(file_path, model_type, model) 236 FreeCAD.closeDocument(doc.Name)
Creates a CAD model of the SymPart in the specified format.
Parameters
- file_save_path (
str): Output file path at which to store the generated CAD model. - model_type ({'freecad', 'step', 'stl'}): Format of the CAD model to export.
- concrete_parameters (
Dict[str, float]): Dictionary of free variables along with their desired concrete values. - placement_point (
Tuple[float, float, float]): Local coordinate (in percent length) to be used for the center of placement of the CAD model. - yaw_pitch_roll_deg (
Tuple[float, float, float]): Global yaw-, pitch-, and roll-orientation in degrees of the CAD object.