def test_export_with_keep_unresolved_part_containing_primitive(self): document = App.newDocument() box = document.addObject('Part::Box', 'Box') box.Label = 'Cube' box.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) part = document.addObject('App::Part', 'Part') part.Label = 'Part' part.addObject(box) part.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) document.recompute() obj_filename = 'translated_part_containing_cube.obj' with open(os.path.join(os.path.dirname(__file__), obj_filename)) as f: expected = f.read() def keep_unresolved(obj: object, path: List[object]) -> bool: return obj.Name == 'Part' obj_file_contents = freecad_to_obj.export( [part], keep_unresolved=keep_unresolved) self.assertEqual(obj_file_contents, expected)
def test_export_with_keep_unresolved_transform_link_to_primitive(self): document = App.newDocument() box = document.addObject('Part::Box', 'Box') box.Label = 'Cube' box.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) link = document.addObject('App::Link', 'Link') link.Label = 'CubeLink' link.setLink(box) link.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) link.LinkTransform = True document.recompute() obj_filename = 'link_to_translated_cube.obj' with open(os.path.join(os.path.dirname(__file__), obj_filename)) as f: expected = f.read() def keep_unresolved(obj: object, path: List[object]) -> bool: return obj.Label == 'CubeLink' obj_file_contents = freecad_to_obj.export( [link], keep_unresolved=keep_unresolved) self.assertEqual(obj_file_contents, expected)
def calculate_global_placement(child: object, placements: Placement = []) -> Placement: placements.append(child.Placement) in_list = child.InList num_in = len(in_list) if len(in_list) == 0: global_placement = Placement() placements.reverse() # Reverse list in order of parent to child. for placement in placements: global_placement *= placement return global_placement if num_in > 1: Console.PrintWarning( f'{child.Label} has more than 1 parent. Choosing 1st.\n') parent = in_list[0] return calculate_global_placement(parent, placements)
def test_get_axis_frame_attachment_kwargs_for_right_face(self): right_faces = self.frame.Proxy.get_faces_for_side(Side.RIGHT) for right_face in right_faces: result = get_axis_frame_attachment_kwargs(self.frame, right_face, CoordinateAxis.Y) frame_size = self.frame.Size.Value three_inches = 76.2 # in millimeters (mm) length = frame_size + three_inches expected = { 'origin_translation_offset': Vector(0.0, 0.0, -1.0), 'placement': Placement( Vector( frame_size + AngleFrameConnector.axis_side_mount_width, -21.14446, # TODO: Replace magic number with something meaningful 305.2 # TODO: Replace magic number with something meaningful ), Rotation(), Vector()), 'length_value': length, 'orientation': CoordinateAxis.Y, 'side': Side.RIGHT } self.assert_result_and_expected_are_equal(result, expected)
def test_export_with_sphere_link_transform_true(self): test_package_path = Path(__file__).parent sphere_document = App.newDocument('Sphere') sphere = sphere_document.addObject('Part::Sphere', 'Sphere') sphere.Label = 'Sphere' sphere.Placement = Placement(Vector(10, 0, 0), Rotation(Vector(0, 0, 1), 0)) sphere_document.recompute() sphere_document_path = test_package_path.joinpath('Sphere.FCStd') sphere_document.saveAs(str(sphere_document_path)) link_document = App.newDocument('Link') link_document_path = test_package_path.joinpath('Link.FCStd') link_document.saveAs(str(link_document_path)) link = link_document.addObject('App::Link', 'Link') link.setLink(sphere) link.Label = sphere.Label link.LinkTransform = True link_document.recompute() with open( os.path.join(os.path.dirname(__file__), 'translated_sphere.obj')) as f: expected = f.read() obj_file_contents = freecad_to_obj.export([link]) self.assertEqual(obj_file_contents, expected) sphere_document_path.unlink() link_document_path.unlink()
def test_export_with_link_to_translated_primitive(self): test_package_path = Path(__file__).parent cube_document = App.newDocument('Cube') box = cube_document.addObject('Part::Box', 'Box') box.Label = 'Cube' box.Placement = Placement(Vector(10, 0, 0), Rotation(Vector(0, 0, 1), 0)) cube_document_path = test_package_path.joinpath('Cube.FCStd') cube_document.saveAs(str(cube_document_path)) link_document = App.newDocument('CubeLink') link_document_path = test_package_path.joinpath('LinkCube.FCStd') link_document.saveAs(str(link_document_path)) link = link_document.addObject('App::Link', 'Link') link.setLink(box) link.Label = box.Label link.LinkTransform = False link_document.recompute() with open(os.path.join(os.path.dirname(__file__), 'cube.obj')) as f: expected = f.read() obj_file_contents = freecad_to_obj.export([link]) self.assertEqual(obj_file_contents, expected) cube_document_path.unlink() link_document_path.unlink()
def __init__( self, obj, size=304.8, # 12 inches width=38.1, # 1.5 inches thickness=3.175, # 1/8 inch has_corners=False, placement=Placement(), origin_translation_offset=Vector()): super(FrameModel, self).__init__(obj) # Size property size_tooltip = 'Size or dimension of cubic frame.' obj.addProperty('App::PropertyLength', 'Size', 'Base', size_tooltip) obj.Size = size # Width property width_tooltip = 'Width of frame.' obj.addProperty('App::PropertyLength', 'Width', 'Base', width_tooltip) obj.Width = width # Thickness property thickness_tooltip = 'Thickness of frame.' obj.addProperty('App::PropertyLength', 'Thickness', 'Base', thickness_tooltip) obj.Thickness = thickness # HasCorners property has_corners_tooltip = 'Whether the frame has 3d printed corners or not.' obj.addProperty('App::PropertyBool', 'HasCorners', 'Base', has_corners_tooltip) obj.HasCorners = has_corners
def _render_pins(self, label, base_plane, backwards, count): Console.PrintMessage("_render_pins({},{},{})\n".format( label, backwards, count)) pin_base_datum_point = self.brick.newObject( 'PartDesign::Point', 'pin_base_{}_datum_point'.format(label)) pin_base_datum_point.Support = [(base_plane, '')] pin_base_datum_point.MapMode = 'ObjectOrigin' pin_base_datum_point.ViewObject.Visibility = False if self.pins_offset: pin_base_datum_point.AttachmentOffset = Placement( Vector(DIMS_STUD_SPACING / 2, DIMS_TECHNIC_HOLE_CENTRE_HEIGHT, 0), Rotation(0, 0, 0)) else: pin_base_datum_point.AttachmentOffset = Placement( Vector(0, DIMS_TECHNIC_HOLE_CENTRE_HEIGHT, 0), Rotation(0, 0, 0)) pin_tip_offset = DIMS_PIN_LENGTH if backwards: pin_tip_offset *= -1 pin_tip_datum_point = self.brick.newObject( 'PartDesign::Point', 'pin_tip_{}_datum_point'.format(label)) pin_tip_datum_point.Support = [(base_plane, '')] pin_tip_datum_point.MapMode = 'ObjectOrigin' pin_tip_datum_point.ViewObject.Visibility = False if self.pins_offset: pin_tip_datum_point.AttachmentOffset = Placement( Vector(DIMS_STUD_SPACING / 2, DIMS_TECHNIC_HOLE_CENTRE_HEIGHT, pin_tip_offset), Rotation(0, 0, 0)) else: pin_tip_datum_point.AttachmentOffset = Placement( Vector(0, DIMS_TECHNIC_HOLE_CENTRE_HEIGHT, pin_tip_offset), Rotation(0, 0, 0)) pin_datum_line = self.brick.newObject( 'PartDesign::Line', 'pin_{}_datum_line'.format(label)) pin_datum_line.Support = [(pin_base_datum_point, ''), (pin_tip_datum_point, '')] pin_datum_line.MapMode = 'TwoPointLine' pin_datum_line.ViewObject.Visibility = False pin_features = render_pin(label, pin_datum_line, self.brick, self.doc) if count > 1: self._render_linear_pattern(label, pin_features, count)
def create_window(obj_name, document): """Create a window.""" obj = document.addObject('Part::Feature', obj_name) obj.Shape = Part.makeBox(5, 4, 5) # Center window to wall obj.Placement = Placement(Vector(2.5, -1, 2.5), Rotation()) return obj
def _get_placement_for_bottom_face(frame): Console.PrintMessage('Attaching axis to bottom face is not supported.\n') placement = Placement() origin_translation_offset = Vector() return { 'placement': placement, 'origin_translation_offset': origin_translation_offset, 'orientation': CoordinateAxis.X, 'length': frame.Size }
def getFluidShape(self, fp, vol, roll=Units.parseQuantity("0 deg"), trim=Units.parseQuantity("0 deg")): """Return the tank fluid shape for the provided rotation angles. The returned shape is however not rotated at all Keyword arguments: fp -- Part::FeaturePython object affected. vol -- Volume of fluid. roll -- Ship roll angle. trim -- Ship trim angle. """ if vol <= 0.0: return None if vol >= fp.Shape.Volume: return fp.Shape.copy() # Get a first estimation of the level level = vol.Value / fp.Shape.Volume # Transform the tank shape current_placement = fp.Placement m = current_placement.toMatrix() m.rotateX(roll) m.rotateY(-trim) fp.Placement = Placement(m) # Iterate to find the fluid shape for i in range(COMMON_BOOLEAN_ITERATIONS): shape = self.getVolume(fp, level, return_shape=True) error = (vol.Value - shape.Volume) / fp.Shape.Volume if abs(error) < 0.01: break level += COMMON_BOOLEAN_RELAXATION[i] * error # Untransform the object to retrieve the original position fp.Placement = current_placement m = shape.Placement.toMatrix() m.rotateY(trim) m.rotateX(-roll) shape.Placement = Placement(m) return shape
class Geometry(PyObjectBase): def __init__(self, name, edges = [], wires = []): super(Geometry, self).__init__(name, edges = [], wires = []) self.Label = name self.Name = name self._Tag = uuid1().__str__() self.Construction = False self.Continuity = 'CN' @property def Tag(self): return self._Tag def translate(self, vec): return def transform(self, mat): self.Placement = PLC(self.Placement.toMatrix()*mat)
def test_export_with_translated_part_containing_translated_primitive(self): document = App.newDocument() box = document.addObject('Part::Box', 'Box') box.Label = 'Cube' box.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) part = document.addObject('App::Part', 'Part') part.addObject(box) part.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) document.recompute() with open( os.path.join(os.path.dirname(__file__), 'translated_cube.obj')) as f: expected = f.read() obj_file_contents = freecad_to_obj.export([part]) self.assertEqual(obj_file_contents, expected)
class Vertex(Shape): def __init__(self, point): super(Vertex, self).__init__(None,None,None,[self]) self._point = point self._placement = PLC(CENTER, ROT(DIR_Z, 0.0), CENTER) def __repr__(self): return "Vertex: %s" %(self.Point) @property def Point(self): return self._placement.toMatrix().multiply(self._point) @Point.setter def Point(self, point): self._point = point
def create_heated_bed(document, name, placement=Placement(), origin_translation_offset=Vector()): """ Creates a heated bed object with the given name, and adds it to the document. """ obj = document.addObject('Part::FeaturePython', name) HeatedBedModel(obj, placement, origin_translation_offset) obj.ViewObject.Proxy = 0 # Mandatory unless ViewProvider is coded return obj
def get_2d_projection(obj: object) -> object: # Reset Placement of object, # as objects not aligned with the XY plane are exported to DXF incorrectly. # See Also: https://forum.freecadweb.org/viewtopic.php?p=539543 original_placement = obj.Placement obj.Placement = Placement() projection = None if obj.Label == 'Tail_Stop_HighEnd': projection = get_2d_high_end_stop_projection(obj) else: projection = get_2d_projection_on_xy_plane(obj) obj.Placement = original_placement return projection
def _get_placement_for_rear_face(frame): x = frame.Shape.BoundBox.Center.x y = frame.Proxy.YMax z = frame.Shape.BoundBox.ZMin placement = Placement(Vector(x, y, z), frame.Placement.Rotation, Vector(0, 0, 0)) return { 'placement': placement, 'origin_translation_offset': Vector(-0.5, 0, 0), 'orientation': CoordinateAxis.Z, 'length': frame.Size, 'carriage_position': 90 }
def _get_placement_for_top_face(frame): x = frame.Proxy.XMin y = frame.Shape.BoundBox.Center.y z = frame.Shape.BoundBox.ZMax placement = Placement(Vector(x, y, z), frame.Placement.Rotation, Vector(0, 0, 0)) origin_translation_offset = Vector(0, -0.5, 0) return { 'placement': placement, 'origin_translation_offset': origin_translation_offset, 'orientation': CoordinateAxis.X, 'length': frame.Size }
def _get_placement_for_right_face_on_frame(frame): x = frame.Shape.BoundBox.XMax y = frame.Shape.BoundBox.YMin z = frame.Shape.BoundBox.ZMax placement = Placement(Vector(x, y, z), frame.Placement.Rotation, Vector(0, 0, 0)) return { 'placement': placement, 'origin_translation_offset': Vector(0, 0, -1), 'orientation': CoordinateAxis.Y, 'length': frame.Size }
def __init__(self, obj, length=304.80, carriage_position=50, orientation=CoordinateAxis.X, side=Side.TOP, placement=Placement(), origin_translation_offset=Vector()): """ Constructor Arguments --------- - obj: Created with document.addObject('Part::FeaturePython', '{name}') """ super(AxisModel, self).__init__(obj) self.placement = placement self.origin_translation_offset = origin_translation_offset # Length property length_tooltip = 'Length of axis corresponds to rod length.' obj.addProperty('App::PropertyLength', 'Length', 'Base', length_tooltip) obj.Length = length # Rod Diameter property rod_diameter_tooltip = 'Diameter of rod.' read_only = 1 obj.addProperty('App::PropertyLength', 'RodDiameter', 'Base', rod_diameter_tooltip, read_only) obj.RodDiameter = 8 # Carriage Position property carriage_position_tooltip = 'Position of carriage relative to available rod.' obj.addProperty('App::PropertyPercent', 'CarriagePosition', 'Base', carriage_position_tooltip) obj.CarriagePosition = carriage_position # Orientation property orientation_tooltip = 'Orientation of axis: X, Y, or Z.' hidden = 4 obj.addProperty('App::PropertyString', 'Orientation', 'Base', orientation_tooltip, hidden) obj.Orientation = orientation # Side property side_tooltip = 'Which side the bottom of the axis faces.' obj.addProperty('App::PropertyString', 'Side', 'Base', side_tooltip, hidden) obj.Side = side
def test_export_with_translated_part_containing_translated_link_to_primitive( self): test_package_path = Path(__file__).parent cube_document = App.newDocument('Cube') box = cube_document.addObject('Part::Box', 'Box') box.Label = 'Cube' cube_document_path = test_package_path.joinpath('Cube.FCStd') cube_document.saveAs(str(cube_document_path)) part_document = App.newDocument('Part') part_document_path = test_package_path.joinpath('Part.FCStd') part_document.saveAs(str(part_document_path)) box_link = part_document.addObject('App::Link', 'Link') box_link.setLink(box) box_link.Label = box.Label box_link.LinkPlacement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) part = part_document.addObject('App::Part', 'Part') part.addObject(box_link) part.Placement = Placement(Vector(5, 0, 0), Rotation(Vector(0, 0, 1), 0)) part_document.recompute() with open( os.path.join(os.path.dirname(__file__), 'translated_cube.obj')) as f: expected = f.read() obj_file_contents = freecad_to_obj.export([part]) self.assertEqual(obj_file_contents, expected) cube_document_path.unlink() part_document_path.unlink()
def test_export_with_translated_sphere(self): document = App.newDocument() sphere = document.addObject('Part::Sphere', 'Sphere') sphere.Label = 'Sphere' sphere.Placement = Placement(Vector(10, 0, 0), Rotation(Vector(0, 0, 1), 0)) document.recompute() with open( os.path.join(os.path.dirname(__file__), 'translated_sphere.obj')) as f: expected = f.read() obj_file_contents = freecad_to_obj.export([sphere]) self.assertEqual(obj_file_contents, expected)
def get_placement(orientation, side, box_height, length, motor_box_length): try: return { CoordinateAxis.X: { Side.TOP: Placement() }, CoordinateAxis.Y: { Side.LEFT: Placement(Vector(box_height, length, 0), Rotation(-90, 0, 90)), Side.RIGHT: Placement(Vector(0, length, motor_box_length), Rotation(-90, 0, -90)) }, CoordinateAxis.Z: { Side.FRONT: Placement(Vector(0, box_height, length), Rotation(0, 90, 90)), Side.REAR: Placement(Vector(motor_box_length, 0, length), Rotation(0, 90, -90)) } }[orientation][side] except KeyError: message = 'Invalid combination of orientation "{}" and side "{}" passed.' raise ValueError(message.format(orientation, side))
def _get_placement_for_right_face_on_frame_with_corners(frame_with_corners): x = frame_with_corners.Shape.BoundBox.XMax y = _calculate_y_of_y_axis_for_frame_with_corners(frame_with_corners) z = _calculate_z_of_y_axis_for_frame_with_corners(frame_with_corners) placement = Placement(Vector(x, y, z), frame_with_corners.Placement.Rotation, Vector()) length = _calculate_y_axis_length_for_frame_with_corners( frame_with_corners) return { 'placement': placement, 'origin_translation_offset': Vector(0, 0, -1), 'orientation': CoordinateAxis.Y, 'length': _convert_value_to_quantity(length) }
def make_steps(rail_dist, step_rail_gap, step_diam, first_step, padding, L_thickness, n_steps, step_dist): step_length = rail_dist - 2 * L_thickness - 2 * step_rail_gap cyl = doc.addObject("Part::Cylinder", "Cylinder") cyl.Height = step_length cyl.Radius = step_diam / 2 shift = [ first_step, (rail_dist - step_length) / 2, step_diam / 2 + padding + L_thickness ] cyl.Placement = Placement(Vector(shift), Rotation(Vector(1, 0, 0), -90)) obj = Draft.makeArray(cyl, Vector(step_dist, 0, 0), Vector(0, 1, 0), n_steps, 1) Draft.autogroup(obj) return obj
def create_axis(document, name, length=304.80, carriage_position=50, orientation=CoordinateAxis.X, side=Side.TOP, placement=Placement(), origin_translation_offset=Vector()): """ Creates a axis object with the given name, and adds it to the document. """ obj = document.addObject('Part::FeaturePython', name) AxisModel(obj, length, carriage_position, orientation, side, placement, origin_translation_offset) obj.ViewObject.Proxy = 0 # Mandatory unless ViewProvider is coded return obj
def _get_placement_by_corner(length: float) -> Dict[str, Placement]: return { Corner.BOTTOM_LEFT_FRONT: Placement(), Corner.BOTTOM_LEFT_REAR: Placement(Vector(0, length, 0), Rotation(-90, 0, 0)), Corner.BOTTOM_RIGHT_REAR: Placement(Vector(length, length, 0), Rotation(180, 0, 0)), Corner.BOTTOM_RIGHT_FRONT: Placement(Vector(length, 0, 0), Rotation(90, 0, 0)), Corner.TOP_LEFT_FRONT: Placement(Vector(0, 0, length), Rotation(0, 90, 0)), Corner.TOP_LEFT_REAR: Placement(Vector(0, length, length), Rotation(-90, 90, 0)), Corner.TOP_RIGHT_REAR: Placement(Vector(length, length, length), Rotation(90, 180, 0)), Corner.TOP_RIGHT_FRONT: Placement(Vector(length, 0, length), Rotation(0, 180, 0)) }
def test_get_axis_frame_attachment_kwargs_for_front_face(self): front_face = self.frame.Proxy.get_faces_for_side(Side.FRONT)[0] result = get_axis_frame_attachment_kwargs(self.frame, front_face, CoordinateAxis.Z) expected = { 'origin_translation_offset': Vector(-0.5, -1.0, 0.0), 'placement': Placement(Vector(self.frame.Size / 2, 0, 0), Rotation(), Vector()), 'orientation': CoordinateAxis.Z, 'side': Side.FRONT } self.assert_result_and_expected_are_equal(result, expected)
def get_heated_bed_frame_axis_attachment_kwargs(frame, axis) -> dict: """Get a dictionary describing how to attach a heated bed to a frame and axis. :param frame: Frame object to attach heated bed to. :param axis: Axis object to attach heated bed to. :return: Dictionary describing how to attach a heated bed to a frame and axis. """ _validate_frame(frame) _validate_axis(axis) x = frame.Shape.BoundBox.Center.x y = frame.Shape.BoundBox.Center.y z = axis.Proxy.calculate_top_of_carriage_box_for_z_axis() placement = Placement(Vector(x, y, z), Rotation()) origin_translation_offset = Vector(-0.5, -0.5, 0) return { 'placement': placement, 'origin_translation_offset': origin_translation_offset }
def test_get_axis_frame_attachment_kwargs_for_right_face(self): right_face = self.frame.Proxy.get_faces_for_side(Side.RIGHT)[0] result = get_axis_frame_attachment_kwargs(self.frame, right_face, CoordinateAxis.Y) expected = { 'origin_translation_offset': Vector(0.0, 0.0, -1.0), 'placement': Placement(Vector(self.frame.Size, 0, self.frame.Size), Rotation(), Vector()), 'orientation': CoordinateAxis.Y, 'side': Side.RIGHT } self.assert_result_and_expected_are_equal(result, expected)