Skip to content

Commit

Permalink
Add a Sphere (3D) Class
Browse files Browse the repository at this point in the history
  • Loading branch information
lbartoletti committed Sep 13, 2024
1 parent 2711472 commit 91170f5
Show file tree
Hide file tree
Showing 3 changed files with 497 additions and 0 deletions.
209 changes: 209 additions & 0 deletions src/Sphere.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright (c) 2024-2024, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later

#include "SFCGAL/Sphere.h"
#include <CGAL/Polyhedron_incremental_builder_3.h>
#include <cmath>

namespace SFCGAL {

using Point_3 = Kernel::Point_3;
using Polyhedron_3 = CGAL::Polyhedron_3<Kernel>;

// Helper class for building the sphere polyhedron
template <class HDS>
class Sphere_builder : public CGAL::Modifier_base<HDS> {
public:
Sphere_builder(double radius, int num_vertical, int num_horizontal,
Point_3 center = Point_3(0, 0, 0))
: radius(radius), num_vertical(num_vertical),
num_horizontal(num_horizontal), center(center)
{
}

void
operator()(HDS &hds)
{
CGAL::Polyhedron_incremental_builder_3<HDS> B(hds, true);

int num_vertices = (num_vertical - 1) * num_horizontal + 2;
int num_faces = num_vertical * num_horizontal * 2;

B.begin_surface(num_vertices, num_faces);

// Add vertices
addVertices(B);

// Add faces
addTopFaces(B);
addMiddleFaces(B);
addBottomFaces(B);

B.end_surface();
}

private:
// Add all vertices of the sphere
void
addVertices(CGAL::Polyhedron_incremental_builder_3<HDS> &B)
{
// Add top vertex
B.add_vertex(Point_3(center.x(), center.y(), center.z() + radius));

// Add middle vertices
for (int i = 1; i < num_vertical; ++i) {
double phi = M_PI * double(i) / double(num_vertical);
double z = radius * std::cos(phi);
double r = radius * std::sin(phi);
for (int j = 0; j < num_horizontal; ++j) {
double theta = 2 * M_PI * double(j) / double(num_horizontal);
double x = r * std::cos(theta);
double y = r * std::sin(theta);
B.add_vertex(Point_3(center.x() + x, center.y() + y, center.z() + z));
}
}

// Add bottom vertex
B.add_vertex(Point_3(center.x(), center.y(), center.z() - radius));
}

// Add faces connecting the top vertex to the first ring of vertices
void
addTopFaces(CGAL::Polyhedron_incremental_builder_3<HDS> &B)
{
for (int j = 0; j < num_horizontal; ++j) {
B.begin_facet();
B.add_vertex_to_facet(0);
B.add_vertex_to_facet(1 + (j + 1) % num_horizontal);
B.add_vertex_to_facet(1 + j);
B.end_facet();
}
}

// Add faces for the middle rings of vertices
void
addMiddleFaces(CGAL::Polyhedron_incremental_builder_3<HDS> &B)
{
for (int i = 1; i < num_vertical - 1; ++i) {
for (int j = 0; j < num_horizontal; ++j) {
int current = 1 + (i - 1) * num_horizontal + j;
int next = 1 + (i - 1) * num_horizontal + (j + 1) % num_horizontal;
int below_current = 1 + i * num_horizontal + j;
int below_next = 1 + i * num_horizontal + (j + 1) % num_horizontal;

B.begin_facet();
B.add_vertex_to_facet(current);
B.add_vertex_to_facet(next);
B.add_vertex_to_facet(below_next);
B.end_facet();

B.begin_facet();
B.add_vertex_to_facet(current);
B.add_vertex_to_facet(below_next);
B.add_vertex_to_facet(below_current);
B.end_facet();
}
}
}

// Add faces connecting the bottom vertex to the last ring of vertices
void
addBottomFaces(CGAL::Polyhedron_incremental_builder_3<HDS> &B)
{
int last_vertex = (num_vertical - 1) * num_horizontal + 1;
int last_row = 1 + (num_vertical - 2) * num_horizontal;
for (int j = 0; j < num_horizontal; ++j) {
B.begin_facet();
B.add_vertex_to_facet(last_vertex);
B.add_vertex_to_facet(last_row + j);
B.add_vertex_to_facet(last_row + (j + 1) % num_horizontal);
B.end_facet();
}
}

double radius;
int num_vertical;
int num_horizontal;
Point_3 center;
};

Sphere::Sphere(const Kernel::FT &radius, const Point_3 &center,
int num_vertical, int num_horizontal)
: m_radius(radius), m_center(center), m_num_vertical(num_vertical),
m_num_horizontal(num_horizontal)
{
}

Sphere &
Sphere::operator=(Sphere other)
{
std::swap(m_radius, other.m_radius);
std::swap(m_center, other.m_center);
std::swap(m_num_vertical, other.m_num_vertical);
std::swap(m_num_horizontal, other.m_num_horizontal);
std::swap(m_polyhedron, other.m_polyhedron);
std::swap(m_points, other.m_points);
return *this;
}

void
Sphere::invalidateCache()
{
m_polyhedron.reset();
m_points.reset();
}

// Generate the polyhedron representation of the sphere
Polyhedron_3
Sphere::generateSpherePolyhedron()
{
Polyhedron_3 P;
Sphere_builder<Polyhedron_3::HalfedgeDS> builder(
CGAL::to_double(m_radius), m_num_vertical, m_num_horizontal, m_center);
P.delegate(builder);
return P;
}

Polyhedron_3
Sphere::generatePolyhedron()
{
if (!m_polyhedron) {
m_polyhedron = generateSpherePolyhedron();
}
return *m_polyhedron;
}

std::vector<Point_3>
Sphere::generatePoints()
{
if (!m_points) {
m_points = generateSpherePoints();
}
return *m_points;
}

// Generate points on the sphere's surface
std::vector<Point_3>
Sphere::generateSpherePoints()
{
std::vector<Point_3> points;
points.reserve(m_num_vertical * m_num_horizontal);

Kernel::FT d_lat = CGAL_PI / (m_num_vertical - 1);
Kernel::FT d_lon = 2 * CGAL_PI / m_num_horizontal;

for (int i = 0; i < m_num_vertical; ++i) {
Kernel::FT lat = CGAL_PI / 2 - i * d_lat;
Kernel::FT z = m_radius * std::sin(CGAL::to_double(lat));
Kernel::FT r = m_radius * std::cos(CGAL::to_double(lat));
for (int j = 0; j < m_num_horizontal; ++j) {
Kernel::FT lon = j * d_lon;
points.emplace_back(m_center.x() + r * std::cos(CGAL::to_double(lon)),
m_center.y() + r * std::sin(CGAL::to_double(lon)),
m_center.z() + z);
}
}
return points;
}

} // namespace SFCGAL
Loading

0 comments on commit 91170f5

Please sign in to comment.