add cylinder
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
|
||||
set(HEADERS ContainerBox.h
|
||||
Cylinder.h
|
||||
Dense.h
|
||||
Geometry.h
|
||||
Transform.h
|
||||
@@ -70,4 +71,3 @@ if(BUILD_TESTING)
|
||||
include(uLibTargetMacros)
|
||||
add_subdirectory(testing)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -51,10 +51,8 @@ class ContainerBox : public AffineTransform, public Object {
|
||||
public:
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// PROPERTIES //
|
||||
Property<float> Width;
|
||||
Property<float> Height;
|
||||
Property<float> Depth;
|
||||
|
||||
Property<Vector3f> p_Size;
|
||||
Property<Vector3f> p_Origin;
|
||||
virtual const char * GetClassName() const { return "ContainerBox"; }
|
||||
|
||||
/**
|
||||
@@ -63,12 +61,10 @@ public:
|
||||
*/
|
||||
ContainerBox()
|
||||
: m_LocalT(this), // BaseClass is Parent of m_LocalTransform
|
||||
Width(this, "Width", 1.0f),
|
||||
Height(this, "Height", 1.0f),
|
||||
Depth(this, "Depth", 1.0f) {
|
||||
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
p_Size(this, "Size", Vector3f(1.0f, 1.0f, 1.0f)),
|
||||
p_Origin(this, "Origin", Vector3f(0.0f, 0.0f, 0.0f)) {
|
||||
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,12 +73,11 @@ public:
|
||||
*/
|
||||
ContainerBox(const Vector3f &size)
|
||||
: m_LocalT(this),
|
||||
Width(this, "Width", size(0)),
|
||||
Height(this, "Height", size(1)),
|
||||
Depth(this, "Depth", size(2)) {
|
||||
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
p_Size(this, "Size", size),
|
||||
p_Origin(this, "Origin", Vector3f(0.0f, 0.0f, 0.0f)) {
|
||||
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
|
||||
this->SetSize(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,20 +87,20 @@ public:
|
||||
ContainerBox(const ContainerBox ©)
|
||||
: m_LocalT(this), // BaseClass is Parent of m_LocalTransform
|
||||
AffineTransform(copy),
|
||||
Width(this, "Width", copy.Width),
|
||||
Height(this, "Height", copy.Height),
|
||||
Depth(this, "Depth", copy.Depth) {
|
||||
Object::connect(&Width, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Height, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&Depth, &Property<float>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
this->SetOrigin(copy.GetOrigin());
|
||||
p_Size(this, "Size", copy.p_Size),
|
||||
p_Origin(this, "Origin", copy.p_Origin) {
|
||||
Object::connect(&p_Size, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncSize);
|
||||
Object::connect(&p_Origin, &Property<Vector3f>::PropertyChanged, this, &ContainerBox::SyncOrigin);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the box origin relative to its coordinate system.
|
||||
* @param v The origin position vector.
|
||||
*/
|
||||
void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
|
||||
void SetOrigin(const Vector3f &v) {
|
||||
p_Origin = v;
|
||||
m_LocalT.SetPosition(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the box origin relative to its coordinate system.
|
||||
@@ -119,9 +114,7 @@ public:
|
||||
* @param v The size vector (width, height, depth).
|
||||
*/
|
||||
void SetSize(const Vector3f &v) {
|
||||
Width = v(0);
|
||||
Height = v(1);
|
||||
Depth = v(2);
|
||||
p_Size = v;
|
||||
Vector3f pos = this->GetOrigin();
|
||||
m_LocalT = AffineTransform(this); // regenerate local transform
|
||||
m_LocalT.Scale(v);
|
||||
@@ -213,11 +206,17 @@ signals:
|
||||
// signal to emit when the box is updated //
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(ContainerBox::Updated); }
|
||||
|
||||
private:
|
||||
private slots:
|
||||
void SyncSize() {
|
||||
this->SetSize(Vector3f(Width, Height, Depth));
|
||||
this->SetSize(p_Size);
|
||||
}
|
||||
|
||||
void SyncOrigin() {
|
||||
this->SetOrigin(p_Origin);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
AffineTransform m_LocalT;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,32 +34,32 @@
|
||||
namespace uLib {
|
||||
|
||||
/**
|
||||
* @brief Represents a cylindrical volume centered in the base circle.
|
||||
* @brief Represents a cylindrical volume.
|
||||
*
|
||||
* Cylinder inherits from AffineTransform, which defines its parent
|
||||
* coordinate system. It contains an internal local transformation (m_LocalT)
|
||||
* that defines the cylinder's actual volume (radius and height)
|
||||
* relative to the emitter's origin (base circle center).
|
||||
* The cylinder orientation is defined by the Axis property (0=X, 1=Y, 2=Z).
|
||||
* By default, it is aligned with the Y axis (Axis=1).
|
||||
*/
|
||||
class Cylinder : public AffineTransform, public Object {
|
||||
typedef AffineTransform BaseClass;
|
||||
|
||||
public:
|
||||
uLibTypeMacro(Cylinder, Object)
|
||||
|
||||
virtual const char * GetClassName() const { return "Cylinder"; }
|
||||
virtual const char * GetClassName() const override { return "Cylinder"; }
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
* Initializes with radius 1 and height 1.
|
||||
* @brief Default constructor. Aligns with Y by default.
|
||||
*/
|
||||
Cylinder() : m_LocalT(this), m_Radius(1.0), m_Height(1.0) {
|
||||
Cylinder() : m_LocalT(this), Radius(1.0), Height(1.0), Axis(1) {
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with radius and height.
|
||||
*/
|
||||
Cylinder(float radius, float height) : m_LocalT(this), m_Radius(radius), m_Height(height) {
|
||||
Cylinder(float radius, float height, int axis = 1)
|
||||
: m_LocalT(this), Radius(radius), Height(height), Axis(axis) {
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
@@ -67,75 +67,115 @@ public:
|
||||
* @brief Copy constructor.
|
||||
*/
|
||||
Cylinder(const Cylinder ©)
|
||||
: m_LocalT(this), AffineTransform(copy) {
|
||||
this->SetRadius(copy.GetRadius());
|
||||
this->SetHeight(copy.GetHeight());
|
||||
: m_LocalT(this), AffineTransform(copy), Radius(copy.Radius), Height(copy.Height), Axis(copy.Axis) {
|
||||
ULIB_ACTIVATE_PROPERTIES(*this);
|
||||
this->UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialization template for property registration and persistence.
|
||||
*/
|
||||
template <class ArchiveT>
|
||||
void serialize(ArchiveT & ar, const unsigned int version) {
|
||||
ar & HRP(Radius);
|
||||
ar & HRP(Height);
|
||||
ar & HRP(Axis);
|
||||
}
|
||||
|
||||
/** Sets the radius of the cylinder */
|
||||
inline void SetRadius(float r) {
|
||||
m_Radius = r;
|
||||
Radius = r;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/** Gets the radius of the cylinder */
|
||||
inline float GetRadius() const { return m_Radius; }
|
||||
inline float GetRadius() const { return Radius; }
|
||||
|
||||
/** Sets the height of the cylinder */
|
||||
inline void SetHeight(float h) {
|
||||
m_Height = h;
|
||||
Height = h;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/** Gets the height of the cylinder */
|
||||
inline float GetHeight() const { return m_Height; }
|
||||
inline float GetHeight() const { return Height; }
|
||||
|
||||
/** Sets the main axis (0=X, 1=Y, 2=Z) */
|
||||
inline void SetAxis(int axis) {
|
||||
Axis = axis;
|
||||
UpdateLocalMatrix();
|
||||
}
|
||||
|
||||
/** Gets the main axis */
|
||||
inline int GetAxis() const { return Axis; }
|
||||
|
||||
/**
|
||||
* @brief Returns the world transformation matrix of the cylinder's volume.
|
||||
* @brief Returns the world transformation matrix.
|
||||
*/
|
||||
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
|
||||
|
||||
/**
|
||||
* @brief Returns the local transformation matrix of the cylinder's volume.
|
||||
* @brief Returns the local transformation matrix.
|
||||
*/
|
||||
Matrix4f GetLocalMatrix() const { return m_LocalT.GetMatrix(); }
|
||||
|
||||
/**
|
||||
* @brief Transforms local cylindrical coordinates to world space.
|
||||
* @param r Local radius (absolute).
|
||||
* @param theta Local angle in radians.
|
||||
* @param z Local height (absolute, relative to base circle).
|
||||
* @return Transformed point in world space.
|
||||
* @param r Local radius.
|
||||
* @param theta Local angle in radians (around main axis).
|
||||
* @param h Local height along main axis.
|
||||
*/
|
||||
inline Vector4f GetWorldPoint(float r, float theta, float z) const {
|
||||
return BaseClass::GetWorldMatrix() * Vector4f(r * std::cos(theta), r * std::sin(theta), z, 1.0f);
|
||||
inline Vector4f GetWorldPoint(float r, float theta, float h) const {
|
||||
Vector3f p;
|
||||
if (Axis == 0) p = Vector3f(h, r * std::cos(theta), r * std::sin(theta));
|
||||
else if (Axis == 1) p = Vector3f(r * std::cos(theta), h, r * std::sin(theta));
|
||||
else p = Vector3f(r * std::cos(theta), r * std::sin(theta), h);
|
||||
|
||||
return AffineTransform::GetWorldMatrix() * Vector4f(p.x(), p.y(), p.z(), 1.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transforms a world point to cylindrical local space.
|
||||
* @return Vector3f(r, theta, z)
|
||||
* @return Vector3f(r, theta, h)
|
||||
*/
|
||||
inline Vector3f GetCylindricalLocal(const Vector4f &world_v) const {
|
||||
Vector4f local_v = BaseClass::GetWorldMatrix().inverse() * world_v;
|
||||
float r = std::sqrt(local_v.x() * local_v.x() + local_v.y() * local_v.y());
|
||||
float theta = std::atan2(local_v.y(), local_v.x());
|
||||
return Vector3f(r, theta, local_v.z());
|
||||
Vector4f local_v = AffineTransform::GetWorldMatrix().inverse() * world_v;
|
||||
float r, theta, h;
|
||||
if (Axis == 0) {
|
||||
h = local_v.x();
|
||||
r = std::sqrt(local_v.y() * local_v.y() + local_v.z() * local_v.z());
|
||||
theta = std::atan2(local_v.z(), local_v.y());
|
||||
} else if (Axis == 1) {
|
||||
h = local_v.y();
|
||||
r = std::sqrt(local_v.x() * local_v.x() + local_v.z() * local_v.z());
|
||||
theta = std::atan2(local_v.z(), local_v.x());
|
||||
} else {
|
||||
h = local_v.z();
|
||||
r = std::sqrt(local_v.x() * local_v.x() + local_v.y() * local_v.y());
|
||||
theta = std::atan2(local_v.y(), local_v.x());
|
||||
}
|
||||
return Vector3f(r, theta, h);
|
||||
}
|
||||
|
||||
signals:
|
||||
/** Signal emitted when the cylinder geometry or transform is updated */
|
||||
virtual void Updated() override { ULIB_SIGNAL_EMIT(Cylinder::Updated); }
|
||||
|
||||
private:
|
||||
/** Recalculates the internal local matrix based on radius and height */
|
||||
void UpdateLocalMatrix() {
|
||||
m_LocalT = AffineTransform(this); // BaseClass is parent
|
||||
m_LocalT.Scale(Vector3f(m_Radius, m_Radius, m_Height));
|
||||
this->Updated();
|
||||
/** Signal emitted when properties change */
|
||||
virtual void Updated() override {
|
||||
this->UpdateLocalMatrix();
|
||||
ULIB_SIGNAL_EMIT(Cylinder::Updated);
|
||||
}
|
||||
|
||||
float m_Radius;
|
||||
float m_Height;
|
||||
private:
|
||||
/** Recalculates the internal local matrix based on dimensions and axis */
|
||||
void UpdateLocalMatrix() {
|
||||
m_LocalT = AffineTransform(this);
|
||||
if (Axis == 0) m_LocalT.Scale(Vector3f(Height, Radius, Radius));
|
||||
else if (Axis == 1) m_LocalT.Scale(Vector3f(Radius, Height, Radius));
|
||||
else m_LocalT.Scale(Vector3f(Radius, Radius, Height));
|
||||
}
|
||||
|
||||
float Radius;
|
||||
float Height;
|
||||
int Axis;
|
||||
AffineTransform m_LocalT;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user