Ignore:
Timestamp:
07/20/20 16:37:38 (4 years ago)
Author:
Maciej Komosinski
Message:

fS: faster collision detection, depends on "geometry" algorithms

File:
1 edited

Legend:

Unmodified
Added
Removed
  • cpp/frams/genetics/fS/part_distance_estimator.h

    r1006 r1017  
    55#ifndef _PART_DISTANCE_ESTIMATOR_H_
    66#define _PART_DISTANCE_ESTIMATOR_H_
     7
     8#include "frams/model/geometry/meshbuilder.h"
    79
    810class fS_Utils
     
    4446class PartDistanceEstimator
    4547{
    46         /**
    47          * Used in finding the proper distance between the parts
    48          * distance between spheres / sphere radius
    49          * That default value can be changed in certain cases
    50          * */
    51         static constexpr float SPHERE_RELATIVE_DISTANCE = 0.5;
    52         /**
    53          * Used in finding the proper distance between the parts
    54          * The maximal allowed value for
    55          * maximal radius of the node / sphere radius
    56          */
    57         static const int MAX_DIAMETER_QUOTIENT = 30;
    58         /**
    59          * The tolerance of the value of distance between parts
    60          */
    61         static constexpr double SPHERE_DISTANCE_TOLERANCE = 0.96;
    6248
    63         static constexpr double SPHERE_SIZE_QUOTIENT = 0.25;
     49public:
     50        static constexpr double PRECISION = 0.05;
     51        static constexpr double RELATIVE_DENSITY = 5.0;
    6452
    6553
    66         static bool isInsidePart(Part::Shape shape, const Pt3D &partRadii, const Pt3D &center, double sphereRadius)
     54        static Part *buildTemporaryPart(Part::Shape shape, const Pt3D &scale, const Pt3D &rotations)
    6755        {
    68                 if(sphereRadius >= fS_Utils::min3(partRadii))
    69                         return true; // TODO Special case
    70                 Pt3D mostRemote = Pt3D(fabs(center.x), fabs(center.y), fabs(center.z)) + Pt3D(sphereRadius);
    71                 bool isInEllipsis;
    72 
    73                 bool result = false;
    74 
    75                 switch (shape)
    76                 {
    77                         case Part::Shape::SHAPE_CUBOID:
    78                                 if(mostRemote.x <= partRadii.x && mostRemote.y <= partRadii.y && mostRemote.z <= partRadii.z)
    79                                         result = true;
    80                                 break;
    81                         case Part::Shape::SHAPE_CYLINDER:
    82                                 isInEllipsis = pow(center.y / (partRadii.y - sphereRadius), 2) + pow(center.z / (partRadii.z - sphereRadius), 2) <= 1.0;
    83                                 if (mostRemote.x <= partRadii.x && isInEllipsis)
    84                                         result = true;
    85                                 break;
    86                         case Part::Shape::SHAPE_ELLIPSOID:
    87                                 if(pow(center.x / (partRadii.x - sphereRadius), 2) + pow(center.y / (partRadii.y - sphereRadius), 2) + pow(center.z / (partRadii.z - sphereRadius), 2) <= 1.0)
    88                                         result = true;
    89                                 break;
    90                         default:
    91                                 logMessage("fS", "calculateVolume", LOG_ERROR, "Invalid part type");
    92                 }
    93                 return result;
     56                Part *tmpPart = new Part(shape);
     57                tmpPart->scale = scale;
     58                tmpPart->setRot(rotations);
     59                return tmpPart;
    9460        }
    9561
    96 public:
    97         static vector<Pt3D> findSphereCenters(Part::Shape shape, double &sphereRadius, const Pt3D &radii, const Pt3D &rotations)
     62        static vector <Pt3D> findSphereCenters(Part *part)
    9863        {
    99                 double sphereRelativeDistance = SPHERE_RELATIVE_DISTANCE;
    100                 double minRadius = fS_Utils::min3(radii);
    101                 if(minRadius <= 0)
    102                         throw fS_Exception("Invalid part size in PartDistanceEstimator", 0);
    103                 double maxRadius = fS_Utils::max3(radii);
    104                 sphereRadius = SPHERE_SIZE_QUOTIENT * minRadius;
    105                 if (MAX_DIAMETER_QUOTIENT < maxRadius / sphereRadius)
     64                // Divide by maximal radius to avoid long computations
     65                MeshBuilder::PartSurface surface(RELATIVE_DENSITY / fS_Utils::max3(part->scale));
     66                surface.initialize(part);
     67
     68                vector <Pt3D> centers;
     69                Pt3D point;
     70                while (surface.tryGetNext(point))
    10671                {
    107                         // When max radius is much bigger than sphereRadius and there are to many spheresZ
    108                         sphereRelativeDistance = 1.0;   // Make the spheres adjacent to speed up the computation
    109                         sphereRadius = maxRadius / MAX_DIAMETER_QUOTIENT;
    110                 }
    111                 else if(shape == Part::Shape::SHAPE_ELLIPSOID)
    112                         sphereRadius = minRadius;
    113 
    114                 double sphereDiameter = 2 * sphereRadius;
    115 
    116                 double radiiArr[3]{radii.x, radii.y, radii.z};
    117                 vector<double> coordinates[3];
    118                 for (int dim = 0; dim < 3; dim++)
    119                 {
    120                         double spaceForSphereCenters = 2 * radiiArr[dim] - sphereDiameter;
    121                         if (spaceForSphereCenters > 0)
    122                         {
    123                                 int lastIndex = ceil(spaceForSphereCenters / (sphereDiameter * sphereRelativeDistance));
    124                                 for (int i = 0; i <= lastIndex; i++)
    125                                 {
    126                                         coordinates[dim].push_back(spaceForSphereCenters * (double(i) / lastIndex - 0.5));
    127                                 }
    128                         }
    129                         else
    130                                 coordinates[dim].push_back(0.0);
    131                 }
    132 
    133                 vector<Pt3D> centers;
    134                 for (int xi = 0; xi < int(coordinates[0].size()); xi++)
    135                 {
    136                         for (int yi = 0; yi < int(coordinates[1].size()); yi++)
    137                         {
    138                                 for (int zi = 0; zi < int(coordinates[2].size()); zi++)
    139                                 {
    140                                                 Pt3D center = Pt3D(coordinates[0][xi], coordinates[1][yi], coordinates[2][zi]);
    141                                                 if (isInsidePart(shape, radii, center, sphereRadius))
    142                                                 {
    143                                                         fS_Utils::rotateVector(center, rotations);
    144                                                         centers.push_back(center);
    145                                                 }
    146                                 }
    147                         }
     72                        centers.push_back(point);
    14873                }
    14974                return centers;
    15075        }
    15176
    152         static int isCollision(vector<Pt3D> &centersParent, vector<Pt3D> &centers, Pt3D &vectorBetweenParts,
    153                                         double distanceThreshold)
     77        static bool isCollision(Part *parentPart, vector <Pt3D> &centers, Pt3D &vectorBetweenParts)
    15478        {
    155                 double upperThresholdSq = distanceThreshold * distanceThreshold;
    156                 double lowerThresholdSq = pow(SPHERE_DISTANCE_TOLERANCE * distanceThreshold, 2);
    157                 double distanceSq;
    158                 double dx, dy, dz;
    159                 bool existsAdjacent = false;
    160                 Pt3D *tmpPoint;
    161                 for (int sc = 0; sc < int(centers.size()); sc++)
     79                static double CBRT_3 = std::cbrt(3);
     80                double maxParentReachSq = pow(CBRT_3 * fS_Utils::max3(parentPart->scale), 2);
     81                for (int i = 0; i < int(centers.size()); i++)
    16282                {
    163                         Pt3D shiftedSphere = Pt3D(centers[sc]);
    164                         shiftedSphere += vectorBetweenParts;
    165                         for (int psc = 0; psc < int(centersParent.size()); psc++)
    166                         {
    167                                 tmpPoint = &centersParent[psc];
    168                                 dx = shiftedSphere.x - tmpPoint->x;
    169                                 dy = shiftedSphere.y - tmpPoint->y;
    170                                 dz = shiftedSphere.z - tmpPoint->z;
    171                                 distanceSq = dx * dx + dy * dy + dz * dz;
    172 
    173                                 if (distanceSq <= upperThresholdSq)
    174                                 {
    175                                         if (distanceSq >= lowerThresholdSq)
    176                                                 existsAdjacent = true;
    177                                         else
    178                                         {
    179                                                 return COLLISION;
    180                                         }
    181                                 }
    182                         }
     83                        Pt3D shifted = centers[i] + vectorBetweenParts;
     84                        double distanceToCenterSq = shifted.x * shifted.x + shifted.y * shifted.y + shifted.z * shifted.z;
     85                        if (distanceToCenterSq <= maxParentReachSq && GeometryUtils::isPointInsidePart(shifted, parentPart))
     86                                return true;
    18387                }
    184                 if (existsAdjacent)
    185                         return ADJACENT;
    186                 else
    187                         return DISJOINT;
     88                return false;
    18889        }
    18990};
     
    19192double Node::getDistance()
    19293{
    193         Pt3D size = calculateSize();
    194         Pt3D parentSize = parent->calculateSize();      // Here we are sure that parent is not nullptr
    195         double parentSphereRadius, sphereRadius;
    196         vector<Pt3D> centersParent = PartDistanceEstimator::findSphereCenters(parent->partType, parentSphereRadius, parentSize, parent->getRotation());
    197         vector<Pt3D> centers = PartDistanceEstimator::findSphereCenters(partType, sphereRadius, size, getRotation());
     94        Pt3D size;
     95        calculateSize(size);
     96        Pt3D parentSize;
     97        parent->calculateSize(parentSize);    // Here we are sure that parent is not nullptr
     98        Part *tmpPart = PartDistanceEstimator::buildTemporaryPart(partType, size, getRotation());
     99        Part *parentTmpPart = PartDistanceEstimator::buildTemporaryPart(parent->partType, parentSize, parent->getRotation());
    198100
    199         double distanceThreshold = sphereRadius + parentSphereRadius;
     101        vector <Pt3D> centers = PartDistanceEstimator::findSphereCenters(tmpPart);
     102
    200103        double minDistance = 0.0;
    201104        double maxDistance = 2 * (fS_Utils::max3(parentSize) + fS_Utils::max3(size));
    202105        double currentDistance = fS_Utils::avg(maxDistance, minDistance);
    203         int result = -1;
    204         int iterationNo = 0;
    205         while (result != ADJACENT)
     106        int collisionDetected = false;
     107        while (maxDistance - minDistance > PartDistanceEstimator::PRECISION)
    206108        {
    207                 iterationNo++;
    208109                Pt3D vectorBetweenParts = state->v * currentDistance;
    209                 result = PartDistanceEstimator::isCollision(centersParent, centers, vectorBetweenParts, distanceThreshold);
     110                collisionDetected = PartDistanceEstimator::isCollision(parentTmpPart, centers, vectorBetweenParts);
    210111
    211                 if (result == DISJOINT)
     112                if (collisionDetected)
     113                {
     114                        minDistance = currentDistance;
     115                        currentDistance = fS_Utils::avg(maxDistance, currentDistance);
     116                } else
    212117                {
    213118                        maxDistance = currentDistance;
    214119                        currentDistance = fS_Utils::avg(currentDistance, minDistance);
    215                 } else if (result == COLLISION)
    216                 {
    217                         minDistance = currentDistance;
    218                         currentDistance = fS_Utils::avg(maxDistance, currentDistance);
    219120                }
    220 
    221                 if(maxDistance <= 0 || iterationNo > 1000)
    222                         throw fS_Exception("Computing of distances between parts failed", 0);
    223                 if (currentDistance > maxDistance)
    224                 {
    225                         throw fS_Exception("Internal error; the maximal distance between parts exceeded.", 0);
    226                 }
    227                 if (currentDistance < minDistance)
    228                         throw fS_Exception("Internal error; the minimal distance between parts exceeded.", 0);
    229 
    230121        }
    231 
     122        delete tmpPart;
     123        delete parentTmpPart;
    232124        return currentDistance;
    233125}
Note: See TracChangeset for help on using the changeset viewer.