Compare commits
4 Commits
bcbeaba504
...
5391e0cddf
| Author | SHA1 | Date | |
|---|---|---|---|
| 5391e0cddf | |||
| fb788666cc | |||
| ac3309ffd1 | |||
| 8b642f6903 |
@@ -114,6 +114,31 @@ constexpr float WAVE_GRID_FREQUENCY = 3.0f; // Frecuencia de ondas (ciclo
|
||||
constexpr float WAVE_GRID_PHASE_SPEED = 2.0f; // Velocidad de animación de ondas (rad/s)
|
||||
constexpr float WAVE_GRID_ROTATION_SPEED_Y = 0.4f; // Velocidad rotación eje Y (rad/s)
|
||||
|
||||
// Configuración de Torus (toroide/donut 3D)
|
||||
constexpr float TORUS_MAJOR_RADIUS_FACTOR = 0.25f; // Radio mayor R (centro torus a centro tubo)
|
||||
constexpr float TORUS_MINOR_RADIUS_FACTOR = 0.12f; // Radio menor r (grosor del tubo)
|
||||
constexpr float TORUS_ROTATION_SPEED_X = 0.6f; // Velocidad rotación eje X (rad/s)
|
||||
constexpr float TORUS_ROTATION_SPEED_Y = 0.9f; // Velocidad rotación eje Y (rad/s)
|
||||
constexpr float TORUS_ROTATION_SPEED_Z = 0.3f; // Velocidad rotación eje Z (rad/s)
|
||||
|
||||
// Configuración de Cylinder (cilindro 3D)
|
||||
constexpr float CYLINDER_RADIUS_FACTOR = 0.25f; // Radio del cilindro (proporción de altura)
|
||||
constexpr float CYLINDER_HEIGHT_FACTOR = 0.5f; // Altura del cilindro (proporción de altura)
|
||||
constexpr float CYLINDER_ROTATION_SPEED_Y = 1.0f; // Velocidad rotación eje Y (rad/s)
|
||||
|
||||
// Configuración de Icosahedron (icosaedro D20)
|
||||
constexpr float ICOSAHEDRON_RADIUS_FACTOR = 0.30f; // Radio de la esfera circunscrita
|
||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_X = 0.4f; // Velocidad rotación eje X (rad/s)
|
||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_Y = 0.7f; // Velocidad rotación eje Y (rad/s)
|
||||
constexpr float ICOSAHEDRON_ROTATION_SPEED_Z = 0.2f; // Velocidad rotación eje Z (rad/s)
|
||||
|
||||
// Configuración de Atom (núcleo con órbitas electrónicas)
|
||||
constexpr float ATOM_NUCLEUS_RADIUS_FACTOR = 0.08f; // Radio del núcleo central
|
||||
constexpr float ATOM_ORBIT_RADIUS_FACTOR = 0.30f; // Radio de las órbitas
|
||||
constexpr float ATOM_NUM_ORBITS = 3; // Número de órbitas
|
||||
constexpr float ATOM_ORBIT_ROTATION_SPEED = 2.0f; // Velocidad de electrones (rad/s)
|
||||
constexpr float ATOM_ROTATION_SPEED_Y = 0.5f; // Velocidad rotación global (rad/s)
|
||||
|
||||
// Control manual de escala de figuras 3D (Numpad +/-)
|
||||
constexpr float SHAPE_SCALE_MIN = 0.3f; // Escala mínima (30%)
|
||||
constexpr float SHAPE_SCALE_MAX = 3.0f; // Escala máxima (300%)
|
||||
|
||||
@@ -23,10 +23,14 @@
|
||||
#include "ball.h" // for Ball
|
||||
#include "external/dbgtxt.h" // for dbg_init, dbg_print
|
||||
#include "external/texture.h" // for Texture
|
||||
#include "shapes/sphere_shape.h" // for SphereShape
|
||||
#include "shapes/cube_shape.h" // for CubeShape
|
||||
#include "shapes/helix_shape.h" // for HelixShape
|
||||
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
||||
#include "shapes/sphere_shape.h" // for SphereShape
|
||||
#include "shapes/cube_shape.h" // for CubeShape
|
||||
#include "shapes/helix_shape.h" // for HelixShape
|
||||
#include "shapes/wave_grid_shape.h" // for WaveGridShape
|
||||
#include "shapes/torus_shape.h" // for TorusShape
|
||||
#include "shapes/cylinder_shape.h" // for CylinderShape
|
||||
#include "shapes/icosahedron_shape.h" // for IcosahedronShape
|
||||
#include "shapes/atom_shape.h" // for AtomShape
|
||||
|
||||
// Función auxiliar para obtener la ruta del directorio del ejecutable
|
||||
std::string getExecutableDirectory() {
|
||||
@@ -1073,10 +1077,21 @@ void Engine::activateShape(ShapeType type) {
|
||||
case ShapeType::HELIX:
|
||||
active_shape_ = std::make_unique<HelixShape>();
|
||||
break;
|
||||
case ShapeType::TORUS:
|
||||
active_shape_ = std::make_unique<TorusShape>();
|
||||
break;
|
||||
case ShapeType::WAVE_GRID:
|
||||
active_shape_ = std::make_unique<WaveGridShape>();
|
||||
break;
|
||||
// Futuras figuras se añadirán aquí
|
||||
case ShapeType::CYLINDER:
|
||||
active_shape_ = std::make_unique<CylinderShape>();
|
||||
break;
|
||||
case ShapeType::ICOSAHEDRON:
|
||||
active_shape_ = std::make_unique<IcosahedronShape>();
|
||||
break;
|
||||
case ShapeType::ATOM:
|
||||
active_shape_ = std::make_unique<AtomShape>();
|
||||
break;
|
||||
default:
|
||||
active_shape_ = std::make_unique<SphereShape>(); // Fallback
|
||||
break;
|
||||
|
||||
96
source/shapes/atom_shape.cpp
Normal file
96
source/shapes/atom_shape.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "atom_shape.h"
|
||||
#include "../defines.h"
|
||||
#include <cmath>
|
||||
|
||||
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
num_points_ = num_points;
|
||||
nucleus_radius_ = screen_height * ATOM_NUCLEUS_RADIUS_FACTOR;
|
||||
orbit_radius_ = screen_height * ATOM_ORBIT_RADIUS_FACTOR;
|
||||
// Las posiciones se calculan en getPoint3D()
|
||||
}
|
||||
|
||||
void AtomShape::update(float delta_time, float screen_width, float screen_height) {
|
||||
// Recalcular dimensiones por si cambió resolución (F4)
|
||||
nucleus_radius_ = screen_height * ATOM_NUCLEUS_RADIUS_FACTOR;
|
||||
orbit_radius_ = screen_height * ATOM_ORBIT_RADIUS_FACTOR;
|
||||
|
||||
// Actualizar rotación global del átomo
|
||||
angle_y_ += ATOM_ROTATION_SPEED_Y * delta_time;
|
||||
|
||||
// Actualizar fase de rotación de electrones en órbitas
|
||||
orbit_phase_ += ATOM_ORBIT_ROTATION_SPEED * delta_time;
|
||||
}
|
||||
|
||||
void AtomShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||
int num_orbits = static_cast<int>(ATOM_NUM_ORBITS);
|
||||
|
||||
// Calcular cuántos puntos para núcleo vs órbitas
|
||||
int nucleus_points = (num_points_ < 10) ? 1 : (num_points_ / 10); // 10% para núcleo
|
||||
if (nucleus_points < 1) nucleus_points = 1;
|
||||
|
||||
// Si estamos en el núcleo
|
||||
if (index < nucleus_points) {
|
||||
// Distribuir puntos en esfera pequeña (núcleo)
|
||||
float t = static_cast<float>(index) / static_cast<float>(nucleus_points);
|
||||
float phi = acosf(1.0f - 2.0f * t);
|
||||
float theta = PI * 2.0f * t * 3.61803398875f; // Golden ratio
|
||||
|
||||
float x_nuc = nucleus_radius_ * cosf(theta) * sinf(phi);
|
||||
float y_nuc = nucleus_radius_ * sinf(theta) * sinf(phi);
|
||||
float z_nuc = nucleus_radius_ * cosf(phi);
|
||||
|
||||
// Aplicar rotación global
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
x = x_nuc * cos_y - z_nuc * sin_y;
|
||||
y = y_nuc;
|
||||
z = x_nuc * sin_y + z_nuc * cos_y;
|
||||
return;
|
||||
}
|
||||
|
||||
// Puntos restantes: distribuir en órbitas
|
||||
int orbit_points = num_points_ - nucleus_points;
|
||||
int points_per_orbit = orbit_points / num_orbits;
|
||||
if (points_per_orbit < 1) points_per_orbit = 1;
|
||||
|
||||
int orbit_index = (index - nucleus_points) / points_per_orbit;
|
||||
if (orbit_index >= num_orbits) orbit_index = num_orbits - 1;
|
||||
|
||||
int point_in_orbit = (index - nucleus_points) % points_per_orbit;
|
||||
|
||||
// Ángulo del electrón en su órbita
|
||||
float electron_angle = (static_cast<float>(point_in_orbit) / static_cast<float>(points_per_orbit)) * 2.0f * PI;
|
||||
electron_angle += orbit_phase_; // Añadir rotación animada
|
||||
|
||||
// Inclinación del plano orbital (cada órbita en ángulo diferente)
|
||||
float orbit_tilt = (static_cast<float>(orbit_index) / static_cast<float>(num_orbits)) * PI;
|
||||
|
||||
// Posición del electrón en su órbita (plano XY local)
|
||||
float x_local = orbit_radius_ * cosf(electron_angle);
|
||||
float y_local = orbit_radius_ * sinf(electron_angle);
|
||||
float z_local = 0.0f;
|
||||
|
||||
// Inclinar el plano orbital (rotación en eje X local)
|
||||
float cos_tilt = cosf(orbit_tilt);
|
||||
float sin_tilt = sinf(orbit_tilt);
|
||||
float y_tilted = y_local * cos_tilt - z_local * sin_tilt;
|
||||
float z_tilted = y_local * sin_tilt + z_local * cos_tilt;
|
||||
|
||||
// Aplicar rotación global del átomo (eje Y)
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
float x_rot = x_local * cos_y - z_tilted * sin_y;
|
||||
float z_rot = x_local * sin_y + z_tilted * cos_y;
|
||||
|
||||
x = x_rot;
|
||||
y = y_tilted;
|
||||
z = z_rot;
|
||||
}
|
||||
|
||||
float AtomShape::getScaleFactor(float screen_height) const {
|
||||
// Factor de escala para física: proporcional al radio de órbita
|
||||
// Radio órbita base = 72px (0.30 * 240px en resolución 320x240)
|
||||
const float BASE_RADIUS = 72.0f;
|
||||
float current_radius = screen_height * ATOM_ORBIT_RADIUS_FACTOR;
|
||||
return current_radius / BASE_RADIUS;
|
||||
}
|
||||
22
source/shapes/atom_shape.h
Normal file
22
source/shapes/atom_shape.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
// Figura: Átomo con núcleo central y órbitas electrónicas
|
||||
// Comportamiento: Núcleo estático + electrones orbitando en planos inclinados
|
||||
// Efecto: Modelo atómico clásico Bohr
|
||||
class AtomShape : public Shape {
|
||||
private:
|
||||
float angle_y_ = 0.0f; // Ángulo de rotación global en eje Y (rad)
|
||||
float orbit_phase_ = 0.0f; // Fase de rotación de electrones (rad)
|
||||
float nucleus_radius_ = 0.0f; // Radio del núcleo central (píxeles)
|
||||
float orbit_radius_ = 0.0f; // Radio de las órbitas (píxeles)
|
||||
int num_points_ = 0; // Cantidad total de puntos
|
||||
|
||||
public:
|
||||
void generatePoints(int num_points, float screen_width, float screen_height) override;
|
||||
void update(float delta_time, float screen_width, float screen_height) override;
|
||||
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||
const char* getName() const override { return "ATOM"; }
|
||||
float getScaleFactor(float screen_height) const override;
|
||||
};
|
||||
77
source/shapes/cylinder_shape.cpp
Normal file
77
source/shapes/cylinder_shape.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "cylinder_shape.h"
|
||||
#include "../defines.h"
|
||||
#include <cmath>
|
||||
|
||||
void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
num_points_ = num_points;
|
||||
radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
|
||||
height_ = screen_height * CYLINDER_HEIGHT_FACTOR;
|
||||
// Las posiciones 3D se calculan en getPoint3D() usando ecuaciones paramétricas del cilindro
|
||||
}
|
||||
|
||||
void CylinderShape::update(float delta_time, float screen_width, float screen_height) {
|
||||
// Recalcular dimensiones por si cambió resolución (F4)
|
||||
radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
|
||||
height_ = screen_height * CYLINDER_HEIGHT_FACTOR;
|
||||
|
||||
// Actualizar ángulo de rotación en eje Y
|
||||
angle_y_ += CYLINDER_ROTATION_SPEED_Y * delta_time;
|
||||
}
|
||||
|
||||
void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||
// Distribuir puntos uniformemente en la superficie del cilindro
|
||||
// Calcular número de anillos (altura) y puntos por anillo (circunferencia)
|
||||
|
||||
int num_rings = static_cast<int>(sqrtf(static_cast<float>(num_points_) * 0.5f));
|
||||
if (num_rings < 2) num_rings = 2;
|
||||
|
||||
int points_per_ring = num_points_ / num_rings;
|
||||
if (points_per_ring < 3) points_per_ring = 3;
|
||||
|
||||
// Obtener parámetros u (ángulo) y v (altura) del índice
|
||||
int ring = index / points_per_ring;
|
||||
int point_in_ring = index % points_per_ring;
|
||||
|
||||
// Si nos pasamos del número de anillos, usar el último
|
||||
if (ring >= num_rings) {
|
||||
ring = num_rings - 1;
|
||||
point_in_ring = index - (ring * points_per_ring);
|
||||
if (point_in_ring >= points_per_ring) {
|
||||
point_in_ring = points_per_ring - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Parámetro u (ángulo alrededor del cilindro): [0, 2π]
|
||||
float u = (static_cast<float>(point_in_ring) / static_cast<float>(points_per_ring)) * 2.0f * PI;
|
||||
|
||||
// Parámetro v (altura normalizada): [-1, 1]
|
||||
float v = (static_cast<float>(ring) / static_cast<float>(num_rings - 1)) * 2.0f - 1.0f;
|
||||
if (num_rings == 1) v = 0.0f;
|
||||
|
||||
// Ecuaciones paramétricas del cilindro
|
||||
// x = radius * cos(u)
|
||||
// y = height * v
|
||||
// z = radius * sin(u)
|
||||
float x_base = radius_ * cosf(u);
|
||||
float y_base = (height_ * 0.5f) * v; // Centrar verticalmente
|
||||
float z_base = radius_ * sinf(u);
|
||||
|
||||
// Aplicar rotación en eje Y
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
float x_rot = x_base * cos_y - z_base * sin_y;
|
||||
float z_rot = x_base * sin_y + z_base * cos_y;
|
||||
|
||||
// Retornar coordenadas finales rotadas
|
||||
x = x_rot;
|
||||
y = y_base;
|
||||
z = z_rot;
|
||||
}
|
||||
|
||||
float CylinderShape::getScaleFactor(float screen_height) const {
|
||||
// Factor de escala para física: proporcional a la dimensión mayor (altura)
|
||||
// Altura base = 120px (0.5 * 240px en resolución 320x240)
|
||||
const float BASE_HEIGHT = 120.0f;
|
||||
float current_height = screen_height * CYLINDER_HEIGHT_FACTOR;
|
||||
return current_height / BASE_HEIGHT;
|
||||
}
|
||||
21
source/shapes/cylinder_shape.h
Normal file
21
source/shapes/cylinder_shape.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
// Figura: Cilindro 3D rotante
|
||||
// Comportamiento: Superficie cilíndrica con rotación en eje Y
|
||||
// Ecuaciones: x = r*cos(u), y = v, z = r*sin(u)
|
||||
class CylinderShape : public Shape {
|
||||
private:
|
||||
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
|
||||
float radius_ = 0.0f; // Radio del cilindro (píxeles)
|
||||
float height_ = 0.0f; // Altura del cilindro (píxeles)
|
||||
int num_points_ = 0; // Cantidad de puntos generados
|
||||
|
||||
public:
|
||||
void generatePoints(int num_points, float screen_width, float screen_height) override;
|
||||
void update(float delta_time, float screen_width, float screen_height) override;
|
||||
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||
const char* getName() const override { return "CYLINDER"; }
|
||||
float getScaleFactor(float screen_height) const override;
|
||||
};
|
||||
151
source/shapes/icosahedron_shape.cpp
Normal file
151
source/shapes/icosahedron_shape.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include "icosahedron_shape.h"
|
||||
#include "../defines.h"
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
void IcosahedronShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
num_points_ = num_points;
|
||||
radius_ = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
|
||||
// Los 12 vértices del icosaedro se calculan en getPoint3D()
|
||||
}
|
||||
|
||||
void IcosahedronShape::update(float delta_time, float screen_width, float screen_height) {
|
||||
// Recalcular radio por si cambió resolución (F4)
|
||||
radius_ = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
|
||||
|
||||
// Actualizar ángulos de rotación (triple rotación XYZ)
|
||||
angle_x_ += ICOSAHEDRON_ROTATION_SPEED_X * delta_time;
|
||||
angle_y_ += ICOSAHEDRON_ROTATION_SPEED_Y * delta_time;
|
||||
angle_z_ += ICOSAHEDRON_ROTATION_SPEED_Z * delta_time;
|
||||
}
|
||||
|
||||
void IcosahedronShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||
// Proporción áurea (golden ratio)
|
||||
const float phi = (1.0f + sqrtf(5.0f)) / 2.0f;
|
||||
|
||||
// 12 vértices del icosaedro regular normalizado
|
||||
// Basados en 3 rectángulos áureos ortogonales
|
||||
static const float vertices[12][3] = {
|
||||
// Rectángulo XY
|
||||
{-1.0f, phi, 0.0f},
|
||||
{ 1.0f, phi, 0.0f},
|
||||
{-1.0f, -phi, 0.0f},
|
||||
{ 1.0f, -phi, 0.0f},
|
||||
// Rectángulo YZ
|
||||
{ 0.0f, -1.0f, phi},
|
||||
{ 0.0f, 1.0f, phi},
|
||||
{ 0.0f, -1.0f, -phi},
|
||||
{ 0.0f, 1.0f, -phi},
|
||||
// Rectángulo ZX
|
||||
{ phi, 0.0f, -1.0f},
|
||||
{ phi, 0.0f, 1.0f},
|
||||
{-phi, 0.0f, -1.0f},
|
||||
{-phi, 0.0f, 1.0f}
|
||||
};
|
||||
|
||||
// Normalizar para esfera circunscrita
|
||||
const float normalization = sqrtf(1.0f + phi * phi);
|
||||
|
||||
// Si tenemos 12 o menos puntos, usar solo vértices
|
||||
if (num_points_ <= 12) {
|
||||
int vertex_index = index % 12;
|
||||
float x_base = vertices[vertex_index][0] / normalization * radius_;
|
||||
float y_base = vertices[vertex_index][1] / normalization * radius_;
|
||||
float z_base = vertices[vertex_index][2] / normalization * radius_;
|
||||
|
||||
// Aplicar rotaciones
|
||||
applyRotations(x_base, y_base, z_base, x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
// Para más de 12 puntos: subdividir caras triangulares
|
||||
// Distribuir puntos entre vértices (primero) y caras (después)
|
||||
if (index < 12) {
|
||||
// Primeros 12 puntos: vértices del icosaedro
|
||||
float x_base = vertices[index][0] / normalization * radius_;
|
||||
float y_base = vertices[index][1] / normalization * radius_;
|
||||
float z_base = vertices[index][2] / normalization * radius_;
|
||||
applyRotations(x_base, y_base, z_base, x, y, z);
|
||||
return;
|
||||
}
|
||||
|
||||
// Puntos restantes: distribuir en caras usando interpolación
|
||||
// El icosaedro tiene 20 caras triangulares
|
||||
int remaining_points = index - 12;
|
||||
int points_per_face = (num_points_ - 12) / 20;
|
||||
if (points_per_face < 1) points_per_face = 1;
|
||||
|
||||
int face_index = remaining_points / points_per_face;
|
||||
if (face_index >= 20) face_index = 19;
|
||||
|
||||
int point_in_face = remaining_points % points_per_face;
|
||||
|
||||
// Definir algunas caras del icosaedro (usando índices de vértices)
|
||||
// Solo necesitamos generar puntos, no renderizar caras completas
|
||||
static const int faces[20][3] = {
|
||||
{0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
|
||||
{1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
|
||||
{3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
|
||||
{4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
|
||||
};
|
||||
|
||||
// Obtener vértices de la cara
|
||||
int v0 = faces[face_index][0];
|
||||
int v1 = faces[face_index][1];
|
||||
int v2 = faces[face_index][2];
|
||||
|
||||
// Interpolar dentro del triángulo usando coordenadas baricéntricas simples
|
||||
float t = static_cast<float>(point_in_face) / static_cast<float>(points_per_face + 1);
|
||||
float u = sqrtf(t);
|
||||
float v = t - u;
|
||||
|
||||
float x_interp = vertices[v0][0] * (1.0f - u - v) + vertices[v1][0] * u + vertices[v2][0] * v;
|
||||
float y_interp = vertices[v0][1] * (1.0f - u - v) + vertices[v1][1] * u + vertices[v2][1] * v;
|
||||
float z_interp = vertices[v0][2] * (1.0f - u - v) + vertices[v1][2] * u + vertices[v2][2] * v;
|
||||
|
||||
// Proyectar a la esfera
|
||||
float len = sqrtf(x_interp * x_interp + y_interp * y_interp + z_interp * z_interp);
|
||||
if (len > 0.0001f) {
|
||||
x_interp /= len;
|
||||
y_interp /= len;
|
||||
z_interp /= len;
|
||||
}
|
||||
|
||||
float x_base = x_interp * radius_;
|
||||
float y_base = y_interp * radius_;
|
||||
float z_base = z_interp * radius_;
|
||||
|
||||
applyRotations(x_base, y_base, z_base, x, y, z);
|
||||
}
|
||||
|
||||
void IcosahedronShape::applyRotations(float x_in, float y_in, float z_in, float& x_out, float& y_out, float& z_out) const {
|
||||
// Aplicar rotación en eje X
|
||||
float cos_x = cosf(angle_x_);
|
||||
float sin_x = sinf(angle_x_);
|
||||
float y_rot_x = y_in * cos_x - z_in * sin_x;
|
||||
float z_rot_x = y_in * sin_x + z_in * cos_x;
|
||||
|
||||
// Aplicar rotación en eje Y
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
float x_rot_y = x_in * cos_y - z_rot_x * sin_y;
|
||||
float z_rot_y = x_in * sin_y + z_rot_x * cos_y;
|
||||
|
||||
// Aplicar rotación en eje Z
|
||||
float cos_z = cosf(angle_z_);
|
||||
float sin_z = sinf(angle_z_);
|
||||
float x_final = x_rot_y * cos_z - y_rot_x * sin_z;
|
||||
float y_final = x_rot_y * sin_z + y_rot_x * cos_z;
|
||||
|
||||
x_out = x_final;
|
||||
y_out = y_final;
|
||||
z_out = z_rot_y;
|
||||
}
|
||||
|
||||
float IcosahedronShape::getScaleFactor(float screen_height) const {
|
||||
// Factor de escala para física: proporcional al radio
|
||||
// Radio base = 72px (0.30 * 240px en resolución 320x240)
|
||||
const float BASE_RADIUS = 72.0f;
|
||||
float current_radius = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
|
||||
return current_radius / BASE_RADIUS;
|
||||
}
|
||||
25
source/shapes/icosahedron_shape.h
Normal file
25
source/shapes/icosahedron_shape.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
// Figura: Icosaedro 3D (D20, poliedro regular de 20 caras)
|
||||
// Comportamiento: 12 vértices distribuidos uniformemente con rotación triple
|
||||
// Geometría: Basado en proporción áurea (golden ratio)
|
||||
class IcosahedronShape : public Shape {
|
||||
private:
|
||||
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (rad)
|
||||
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
|
||||
float angle_z_ = 0.0f; // Ángulo de rotación en eje Z (rad)
|
||||
float radius_ = 0.0f; // Radio de la esfera circunscrita (píxeles)
|
||||
int num_points_ = 0; // Cantidad de puntos generados
|
||||
|
||||
// Helper para aplicar rotaciones triple XYZ
|
||||
void applyRotations(float x_in, float y_in, float z_in, float& x_out, float& y_out, float& z_out) const;
|
||||
|
||||
public:
|
||||
void generatePoints(int num_points, float screen_width, float screen_height) override;
|
||||
void update(float delta_time, float screen_width, float screen_height) override;
|
||||
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||
const char* getName() const override { return "ICOSAHEDRON"; }
|
||||
float getScaleFactor(float screen_height) const override;
|
||||
};
|
||||
96
source/shapes/torus_shape.cpp
Normal file
96
source/shapes/torus_shape.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "torus_shape.h"
|
||||
#include "../defines.h"
|
||||
#include <cmath>
|
||||
|
||||
void TorusShape::generatePoints(int num_points, float screen_width, float screen_height) {
|
||||
num_points_ = num_points;
|
||||
major_radius_ = screen_height * TORUS_MAJOR_RADIUS_FACTOR;
|
||||
minor_radius_ = screen_height * TORUS_MINOR_RADIUS_FACTOR;
|
||||
// Las posiciones 3D se calculan en getPoint3D() usando ecuaciones paramétricas del torus
|
||||
}
|
||||
|
||||
void TorusShape::update(float delta_time, float screen_width, float screen_height) {
|
||||
// Recalcular radios por si cambió resolución (F4)
|
||||
major_radius_ = screen_height * TORUS_MAJOR_RADIUS_FACTOR;
|
||||
minor_radius_ = screen_height * TORUS_MINOR_RADIUS_FACTOR;
|
||||
|
||||
// Actualizar ángulos de rotación (triple rotación XYZ)
|
||||
angle_x_ += TORUS_ROTATION_SPEED_X * delta_time;
|
||||
angle_y_ += TORUS_ROTATION_SPEED_Y * delta_time;
|
||||
angle_z_ += TORUS_ROTATION_SPEED_Z * delta_time;
|
||||
}
|
||||
|
||||
void TorusShape::getPoint3D(int index, float& x, float& y, float& z) const {
|
||||
// Distribuir puntos uniformemente en la superficie del torus
|
||||
// Usamos distribución aproximadamente uniforme basada en área
|
||||
|
||||
// Calcular número aproximado de anillos y puntos por anillo
|
||||
int num_rings = static_cast<int>(sqrtf(static_cast<float>(num_points_) * 0.5f));
|
||||
if (num_rings < 2) num_rings = 2;
|
||||
|
||||
int points_per_ring = num_points_ / num_rings;
|
||||
if (points_per_ring < 3) points_per_ring = 3;
|
||||
|
||||
// Obtener parámetros u y v del índice
|
||||
int ring = index / points_per_ring;
|
||||
int point_in_ring = index % points_per_ring;
|
||||
|
||||
// Si nos pasamos del número de anillos, usar el último
|
||||
if (ring >= num_rings) {
|
||||
ring = num_rings - 1;
|
||||
point_in_ring = index - (ring * points_per_ring);
|
||||
if (point_in_ring >= points_per_ring) {
|
||||
point_in_ring = points_per_ring - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Parámetros u y v normalizados [0, 2π]
|
||||
float u = (static_cast<float>(ring) / static_cast<float>(num_rings)) * 2.0f * PI;
|
||||
float v = (static_cast<float>(point_in_ring) / static_cast<float>(points_per_ring)) * 2.0f * PI;
|
||||
|
||||
// Ecuaciones paramétricas del torus
|
||||
// x = (R + r*cos(v)) * cos(u)
|
||||
// y = (R + r*cos(v)) * sin(u)
|
||||
// z = r * sin(v)
|
||||
float cos_v = cosf(v);
|
||||
float sin_v = sinf(v);
|
||||
float cos_u = cosf(u);
|
||||
float sin_u = sinf(u);
|
||||
|
||||
float radius_at_v = major_radius_ + minor_radius_ * cos_v;
|
||||
|
||||
float x_base = radius_at_v * cos_u;
|
||||
float y_base = radius_at_v * sin_u;
|
||||
float z_base = minor_radius_ * sin_v;
|
||||
|
||||
// Aplicar rotación en eje X
|
||||
float cos_x = cosf(angle_x_);
|
||||
float sin_x = sinf(angle_x_);
|
||||
float y_rot_x = y_base * cos_x - z_base * sin_x;
|
||||
float z_rot_x = y_base * sin_x + z_base * cos_x;
|
||||
|
||||
// Aplicar rotación en eje Y
|
||||
float cos_y = cosf(angle_y_);
|
||||
float sin_y = sinf(angle_y_);
|
||||
float x_rot_y = x_base * cos_y - z_rot_x * sin_y;
|
||||
float z_rot_y = x_base * sin_y + z_rot_x * cos_y;
|
||||
|
||||
// Aplicar rotación en eje Z
|
||||
float cos_z = cosf(angle_z_);
|
||||
float sin_z = sinf(angle_z_);
|
||||
float x_final = x_rot_y * cos_z - y_rot_x * sin_z;
|
||||
float y_final = x_rot_y * sin_z + y_rot_x * cos_z;
|
||||
|
||||
// Retornar coordenadas finales rotadas
|
||||
x = x_final;
|
||||
y = y_final;
|
||||
z = z_rot_y;
|
||||
}
|
||||
|
||||
float TorusShape::getScaleFactor(float screen_height) const {
|
||||
// Factor de escala para física: proporcional al radio mayor
|
||||
// Radio mayor base = 60px (0.25 * 240px en resolución 320x240)
|
||||
const float BASE_RADIUS = 60.0f;
|
||||
float current_radius = screen_height * TORUS_MAJOR_RADIUS_FACTOR;
|
||||
return current_radius / BASE_RADIUS;
|
||||
}
|
||||
23
source/shapes/torus_shape.h
Normal file
23
source/shapes/torus_shape.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
// Figura: Torus/Toroide 3D (donut/rosquilla)
|
||||
// Comportamiento: Superficie toroidal con rotación triple (X, Y, Z)
|
||||
// Ecuaciones: x = (R + r*cos(v))*cos(u), y = (R + r*cos(v))*sin(u), z = r*sin(v)
|
||||
class TorusShape : public Shape {
|
||||
private:
|
||||
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (rad)
|
||||
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
|
||||
float angle_z_ = 0.0f; // Ángulo de rotación en eje Z (rad)
|
||||
float major_radius_ = 0.0f; // Radio mayor R (del centro al tubo)
|
||||
float minor_radius_ = 0.0f; // Radio menor r (grosor del tubo)
|
||||
int num_points_ = 0; // Cantidad de puntos generados
|
||||
|
||||
public:
|
||||
void generatePoints(int num_points, float screen_width, float screen_height) override;
|
||||
void update(float delta_time, float screen_width, float screen_height) override;
|
||||
void getPoint3D(int index, float& x, float& y, float& z) const override;
|
||||
const char* getName() const override { return "TORUS"; }
|
||||
float getScaleFactor(float screen_height) const override;
|
||||
};
|
||||
Reference in New Issue
Block a user