/*************************************************************************** * Copyright (c) 2023 WandererFan * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ //! a class to contain useful shape manipulations. these methods were originally // in GeometryObject. #include "PreCompiled.h" #ifndef _PreComp_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif// #ifndef _PreComp_ #include #include #include #include "DrawUtil.h" #include "ShapeUtils.h" using namespace TechDraw; using namespace std; using DU = DrawUtil; //! gets a coordinate system that matches view system used in 3D with +Z up (or +Y up if necessary) //! used for individual views, but not secondary views in projection groups //! flip determines Y mirror or not. // getViewAxis 1 gp_Ax2 ShapeUtils::getViewAxis(const Base::Vector3d origin, const Base::Vector3d& direction, const bool flip) { // Base::Console().Message("GO::getViewAxis() - 1 - use only with getLegacyX\n"); (void)flip; gp_Ax2 viewAxis; gp_Pnt inputCenter(origin.x, origin.y, origin.z); Base::Vector3d stdZ(0.0, 0.0, 1.0); Base::Vector3d stdOrg(0.0, 0.0, 0.0); Base::Vector3d cross = direction; if (DU::checkParallel(direction, stdZ)) { cross = Base::Vector3d(1.0, 0.0, 0.0); } else { cross.Normalize(); cross = cross.Cross(stdZ); } if (cross.IsEqual(stdOrg, FLT_EPSILON)) { viewAxis = gp_Ax2(inputCenter, gp_Dir(direction.x, direction.y, direction.z)); return viewAxis; } viewAxis = gp_Ax2(inputCenter, gp_Dir(direction.x, direction.y, direction.z), gp_Dir(cross.x, cross.y, cross.z)); return viewAxis; } //! gets a coordinate system specified by Z and X directions //getViewAxis 2 gp_Ax2 ShapeUtils::getViewAxis(const Base::Vector3d origin, const Base::Vector3d& direction, const Base::Vector3d& xAxis, const bool flip) { // Base::Console().Message("GO::getViewAxis() - 2\n"); (void)flip; gp_Pnt inputCenter(origin.x, origin.y, origin.z); return gp_Ax2(inputCenter, gp_Dir(direction.x, direction.y, direction.z), gp_Dir(xAxis.x, xAxis.y, xAxis.z)); } // was getViewAxis 1 // getViewAxis as used before XDirection property adopted gp_Ax2 ShapeUtils::legacyViewAxis1(const Base::Vector3d origin, const Base::Vector3d& direction, const bool flip) { // Base::Console().Message("GO::legacyViewAxis1()\n"); gp_Pnt inputCenter(origin.x, origin.y, origin.z); Base::Vector3d stdZ(0.0, 0.0, 1.0); Base::Vector3d stdOrg(0.0, 0.0, 0.0); Base::Vector3d flipDirection(direction.x, -direction.y, direction.z); if (!flip) { flipDirection = Base::Vector3d(direction.x, direction.y, direction.z); } Base::Vector3d cross = flipDirection; // //special case if (DU::checkParallel(flipDirection, stdZ)) { cross = Base::Vector3d(1.0, 0.0, 0.0); } else { cross.Normalize(); cross = cross.Cross(stdZ); } if (cross.IsEqual(stdOrg, FLT_EPSILON)) { return gp_Ax2(inputCenter, gp_Dir(flipDirection.x, flipDirection.y, flipDirection.z)); } gp_Ax2 viewAxis = gp_Ax2(inputCenter, gp_Dir(flipDirection.x, flipDirection.y, flipDirection.z), gp_Dir(cross.x, cross.y, cross.z)); //this bit is to handle the old mirror Y logic, but it messes up //some old files. gp_Trsf mirrorXForm; gp_Ax2 mirrorCS(inputCenter, gp_Dir(0, -1, 0)); mirrorXForm.SetMirror(mirrorCS); viewAxis = viewAxis.Transformed(mirrorXForm); return viewAxis; } //! Returns the centroid of shape based on R3 gp_Pnt ShapeUtils::findCentroid(const TopoDS_Shape& shape) { Bnd_Box tBounds; tBounds.SetGap(0.0); BRepBndLib::AddOptimal(shape, tBounds, true, false); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; tBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); Standard_Real x = (xMin + xMax) / 2.0, y = (yMin + yMax) / 2.0, z = (zMin + zMax) / 2.0; return gp_Pnt(x, y, z); } //! Returns the centroid of shape, as viewed according to direction gp_Pnt ShapeUtils::findCentroid(const TopoDS_Shape& shape, const Base::Vector3d& direction) { // Base::Console().Message("GO::findCentroid() - 1\n"); Base::Vector3d origin(0.0, 0.0, 0.0); gp_Ax2 viewAxis = getViewAxis(origin, direction); return findCentroid(shape, viewAxis); } //! Returns the centroid of shape, as viewed according to direction gp_Pnt ShapeUtils::findCentroid(const TopoDS_Shape& shape, const gp_Ax2& viewAxis) { // Base::Console().Message("GO::findCentroid() - 2\n"); gp_Trsf tempTransform; tempTransform.SetTransformation(viewAxis); BRepBuilderAPI_Transform builder(shape, tempTransform); Bnd_Box tBounds; tBounds.SetGap(0.0); BRepBndLib::AddOptimal(builder.Shape(), tBounds, true, false); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; tBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); Standard_Real x = (xMin + xMax) / 2.0, y = (yMin + yMax) / 2.0, z = (zMin + zMax) / 2.0; // Get centroid back into object space tempTransform.Inverted().Transforms(x, y, z); return gp_Pnt(x, y, z); } Base::Vector3d ShapeUtils::findCentroidVec(const TopoDS_Shape& shape, const Base::Vector3d& direction) { // Base::Console().Message("GO::findCentroidVec() - 1\n"); gp_Pnt p = ShapeUtils::findCentroid(shape, direction); return Base::Vector3d(p.X(), p.Y(), p.Z()); } Base::Vector3d ShapeUtils::findCentroidVec(const TopoDS_Shape& shape, const gp_Ax2& cs) { // Base::Console().Message("GO::findCentroidVec() - 2\n"); gp_Pnt p = ShapeUtils::findCentroid(shape, cs); return Base::Vector3d(p.X(), p.Y(), p.Z()); } //! Returns the XY plane center of shape with respect to coordSys gp_Pnt ShapeUtils::findCentroidXY(const TopoDS_Shape& shape, const gp_Ax2& coordSys) { // Base::Console().Message("GO::findCentroid() - 2\n"); gp_Trsf tempTransform; tempTransform.SetTransformation(coordSys); BRepBuilderAPI_Transform builder(shape, tempTransform); Bnd_Box tBounds; tBounds.SetGap(0.0); BRepBndLib::AddOptimal(builder.Shape(), tBounds, true, false); Standard_Real xMin, yMin, zMin, xMax, yMax, zMax; tBounds.Get(xMin, yMin, zMin, xMax, yMax, zMax); Standard_Real x = (xMin + xMax) / 2.0, y = (yMin + yMax) / 2.0, z = 0.0; // Get "centroid" back into object space tempTransform.Inverted().Transforms(x, y, z); return gp_Pnt(x, y, z); } //!scales & mirrors a shape about a center TopoDS_Shape ShapeUtils::mirrorShapeVec(const TopoDS_Shape& input, const Base::Vector3d& inputCenter, double scale) { gp_Pnt gInput(inputCenter.x, inputCenter.y, inputCenter.z); return ShapeUtils::mirrorShape(input, gInput, scale); } TopoDS_Shape ShapeUtils::mirrorShape(const TopoDS_Shape& input, const gp_Pnt& inputCenter, double scale) { TopoDS_Shape transShape; if (input.IsNull()) { return transShape; } try { // Make tempTransform scale the object around its centre point and // mirror about the Y axis gp_Trsf tempTransform; //BRepBuilderAPI_Transform will loop forever if asked to use 0.0 as scale if (scale <= 0.0) { tempTransform.SetScale(inputCenter, 1.0); } else { tempTransform.SetScale(inputCenter, scale); } gp_Trsf mirrorTransform; mirrorTransform.SetMirror(gp_Ax2(inputCenter, gp_Dir(0, -1, 0))); tempTransform.Multiply(mirrorTransform); // Apply that transform to the shape. This should preserve the centre. BRepBuilderAPI_Transform mkTrf(input, tempTransform); transShape = mkTrf.Shape(); } catch (...) { return transShape; } return transShape; } //!rotates a shape about a viewAxis TopoDS_Shape ShapeUtils::rotateShape(const TopoDS_Shape& input, const gp_Ax2& viewAxis, double rotAngle) { TopoDS_Shape transShape; if (input.IsNull()) { return transShape; } gp_Ax1 rotAxis = viewAxis.Axis(); double rotation = rotAngle * M_PI / 180.0; try { gp_Trsf tempTransform; tempTransform.SetRotation(rotAxis, rotation); BRepBuilderAPI_Transform mkTrf(input, tempTransform); transShape = mkTrf.Shape(); } catch (...) { return transShape; } return transShape; } //!scales a shape about origin TopoDS_Shape ShapeUtils::scaleShape(const TopoDS_Shape& input, double scale) { TopoDS_Shape transShape; try { gp_Trsf scaleTransform; scaleTransform.SetScale(gp_Pnt(0, 0, 0), scale); BRepBuilderAPI_Transform mkTrf(input, scaleTransform); transShape = mkTrf.Shape(); } catch (...) { return transShape; } return transShape; } //!moves a shape TopoDS_Shape ShapeUtils::moveShape(const TopoDS_Shape& input, const Base::Vector3d& motion) { TopoDS_Shape transShape; try { gp_Trsf xlate; xlate.SetTranslation(gp_Vec(motion.x, motion.y, motion.z)); BRepBuilderAPI_Transform mkTrf(input, xlate); transShape = mkTrf.Shape(); } catch (...) { return transShape; } return transShape; } TopoDS_Shape ShapeUtils::centerShapeXY(const TopoDS_Shape& inShape, const gp_Ax2& coordSys) { gp_Pnt inputCenter = findCentroidXY(inShape, coordSys); Base::Vector3d centroid = DrawUtil::toVector3d(inputCenter); return ShapeUtils::moveShape(inShape, centroid * -1.0); } std::pair ShapeUtils::getEdgeEnds(TopoDS_Edge edge) { std::pair result; TopoDS_Vertex tvFirst, tvLast; TopExp::Vertices(edge, tvFirst, tvLast); gp_Pnt gpFirst = BRep_Tool::Pnt(tvFirst); gp_Pnt gpLast = BRep_Tool::Pnt(tvLast); result.first = DU::toVector3d(gpFirst); result.second = DU::toVector3d(gpLast); return result; } //! check for shape is null or shape has no subshapes(vertex/edge/face/etc) //! this handles the case of an empty compound which is not IsNull, but has no //! content. bool ShapeUtils::isShapeReallyNull(TopoDS_Shape shape) { // if the shape is null or it has no subshapes, then it is really null return shape.IsNull() || !TopoDS_Iterator(shape).More(); }