Skip to content

Commit

Permalink
Add a Cylinder (3D) Class
Browse files Browse the repository at this point in the history
  • Loading branch information
lbartoletti committed Sep 13, 2024
1 parent b84d6df commit 2711472
Show file tree
Hide file tree
Showing 3 changed files with 452 additions and 0 deletions.
142 changes: 142 additions & 0 deletions src/Cylinder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include "SFCGAL/Cylinder.h"

namespace SFCGAL {

Cylinder::Cylinder(const Point_3 &base_center, const Vector_3 &axis,
const Kernel::FT &radius, const Kernel::FT &height,
int num_radial)
: m_base_center(base_center), m_axis(axis), m_radius(radius),
m_height(height), m_num_radial(num_radial)
{
}

Cylinder &
Cylinder::operator=(Cylinder other)
{
std::swap(m_base_center, other.m_base_center);
std::swap(m_axis, other.m_axis);
std::swap(m_radius, other.m_radius);
std::swap(m_height, other.m_height);
std::swap(m_num_radial, other.m_num_radial);
std::swap(m_polyhedron, other.m_polyhedron);
std::swap(m_surface_mesh, other.m_surface_mesh);
return *this;
}

void
Cylinder::setBaseCenter(const Point_3 &base_center)
{
m_base_center = base_center;
invalidateCache();
}

void
Cylinder::setAxis(const Vector_3 &axis)
{
m_axis = axis;
invalidateCache();
}

void
Cylinder::setRadius(const Kernel::FT &radius)
{
m_radius = radius;
invalidateCache();
}

void
Cylinder::setHeight(const Kernel::FT &height)
{
m_height = height;
invalidateCache();
}

void
Cylinder::setNumRadial(int num)
{
m_num_radial = num;
invalidateCache();
}

void
Cylinder::invalidateCache()
{
m_polyhedron.reset();
m_surface_mesh.reset();
}

Cylinder::Vector_3
Cylinder::normalize(const Vector_3 &v)
{
double length = std::sqrt(CGAL::to_double(v.squared_length()));
if (length < 1e-8)
return v;
return v / length;
}

Cylinder::Polyhedron_3
Cylinder::generatePolyhedron()
{
if (m_polyhedron) {
return *m_polyhedron;
}

Polyhedron_3 poly;
Surface_mesh sm = generateSurfaceMesh();
CGAL::copy_face_graph(sm, poly);
m_polyhedron = poly;
return poly;
}

Cylinder::Surface_mesh
Cylinder::generateSurfaceMesh()
{
if (m_surface_mesh) {
return *m_surface_mesh;
}

Surface_mesh mesh;

Vector_3 normalized_axis = normalize(m_axis);
Vector_3 perpendicular =
normalize(CGAL::cross_product(normalized_axis, Vector_3(0, 0, 1)));
if (perpendicular.squared_length() < 1e-8) {
perpendicular =
normalize(CGAL::cross_product(normalized_axis, Vector_3(0, 1, 0)));
}
Vector_3 perpendicular2 = CGAL::cross_product(normalized_axis, perpendicular);

std::vector<Surface_mesh::Vertex_index> base_vertices, top_vertices;

// Create vertices for the base and top
for (int i = 0; i < m_num_radial; ++i) {
double angle = 2.0 * M_PI * i / m_num_radial;
Vector_3 offset = m_radius * (std::cos(angle) * perpendicular +
std::sin(angle) * perpendicular2);
base_vertices.push_back(mesh.add_vertex(m_base_center + offset));
top_vertices.push_back(
mesh.add_vertex(m_base_center + offset + m_height * normalized_axis));
}

// Add side faces
for (int i = 0; i < m_num_radial; ++i) {
int next = (i + 1) % m_num_radial;
mesh.add_face(base_vertices[i], top_vertices[i], top_vertices[next]);
mesh.add_face(base_vertices[i], top_vertices[next], base_vertices[next]);
}

// Add base and top faces
Surface_mesh::Vertex_index base_center = mesh.add_vertex(m_base_center);
Surface_mesh::Vertex_index top_center =
mesh.add_vertex(m_base_center + m_height * normalized_axis);

for (int i = 0; i < m_num_radial; ++i) {
int next = (i + 1) % m_num_radial;
mesh.add_face(base_center, base_vertices[i], base_vertices[next]);
mesh.add_face(top_center, top_vertices[next], top_vertices[i]);
}
m_surface_mesh = mesh;
return mesh;
}

} // namespace SFCGAL
197 changes: 197 additions & 0 deletions src/Cylinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
// Copyright (c) 2024-2024, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later

