// This file is a part of Framsticks SDK. http://www.framsticks.com/ // Copyright (C) 1999-2020 Maciej Komosinski and Szymon Ulatowski. // See LICENSE.txt for details. #ifndef _GEOMETRYUTILS_H_ #define _GEOMETRYUTILS_H_ #include #include #include #include /*Binary literals like 0b010 are standardized only in C++14. We use macros as they are compatible with older compilers too. 3-bit numbers are used when iterating through octants in a 3D space. Example: when creating points that cover the surface of an ellipsoid, the points are only created for the positive octant (x, y, and z coordinates are positive). Points in the remaining 7 octants are created by reflecting points from the positive octant through the appropriate planes defined by pairs of axes. 2-bit numbers are used for 2D. Example: cylinders are aligned along the x axis so that both bases are parallel to the yz plane. When points are created along the edge of the base (these will be used later to create points along the side of the cylinder), only y and z axes are important, so quadrants of the 2D are sufficient. Just as in the 3D example above, only points for the positive quadrant, QuadrantYZ, are created, and points of the remaining quadrants are created by reflection. QuadrantXY and QuadrantZX enumerations are never used and are provided only for completeness. */ #define b000 0 #define b01 1 #define b001 1 #define b10 2 #define b010 2 #define b100 4 #define b110 6 namespace CuboidFaces { enum Face { NEGATIVE_X = 0, POSITIVE_X = 1, NEGATIVE_Y = 2, POSITIVE_Y = 3, NEGATIVE_Z = 4, POSITIVE_Z = 5, FIRST = 0, NUMBER = 6 }; inline bool isPositive(Face f) { return f & b001; } inline bool isNegative(Face f) { return !isPositive(f); } inline bool isX(Face f) { return (f & b110) == b000; } inline bool isY(Face f) { return (f & b110) == b010; } inline bool isZ(Face f) { return (f & b110) == b100; } } namespace CylinderBases { enum Base { NEGATIVE_X = 0, POSITIVE_X = 1, FIRST = 0, NUMBER = 2 }; inline bool isPositive(Base b) { return b & b001; } inline bool isNegative(Base b) { return !isPositive(b); } } namespace QuadrantsXY { enum QuadrantXY { NEGATIVE_X_NEGATIVE_Y = 0, NEGATIVE_X_POSITIVE_Y = 1, POSITIVE_X_NEGATIVE_Y = 2, POSITIVE_X_POSITIVE_Y = 3, FIRST = 0, NUMBER = 4 }; inline bool isPositiveX(QuadrantXY q) { return (q & b10) != 0; } inline bool isNegativeX(QuadrantXY q) { return !isPositiveX(q); } inline bool isPositiveY(QuadrantXY q) { return q & b01; } inline bool isNegativeY(QuadrantXY q) { return !isPositiveY(q); } } namespace QuadrantsYZ { enum QuadrantYZ { NEGATIVE_Y_NEGATIVE_Z = 0, NEGATIVE_Y_POSITIVE_Z = 1, POSITIVE_Y_NEGATIVE_Z = 2, POSITIVE_Y_POSITIVE_Z = 3, FIRST = 0, NUMBER = 4 }; inline bool isPositiveY(QuadrantYZ q) { return (q & b10) != 0; } inline bool isNegativeY(QuadrantYZ q) { return !isPositiveY(q); } inline bool isPositiveZ(QuadrantYZ q) { return q & b01; } inline bool isNegativeZ(QuadrantYZ q) { return !isPositiveZ(q); } } namespace QuadrantsZX { enum QuadrantZX { NEGATIVE_Z_NEGATIVE_X = 0, NEGATIVE_Z_POSITIVE_X = 1, POSITIVE_Z_NEGATIVE_X = 2, POSITIVE_Z_POSITIVE_X = 3, FIRST = 0, NUMBER = 4 }; inline bool isPositiveZ(QuadrantZX q) { return (q & b10) != 0; } inline bool isNegativeZ(QuadrantZX q) { return !isPositiveZ(q); } inline bool isPositiveX(QuadrantZX q) { return (q & b01) != 0; } inline bool isNegativeX(QuadrantZX q) { return !isPositiveX(q); } } namespace Octants { enum Octant { NEGATIVE_X_NEGATIVE_Y_NEGATIVE_Z = 0, NEGATIVE_X_NEGATIVE_Y_POSITIVE_Z = 1, NEGATIVE_X_POSITIVE_Y_NEGATIVE_Z = 2, NEGATIVE_X_POSITIVE_Y_POSITIVE_Z = 3, POSITIVE_X_NEGATIVE_Y_NEGATIVE_Z = 4, POSITIVE_X_NEGATIVE_Y_POSITIVE_Z = 5, POSITIVE_X_POSITIVE_Y_NEGATIVE_Z = 6, POSITIVE_X_POSITIVE_Y_POSITIVE_Z = 7, FIRST = 0, NUMBER = 8 }; inline bool isPositiveX(Octant o) { return (o & b100) != 0; } inline bool isNegativeX(Octant o) { return !isPositiveX(o); } inline bool isPositiveY(Octant o) { return (o & b010) != 0; } inline bool isNegativeY(Octant o) { return !isPositiveY(o); } inline bool isPositiveZ(Octant o) { return o & b001; } inline bool isNegativeZ(Octant o) { return !isPositiveZ(o); } } namespace GeometryUtils { double pointPosition(const int pointIndex, const int numberOfPoints); double pointOnAxis(const double scale, const double position); double pointOnAxis(const double scale, const int pointIndex, const int numberOfPoints); double combination(const double value1, const double value2, const double position); double combination(const double value1, const double value2, const int pointIndex, const int numberOfPoints); bool isPointInsideModelExcludingPart(const Pt3D &point, const Model *model, const int excludedPartIndex); bool isPointInsideModel(const Pt3D &point, const Model &model); bool isPointInsidePart(const Pt3D &point, const Part *part); bool isPointStrictlyInsidePart(const Pt3D &point, const Part *part); bool isPointInsideEllipsoid(const Pt3D &point, const Part *part); bool isPointStrictlyInsideEllipsoid(const Pt3D &point, const Part *part); bool isPointInsideCuboid(const Pt3D &point, const Part *part); bool isPointStrictlyInsideCuboid(const Pt3D &point, const Part *part); bool isPointInsideCylinder(const Pt3D &point, const Part *part); bool isPointStrictlyInsideCylinder(const Pt3D &point, const Part *part); void findSizesAndAxesOfPointsGroup(SListTempl &points, Pt3D &sizes, Orient &axes); void findSizeAndAxisOfPointsGroup(const SListTempl &points, double &size, Pt3D &axis); double findTwoFurthestPoints(const SListTempl &points, int &index1, int &index2); void createAxisFromTwoPoints(Pt3D &axis, const Pt3D &point1, const Pt3D &point2); void orthographicProjectionToPlane(SListTempl &points, const Pt3D &planeNormalVector); double pointDistanceToPlane(const Pt3D &point, const Pt3D &planeNormalVector); void getRectangleApicesFromCuboid(const Part *part, const CuboidFaces::Face face, Pt3D &apex1, Pt3D &apex2, Pt3D &apex3, Pt3D &apex4); void getRectangleApices(const double width, const double height, const Pt3D &position, const Orient &orient, Pt3D &apex1, Pt3D &apex2, Pt3D &apex3, Pt3D &apex4); void getNextEllipseSegmentationPoint(const double d, const double a, const double b, double &x, double &y); double ellipsoidArea(const Pt3D &sizes); double ellipsoidArea(const double a, const double b, const double c); double ellipsePerimeter(const double a, const double b); double calculateSolidVolume(const Part *part); bool isSolidPartScaleValid(const Part::Shape &partShape, const Pt3D &scale, bool ensureCircleSection); /** * @brief Adds anchor to the specified Model. * @details An anchor has two functions. First is to provide Model consistency. Some functions in * GeometryTestUtils namespace requires Model passed to them as an argument to contain at * least one Part. All new Parts are bonded to the rest of Model using Joint connecting them * with first Part of Model. Second is to provide reference which helps to understand Model * position, scale and orientation. Anchor is built from four Parts: small sphere placed in * global coordinate system origin and three cuboids visualising global coordinate system * axes. * @see addAxesToModel. * @param[in] model Owner of Parts to be created. */ void addAnchorToModel(Model &model); /** * @brief Adds point marker to Model. * @details Marker of point is a small sphere (radius = 0.05). * @param[in] point Location of marker. * @param[in] model Owner of Part to be created, must contain at least one part. */ void addPointToModel(const Pt3D &point, Model &model); /** * @brief Adds axes markers to Model. * @details Axes markers are three streched (one of scales = 0.5, others = 0.05) and colored * cuboids. Cuboid visualising OX axis is red, OY - green, and OZ - blue. * @param[in] sizes Axes visual lengths. * @param[in] axes Axes orientation, relatively to global coordinate system axes. * @param[in] center Axes intersection point, relatively to global coordinate system origin. * @param[in] model Owner of Parts to be created, must contain at least one part. */ void addAxesToModel(const Pt3D &sizes, const Orient &axes, const Pt3D ¢er, Model &model); /** * @brief Merges two Models. * @details Moves all parts from source Model to target Model and - to provide Model * consistency - creates Joint between firsts Parts of each of them. Each model must contain * at least one Part. * @param[in] target Target Model, must contain at least one part. * @param[in] source Source Model, must contain at least one part. */ void mergeModels(Model &target, Model &source); /** * @brief Randomizes position, scale and rotations of Part. * @details Sets coords of Part position to random values from range (1.5, 2.5), scales to * random values from range (0.1, 1.0), and rotations around each axis to random values from * range (0, M_PI). * @param[in] part Part which position, scale and orient should be randomized. */ void randomizePositionScaleAndOrient(Part *part); } #endif