Skip to content

Commit

Permalink
Add CreateFromPoints() into BoundingSphere class
Browse files Browse the repository at this point in the history
  • Loading branch information
mogemimi committed Dec 9, 2020
1 parent 952669e commit 575ab83
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 0 deletions.
7 changes: 7 additions & 0 deletions include/Pomdog/Math/BoundingSphere.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Pomdog/Basic/Export.hpp"
#include "Pomdog/Math/ForwardDeclarations.hpp"
#include "Pomdog/Math/Vector3.hpp"
#include <functional>
#include <optional>

namespace Pomdog {
Expand Down Expand Up @@ -39,6 +40,12 @@ class POMDOG_EXPORT BoundingSphere final {

[[nodiscard]] std::optional<float>
Intersects(const Ray& ray) const;

[[nodiscard]] static BoundingSphere
CreateFromPoints(const Vector3* points, std::size_t pointCount) noexcept;

[[nodiscard]] static BoundingSphere
CreateFromPoints(std::function<Vector3(std::size_t)> points, std::size_t pointCount) noexcept;
};

} // namespace Pomdog
86 changes: 86 additions & 0 deletions src/Math/BoundingSphere.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Pomdog/Math/Plane.hpp"
#include "Pomdog/Math/Ray.hpp"
#include "Pomdog/Utility/Assert.hpp"
#include <cmath>

namespace Pomdog {

Expand Down Expand Up @@ -80,4 +81,89 @@ std::optional<float> BoundingSphere::Intersects(const Ray& ray) const
return ray.Intersects(*this);
}

BoundingSphere
BoundingSphere::CreateFromPoints(const Vector3* points, std::size_t pointCount) noexcept
{
POMDOG_ASSERT(points != nullptr);
POMDOG_ASSERT(pointCount > 0);
auto accessor = [&](std::size_t i) -> Vector3 { return points[i]; };
return CreateFromPoints(std::move(accessor), pointCount);
}

BoundingSphere
BoundingSphere::CreateFromPoints(std::function<Vector3(std::size_t)> points, std::size_t pointCount) noexcept
{
POMDOG_ASSERT(points != nullptr);
POMDOG_ASSERT(pointCount > 0);

// NOTE: Compute bounding sphere using Jack Ritter's algorithm.
std::size_t maxX = 0;
std::size_t maxY = 0;
std::size_t maxZ = 0;
std::size_t minX = 0;
std::size_t minY = 0;
std::size_t minZ = 0;

for (std::size_t i = 0; i < pointCount; i++) {
const auto& p = points(i);
if (p.X < points(minX).X) {
minX = i;
}
if (p.X > points(maxX).X) {
maxX = i;
}
if (p.Y < points(minY).Y) {
minY = i;
}
if (p.Y > points(maxY).Y) {
maxY = i;
}
if (p.Z < points(minZ).Z) {
minZ = i;
}
if (p.Z > points(maxZ).Z) {
maxZ = i;
}
}

const auto distX = Vector3::DistanceSquared(points(maxX), points(minX));
const auto distY = Vector3::DistanceSquared(points(maxY), points(minY));
const auto distZ = Vector3::DistanceSquared(points(maxZ), points(minZ));

std::size_t max = maxX;
std::size_t min = minX;
if (distY > distX && distY > distZ) {
max = maxY;
min = minY;
}
else if (distZ > distX && distZ > distY) {
max = maxZ;
min = minZ;
}

auto center = (points(max) + points(min)) * 0.5f;
auto radius = Vector3::Distance(points(max), center);
auto radiusSq = radius * radius;

// NOTE: Compute strict bounding sphere.
for (std::size_t i = 0; i < pointCount; i++) {
const auto p = points(i);
const auto diff = p - center;
const auto distanceSq = diff.LengthSquared();
if (distanceSq > radiusSq) {
const auto distance = std::sqrt(distanceSq);
const auto direction = diff / distance;
const auto g = center - radius * direction;
center = (g + p) * 0.5f;
radius = Vector3::Distance(p, center);
radiusSq = radius * radius;
}
}

BoundingSphere sphere;
sphere.Center = center;
sphere.Radius = radius;
return sphere;
}

} // namespace Pomdog

0 comments on commit 575ab83

Please sign in to comment.