#ifndef _SFCGAL_CYLINDER_H_
#define _SFCGAL_CYLINDER_H_

#include "SFCGAL/Kernel.h"
#include "SFCGAL/export.h"
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Surface_mesh.h>
#include <optional>
#include <vector>

namespace SFCGAL {

/**
* @class Cylinder
* @brief Represents a cylinder in 3D space
*
* This class provides methods to generate a polyhedron and a surface mesh
* representation of a cylinder. It uses SFCGAL's Kernel for exact computations.
*/
class SFCGAL_API Cylinder {
public:
using Point_3 = Kernel::Point_3;
using Vector_3 = Kernel::Vector_3;
using Polyhedron_3 = CGAL::Polyhedron_3<Kernel>;
using Surface_mesh = CGAL::Surface_mesh<Point_3>;

/**
* @brief Constructs a Cylinder object
* @param base_center The center point of the base of the cylinder
* @param axis The axis of the cylinder
* @param radius The radius of the cylinder
* @param height The height of the cylinder
* @param num_radial The number of radial divisions
*/
Cylinder(const Point_3 &base_center = Point_3(0, 0, 0),
const Vector_3 &axis = Vector_3(0, 0, 1),
const Kernel::FT &radius = 1.0, const Kernel::FT &height = 1.0,
int num_radial = 32);

/**
* @brief Copy constructor
*/
Cylinder(const Cylinder &other) = default;

/**
* @brief Assignment operator
*/
Cylinder &
operator=(Cylinder other);

/**
* @brief Destructor
*/
~Cylinder() = default;

/**
* @brief Sets the base center of the cylinder
* @param base_center The new base center point
*/
void
setBaseCenter(const Point_3 &base_center);

/**
* @brief Sets the axis of the cylinder
* @param axis The new axis vector
*/
void
setAxis(const Vector_3 &axis);

/**
* @brief Sets the radius of the cylinder
* @param radius The new radius
*/
void
setRadius(const Kernel::FT &radius);

/**
* @brief Sets the height of the cylinder
* @param height The new height
*/
void
setHeight(const Kernel::FT &height);

/**
* @brief Sets the number of radial divisions
* @param num The new number of radial divisions
*/
void
setNumRadial(int num);

/**
* @brief Gets the base center of the cylinder
* @return The base center point
*/
const Point_3 &
baseCenter() const
{
return m_base_center;
}

/**
* @brief Gets the axis of the cylinder
* @return The axis vector
*/
const Vector_3 &
axis() const
{
return m_axis;
}

/**
* @brief Gets the radius of the cylinder
* @return The radius
*/
const Kernel::FT &
radius() const
{
return m_radius;
}

/**
* @brief Gets the height of the cylinder
* @return The height
*/
const Kernel::FT &
height() const
{
return m_height;
}

/**
* @brief Gets the number of radial divisions
* @return The number of radial divisions
*/
int
numRadial() const
{
return m_num_radial;
}

/**
* @brief Generates a polyhedron representation of the cylinder
* @return A CGAL::Polyhedron_3 object representing the cylinder
*/
Polyhedron_3
generatePolyhedron();

/**
* @brief Generates a surface mesh representation of the cylinder
* @return A CGAL::Surface_mesh object representing the cylinder
*/
Surface_mesh
generateSurfaceMesh();

double
volume() const
{
return CGAL::to_double(m_radius * m_radius * m_height * CGAL_PI);
}

double
area() const
{
return CGAL::to_double(2 * m_radius * m_radius * CGAL_PI +
2 * m_radius * m_height * CGAL_PI);
}

private:
Point_3 m_base_center;
Vector_3 m_axis;
Kernel::FT m_radius;
Kernel::FT m_height;
int m_num_radial;
std::optional<Polyhedron_3> m_polyhedron;
std::optional<Surface_mesh> m_surface_mesh;

/**
* @brief Invalidates the cached polyhedron and surface mesh
*/
void
invalidateCache();

/**
* @brief Normalizes a vector
* @param v The vector to normalize
* @return The normalized vector
*/
Vector_3
normalize(const Vector_3 &v);
};

} // namespace SFCGAL

#endif // _SFCGAL_CYLINDER_H_
Loading

0 comments on commit 2711472

Please sign in to comment.