def test_add(self): box1 = AxisAlignedBox(minimum = Vector(-10.0, -10.0, -10.0), maximum = Vector(0.0, 0.0, 0.0)) box2 = AxisAlignedBox(minimum = Vector(0.0, 0.0, 0.0), maximum = Vector(10.0, 10.0, 10.0)) joined = box1 + box2 self.assertEqual(Vector(-10.0, -10.0, -10.0), joined.minimum) self.assertEqual(Vector(10.0, 10.0, 10.0), joined.maximum) self.assertTrue(joined.isValid())
def test_getSelectionCenter(self): node_1 = SceneNode() node_1.getBoundingBox = MagicMock(return_value = AxisAlignedBox(Vector(0, 0, 0), Vector(10, 20, 30))) Selection.add(node_1) assert Selection.getSelectionCenter() == Vector(5, 10, 15) node_2 = SceneNode() node_2.getBoundingBox = MagicMock(return_value=AxisAlignedBox(Vector(0, 0, 0), Vector(20, 30, 40))) Selection.add(node_2) assert Selection.getSelectionCenter() == Vector(10, 15, 20)
def test_create(self): box = AxisAlignedBox() self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(0.0, 0.0, 0.0), box.maximum) self.assertFalse(box.isValid()) box = AxisAlignedBox(minimum=Vector(), maximum=Vector(10.0, 10.0, 10.0)) self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(10.0, 10.0, 10.0), box.maximum) self.assertTrue(box.isValid()) box = AxisAlignedBox(10.0, 10.0, 10.0) self.assertEqual(Vector(-5.0, -5.0, -5.0), box.minimum) self.assertEqual(Vector(5.0, 5.0, 5.0), box.maximum) self.assertTrue(box.isValid()) self.assertEqual(-5.0, box.left) self.assertEqual(-5.0, box.bottom) self.assertEqual(-5.0, box.back) self.assertEqual(5.0, box.right) self.assertEqual(5.0, box.top) self.assertEqual(5.0, box.front)
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool: bbox = self.getBoundingBox() if bbox is not None: if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: return True return False
def getBoundingBox(self): if self._aabb: return self._aabb if not self._aabb_job: self._resetAABB() return AxisAlignedBox()
def createMockedSelectionWithBoundingBox(min_size, max_size): selection = MagicMock() node = MagicMock() binding_box = AxisAlignedBox(min_size, max_size) node.getBoundingBox = MagicMock(return_value=binding_box) selection.hasSelection = MagicMock(return_value=True) selection.getSelectedObject = MagicMock(return_value=node) return selection
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool: bbox = self.getBoundingBox() if bbox is not None: # Mark the node as outside the build volume if the bounding box test fails. if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: return True return False
def collidesWithBbox(self, check_bbox: AxisAlignedBox) -> bool: """Return if the provided bbox collides with the bbox of this SceneNode""" bbox = self.getBoundingBox() if bbox is not None: if check_bbox.intersectsBox(bbox) != AxisAlignedBox.IntersectionResult.FullIntersection: return True return False
def getExtents(self, matrix=None): if self._vertices is None: return AxisAlignedBox() data = numpy.pad(self._vertices, ((0, 0), (0, 1)), "constant", constant_values=(0.0, 1.0)) if matrix is not None: transposed = matrix.getTransposed().getData() data = data.dot(transposed) data += transposed[:, 3] data = data[:, 0:3] min = data.min(axis=0) max = data.max(axis=0) return AxisAlignedBox(minimum=Vector(min[0], min[1], min[2]), maximum=Vector(max[0], max[1], max[2]))
def _calculateAABB(self): aabb = None original_aabb = None if self._mesh_data: aabb = self._mesh_data.getExtents(self.getWorldTransformation()) original_aabb = self._mesh_data.getExtents() else: # If there is no mesh_data, use a boundingbox that encompasses the local (0,0,0) position = self.getWorldPosition() aabb = AxisAlignedBox(minimum = position, maximum = position) original_aabb = AxisAlignedBox(minimum = position, maximum = position) for child in self._children: if aabb is None: aabb = child.getBoundingBox() original_aabb = child.getOriginalBoundingBox() else: aabb = aabb + child.getBoundingBox() original_aabb = original_aabb + child.getOriginalBoundingBox() self._aabb = aabb self._original_aabb = original_aabb
def __init__(self): Resources.addSearchPath(os.path.join(QtApplication.getInstallPrefix(), "share", "cura")) if not hasattr(sys, "frozen"): Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)), "..")) super().__init__(name = "cura", version = CuraVersion) self.setWindowIcon(QIcon(Resources.getPath(Resources.Images, "cura-icon.png"))) self.setRequiredPlugins([ "CuraEngineBackend", "MeshView", "LayerView", "STLReader", "SelectionTool", "CameraTool", "GCodeWriter", "LocalFileOutputDevice" ]) self._physics = None self._volume = None self._platform = None self._output_devices = {} self._print_information = None self._i18n_catalog = None self._previous_active_tool = None self._platform_activity = False self._scene_boundingbox = AxisAlignedBox() self._job_name = None self.getMachineManager().activeMachineInstanceChanged.connect(self._onActiveMachineChanged) self.getMachineManager().addMachineRequested.connect(self._onAddMachineRequested) self.getController().getScene().sceneChanged.connect(self.updatePlatformActivity) Resources.addType(self.ResourceTypes.QmlFiles, "qml") Resources.addType(self.ResourceTypes.Firmware, "firmware") Preferences.getInstance().addPreference("cura/active_machine", "") Preferences.getInstance().addPreference("cura/active_mode", "simple") Preferences.getInstance().addPreference("cura/recent_files", "") Preferences.getInstance().addPreference("cura/categories_expanded", "") Preferences.getInstance().addPreference("view/center_on_select", True) Preferences.getInstance().addPreference("mesh/scale_to_fit", True) JobQueue.getInstance().jobFinished.connect(self._onJobFinished) self._recent_files = [] files = Preferences.getInstance().getValue("cura/recent_files").split(";") for f in files: if not os.path.isfile(f): continue self._recent_files.append(QUrl.fromLocalFile(f))
def test_create(self): box = AxisAlignedBox() self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(0.0, 0.0, 0.0), box.maximum) self.assertFalse(box.isValid()) box = AxisAlignedBox(minimum = Vector(), maximum = Vector(10.0, 10.0, 10.0)) self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(10.0, 10.0, 10.0), box.maximum) self.assertTrue(box.isValid()) box = AxisAlignedBox(10.0, 10.0, 10.0) self.assertEqual(Vector(-5.0, -5.0, -5.0), box.minimum) self.assertEqual(Vector(5.0, 5.0, 5.0), box.maximum) self.assertTrue(box.isValid()) self.assertEqual(-5.0, box.left) self.assertEqual(-5.0, box.bottom) self.assertEqual(-5.0, box.back) self.assertEqual(5.0, box.right) self.assertEqual(5.0, box.top) self.assertEqual(5.0, box.front)
def getBoundingBox(cls): bounding_box = None # don't start with an empty bounding box, because that includes (0,0,0) for node in cls.__selection: if type(node) is not SceneNode or not node.getMeshData(): continue if not bounding_box: bounding_box = copy.deepcopy(node.getBoundingBox()) else: bounding_box += node.getBoundingBox() if not bounding_box: bounding_box = AxisAlignedBox() return bounding_box
def updatePlatformActivity(self, node = None): count = 0 scene_boundingbox = AxisAlignedBox() for node in DepthFirstIterator(self.getController().getScene().getRoot()): if type(node) is not SceneNode or not node.getMeshData(): continue count += 1 scene_boundingbox += node.getBoundingBox() if repr(self._scene_boundingbox) != repr(scene_boundingbox): self._scene_boundingbox = scene_boundingbox self.sceneBoundingBoxChanged.emit() self._platform_activity = True if count > 0 else False self.activityChanged.emit()
def _calculateAABB(self) -> None: if self._mesh_data: aabb = self._mesh_data.getExtents(self.getWorldTransformation()) else: # If there is no mesh_data, use a boundingbox that encompasses the local (0,0,0) position = self.getWorldPosition() aabb = AxisAlignedBox(minimum = position, maximum = position) for child in self._children: if child.callDecoration("isNonPrintingMesh"): # Non-printing-meshes inside a group should not affect push apart or drop to build plate continue if aabb is None: aabb = child.getBoundingBox() else: aabb = aabb + child.getBoundingBox() self._aabb = aabb
def test_create(self): box = AxisAlignedBox() self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(0.0, 0.0, 0.0), box.maximum) self.assertFalse(box.isValid()) box = AxisAlignedBox(minimum = Vector(), maximum = Vector(10.0, 10.0, 10.0)) self.assertEqual(Vector(0.0, 0.0, 0.0), box.minimum) self.assertEqual(Vector(10.0, 10.0, 10.0), box.maximum) self.assertTrue(box.isValid())
def getExtents(self, matrix: Optional[Matrix] = None ) -> Optional[AxisAlignedBox]: if self._vertices is None: return None if matrix is not None: data = self.getConvexHullTransformedVertices(matrix) else: data = self.getConvexHullVertices() if data is None: return None min = data.min(axis=0) max = data.max(axis=0) return AxisAlignedBox(minimum=Vector(min[0], min[1], min[2]), maximum=Vector(max[0], max[1], max[2]))
def _calculateAABB(self) -> None: aabb = None if self._mesh_data: aabb = self._mesh_data.getExtents( self.getWorldTransformation(copy=False)) for child in self._children: child_bb = child.getBoundingBox() if child_bb is None or child_bb.minimum == child_bb.maximum: # Child had a degenerate bounding box, such as an empty group. Don't count it along. continue if aabb is None: aabb = child_bb else: aabb = aabb + child_bb if aabb is None: # There is no mesh data and no children with bounding box. Use the current position then, but it's a degenerate AABB. position = self.getWorldPosition() aabb = AxisAlignedBox(minimum=position, maximum=position) self._aabb = aabb
def getExtents(self, matrix: Optional[Matrix] = None) -> Optional[AxisAlignedBox]: """Get the extents of this mesh. :param matrix: The transformation matrix from model to world coordinates. """ if self._vertices is None: return None if matrix is not None: data = self.getConvexHullTransformedVertices(matrix) else: data = self.getConvexHullVertices() if data is None: return None min = data.min(axis=0) max = data.max(axis=0) return AxisAlignedBox(minimum=Vector(min[0], min[1], min[2]), maximum=Vector(max[0], max[1], max[2]))
def _calculateAABB(self) -> None: """Override of SceneNode._calculateAABB to exclude non-printing-meshes from bounding box""" self._aabb = None if self._mesh_data: self._aabb = self._mesh_data.getExtents(self.getWorldTransformation()) else: # If there is no mesh_data, use a bounding box that encompasses the local (0,0,0) position = self.getWorldPosition() self._aabb = AxisAlignedBox(minimum = position, maximum = position) for child in self.getAllChildren(): if child.callDecoration("isNonPrintingMesh"): # Non-printing-meshes inside a group should not affect push apart or drop to build plate continue if not child.getMeshData(): # Nodes without mesh data should not affect bounding boxes of their parents. continue if self._aabb is None: self._aabb = child.getBoundingBox() else: self._aabb = self._aabb + child.getBoundingBox()
def _calculateAABB(self) -> None: """Override of SceneNode._calculateAABB to exclude non-printing-meshes from bounding box""" self._aabb = None if self._mesh_data: self._aabb = self._mesh_data.getExtents( self.getWorldTransformation(copy=False)) for child in self.getAllChildren(): if child.callDecoration("isNonPrintingMesh"): # Non-printing-meshes inside a group should not affect push apart or drop to build plate continue child_bb = child.getBoundingBox() if child_bb is None or child_bb.minimum == child_bb.maximum: # Child had a degenerate bounding box, such as an empty group. Don't count it along. continue if self._aabb is None: self._aabb = child_bb else: self._aabb = self._aabb + child_bb if self._aabb is None: # No children that should be included? Just use your own position then, but it's an invalid AABB. position = self.getWorldPosition() self._aabb = AxisAlignedBox(minimum=position, maximum=position)
# Copyright (c) 2015 Ultimaker B.V. # Uranium is released under the terms of the AGPLv3 or higher. from UM.Math.AxisAlignedBox import AxisAlignedBox from UM.Math.Ray import Ray from UM.Math.Vector import Vector @profile def intersects(box, ray): return box.intersectsRay(ray) ray = Ray(Vector(10, 10, 10), Vector(-1, -1, -1)) box = AxisAlignedBox(10, 10, 10) for i in range(100000): intersects(box, ray)
def rebuild(self): if self._width == 0 or self._height == 0 or self._depth == 0: return minW = -self._width / 2 maxW = self._width / 2 minH = 0.0 maxH = self._height minD = -self._depth / 2 maxD = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(minW, minH, minD), Vector(maxW, minH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, minD), Vector(minW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, minD), Vector(maxW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, minD), Vector(maxW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, maxD), Vector(maxW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, maxD), Vector(minW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, maxD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, maxD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, minD), Vector(minW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, minD), Vector(maxW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, minD), Vector(minW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, maxH, minD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) self.setMeshData(mb.getData()) mb = MeshBuilder() mb.addQuad(Vector(minW, minH, minD), Vector(maxW, minH, minD), Vector(maxW, minH, maxD), Vector(minW, minH, maxD)) self._grid_mesh = mb.getData() for n in range(0, 6): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() for polygon in self._disallowed_areas: points = polygon.getPoints() mb.addQuad(Vector(points[0, 0], 0.1, points[0, 1]), Vector(points[1, 0], 0.1, points[1, 1]), Vector(points[2, 0], 0.1, points[2, 1]), Vector(points[3, 0], 0.1, points[3, 1]), color=Color(174, 174, 174, 255)) # Find the largest disallowed area to exclude it from the maximum scale bounds size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.getData() else: self._disallowed_area_mesh = None self._aabb = AxisAlignedBox(minimum=Vector(minW, minH - 1.0, minD), maximum=Vector(maxW, maxH, maxD)) settings = Application.getInstance().getActiveMachine() skirt_size = 0.0 if settings.getSettingValueByKey("adhesion_type") == "None": skirt_size = settings.getSettingValueByKey( "skirt_line_count") * settings.getSettingValueByKey( "skirt_line_width") + settings.getSettingValueByKey( "skirt_gap") elif settings.getSettingValueByKey("adhesion_type") == "Brim": skirt_size = settings.getSettingValueByKey( "brim_line_count") * settings.getSettingValueByKey( "skirt_line_width") else: skirt_size = settings.getSettingValueByKey("skirt_line_width") skirt_size += settings.getSettingValueByKey("skirt_line_width") scale_to_max_bounds = AxisAlignedBox( minimum=Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size), maximum=Vector(maxW - skirt_size, maxH, maxD - skirt_size - disallowed_area_size)) Application.getInstance().getController().getScene( )._maximum_bounds = scale_to_max_bounds
def rebuild(self): if self._width == 0 or self._height == 0 or self._depth == 0: return minW = -self._width / 2 maxW = self._width / 2 minH = 0.0 maxH = self._height minD = -self._depth / 2 maxD = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(minW, minH, minD), Vector(maxW, minH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, minD), Vector(minW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, minD), Vector(maxW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, minD), Vector(maxW, maxH, minD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, maxD), Vector(maxW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, maxD), Vector(minW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, maxD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, maxD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, minH, minD), Vector(minW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, minH, minD), Vector(maxW, minH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(minW, maxH, minD), Vector(minW, maxH, maxD), color=self.VolumeOutlineColor) mb.addLine(Vector(maxW, maxH, minD), Vector(maxW, maxH, maxD), color=self.VolumeOutlineColor) self.setMeshData(mb.getData()) mb = MeshBuilder() mb.addQuad(Vector(minW, minH, minD), Vector(maxW, minH, minD), Vector(maxW, minH, maxD), Vector(minW, minH, maxD)) self._grid_mesh = mb.getData() for n in range(0, 6): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) disallowed_area_height = 0.2 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], minW, maxW), disallowed_area_height, self._clamp(points[0][1], minD, maxD)) previous_point = Vector(self._clamp(points[0][0], minW, maxW), disallowed_area_height, self._clamp(points[0][1], minD, maxD)) for point in points: new_point = Vector(self._clamp(point[0], minW, maxW), disallowed_area_height, self._clamp(point[1], minD, maxD)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds size = abs(numpy.max(points[:, 1]) - numpy.min(points[:, 1])) disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.getData() else: self._disallowed_area_mesh = None self._aabb = AxisAlignedBox(minimum=Vector(minW, minH - 1.0, minD), maximum=Vector(maxW, maxH, maxD)) skirt_size = 0.0 profile = Application.getInstance().getMachineManager( ).getActiveProfile() if profile: skirt_size = self._getSkirtSize(profile) scale_to_max_bounds = AxisAlignedBox( minimum=Vector(minW + skirt_size, minH, minD + skirt_size + disallowed_area_size), maximum=Vector(maxW - skirt_size, maxH, maxD - skirt_size - disallowed_area_size)) Application.getInstance().getController().getScene( )._maximum_bounds = scale_to_max_bounds
def _rebuild(self): if not self._build_volume._width or not self._build_volume._height or not self._build_volume._depth: return if not self._build_volume._engine_ready: return if not self._build_volume._volume_outline_color: theme = Application.getInstance().getTheme() self._build_volume._volume_outline_color = Color( *theme.getColor("volume_outline").getRgb()) self._build_volume._x_axis_color = Color( *theme.getColor("x_axis").getRgb()) self._build_volume._y_axis_color = Color( *theme.getColor("y_axis").getRgb()) self._build_volume._z_axis_color = Color( *theme.getColor("z_axis").getRgb()) self._build_volume._disallowed_area_color = Color( *theme.getColor("disallowed_area").getRgb()) self._build_volume._error_area_color = Color( *theme.getColor("error_area").getRgb()) ### START PATCH # Get a dict from the machine metadata optionally overriding the build volume # Note that CuraEngine is blissfully unaware of this; it is just what the user is shown in Cura limit_buildvolume = self._build_volume._global_container_stack.getMetaDataEntry( "limit_buildvolume", {}) if not isinstance(limit_buildvolume, dict): limit_buildvolume = {} min_w = limit_buildvolume.get("width", {}).get("minimum", -self._build_volume._width / 2) max_w = limit_buildvolume.get("width", {}).get("maximum", self._build_volume._width / 2) min_h = limit_buildvolume.get("height", {}).get("minimum", 0.0) max_h = limit_buildvolume.get("height", {}).get("maximum", self._build_volume._height) min_d = limit_buildvolume.get("depth", {}).get("minimum", -self._build_volume._depth / 2) max_d = limit_buildvolume.get("depth", {}).get("maximum", self._build_volume._depth / 2) ### END PATCH z_fight_distance = 0.2 # Distance between buildplate and disallowed area meshes to prevent z-fighting if self._build_volume._shape != "elliptic": # Outline 'cube' of the build volume mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color=self._build_volume._volume_outline_color) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color=self._build_volume._volume_outline_color) self._build_volume.setMeshData(mb.build()) # Build plate grid mesh mb = MeshBuilder() mb.addQuad(Vector(min_w, min_h - z_fight_distance, min_d), Vector(max_w, min_h - z_fight_distance, min_d), Vector(max_w, min_h - z_fight_distance, max_d), Vector(min_w, min_h - z_fight_distance, max_d)) for n in range(0, 6): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2]) self._build_volume._grid_mesh = mb.build() else: # Bottom and top 'ellipse' of the build volume aspect = 1.0 scale_matrix = Matrix() if self._build_volume._width != 0: # Scale circular meshes by aspect ratio if width != height aspect = self._build_volume._depth / self._build_volume._width scale_matrix.compose(scale=Vector(1, 1, aspect)) mb = MeshBuilder() mb.addArc(max_w, Vector.Unit_Y, center=(0, min_h - z_fight_distance, 0), color=self._build_volume._volume_outline_color) mb.addArc(max_w, Vector.Unit_Y, center=(0, max_h, 0), color=self._build_volume._volume_outline_color) self._build_volume.setMeshData( mb.build().getTransformed(scale_matrix)) # Build plate grid mesh mb = MeshBuilder() mb.addVertex(0, min_h - z_fight_distance, 0) mb.addArc(max_w, Vector.Unit_Y, center=Vector(0, min_h - z_fight_distance, 0)) sections = mb.getVertexCount( ) - 1 # Center point is not an arc section indices = [] for n in range(0, sections - 1): indices.append([0, n + 2, n + 1]) mb.addIndices(numpy.asarray(indices, dtype=numpy.int32)) mb.calculateNormals() for n in range(0, mb.getVertexCount()): v = mb.getVertex(n) mb.setVertexUVCoordinates(n, v[0], v[2] * aspect) self._build_volume._grid_mesh = mb.build().getTransformed( scale_matrix) # Indication of the machine origin if self._build_volume._global_container_stack.getProperty( "machine_center_is_zero", "value"): origin = (Vector(min_w, min_h, min_d) + Vector(max_w, min_h, max_d)) / 2 else: origin = Vector(min_w, min_h, max_d) mb = MeshBuilder() mb.addCube(width=self._build_volume._origin_line_length, height=self._build_volume._origin_line_width, depth=self._build_volume._origin_line_width, center=origin + Vector(self._build_volume._origin_line_length / 2, 0, 0), color=self._build_volume._x_axis_color) mb.addCube(width=self._build_volume._origin_line_width, height=self._build_volume._origin_line_length, depth=self._build_volume._origin_line_width, center=origin + Vector(0, self._build_volume._origin_line_length / 2, 0), color=self._build_volume._y_axis_color) mb.addCube(width=self._build_volume._origin_line_width, height=self._build_volume._origin_line_width, depth=self._build_volume._origin_line_length, center=origin - Vector(0, 0, self._build_volume._origin_line_length / 2), color=self._build_volume._z_axis_color) self._build_volume._origin_mesh = mb.build() disallowed_area_height = 0.1 disallowed_area_size = 0 if self._build_volume._disallowed_areas: mb = MeshBuilder() color = self._build_volume._disallowed_area_color for polygon in self._build_volume._disallowed_areas: points = polygon.getPoints() if len(points) == 0: continue first = Vector( self._build_volume._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(points[0][1], min_d, max_d)) previous_point = Vector( self._build_volume._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector( self._build_volume._clamp(point[0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. # This disallowed area_size needs a -lot- of rework at some point in the future: TODO if numpy.min( points[:, 1] ) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs( numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._build_volume._disallowed_area_mesh = mb.build() else: self._build_volume._disallowed_area_mesh = None if self._build_volume._error_areas: mb = MeshBuilder() for error_area in self._build_volume._error_areas: color = self._build_volume._error_area_color points = error_area.getPoints() first = Vector( self._build_volume._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(points[0][1], min_d, max_d)) previous_point = Vector( self._build_volume._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector( self._build_volume._clamp(point[0], min_w, max_w), disallowed_area_height, self._build_volume._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point self._build_volume._error_mesh = mb.build() else: self._build_volume._error_mesh = None self._build_volume._volume_aabb = AxisAlignedBox( minimum=Vector(min_w, min_h - 1.0, min_d), maximum=Vector( max_w, max_h - self._build_volume._raft_thickness - self._build_volume._extra_z_clearance, max_d)) bed_adhesion_size = self._build_volume.getEdgeDisallowedSize() # As this works better for UM machines, we only add the disallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum=Vector( min_w + bed_adhesion_size + 1, min_h, min_d + disallowed_area_size - bed_adhesion_size + 1), maximum=Vector( max_w - bed_adhesion_size - 1, max_h - self._build_volume._raft_thickness - self._build_volume._extra_z_clearance, max_d - disallowed_area_size + bed_adhesion_size - 1)) Application.getInstance().getController().getScene( )._maximum_bounds = scale_to_max_bounds self._build_volume.updateNodeBoundaryCheck()
def test_intersectsRay(self): box = AxisAlignedBox(minimum=Vector(-5,-5,-5), maximum=Vector(5.0, 5.0, 5.0)) ray = Ray(Vector(-10.0, 0.0, 0.0), Vector(1.0, 0.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(10.0, 0.0, 0.0), Vector(-1.0, 0.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(0.0, -10.0, 0.0), Vector(0.0, 1.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(0.0, 10.0, 0.0), Vector(0.0, -1.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(0.0, 0.0, -10.0), Vector(0.0, 0.0, 1.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(0.0, 0.0, 10.0), Vector(0.0, 0.0, -1.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(5.0, result[0]) self.assertEqual(15.0, result[1]) ray = Ray(Vector(15.0, 0.0, 0.0), Vector(0.0, 1.0, 0.0)) result = box.intersectsRay(ray) self.assertEqual(False, result) ray = Ray(Vector(15.0, 15.0, 0.0), Vector(-1.0, -1.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(10.0, result[0]) self.assertEqual(20.0, result[1]) ray = Ray(Vector(10.0, -15.0, 0.0), Vector(-1.0, 1.0, 0.0)) result = box.intersectsRay(ray) self.assertNotEqual(False, result) self.assertEqual(10.0, result[0]) self.assertEqual(15.0, result[1])
def test_set(self): box = AxisAlignedBox() box.setLeft(-5.0) self.assertEqual(-5.0, box.left) self.assertEqual(0.0, box.right) self.assertFalse(box.isValid()) box.setBottom(-5.0) self.assertEqual(-5.0, box.bottom) self.assertEqual(0.0, box.top) self.assertFalse(box.isValid()) box.setBack(-5.0) self.assertEqual(-5.0, box.back) self.assertEqual(0.0, box.front) self.assertTrue(box.isValid()) box.setRight(5.0) self.assertEqual(-5.0, box.left) self.assertEqual(5.0, box.right) self.assertTrue(box.isValid()) box.setTop(5.0) self.assertEqual(-5.0, box.bottom) self.assertEqual(5.0, box.top) self.assertTrue(box.isValid()) box.setFront(5.0) self.assertEqual(-5.0, box.back) self.assertEqual(5.0, box.front) self.assertTrue(box.isValid()) box.setRight(-10.0) self.assertEqual(-10.0, box.left) self.assertEqual(-5.0, box.right) self.assertTrue(box.isValid()) box.setTop(-10.0) self.assertEqual(-10.0, box.bottom) self.assertEqual(-5.0, box.top) self.assertTrue(box.isValid()) box.setFront(-10.0) self.assertEqual(-10.0, box.back) self.assertEqual(-5.0, box.front) self.assertTrue(box.isValid())
def test_set(self): box = AxisAlignedBox() box = box.set(left=-5.0) self.assertEqual(-5.0, box.left) self.assertEqual(0.0, box.right) self.assertFalse(box.isValid()) box = box.set(bottom=-5.0) self.assertEqual(-5.0, box.bottom) self.assertEqual(0.0, box.top) self.assertFalse(box.isValid()) box = box.set(back=-5.0) self.assertEqual(-5.0, box.back) self.assertEqual(0.0, box.front) self.assertTrue(box.isValid()) box = box.set(right=5.0) self.assertEqual(-5.0, box.left) self.assertEqual(5.0, box.right) self.assertTrue(box.isValid()) box = box.set(top=5.0) self.assertEqual(-5.0, box.bottom) self.assertEqual(5.0, box.top) self.assertTrue(box.isValid()) box = box.set(front=5.0) self.assertEqual(-5.0, box.back) self.assertEqual(5.0, box.front) self.assertTrue(box.isValid()) box = box.set(right=-10.0) self.assertEqual(-10.0, box.left) self.assertEqual(-5.0, box.right) self.assertTrue(box.isValid()) box = box.set(top=-10.0) self.assertEqual(-10.0, box.bottom) self.assertEqual(-5.0, box.top) self.assertTrue(box.isValid()) box = box.set(front=-10.0) self.assertEqual(-10.0, box.back) self.assertEqual(-5.0, box.front) self.assertTrue(box.isValid())
def rebuild(self): if self._width == 0 or self._height == 0 or self._depth == 0: return min_w = -self._width / 2 max_w = self._width / 2 min_h = 0.0 max_h = self._height min_d = -self._depth / 2 max_d = self._depth / 2 mb = MeshBuilder() mb.addLine(Vector(min_w, min_h, min_d), Vector(max_w, min_h, min_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, max_h, min_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(max_w, max_h, min_d), color=self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, max_h, min_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(max_w, min_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, max_d), Vector(min_w, max_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, max_d), Vector(max_w, max_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, max_d), Vector(max_w, max_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, min_h, min_d), Vector(min_w, min_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(max_w, min_h, min_d), Vector(max_w, min_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(min_w, max_h, min_d), Vector(min_w, max_h, max_d), color=self.VolumeOutlineColor) mb.addLine(Vector(max_w, max_h, min_d), Vector(max_w, max_h, max_d), color=self.VolumeOutlineColor) self.setMeshData(mb.getData()) mb = MeshBuilder() mb.addQuad(Vector(min_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, min_d), Vector(max_w, min_h - 0.2, max_d), Vector(min_w, min_h - 0.2, max_d)) self._grid_mesh = mb.getData() for n in range(0, 6): v = self._grid_mesh.getVertex(n) self._grid_mesh.setVertexUVCoordinates(n, v[0], v[2]) disallowed_area_height = 0.1 disallowed_area_size = 0 if self._disallowed_areas: mb = MeshBuilder() color = Color(0.0, 0.0, 0.0, 0.15) for polygon in self._disallowed_areas: points = polygon.getPoints() first = Vector(self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) previous_point = Vector( self._clamp(points[0][0], min_w, max_w), disallowed_area_height, self._clamp(points[0][1], min_d, max_d)) for point in points: new_point = Vector(self._clamp(point[0], min_w, max_w), disallowed_area_height, self._clamp(point[1], min_d, max_d)) mb.addFace(first, previous_point, new_point, color=color) previous_point = new_point # Find the largest disallowed area to exclude it from the maximum scale bounds. # This is a very nasty hack. This pretty much only works for UM machines. This disallowed area_size needs # A -lot- of rework at some point in the future: TODO if numpy.min( points[:, 1] ) >= 0: # This filters out all areas that have points to the left of the centre. This is done to filter the skirt area. size = abs( numpy.max(points[:, 1]) - numpy.min(points[:, 1])) else: size = 0 disallowed_area_size = max(size, disallowed_area_size) self._disallowed_area_mesh = mb.getData() else: self._disallowed_area_mesh = None self._aabb = AxisAlignedBox(minimum=Vector(min_w, min_h - 1.0, min_d), maximum=Vector(max_w, max_h, max_d)) skirt_size = 0.0 profile = Application.getInstance().getMachineManager( ).getWorkingProfile() if profile: skirt_size = self._getSkirtSize(profile) # As this works better for UM machines, we only add the dissallowed_area_size for the z direction. # This is probably wrong in all other cases. TODO! # The +1 and -1 is added as there is always a bit of extra room required to work properly. scale_to_max_bounds = AxisAlignedBox( minimum=Vector(min_w + skirt_size + 1, min_h, min_d + disallowed_area_size - skirt_size + 1), maximum=Vector(max_w - skirt_size - 1, max_h, max_d - disallowed_area_size + skirt_size - 1)) Application.getInstance().getController().getScene( )._maximum_bounds = scale_to_max_bounds
def snapshot(width=300, height=300): scene = Application.getInstance().getController().getScene() active_camera = scene.getActiveCamera() render_width, render_height = active_camera.getWindowSize() render_width = int(render_width) render_height = int(render_height) # Result should have enough resolution; it is cropped and then scaled down. while (render_width < 1000) or (render_height < 1000): render_width *= 2 render_height *= 2 preview_pass = PreviewPass(render_width, render_height) root = scene.getRoot() camera = Camera("snapshot", root) # determine zoom and look at bbox = None for node in DepthFirstIterator(root): if type(node) == ConvexHullNode: print(node) if node.callDecoration( "isSliceable") and node.getMeshData() and node.isVisible(): if bbox is None: bbox = node.getBoundingBox() else: bbox = bbox + node.getBoundingBox() if bbox is None: bbox = AxisAlignedBox() look_at = bbox.center # guessed size so the objects are hopefully big size = max(bbox.width, bbox.height, bbox.depth * 0.5) # Looking from this direction (x, y, z) in OGL coordinates looking_from_offset = Vector(1, 1, 2) if size > 0: # determine the watch distance depending on the size looking_from_offset = looking_from_offset * size * 1.3 camera.setPosition(look_at + looking_from_offset) camera.lookAt(look_at) satisfied = False size = None fovy = 30 while not satisfied: if size is not None: satisfied = True # always be satisfied after second try projection_matrix = Matrix() # Somehow the aspect ratio is also influenced in reverse by the screen width/height # So you have to set it to render_width/render_height to get 1 projection_matrix.setPerspective(fovy, render_width / render_height, 1, 500) camera.setProjectionMatrix(projection_matrix) preview_pass.setCamera(camera) preview_pass.render() pixel_output = preview_pass.getOutput() min_x, max_x, min_y, max_y = Snapshot.getImageBoundaries( pixel_output) size = max((max_x - min_x) / render_width, (max_y - min_y) / render_height) if size > 0.5 or satisfied: satisfied = True else: # make it big and allow for some empty space around fovy *= 0.5 # strangely enough this messes up the aspect ratio: fovy *= size * 1.1 # make it a square if max_x - min_x >= max_y - min_y: # make y bigger min_y, max_y = int((max_y + min_y) / 2 - (max_x - min_x) / 2), int((max_y + min_y) / 2 + (max_x - min_x) / 2) else: # make x bigger min_x, max_x = int((max_x + min_x) / 2 - (max_y - min_y) / 2), int((max_x + min_x) / 2 + (max_y - min_y) / 2) cropped_image = pixel_output.copy(min_x, min_y, max_x - min_x, max_y - min_y) # Scale it to the correct size scaled_image = cropped_image.scaled( width, height, aspectRatioMode=QtCore.Qt.IgnoreAspectRatio, transformMode=QtCore.Qt.SmoothTransformation) return scaled_image
def _getNullBoundingBox(): return AxisAlignedBox(minimum=Vector(0, 0, 0), maximum=Vector(10, 10, 10))
def test_intersectsBox(self): box1 = AxisAlignedBox(minimum = Vector(5.0, 5.0, 5.0), maximum = Vector(10.0, 10.0, 10.0)) box2 = AxisAlignedBox(minimum = Vector(-10.0, -10.0, -10.0), maximum = Vector(-5.0, -5.0, -5.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.NoIntersection) box2 = AxisAlignedBox(minimum = Vector(-10.0, 0.0, -10.0), maximum = Vector(-5.0, 10.0, -5.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.NoIntersection) box2 = AxisAlignedBox(minimum = Vector(0.0, -10.0, -10.0), maximum = Vector(10.0, -5.0, -5.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.NoIntersection) box2 = AxisAlignedBox(minimum = Vector(-10.0, -10.0, 0.0), maximum = Vector(-5.0, -5.0, 10.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.NoIntersection) box2 = AxisAlignedBox(minimum = Vector(0.0, 0.0, 0.0), maximum = Vector(7.5, 7.5, 7.5)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.PartialIntersection) box2 = AxisAlignedBox(minimum = Vector(5.0, 0.0, 5.0), maximum = Vector(10.0, 7.5, 10.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.PartialIntersection) box2 = AxisAlignedBox(minimum = Vector(6.0, 6.0, 6.0), maximum = Vector(9.0, 9.0, 9.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.FullIntersection) box2 = AxisAlignedBox(minimum = Vector(5.0, 5.0, 5.0), maximum = Vector(10.0, 10.0, 10.0)) self.assertEqual(box1.intersectsBox(box2), AxisAlignedBox.IntersectionResult.FullIntersection)