You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1004 lines
39 KiB
1004 lines
39 KiB
#pragma once
|
|
#include <array>
|
|
#include <sparkstack.hpp>
|
|
#include <uvector.hpp>
|
|
#include "organizer.hpp"
|
|
#include "primitive.hpp"
|
|
#include "timer.hpp"
|
|
|
|
class Vector3D
|
|
{
|
|
public:
|
|
Vector3D() = default;
|
|
|
|
Vector3D(const double x, const double y, const double z)
|
|
{
|
|
this->m_x = x;
|
|
this->m_y = y;
|
|
this->m_z = z;
|
|
}
|
|
|
|
double length() const { return std::sqrt(this->m_x * this->m_x + this->m_y * this->m_y + this->m_z * this->m_z); }
|
|
|
|
Vector3D operator/(const double scalar) const
|
|
{
|
|
if (scalar == 0) { throw std::runtime_error("Division by zero error."); }
|
|
|
|
return Vector3D(this->m_x / scalar, this->m_y / scalar, this->m_z / scalar);
|
|
}
|
|
|
|
Vector3D operator*(const double scalar) const
|
|
{
|
|
return Vector3D(this->m_x * scalar, this->m_y * scalar, this->m_z * scalar);
|
|
}
|
|
|
|
Vector3D cross(const Vector3D& other) const
|
|
{
|
|
return Vector3D(this->m_y * other.m_z - this->m_z * other.m_y, this->m_z * other.m_x - this->m_x * other.m_z,
|
|
this->m_x * other.m_y - this->m_y * other.m_x);
|
|
}
|
|
|
|
double dot(const Vector3D& other) const { return this->m_x * other.m_x + this->m_y * other.m_y + this->m_z * other.m_z; }
|
|
|
|
algoim::uvector3 getUVector3Data() const
|
|
{
|
|
algoim::uvector3 node;
|
|
node(0) = this->m_x;
|
|
node(1) = this->m_y;
|
|
node(2) = this->m_z;
|
|
return node;
|
|
}
|
|
|
|
void rotation(const std::array<std::array<double, 3>, 3>& rotationMatrix)
|
|
{
|
|
for (int i = 0; i < 3; i++) {
|
|
double temp = 0;
|
|
for (int j = 0; j < 3; j++) { temp += rotationMatrix[i][j] * this->getUVector3Data()(j); }
|
|
if (i == 0) {
|
|
this->m_x = temp;
|
|
} else if (i == 1) {
|
|
this->m_y = temp;
|
|
} else {
|
|
this->m_z = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
double m_x, m_y, m_z;
|
|
};
|
|
|
|
class Direction3D : public Vector3D
|
|
{
|
|
public:
|
|
Direction3D() = default;
|
|
|
|
Direction3D(const double x, const double y, const double z)
|
|
{
|
|
this->m_x = x;
|
|
this->m_y = y;
|
|
this->m_z = z;
|
|
|
|
this->normalized();
|
|
}
|
|
|
|
Direction3D(const Vector3D& vector)
|
|
{
|
|
this->m_x = vector.m_x;
|
|
this->m_y = vector.m_y;
|
|
this->m_z = vector.m_z;
|
|
|
|
this->normalized();
|
|
}
|
|
|
|
Vector3D cross(const Direction3D& other) const
|
|
{
|
|
return Vector3D(this->m_y * other.m_z - this->m_z * other.m_y, this->m_z * other.m_x - this->m_x * other.m_z,
|
|
this->m_x * other.m_y - this->m_y * other.m_x);
|
|
}
|
|
|
|
double dot(const Direction3D& other) const { return this->m_x * other.m_x + this->m_y * other.m_y + this->m_z * other.m_z; }
|
|
|
|
void normalized()
|
|
{
|
|
double length = this->length();
|
|
if (std::abs(length) < 1e-8) { throw std::runtime_error("Cannot normalize a zero-length vector."); }
|
|
|
|
this->m_x /= length;
|
|
this->m_y /= length, this->m_z /= length;
|
|
}
|
|
|
|
bool isParallel(const Direction3D& other) const
|
|
{
|
|
auto cross = this->cross(other);
|
|
return std::abs(cross.length()) < 1e-8;
|
|
}
|
|
|
|
Direction3D operator-() const { return Direction3D(-this->m_x, -this->m_y, -this->m_z); }
|
|
};
|
|
|
|
class Point3D : public Vector3D
|
|
{
|
|
public:
|
|
Point3D() = default;
|
|
|
|
Point3D(const double x, const double y, const double z)
|
|
{
|
|
this->m_x = x;
|
|
this->m_y = y;
|
|
this->m_z = z;
|
|
}
|
|
|
|
Vector3D operator-(const Point3D& other) const
|
|
{
|
|
return Vector3D(this->m_x - other.m_x, this->m_y - other.m_y, this->m_z - other.m_z);
|
|
}
|
|
|
|
Point3D operator-(const Direction3D& direction) const
|
|
{
|
|
return Point3D(this->m_x - direction.m_x, this->m_y - direction.m_y, this->m_z - direction.m_z);
|
|
}
|
|
|
|
Point3D operator-(const Vector3D& offset) const
|
|
{
|
|
return Point3D(this->m_x - offset.m_x, this->m_y - offset.m_y, this->m_z - offset.m_z);
|
|
}
|
|
|
|
Point3D operator+(const Direction3D& direction) const
|
|
{
|
|
return Point3D(this->m_x + direction.m_x, this->m_y + direction.m_y, this->m_z + direction.m_z);
|
|
}
|
|
|
|
Point3D operator+(const Vector3D& offset) const
|
|
{
|
|
return Point3D(this->m_x + offset.m_x, this->m_y + offset.m_y, this->m_z + offset.m_z);
|
|
}
|
|
|
|
double getDistance(const Point3D& other) const
|
|
{
|
|
return std::sqrt((other.m_x - this->m_x) * (other.m_x - this->m_x) + (other.m_y - this->m_y) * (other.m_y - this->m_y)
|
|
+ (other.m_z - this->m_z) * (other.m_z - this->m_z));
|
|
}
|
|
|
|
Point3D getMiddlePoint(const Point3D& other) const
|
|
{
|
|
return Point3D((this->m_x + other.m_x) / 2.0, (this->m_z + other.m_z) / 2.0, (this->m_z + other.m_z) / 2.0);
|
|
}
|
|
};
|
|
|
|
typedef unsigned int BodyTag;
|
|
|
|
class Loader
|
|
{
|
|
public:
|
|
/**
|
|
* @brief Union two visible primitive node, result will be writen to first node
|
|
* @param[in] body1 The first index of primitive node
|
|
* @param[in] body2 The second index of primitive node
|
|
*/
|
|
void unionNode(const BodyTag body1, const BodyTag body2)
|
|
{
|
|
auto& rep1 = this->m_allVisible[body1];
|
|
auto& rep2 = this->m_allVisible[body2];
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
|
|
if (rep1.isEmpty()) {
|
|
result = rep2;
|
|
} else if (rep2.isEmpty()) {
|
|
result = rep1;
|
|
} else {
|
|
result = this->unionNode(rep1, rep2, false);
|
|
}
|
|
|
|
this->m_allVisible[body1] = result;
|
|
}
|
|
|
|
/**
|
|
* @brief Intersect two visible primitive node, result will be writen to first node
|
|
* @param[in] body1 The first index of primitive node
|
|
* @param[in] body2 The second index of primitive node
|
|
*/
|
|
void intersectNode(const BodyTag body1, const BodyTag body2)
|
|
{
|
|
auto& rep1 = this->m_allVisible[body1];
|
|
auto& rep2 = this->m_allVisible[body2];
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
|
|
if (rep1.isEmpty() || rep2.isEmpty()) {
|
|
result = algoim::organizer::VisiblePrimitiveRep{};
|
|
} else {
|
|
result = this->intersectNode(rep1, rep2, false);
|
|
}
|
|
|
|
this->m_allVisible[body1] = result;
|
|
}
|
|
|
|
/**
|
|
* @brief Difference two visible primitive node, result will be writen to first node
|
|
* @param[in] body1 The first index of primitive node
|
|
* @param[in] body2 The second index of primitive node
|
|
*/
|
|
void differentNode(const BodyTag body1, const BodyTag body2)
|
|
{
|
|
auto& rep1 = this->m_allVisible[body1];
|
|
auto& rep2 = this->m_allVisible[body2];
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
|
|
if (rep1.isEmpty() || rep2.isEmpty()) {
|
|
result = rep1;
|
|
} else {
|
|
result = this->differentNode(rep1, rep2, false);
|
|
}
|
|
|
|
this->m_allVisible[body1] = result;
|
|
}
|
|
|
|
/**
|
|
* @brief Offset a body
|
|
* @param[in] body The index of primitive node
|
|
* @param[in] directrion The offset direction
|
|
* @param[in] length The length of offset
|
|
*/
|
|
void offset(const BodyTag body, const Direction3D& directrion, const double length)
|
|
{
|
|
algoim::uvector<algoim::real, 3> scale = 1;
|
|
algoim::uvector<algoim::real, 3> bias = -directrion.getUVector3Data();
|
|
|
|
auto& rep = this->m_allVisible[body];
|
|
|
|
for (auto& iter : rep.tensors) { algoim::organizer::detail::powerTransformation(scale, bias, iter); }
|
|
|
|
rep.aabb += directrion.getUVector3Data();
|
|
for (auto& aabb : rep.aabbs) { aabb += directrion.getUVector3Data(); }
|
|
}
|
|
|
|
/**
|
|
* @brief Split a body
|
|
* @param[in] body The index of primitive node
|
|
* @param[in] basePoint The base point of the split face
|
|
* @param[in] normal The normal of the split face
|
|
*/
|
|
void split(const BodyTag body, const Point3D& basePoint, const Direction3D& normal)
|
|
{
|
|
auto& rep = this->m_allVisible[body];
|
|
auto halfPlane = this->createHalfPlane(basePoint, -normal);
|
|
auto result = this->intersectNode(rep, halfPlane, false);
|
|
this->m_allVisible[body] = result;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a extrude body to csg tree
|
|
* @param[in] points All the bottom point which define the base face
|
|
* @param[in] bulges All the bulge on each edge of the base face
|
|
* @param[in] extusion The stretch direction and length
|
|
* @return The index of the added extrude body
|
|
*/
|
|
BodyTag addExtrude(const std::vector<Point3D>& points, const std::vector<double>& bulges, const Vector3D& extusion)
|
|
{
|
|
int pointNumber = points.size();
|
|
assert(pointNumber >= 2);
|
|
if (pointNumber == 2) {
|
|
return addExtrudeWithTwoPoint(points, bulges, extusion);
|
|
} else {
|
|
// return addEmpty();
|
|
}
|
|
|
|
/* Get base polygonal column */
|
|
auto base = createPolygonalColumnWithoutTopBottom(points, extusion);
|
|
|
|
auto normal = Direction3D(extusion);
|
|
for (int i = 0; i < points.size(); i++) {
|
|
/* Get point and bulge data */
|
|
auto bulge = bulges[i];
|
|
if (std::abs(bulge) < 1e-8) { continue; }
|
|
|
|
auto& point1 = points[i];
|
|
Point3D point2;
|
|
if (i + 1 == points.size()) {
|
|
point2 = points[0];
|
|
} else {
|
|
point2 = points[i + 1];
|
|
}
|
|
|
|
/* Compute the origion and radius */
|
|
auto halfDistance = point1.getDistance(point2) / 2.0;
|
|
auto middlePoint = point1.getMiddlePoint(point2);
|
|
auto middleToOrigion = normal.cross(Direction3D(point2 - point1));
|
|
|
|
double sinHalfTheta = 2 * bulge / (1 + bulge * bulge);
|
|
double radius = halfDistance / sinHalfTheta;
|
|
double scalar = std::sqrt(radius * radius - halfDistance * halfDistance);
|
|
|
|
/* Determine whether to merge or subtract */
|
|
/* The operation is merge if flag is true, otherwise it is subtract */
|
|
bool flag;
|
|
/* */
|
|
auto centroidPoint = computePolygonCentroid(points);
|
|
auto middleToCentroid = Direction3D(centroidPoint - middlePoint);
|
|
/* out */
|
|
if (middleToCentroid.dot(middleToOrigion) > 0.0) {
|
|
/* |bulge| > 1 */
|
|
if (std::abs(bulge) > 1.0 + 1e-8) { scalar *= -1; }
|
|
|
|
flag = true;
|
|
}
|
|
/* in */
|
|
else {
|
|
/* |bulge| > 1 */
|
|
if (std::abs(bulge) > 1.0 + 1e-8) { scalar *= -1; }
|
|
|
|
flag = false;
|
|
}
|
|
|
|
Point3D origion = middlePoint + middleToOrigion * scalar;
|
|
|
|
/* Determine which axis is aligned */
|
|
int alignAxis;
|
|
if (normal.isParallel(Direction3D(1, 0, 0))) {
|
|
alignAxis = 0;
|
|
} else if (normal.isParallel(Direction3D(0, 1, 0))) {
|
|
alignAxis = 1;
|
|
} else if (normal.isParallel(Direction3D(0, 0, 1))) {
|
|
alignAxis = 2;
|
|
} else {
|
|
throw std::runtime_error("Non align axis cylinder.");
|
|
}
|
|
|
|
/* Create the cylinder face */
|
|
auto cylinder = createCylinderWithoutTopBottom(origion, radius, extusion.length(), alignAxis);
|
|
|
|
/* Perform union and difference operations on the basic prismatic faces */
|
|
if (flag) {
|
|
/* Union */
|
|
/* cylinder - half plane */
|
|
auto halfPlane = createHalfPlane(middlePoint, middleToOrigion);
|
|
auto subtraction = this->intersectNode(cylinder, halfPlane);
|
|
|
|
/* base + (cylinder - column) */
|
|
base = this->unionNode(base, subtraction);
|
|
} else {
|
|
/* difference */
|
|
/* base - cylinder */
|
|
base = this->differentNode(base, cylinder);
|
|
}
|
|
}
|
|
|
|
auto halfPlane1 = createHalfPlane(points[0], -normal);
|
|
auto halfPlane2 = createHalfPlane(points[0] + extusion, normal);
|
|
base = this->intersectNode(base, halfPlane1);
|
|
base = this->intersectNode(base, halfPlane2);
|
|
// gjj
|
|
for (size_t i = 0; i < base.aabbs.size(); i++) { base.aabbs[i] = base.aabb; }
|
|
|
|
this->m_allVisible.push_back(base);
|
|
// auto result = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result.first);
|
|
// this->m_allVolume.push_back(result.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a cone body to csg tree
|
|
* @param[in] topPoint The top point of the cone
|
|
* @param[in] bottomPoint The bottom point of the cone
|
|
* @param[in] radius1 The radius of the top face
|
|
* @param[in] radius2 The radius of the bottom face
|
|
* @return The index of the added cone body
|
|
*/
|
|
BodyTag addCone(const Point3D& topPoint, const Point3D& bottomPoint, const double radius1, const double radius2)
|
|
{
|
|
// gjj
|
|
// auto bottomToTop = topPoint - bottomPoint;
|
|
// auto normal = Direction3D{bottomToTop};
|
|
|
|
/* Determine which axis is aligned */
|
|
// int alignAxis;
|
|
// if (normal.isParallel(Direction3D(1, 0, 0))) {
|
|
// alignAxis = 0;
|
|
// } else if (normal.isParallel(Direction3D(0, 1, 0))) {
|
|
// alignAxis = 1;
|
|
// } else if (normal.isParallel(Direction3D(0, 0, 1))) {
|
|
// alignAxis = 2;
|
|
// } else {
|
|
// throw std::runtime_error("Non align axis cone.");
|
|
// }
|
|
|
|
// auto coneDesc = algoim::organizer::ConeDesc{bottomPoint.getUVector3Data(), radius1, radius2, alignAxis};
|
|
auto coneDesc =
|
|
algoim::organizer::ConeDesc{bottomPoint.getUVector3Data(), topPoint.getUVector3Data(), radius2, radius1};
|
|
algoim::organizer::VisiblePrimitiveRep cone;
|
|
cone.tensors.resize(3, algoim::tensor3(nullptr, 3));
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, cone.tensors);
|
|
algoim::organizer::makeCone(coneDesc, cone);
|
|
|
|
for (auto& pointer : temp) { this->m_allPointer.push_back(pointer); }
|
|
|
|
for (int i = 0; i < 3; i++) { cone.aabbs.push_back(cone.aabb); }
|
|
|
|
this->m_allVisible.push_back(cone);
|
|
// auto result = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result.first);
|
|
// this->m_allVolume.push_back(result.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a box body to csg tree
|
|
* @param[in] leftBottomPoint The left bottom point of the box
|
|
* @param[in] length The length of the box
|
|
* @param[in] width The width of the box
|
|
* @param[in] height The height of the box
|
|
* @return The index of the added box body
|
|
*/
|
|
BodyTag addBox(const Point3D& leftBottomPoint, const double length, const double width, const double height)
|
|
{
|
|
auto size = Vector3D{length, width, height};
|
|
auto boxDesc =
|
|
algoim::organizer::CuboidDesc{(leftBottomPoint + (size / 2.0)).getUVector3Data(), size.getUVector3Data()};
|
|
algoim::organizer::VisiblePrimitiveRep box;
|
|
box.tensors.resize(6, algoim::tensor3(nullptr, 3));
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, box.tensors);
|
|
algoim::organizer::makeMesh(boxDesc, box);
|
|
|
|
for (auto& pointer : temp) { this->m_allPointer.push_back(pointer); }
|
|
|
|
for (int i = 0; i < 6; i++) { box.aabbs.push_back(box.aabb); }
|
|
|
|
this->m_allVisible.push_back(box);
|
|
// auto result = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result.first);
|
|
// this->m_allVolume.push_back(result.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a cylinder body to csg tree
|
|
* @param[in] bottomOrigion The bottom origion point of the cylinder
|
|
* @param[in] radius The radius of the cylinder
|
|
* @param[in] offset The direction and length of the cylinder
|
|
* @return The index of the added cylinder body
|
|
*/
|
|
BodyTag addCylinder(const Point3D& bottomOrigion, const double radius, const Vector3D& offset)
|
|
{
|
|
auto normal = Direction3D{offset};
|
|
/* Determine which axis is aligned */
|
|
int alignAxis;
|
|
if (normal.isParallel(Direction3D(1, 0, 0))) {
|
|
alignAxis = 0;
|
|
} else if (normal.isParallel(Direction3D(0, 1, 0))) {
|
|
alignAxis = 1;
|
|
} else if (normal.isParallel(Direction3D(0, 0, 1))) {
|
|
alignAxis = 2;
|
|
} else {
|
|
throw std::runtime_error("Non align axis cylinder.");
|
|
}
|
|
|
|
algoim::uvector3 ext = 3;
|
|
ext(alignAxis) = 1;
|
|
|
|
algoim::organizer::VisiblePrimitiveRep cylinder;
|
|
cylinder.tensors.resize(3, algoim::tensor3(nullptr, 3));
|
|
cylinder.tensors[0].ext_ = ext;
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, cylinder.tensors);
|
|
algoim::organizer::CylinderDesc cylinderDesc(bottomOrigion.getUVector3Data(), radius, offset.length(), alignAxis);
|
|
algoim::organizer::makeCylinder(cylinderDesc, cylinder);
|
|
|
|
for (auto& pointer : temp) { this->m_allPointer.push_back(pointer); }
|
|
|
|
for (int i = 0; i < 3; i++) { cylinder.aabbs.push_back(cylinder.aabb); }
|
|
|
|
this->m_allVisible.push_back(cylinder);
|
|
// auto result = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result.first);
|
|
// this->m_allVolume.push_back(result.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a cylinder mesh to csg tree
|
|
* @param[in] points The points of the mesh
|
|
* @param[in] index The indexs of the mesh
|
|
* @param[in] faces The faces of the mesh
|
|
* @return The index of the added mesh body
|
|
*/
|
|
BodyTag addMesh(const std::vector<Point3D> points,
|
|
const std::vector<int> index,
|
|
const std::vector<std::pair<int, int>> faces)
|
|
{
|
|
std::vector<algoim::uvector3> vertices;
|
|
for (auto& point : points) { vertices.push_back(point.getUVector3Data()); }
|
|
|
|
std::vector<algoim::uvector2i> tempFaces;
|
|
for (auto& face : faces) { tempFaces.push_back(algoim::uvector2i(face.first, face.second)); }
|
|
|
|
auto meshDesc = algoim::organizer::MeshDesc{vertices, index, tempFaces};
|
|
algoim::organizer::VisiblePrimitiveRep mesh;
|
|
mesh.tensors.resize(faces.size(), algoim::tensor3(nullptr, 3));
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, mesh.tensors);
|
|
algoim::organizer::makeMesh(meshDesc, mesh);
|
|
|
|
for (auto& pointer : temp) { this->m_allPointer.push_back(pointer); }
|
|
|
|
for (size_t i = 0; i < faces.size(); i++) { mesh.aabbs.push_back(mesh.aabb); }
|
|
|
|
this->m_allVisible.push_back(mesh);
|
|
// auto result = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result.first);
|
|
// this->m_allVolume.push_back(result.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Add an empty body to csg tree
|
|
* @return The index of the added empty body
|
|
*/
|
|
BodyTag addEmpty()
|
|
{
|
|
algoim::organizer::VisiblePrimitiveRep empty;
|
|
this->m_allVisible.push_back(empty);
|
|
|
|
this->m_allArea.push_back(0);
|
|
this->m_allVolume.push_back(0);
|
|
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the area and volume of the body
|
|
* @param[in] body The index of primitive node
|
|
* @return [area, volume] The area and volume of the given body
|
|
*/
|
|
std::pair<double, double> getAreaAndVolume(const BodyTag& tag)
|
|
{
|
|
Timer timer("Building scene octree");
|
|
auto& rep = this->m_allVisible[tag];
|
|
assert(rep.tensors.size() == rep.aabbs.size());
|
|
|
|
std::vector<algoim::tensor3> tensorsCopy(rep.tensors.size(), algoim::tensor3(nullptr, 3));
|
|
for (size_t i = 0; i < rep.tensors.size(); i++) { tensorsCopy[i].ext_ = rep.tensors[i].ext_; }
|
|
algoim::algoim_spark_alloc(algoim::real, tensorsCopy);
|
|
for (size_t i = 0; i < rep.tensors.size(); i++) { tensorsCopy[i] = rep.tensors[i]; }
|
|
|
|
std::vector<algoim::organizer::MinimalPrimitiveRep> minimalReps;
|
|
algoim::uvector3 range = rep.aabb.max - rep.aabb.min;
|
|
for (size_t i = 0; i < rep.tensors.size(); i++) {
|
|
auto& tenser = tensorsCopy[i];
|
|
|
|
algoim::organizer::detail::powerTransformation(range, rep.aabb.min, tenser);
|
|
algoim::organizer::detail::power2BernsteinTensor(tenser);
|
|
|
|
algoim::organizer::MinimalPrimitiveRep minimalRep{tenser, rep.aabbs[i]};
|
|
minimalReps.push_back(minimalRep);
|
|
}
|
|
|
|
auto size = rep.aabb.size();
|
|
algoim::organizer::Scene scene{minimalReps,
|
|
algoim::organizer::AABB(rep.aabb.min - size * 1e-5, rep.aabb.max + size * 1e-5)};
|
|
algoim::organizer::OcTreeNode rootNode(0, 1, rep.subBlobTree);
|
|
for (int i = 0; i < minimalReps.size(); ++i) { rootNode.polyIntersectIndices.emplace_back(i); }
|
|
int cnt = 1;
|
|
std::vector<algoim::organizer::OcTreeNode> leaves;
|
|
algoim::organizer::buildOcTreeV0(scene, rootNode, leaves, 1, cnt, 0, -1);
|
|
std::cout << "octree built over" << std::endl;
|
|
timer.stop();
|
|
// basicTask(scene, leaves[14], q);
|
|
|
|
int q = 10;
|
|
double volume = 0;
|
|
double area = 0;
|
|
// #pragma omp parallel for reduction(+ : volume, area)
|
|
timer.rename("Quadrature");
|
|
std::vector<double> volumes(leaves.size(), 0);
|
|
#pragma omp parallel for
|
|
for (int i = 0; i < leaves.size(); i++) {
|
|
const auto& leaf = leaves[i];
|
|
auto basicRes = algoim::organizer::basicTask(scene, leaf, q);
|
|
if (std::isinf(basicRes.volume)) { std::cout << "inf volume when solving leaf: " << i << std::endl; }
|
|
// volume += basicRes.volume;
|
|
volumes[i] = basicRes.volume;
|
|
std::cout << "Solved leaves: " << i + 1 << "/" << leaves.size()
|
|
<< ", primitive cnt: " << leaf.polyIntersectIndices.size() << ", leaf volume: " << basicRes.volume
|
|
<< std::endl;
|
|
}
|
|
|
|
for (auto& v : volumes) { volume += v; }
|
|
volume *= algoim::prod(rep.aabb.max - rep.aabb.min);
|
|
std::cout << "Volume xxx: " << volume << std::endl;
|
|
timer.stop();
|
|
return std::make_pair(area, volume);
|
|
}
|
|
|
|
void output()
|
|
{
|
|
for (auto& volume : this->m_allVolume) { std::cout << volume << ","; }
|
|
std::cout << std::endl;
|
|
|
|
for (auto& area : this->m_allArea) { std::cout << area << ","; }
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
algoim::organizer::VisiblePrimitiveRep& getVisible(BodyTag tag) { return this->m_allVisible[tag]; }
|
|
|
|
protected:
|
|
/**
|
|
* @brief Compute the barycentric coordinates of polygon
|
|
* @param[in] points All points which define the polygon
|
|
* @return The barycentric coordinates
|
|
*/
|
|
Point3D computePolygonCentroid(const std::vector<Point3D>& points) const
|
|
{
|
|
double centroidX = 0, centroidY = 0, centroidZ = 0;
|
|
for (const auto& point : points) {
|
|
centroidX += point.m_x;
|
|
centroidY += point.m_y;
|
|
centroidZ += point.m_z;
|
|
}
|
|
int n = points.size();
|
|
return Point3D(centroidX / n, centroidY / n, centroidZ / n);
|
|
}
|
|
|
|
/**
|
|
* @brief Create an empty blob tree
|
|
* @return The created empty blob tree
|
|
*/
|
|
algoim::organizer::BlobTree createEmptyBlobTree()
|
|
{
|
|
algoim::organizer::BlobTree tree;
|
|
|
|
algoim::organizer::Blob blob0;
|
|
blob0.isPrimitive = 1;
|
|
blob0.nodeOp = 0;
|
|
blob0.inOut = 0;
|
|
blob0.oneChildInOut = 0;
|
|
blob0.isLeft = 1;
|
|
blob0.ancestor = 2;
|
|
tree.structure.push_back(blob0);
|
|
|
|
algoim::organizer::Blob blob1;
|
|
blob1.isPrimitive = 1;
|
|
blob1.nodeOp = 0;
|
|
blob1.inOut = 0;
|
|
blob1.oneChildInOut = 0;
|
|
blob1.isLeft = 0;
|
|
blob1.ancestor = 0;
|
|
tree.structure.push_back(blob1);
|
|
|
|
algoim::organizer::Blob blob2;
|
|
blob2.isPrimitive = 0;
|
|
blob2.nodeOp = 3; // no set
|
|
blob2.inOut = 0;
|
|
blob2.oneChildInOut = 0;
|
|
blob2.isLeft = 0;
|
|
blob2.ancestor = 0;
|
|
tree.structure.push_back(blob2);
|
|
|
|
tree.primitiveNodeIdx.push_back(0);
|
|
tree.primitiveNodeIdx.push_back(1);
|
|
|
|
return tree;
|
|
}
|
|
|
|
/**
|
|
* @brief Union two visible primitive node
|
|
* @param[in] rep1 The first visible primitive node
|
|
* @param[in] rep2 The second visible primitive node
|
|
* @param[in] modify Whether the aabb of primitive node is need to modify
|
|
* @return The unioned visible primitive
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep unionNode(const algoim::organizer::VisiblePrimitiveRep& rep1,
|
|
const algoim::organizer::VisiblePrimitiveRep& rep2,
|
|
const bool modify = true)
|
|
{
|
|
auto tree = createEmptyBlobTree();
|
|
tree.structure[2].nodeOp = 0;
|
|
|
|
const std::vector<algoim::organizer::VisiblePrimitiveRep> reps = {rep1, rep2};
|
|
std::vector<algoim::organizer::MinimalPrimitiveRep> minimalReps;
|
|
algoim::organizer::mergeSubtree2Leaf(tree, minimalReps, reps);
|
|
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
result.subBlobTree = tree;
|
|
result.aabb = rep1.aabb;
|
|
result.aabb.extend(rep2.aabb);
|
|
|
|
if (modify) {
|
|
for (auto& iter : minimalReps) {
|
|
result.tensors.push_back(iter.tensor);
|
|
result.aabbs.push_back(iter.aabb);
|
|
}
|
|
} else {
|
|
for (auto& iter : rep1.tensors) { result.tensors.push_back(iter); }
|
|
for (auto& iter : rep2.tensors) { result.tensors.push_back(iter); }
|
|
result.aabbs = rep1.aabbs;
|
|
result.aabbs.insert(result.aabbs.end(), rep2.aabbs.begin(), rep2.aabbs.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Intersect two visible primitive node
|
|
* @param[in] rep1 The first visible primitive node
|
|
* @param[in] rep2 The second visible primitive node
|
|
* @param[in] modify Whether the aabb of primitive node is need to modify
|
|
* @return The intersected visible primitive
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep intersectNode(const algoim::organizer::VisiblePrimitiveRep& rep1,
|
|
const algoim::organizer::VisiblePrimitiveRep& rep2,
|
|
const bool modify = true)
|
|
{
|
|
auto tree = createEmptyBlobTree();
|
|
tree.structure[2].nodeOp = 1;
|
|
|
|
const std::vector<algoim::organizer::VisiblePrimitiveRep> reps = {rep1, rep2};
|
|
std::vector<algoim::organizer::MinimalPrimitiveRep> minimalReps;
|
|
algoim::organizer::mergeSubtree2Leaf(tree, minimalReps, reps);
|
|
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
result.subBlobTree = tree;
|
|
result.aabb = rep1.aabb;
|
|
result.aabb.intersect(rep2.aabb);
|
|
|
|
if (modify) {
|
|
for (auto& iter : minimalReps) {
|
|
result.tensors.push_back(iter.tensor);
|
|
result.aabbs.push_back(iter.aabb);
|
|
}
|
|
} else {
|
|
for (auto& iter : rep1.tensors) { result.tensors.push_back(iter); }
|
|
for (auto& iter : rep2.tensors) { result.tensors.push_back(iter); }
|
|
result.aabbs = rep1.aabbs;
|
|
result.aabbs.insert(result.aabbs.end(), rep2.aabbs.begin(), rep2.aabbs.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Difference two visible primitive node
|
|
* @param[in] rep1 The first visible primitive node
|
|
* @param[in] rep2 The second visible primitive node
|
|
* @param[in] modify Whether the aabb of primitive node is need to modify
|
|
* @return The differenced visible primitive
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep differentNode(const algoim::organizer::VisiblePrimitiveRep& rep1,
|
|
const algoim::organizer::VisiblePrimitiveRep& rep2,
|
|
const bool modify = true)
|
|
{
|
|
auto tree = createEmptyBlobTree();
|
|
tree.structure[2].nodeOp = 2;
|
|
|
|
const std::vector<algoim::organizer::VisiblePrimitiveRep> reps = {rep1, rep2};
|
|
std::vector<algoim::organizer::MinimalPrimitiveRep> minimalReps;
|
|
algoim::organizer::mergeSubtree2Leaf(tree, minimalReps, reps);
|
|
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
result.subBlobTree = tree;
|
|
result.aabb = rep1.aabb;
|
|
|
|
if (modify) {
|
|
for (auto& iter : minimalReps) {
|
|
result.tensors.push_back(iter.tensor);
|
|
result.aabbs.push_back(iter.aabb);
|
|
}
|
|
} else {
|
|
for (auto& iter : rep1.tensors) { result.tensors.push_back(iter); }
|
|
for (auto& iter : rep2.tensors) { result.tensors.push_back(iter); }
|
|
result.aabbs = rep1.aabbs;
|
|
result.aabbs.insert(result.aabbs.end(), rep2.aabbs.begin(), rep2.aabbs.end());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a polygonal column without top face and bottom face
|
|
* @param[in] points All the bottom point with counter clockwise
|
|
* @param[in] extusion The stretch direction
|
|
* @return The polygonal column
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep createPolygonalColumnWithoutTopBottom(const std::vector<Point3D>& points,
|
|
const Vector3D& extusion)
|
|
{
|
|
int pointNumber = points.size();
|
|
|
|
std::vector<algoim::uvector3> vertices;
|
|
std::vector<int> indices;
|
|
std::vector<int> indexInclusiveScan;
|
|
|
|
/* All bottom point */
|
|
for (int i = 0; i < pointNumber; i++) { vertices.push_back(points[i].getUVector3Data()); }
|
|
/* All top point */
|
|
for (int i = 0; i < pointNumber; i++) { vertices.push_back((points[i] + extusion).getUVector3Data()); }
|
|
|
|
/* Side face */
|
|
int index = 0;
|
|
for (int i = 0; i < pointNumber; i++) {
|
|
indices.push_back(i);
|
|
indices.push_back((i + 1) % pointNumber);
|
|
indices.push_back((i + 1) % pointNumber + pointNumber);
|
|
indices.push_back(i + pointNumber);
|
|
|
|
index += 4;
|
|
indexInclusiveScan.push_back(index);
|
|
}
|
|
|
|
algoim::organizer::MeshDesc polygonalColumn(vertices, indices, indexInclusiveScan);
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
result.tensors.resize(pointNumber, algoim::tensor3(nullptr, 3));
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, result.tensors);
|
|
algoim::organizer::makeMesh(polygonalColumn, result);
|
|
|
|
for (auto& pointer : temp) { this->m_allPointer.push_back(pointer); }
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a cylinder column without top face and bottom face
|
|
* @param[in] origion The origion point of bottom circle of the cylinder
|
|
* @param[in] radius The radius of the cylinder
|
|
* @param[in] length The length of the cylinder
|
|
* @param[in] alignAxis The align axis of the cylinder
|
|
* @return The cylinder column
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep createCylinderWithoutTopBottom(const Point3D& origion,
|
|
const double radius,
|
|
const double length,
|
|
const int alignAxis)
|
|
{
|
|
algoim::uvector3 ext = 3;
|
|
ext(alignAxis) = 1;
|
|
|
|
algoim::organizer::VisiblePrimitiveRep result;
|
|
result.tensors.resize(1, algoim::tensor3(nullptr, ext));
|
|
std::vector<algoim::SparkStack<algoim::real>*> resultTemp;
|
|
algoim::algoimSparkAllocHeapVector(resultTemp, result.tensors);
|
|
this->m_allPointer.push_back(resultTemp[0]);
|
|
|
|
algoim::organizer::CylinderDesc cylinderDesc(origion.getUVector3Data(), radius, length, alignAxis);
|
|
algoim::organizer::VisiblePrimitiveRep cylinder;
|
|
cylinder.tensors.resize(3, algoim::tensor3(nullptr, 3));
|
|
cylinder.tensors[0].ext_ = ext;
|
|
algoim::algoim_spark_alloc(algoim::real, cylinder.tensors);
|
|
algoim::organizer::makeCylinder(cylinderDesc, cylinder);
|
|
|
|
result.tensors[0] = cylinder.tensors[0];
|
|
result.aabb = cylinder.aabb;
|
|
result.subBlobTree.primitiveNodeIdx.push_back(0);
|
|
result.subBlobTree.structure.push_back(algoim::organizer::Blob{1, 0, 0, 0, 0, 0});
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Create a half plane
|
|
* @param[in] basePoint The base point of the plane
|
|
* @param[in] normal The normal of the plane
|
|
* @return The half plane
|
|
*/
|
|
algoim::organizer::VisiblePrimitiveRep createHalfPlane(const Point3D& basePoint, const Direction3D& normal)
|
|
{
|
|
auto halfPlaneDesc = algoim::organizer::HalfPlaneDesc(basePoint.getUVector3Data(), normal.getUVector3Data());
|
|
algoim::organizer::VisiblePrimitiveRep halfPlane;
|
|
halfPlane.tensors.resize(1, algoim::tensor3(nullptr, 3));
|
|
std::vector<algoim::SparkStack<algoim::real>*> temp;
|
|
algoim::algoimSparkAllocHeapVector(temp, halfPlane.tensors);
|
|
algoim::organizer::makeHalfPlane(halfPlaneDesc, halfPlane);
|
|
this->m_allPointer.push_back(temp[0]);
|
|
halfPlane.aabbs.push_back(halfPlane.aabb);
|
|
return halfPlane;
|
|
}
|
|
|
|
/**
|
|
* @brief Add a extrude body to csg tree with only two points
|
|
* @param[in] points All the bottom point which define the base face
|
|
* @param[in] bulges All the bulge on each edge of the base face
|
|
* @param[in] extusion The stretch direction and length
|
|
* @return The index of the added extrude body
|
|
*/
|
|
BodyTag addExtrudeWithTwoPoint(const std::vector<Point3D>& points,
|
|
const std::vector<double>& bulges,
|
|
const Vector3D& extusion)
|
|
{
|
|
auto normal = Direction3D(extusion);
|
|
auto& point1 = points[0];
|
|
auto& point2 = points[1];
|
|
auto bulge1 = bulges[0];
|
|
auto bulge2 = bulges[1];
|
|
|
|
algoim::organizer::VisiblePrimitiveRep rep1, rep2, result;
|
|
|
|
auto halfDistance = point1.getDistance(point2) / 2.0;
|
|
auto middlePoint = point1.getMiddlePoint(point2);
|
|
|
|
auto middleToOrigion1 = normal.cross(Direction3D(point2 - point1));
|
|
auto middleToOrigion2 = normal.cross(Direction3D(point1 - point2));
|
|
|
|
/* Determine which axis is aligned */
|
|
int alignAxis;
|
|
if (normal.isParallel(Direction3D(1, 0, 0))) {
|
|
alignAxis = 0;
|
|
} else if (normal.isParallel(Direction3D(0, 1, 0))) {
|
|
alignAxis = 1;
|
|
} else if (normal.isParallel(Direction3D(0, 0, 1))) {
|
|
alignAxis = 2;
|
|
} else {
|
|
throw std::runtime_error("Non align axis cylinder.");
|
|
}
|
|
|
|
auto getPrimitive = [this, normal, halfDistance, middlePoint, extusion, alignAxis](
|
|
const Point3D& point1, const Point3D& point2, const double bulge) {
|
|
auto middleToOrigion = normal.cross(Direction3D(point2 - point1));
|
|
double sinHalfTheta = 2 * bulge / (1 + bulge * bulge);
|
|
double radius = halfDistance / sinHalfTheta;
|
|
double scalar = std::sqrt(radius * radius - halfDistance * halfDistance);
|
|
// GJJ
|
|
if (std::abs(bulge) > 1.0) { scalar *= -1; }
|
|
|
|
auto origion = middlePoint + middleToOrigion * scalar;
|
|
|
|
/* Create the cylinder face */
|
|
return this->createCylinderWithoutTopBottom(origion, radius, extusion.length(), alignAxis);
|
|
};
|
|
|
|
if (std::abs(bulge1) <= 1e-8) {
|
|
assert(std::abs(bulge2) > 1e-8);
|
|
// GJJ
|
|
rep1 = this->createHalfPlane(point1, Direction3D{middleToOrigion2});
|
|
// rep1 = this->createHalfPlane(point1, -Direction3D{middleToOrigion2});
|
|
rep2 = getPrimitive(point2, point1, bulge2);
|
|
result = this->intersectNode(rep1, rep2);
|
|
} else if (std::abs(bulge2) <= 1e-8) {
|
|
assert(std::abs(bulge1) > 1e-8);
|
|
// GJJ
|
|
rep1 = getPrimitive(point1, point2, bulge1);
|
|
rep2 = this->createHalfPlane(point2, Direction3D{middleToOrigion1});
|
|
// rep2 = this->createHalfPlane(point2, -Direction3D{middleToOrigion1});
|
|
result = this->intersectNode(rep1, rep2);
|
|
} else {
|
|
rep1 = getPrimitive(point1, point2, bulge1);
|
|
rep2 = getPrimitive(point2, point1, bulge2);
|
|
|
|
/* if the bulge == 1 and bulge == 1, it is a cylinder */
|
|
if (std::abs(bulge1 - 1.0) < 1e-8 && std::abs(bulge2 - 1.0) < 1e-8) {
|
|
result = getPrimitive(point1, point2, bulge1);
|
|
}
|
|
/* if the bulge == -1 and bulge == -1, it is a cylinder */
|
|
else if (std::abs(bulge1 + 1.0) < 1e-8 && std::abs(bulge2 + 1.0) < 1e-8) {
|
|
result = getPrimitive(point1, point2, bulge1);
|
|
}
|
|
/* if the bulge1 and bulge2 has the same symbol, it is merge */
|
|
else if (bulge1 * bulge2 > 0.0) {
|
|
result = this->intersectNode(rep1, rep2);
|
|
// GJJ
|
|
// } else if (bulge1 > 0.0) {
|
|
} else if (std::abs(bulge1) > std::abs(bulge2)) {
|
|
result = this->differentNode(rep1, rep2);
|
|
} else {
|
|
result = this->differentNode(rep2, rep1);
|
|
}
|
|
}
|
|
|
|
auto halfPlane1 = createHalfPlane(points[0], -normal);
|
|
auto halfPlane2 = createHalfPlane(points[0] + extusion, normal);
|
|
result = this->intersectNode(result, halfPlane1);
|
|
result = this->intersectNode(result, halfPlane2);
|
|
|
|
this->m_allVisible.push_back(result);
|
|
// auto result1 = this->getAreaAndVolume(this->m_allVisible.size() - 1);
|
|
// this->m_allArea.push_back(result1.first);
|
|
// this->m_allVolume.push_back(result1.second);
|
|
return this->m_allVisible.size() - 1;
|
|
}
|
|
|
|
private:
|
|
std::vector<algoim::organizer::VisiblePrimitiveRep> m_allVisible;
|
|
std::vector<algoim::SparkStack<algoim::real>*> m_allPointer;
|
|
std::vector<double> m_allVolume;
|
|
std::vector<double> m_allArea;
|
|
};
|
|
|