style: aplicar fixes de clang-tidy (todo excepto uppercase-literal-suffix)

Corregidos ~2570 issues automáticamente con clang-tidy --fix-errors
más ajustes manuales posteriores:

- modernize: designated-initializers, trailing-return-type, use-auto,
  avoid-c-arrays (→ std::array<>), use-ranges, use-emplace,
  deprecated-headers, use-equals-default, pass-by-value,
  return-braced-init-list, use-default-member-init
- readability: math-missing-parentheses, implicit-bool-conversion,
  braces-around-statements, isolate-declaration, use-std-min-max,
  identifier-naming, else-after-return, redundant-casting,
  convert-member-functions-to-static, make-member-function-const,
  static-accessed-through-instance
- performance: avoid-endl, unnecessary-value-param, type-promotion,
  inefficient-vector-operation
- dead code: XOR_KEY (orphan tras eliminar encryptData/decryptData),
  dead stores en engine.cpp y png_shape.cpp
- NOLINT justificado en 10 funciones con alta complejidad cognitiva
  (initialize, render, main, processEvents, update×3, performDemoAction,
  randomizeOnDemoStart, renderDebugHUD, AppLogo::update)

Compilación: gcc -Wall sin warnings. clang-tidy: 0 issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 10:52:07 +01:00
parent 4801f287df
commit c9bcce6f9b
71 changed files with 3469 additions and 2838 deletions

View File

@@ -1,7 +1,10 @@
#include "atom_shape.hpp"
#include "defines.hpp"
#include <algorithm>
#include <cmath>
#include "defines.hpp"
void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
nucleus_radius_ = screen_height * ATOM_NUCLEUS_RADIUS_FACTOR;
@@ -25,15 +28,15 @@ 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;
int nucleus_points = (num_points_ < 10) ? 1 : (num_points_ / 10); // 10% para núcleo
nucleus_points = std::max(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 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);
@@ -51,16 +54,18 @@ void AtomShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
points_per_orbit = std::max(points_per_orbit, 1);
int orbit_index = (index - nucleus_points) / points_per_orbit;
if (orbit_index >= num_orbits) orbit_index = num_orbits - 1;
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
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;
@@ -73,21 +78,21 @@ void AtomShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
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;
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 {
auto AtomShape::getScaleFactor(float screen_height) const -> float {
// 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;

View File

@@ -6,17 +6,17 @@
// 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
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;
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;
};

View File

@@ -1,7 +1,10 @@
#include "cube_shape.hpp"
#include "defines.hpp"
#include <algorithm>
#include <cmath>
#include "defines.hpp"
void CubeShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
size_ = screen_height * CUBE_SIZE_FACTOR;
@@ -52,23 +55,23 @@ void CubeShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Aplicar rotación en eje Z
float cos_z = cosf(angle_z_);
float sin_z = sinf(angle_z_);
float x_rot_z = x_base * cos_z - y_base * sin_z;
float y_rot_z = x_base * sin_z + y_base * cos_z;
float x_rot_z = (x_base * cos_z) - (y_base * sin_z);
float y_rot_z = (x_base * sin_z) + (y_base * cos_z);
float z_rot_z = z_base;
// Aplicar rotación en eje Y
float cos_y = cosf(angle_y_);
float sin_y = sinf(angle_y_);
float x_rot_y = x_rot_z * cos_y + z_rot_z * sin_y;
float x_rot_y = (x_rot_z * cos_y) + (z_rot_z * sin_y);
float y_rot_y = y_rot_z;
float z_rot_y = -x_rot_z * sin_y + z_rot_z * cos_y;
float z_rot_y = (-x_rot_z * sin_y) + (z_rot_z * cos_y);
// Aplicar rotación en eje X
float cos_x = cosf(angle_x_);
float sin_x = sinf(angle_x_);
float x_final = x_rot_y;
float y_final = y_rot_y * cos_x - z_rot_y * sin_x;
float z_final = y_rot_y * sin_x + z_rot_y * cos_x;
float y_final = (y_rot_y * cos_x) - (z_rot_y * sin_x);
float z_final = (y_rot_y * sin_x) + (z_rot_y * cos_x);
// Retornar coordenadas finales rotadas
x = x_final;
@@ -76,7 +79,7 @@ void CubeShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_final;
}
float CubeShape::getScaleFactor(float screen_height) const {
auto CubeShape::getScaleFactor(float screen_height) const -> float {
// Factor de escala para física: proporcional al tamaño del cubo
// Tamaño base = 60px (resolución 320x240, factor 0.25)
const float BASE_SIZE = 60.0f;
@@ -105,12 +108,24 @@ void CubeShape::generateVerticesAndCenters() {
// 2. Añadir 6 centros de caras
// Caras: X=±size (Y,Z varían), Y=±size (X,Z varían), Z=±size (X,Y varían)
base_x_.push_back(size_); base_y_.push_back(0); base_z_.push_back(0); // +X
base_x_.push_back(-size_); base_y_.push_back(0); base_z_.push_back(0); // -X
base_x_.push_back(0); base_y_.push_back(size_); base_z_.push_back(0); // +Y
base_x_.push_back(0); base_y_.push_back(-size_);base_z_.push_back(0); // -Y
base_x_.push_back(0); base_y_.push_back(0); base_z_.push_back(size_); // +Z
base_x_.push_back(0); base_y_.push_back(0); base_z_.push_back(-size_); // -Z
base_x_.push_back(size_);
base_y_.push_back(0);
base_z_.push_back(0); // +X
base_x_.push_back(-size_);
base_y_.push_back(0);
base_z_.push_back(0); // -X
base_x_.push_back(0);
base_y_.push_back(size_);
base_z_.push_back(0); // +Y
base_x_.push_back(0);
base_y_.push_back(-size_);
base_z_.push_back(0); // -Y
base_x_.push_back(0);
base_y_.push_back(0);
base_z_.push_back(size_); // +Z
base_x_.push_back(0);
base_y_.push_back(0);
base_z_.push_back(-size_); // -Z
// 3. Añadir 12 centros de aristas
// Aristas paralelas a X (4), Y (4), Z (4)
@@ -143,16 +158,16 @@ void CubeShape::generateVerticesAndCenters() {
void CubeShape::generateVolumetricGrid() {
// Calcular dimensión del grid cúbico: N³ ≈ num_points
int grid_dim = static_cast<int>(ceilf(cbrtf(static_cast<float>(num_points_))));
if (grid_dim < 3) grid_dim = 3; // Mínimo grid 3x3x3
grid_dim = std::max(grid_dim, 3); // Mínimo grid 3x3x3
float step = (2.0f * size_) / (grid_dim - 1); // Espacio entre puntos
for (int ix = 0; ix < grid_dim; ix++) {
for (int iy = 0; iy < grid_dim; iy++) {
for (int iz = 0; iz < grid_dim; iz++) {
float x = -size_ + ix * step;
float y = -size_ + iy * step;
float z = -size_ + iz * step;
float x = -size_ + (ix * step);
float y = -size_ + (iy * step);
float z = -size_ + (iz * step);
base_x_.push_back(x);
base_y_.push_back(y);

View File

@@ -1,8 +1,9 @@
#pragma once
#include "shape.hpp"
#include <vector>
#include "shape.hpp"
// Figura: Cubo 3D rotante
// Distribución:
// - 1-8 pelotas: Solo vértices (8 puntos)
@@ -10,28 +11,28 @@
// - 27+ pelotas: Grid volumétrico 3D uniforme
// Comportamiento: Rotación simultánea en ejes X, Y, Z (efecto Rubik)
class CubeShape : 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 size_ = 0.0f; // Mitad del lado del cubo (píxeles)
int num_points_ = 0; // Cantidad de puntos generados
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 size_ = 0.0f; // Mitad del lado del cubo (píxeles)
int num_points_ = 0; // Cantidad de puntos generados
// Posiciones base 3D (sin rotar) - se calculan en generatePoints()
std::vector<float> base_x_;
std::vector<float> base_y_;
std::vector<float> base_z_;
// Posiciones base 3D (sin rotar) - se calculan en generatePoints()
std::vector<float> base_x_;
std::vector<float> base_y_;
std::vector<float> base_z_;
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 "CUBE"; }
float getScaleFactor(float screen_height) const override;
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 "CUBE"; }
float getScaleFactor(float screen_height) const override;
private:
// Métodos auxiliares para distribución de puntos
void generateVertices(); // 8 vértices
void generateVerticesAndCenters(); // 26 puntos (vértices + caras + aristas)
void generateVolumetricGrid(); // Grid 3D para muchas pelotas
private:
// Métodos auxiliares para distribución de puntos
void generateVertices(); // 8 vértices
void generateVerticesAndCenters(); // 26 puntos (vértices + caras + aristas)
void generateVolumetricGrid(); // Grid 3D para muchas pelotas
};

View File

@@ -1,8 +1,11 @@
#include "cylinder_shape.hpp"
#include "defines.hpp"
#include <algorithm>
#include <cmath>
#include <cstdlib> // Para rand()
#include "defines.hpp"
void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
@@ -37,7 +40,7 @@ void CylinderShape::update(float delta_time, float screen_width, float screen_he
float t = tumble_progress;
float ease = t < 0.5f
? 2.0f * t * t
: 1.0f - (-2.0f * t + 2.0f) * (-2.0f * t + 2.0f) / 2.0f;
: 1.0f - ((-2.0f * t + 2.0f) * (-2.0f * t + 2.0f) / 2.0f);
angle_x_ = ease * tumble_target_;
}
} else {
@@ -58,10 +61,10 @@ void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
num_rings = std::max(num_rings, 2);
int points_per_ring = num_points_ / num_rings;
if (points_per_ring < 3) points_per_ring = 3;
points_per_ring = std::max(points_per_ring, 3);
// Obtener parámetros u (ángulo) y v (altura) del índice
int ring = index / points_per_ring;
@@ -80,8 +83,10 @@ void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
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;
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)
@@ -94,14 +99,14 @@ void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Aplicar rotación en eje Y (principal, siempre activa)
float cos_y = cosf(angle_y_);
float sin_y = sinf(angle_y_);
float x_rot_y = x_base * cos_y - z_base * sin_y;
float z_rot_y = x_base * sin_y + z_base * cos_y;
float x_rot_y = (x_base * cos_y) - (z_base * sin_y);
float z_rot_y = (x_base * sin_y) + (z_base * cos_y);
// Aplicar rotación en eje X (tumbling ocasional)
float cos_x = cosf(angle_x_);
float sin_x = sinf(angle_x_);
float y_rot = y_base * cos_x - z_rot_y * sin_x;
float z_rot = y_base * sin_x + z_rot_y * cos_x;
float y_rot = (y_base * cos_x) - (z_rot_y * sin_x);
float z_rot = (y_base * sin_x) + (z_rot_y * cos_x);
// Retornar coordenadas finales con ambas rotaciones
x = x_rot_y;
@@ -109,7 +114,7 @@ void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_rot;
}
float CylinderShape::getScaleFactor(float screen_height) const {
auto CylinderShape::getScaleFactor(float screen_height) const -> float {
// 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;

View File

@@ -6,23 +6,23 @@
// Comportamiento: Superficie cilíndrica con rotación en eje Y + tumbling ocasional en X/Z
// 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 angle_x_ = 0.0f; // Ángulo de rotación en eje X (tumbling ocasional)
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
private:
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
float angle_x_ = 0.0f; // Ángulo de rotación en eje X (tumbling ocasional)
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
// Sistema de tumbling ocasional
float tumble_timer_ = 0.0f; // Temporizador para próximo tumble
float tumble_duration_ = 0.0f; // Duración del tumble actual
bool is_tumbling_ = false; // ¿Estamos en modo tumble?
float tumble_target_ = 0.0f; // Ángulo objetivo del tumble
// Sistema de tumbling ocasional
float tumble_timer_ = 0.0f; // Temporizador para próximo tumble
float tumble_duration_ = 0.0f; // Duración del tumble actual
bool is_tumbling_ = false; // ¿Estamos en modo tumble?
float tumble_target_ = 0.0f; // Ángulo objetivo del tumble
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;
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;
};

View File

@@ -1,7 +1,9 @@
#include "helix_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include "defines.hpp"
void HelixShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
radius_ = screen_height * HELIX_RADIUS_FACTOR;
@@ -41,8 +43,8 @@ void HelixShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Aplicar rotación en eje Y (horizontal)
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;
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
x = x_rot;
@@ -50,7 +52,7 @@ void HelixShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_rot;
}
float HelixShape::getScaleFactor(float screen_height) const {
auto HelixShape::getScaleFactor(float screen_height) const -> float {
// Factor de escala para física: proporcional a la dimensión mayor (altura total)
// Altura base = 180px para 3 vueltas con pitch=0.25 en 240px de altura (180 = 240 * 0.25 * 3)
const float BASE_HEIGHT = 180.0f;

View File

@@ -6,18 +6,18 @@
// Comportamiento: Rotación en eje Y + animación de fase vertical
// Ecuaciones: x = r*cos(t), y = pitch*t + phase, z = r*sin(t)
class HelixShape : public Shape {
private:
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
float phase_offset_ = 0.0f; // Offset de fase para animación vertical (rad)
float radius_ = 0.0f; // Radio de la espiral (píxeles)
float pitch_ = 0.0f; // Separación vertical entre vueltas (píxeles)
float total_height_ = 0.0f; // Altura total de la espiral (píxeles)
int num_points_ = 0; // Cantidad de puntos generados
private:
float angle_y_ = 0.0f; // Ángulo de rotación en eje Y (rad)
float phase_offset_ = 0.0f; // Offset de fase para animación vertical (rad)
float radius_ = 0.0f; // Radio de la espiral (píxeles)
float pitch_ = 0.0f; // Separación vertical entre vueltas (píxeles)
float total_height_ = 0.0f; // Altura total de la espiral (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 "HELIX"; }
float getScaleFactor(float screen_height) const override;
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 "HELIX"; }
float getScaleFactor(float screen_height) const override;
};

View File

@@ -1,8 +1,12 @@
#include "icosahedron_shape.hpp"
#include "defines.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <vector>
#include "defines.hpp"
void IcosahedronShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
radius_ = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
@@ -21,37 +25,36 @@ void IcosahedronShape::update(float delta_time, float screen_width, float screen
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;
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] = {
const std::array<std::array<float, 3>, 12> VERTICES = {{
// Rectángulo XY
{-1.0f, phi, 0.0f},
{ 1.0f, phi, 0.0f},
{-1.0f, -phi, 0.0f},
{ 1.0f, -phi, 0.0f},
{-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},
{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}
};
{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);
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_;
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);
@@ -62,9 +65,9 @@ void IcosahedronShape::getPoint3D(int index, float& x, float& y, float& z) const
// 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_;
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;
}
@@ -73,38 +76,55 @@ void IcosahedronShape::getPoint3D(int index, float& x, float& y, float& z) const
// 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;
points_per_face = std::max(points_per_face, 1);
int face_index = remaining_points / points_per_face;
if (face_index >= 20) face_index = 19;
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}
};
static constexpr std::array<std::array<int, 3>, 20> FACES = {{
{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];
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;
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);
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;
@@ -122,27 +142,27 @@ void IcosahedronShape::applyRotations(float x_in, float y_in, float z_in, float&
// 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;
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;
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;
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 {
auto IcosahedronShape::getScaleFactor(float screen_height) const -> float {
// 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;

View File

@@ -6,20 +6,20 @@
// 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
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;
// 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;
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;
};

View File

@@ -1,7 +1,9 @@
#include "lissajous_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include "defines.hpp"
void LissajousShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
amplitude_ = screen_height * LISSAJOUS_SIZE_FACTOR;
@@ -33,21 +35,21 @@ void LissajousShape::getPoint3D(int index, float& x, float& y, float& z) const {
// x(t) = A * sin(freq_x * t + phase_x)
// y(t) = A * sin(freq_y * t)
// z(t) = A * sin(freq_z * t + phase_z)
float x_local = amplitude_ * sinf(freq_x_ * t + phase_x_);
float x_local = amplitude_ * sinf((freq_x_ * t) + phase_x_);
float y_local = amplitude_ * sinf(freq_y_ * t);
float z_local = amplitude_ * sinf(freq_z_ * t + phase_z_);
float z_local = amplitude_ * sinf((freq_z_ * t) + phase_z_);
// Aplicar rotación global en eje X
float cos_x = cosf(rotation_x_);
float sin_x = sinf(rotation_x_);
float y_rot = y_local * cos_x - z_local * sin_x;
float z_rot = y_local * sin_x + z_local * cos_x;
float y_rot = (y_local * cos_x) - (z_local * sin_x);
float z_rot = (y_local * sin_x) + (z_local * cos_x);
// Aplicar rotación global en eje Y
float cos_y = cosf(rotation_y_);
float sin_y = sinf(rotation_y_);
float x_final = x_local * cos_y - z_rot * sin_y;
float z_final = x_local * sin_y + z_rot * cos_y;
float x_final = (x_local * cos_y) - (z_rot * sin_y);
float z_final = (x_local * sin_y) + (z_rot * cos_y);
// Retornar coordenadas rotadas
x = x_final;
@@ -55,7 +57,7 @@ void LissajousShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_final;
}
float LissajousShape::getScaleFactor(float screen_height) const {
auto LissajousShape::getScaleFactor(float screen_height) const -> float {
// Factor de escala para física: proporcional a la amplitud de la curva
// Amplitud base = 84px (0.35 * 240px en resolución 320x240)
const float BASE_SIZE = 84.0f;

View File

@@ -6,21 +6,21 @@
// Comportamiento: Curva paramétrica 3D con rotación global y animación de fase
// Ecuaciones: x(t) = A*sin(freq_x*t + phase_x), y(t) = A*sin(freq_y*t), z(t) = A*sin(freq_z*t + phase_z)
class LissajousShape : public Shape {
private:
float freq_x_ = 0.0f; // Frecuencia en eje X
float freq_y_ = 0.0f; // Frecuencia en eje Y
float freq_z_ = 0.0f; // Frecuencia en eje Z
float phase_x_ = 0.0f; // Desfase X (animado)
float phase_z_ = 0.0f; // Desfase Z (animado)
float rotation_x_ = 0.0f; // Rotación global en eje X (rad)
float rotation_y_ = 0.0f; // Rotación global en eje Y (rad)
float amplitude_ = 0.0f; // Amplitud de la curva (píxeles)
int num_points_ = 0; // Cantidad total de puntos
private:
float freq_x_ = 0.0f; // Frecuencia en eje X
float freq_y_ = 0.0f; // Frecuencia en eje Y
float freq_z_ = 0.0f; // Frecuencia en eje Z
float phase_x_ = 0.0f; // Desfase X (animado)
float phase_z_ = 0.0f; // Desfase Z (animado)
float rotation_x_ = 0.0f; // Rotación global en eje X (rad)
float rotation_y_ = 0.0f; // Rotación global en eje Y (rad)
float amplitude_ = 0.0f; // Amplitud de la curva (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 "LISSAJOUS"; }
float getScaleFactor(float screen_height) const override;
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 "LISSAJOUS"; }
float getScaleFactor(float screen_height) const override;
};

View File

@@ -1,16 +1,18 @@
#include "png_shape.hpp"
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>
#include "defines.hpp"
#include "external/stb_image.h"
#include "resource_manager.hpp"
#include <cmath>
#include <algorithm>
#include <iostream>
#include <map>
PNGShape::PNGShape(const char* png_path) {
// Cargar PNG desde path
if (!loadPNG(png_path)) {
std::cerr << "[PNGShape] Usando fallback 10x10" << std::endl;
std::cerr << "[PNGShape] Usando fallback 10x10" << '\n';
// Fallback: generar un cuadrado simple si falla la carga
image_width_ = 10;
image_height_ = 10;
@@ -21,7 +23,7 @@ PNGShape::PNGShape(const char* png_path) {
next_idle_time_ = PNG_IDLE_TIME_MIN + (rand() % 1000) / 1000.0f * (PNG_IDLE_TIME_MAX - PNG_IDLE_TIME_MIN);
}
bool PNGShape::loadPNG(const char* resource_key) {
auto PNGShape::loadPNG(const char* resource_key) -> bool {
{
std::string fn = std::string(resource_key);
fn = fn.substr(fn.find_last_of("\\/") + 1);
@@ -30,15 +32,16 @@ bool PNGShape::loadPNG(const char* resource_key) {
unsigned char* file_data = nullptr;
size_t file_size = 0;
if (!ResourceManager::loadResource(resource_key, file_data, file_size)) {
std::cerr << "[PNGShape] ERROR: recurso no encontrado: " << resource_key << std::endl;
std::cerr << "[PNGShape] ERROR: recurso no encontrado: " << resource_key << '\n';
return false;
}
int width, height, channels;
unsigned char* pixels = stbi_load_from_memory(file_data, static_cast<int>(file_size),
&width, &height, &channels, 1);
int width;
int height;
int channels;
unsigned char* pixels = stbi_load_from_memory(file_data, static_cast<int>(file_size), &width, &height, &channels, 1);
delete[] file_data;
if (!pixels) {
std::cerr << "[PNGShape] ERROR al decodificar PNG: " << stbi_failure_reason() << std::endl;
if (pixels == nullptr) {
std::cerr << "[PNGShape] ERROR al decodificar PNG: " << stbi_failure_reason() << '\n';
return false;
}
image_width_ = width;
@@ -57,9 +60,11 @@ void PNGShape::detectEdges() {
// Detectar píxeles del contorno (píxeles blancos con al menos un vecino negro)
for (int y = 0; y < image_height_; y++) {
for (int x = 0; x < image_width_; x++) {
int idx = y * image_width_ + x;
int idx = (y * image_width_) + x;
if (!pixel_data_[idx]) continue; // Solo píxeles blancos
if (!pixel_data_[idx]) {
continue; // Solo píxeles blancos
}
// Verificar vecinos (arriba, abajo, izq, der)
bool is_edge = false;
@@ -68,10 +73,10 @@ void PNGShape::detectEdges() {
is_edge = true; // Bordes de la imagen
} else {
// Verificar 4 vecinos
if (!pixel_data_[idx - 1] || // Izquierda
!pixel_data_[idx + 1] || // Derecha
!pixel_data_[idx - image_width_] || // Arriba
!pixel_data_[idx + image_width_]) { // Abajo
if (!pixel_data_[idx - 1] || // Izquierda
!pixel_data_[idx + 1] || // Derecha
!pixel_data_[idx - image_width_] || // Arriba
!pixel_data_[idx + image_width_]) { // Abajo
is_edge = true;
}
}
@@ -90,7 +95,7 @@ void PNGShape::floodFill() {
for (int y = 0; y < image_height_; y++) {
for (int x = 0; x < image_width_; x++) {
int idx = y * image_width_ + x;
int idx = (y * image_width_) + x;
if (pixel_data_[idx]) {
filled_points_.push_back({static_cast<float>(x), static_cast<float>(y)});
}
@@ -114,8 +119,8 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
num_layers_ = PNG_NUM_EXTRUSION_LAYERS;
// Generar AMBOS conjuntos de puntos (relleno Y bordes)
floodFill(); // Generar filled_points_
detectEdges(); // Generar edge_points_
floodFill(); // Generar filled_points_
detectEdges(); // Generar edge_points_
// Guardar copias originales (las funciones de filtrado modifican los vectores)
std::vector<Point2D> filled_points_original = filled_points_;
@@ -123,7 +128,7 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
// Conjunto de puntos ACTIVO (será modificado por filtros)
std::vector<Point2D> active_points_data;
std::string mode_name = "";
std::string mode_name;
// === SISTEMA DE DISTRIBUCIÓN ADAPTATIVA ===
// Estrategia: Optimizar según número de pelotas disponibles
@@ -196,8 +201,6 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
std::vector<Point2D> vertices = extractCornerVertices(source_for_vertices);
if (!vertices.empty() && vertices.size() < active_points_data.size()) {
active_points_data = vertices;
num_2d_points = active_points_data.size();
total_3d_points = num_2d_points * num_layers_;
mode_name = "VÉRTICES";
}
}
@@ -216,7 +219,7 @@ void PNGShape::generatePoints(int num_points, float screen_width, float screen_h
// Extraer filas alternas de puntos (FUNCIÓN PURA: no modifica parámetros)
// Recibe vector original y devuelve nuevo vector filtrado
std::vector<PNGShape::Point2D> PNGShape::extractAlternateRows(const std::vector<Point2D>& source, int row_skip) {
auto PNGShape::extractAlternateRows(const std::vector<Point2D>& source, int row_skip) -> std::vector<PNGShape::Point2D> {
std::vector<Point2D> result;
if (row_skip <= 1 || source.empty()) {
@@ -243,7 +246,7 @@ std::vector<PNGShape::Point2D> PNGShape::extractAlternateRows(const std::vector<
}
// Extraer vértices y esquinas (FUNCIÓN PURA: devuelve nuevo vector)
std::vector<PNGShape::Point2D> PNGShape::extractCornerVertices(const std::vector<Point2D>& source) {
auto PNGShape::extractCornerVertices(const std::vector<Point2D>& source) -> std::vector<PNGShape::Point2D> {
std::vector<Point2D> result;
if (source.empty()) {
@@ -267,9 +270,9 @@ std::vector<PNGShape::Point2D> PNGShape::extractCornerVertices(const std::vector
// Generar puntos en extremos de cada fila
for (const auto& [row_y, extremes] : row_extremes) {
result.push_back({extremes.first, static_cast<float>(row_y)}); // Extremo izquierdo
if (extremes.second != extremes.first) { // Solo añadir derecho si es diferente
result.push_back({extremes.second, static_cast<float>(row_y)}); // Extremo derecho
result.push_back({extremes.first, static_cast<float>(row_y)}); // Extremo izquierdo
if (extremes.second != extremes.first) { // Solo añadir derecho si es diferente
result.push_back({extremes.second, static_cast<float>(row_y)}); // Extremo derecho
}
}
@@ -376,8 +379,8 @@ void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
float v = y_base / (logo_size * 0.5f);
// Calcular pivoteo (amplitudes más grandes)
float tilt_amount_x = sinf(tilt_x_) * 0.15f; // 15%
float tilt_amount_y = sinf(tilt_y_) * 0.1f; // 10%
float tilt_amount_x = sinf(tilt_x_) * 0.15f; // 15%
float tilt_amount_y = sinf(tilt_y_) * 0.1f; // 10%
// Aplicar pivoteo proporcional al tamaño del logo
float z_tilt = (u * tilt_amount_y + v * tilt_amount_x) * logo_size;
@@ -386,14 +389,14 @@ void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Aplicar rotación en eje Y (horizontal)
float cos_y = cosf(angle_y_);
float sin_y = sinf(angle_y_);
float x_rot_y = x_base * cos_y - z_base * sin_y;
float z_rot_y = x_base * sin_y + z_base * cos_y;
float x_rot_y = (x_base * cos_y) - (z_base * sin_y);
float z_rot_y = (x_base * sin_y) + (z_base * cos_y);
// Aplicar rotación en eje X (vertical)
float cos_x = cosf(angle_x_);
float sin_x = sinf(angle_x_);
float y_rot = y_base * cos_x - z_rot_y * sin_x;
float z_rot = y_base * sin_x + z_rot_y * cos_x;
float y_rot = (y_base * cos_x) - (z_rot_y * sin_x);
float z_rot = (y_base * sin_x) + (z_rot_y * cos_x);
// Retornar coordenadas finales
x = x_rot_y;
@@ -408,7 +411,7 @@ void PNGShape::getPoint3D(int index, float& x, float& y, float& z) const {
}
}
float PNGShape::getScaleFactor(float screen_height) const {
auto PNGShape::getScaleFactor(float screen_height) const -> float {
// Escala dinámica según resolución
return PNG_SIZE_FACTOR;
}
@@ -432,7 +435,7 @@ void PNGShape::setConvergence(float convergence) {
}
// Obtener progreso del flip actual (0.0 = inicio del flip, 1.0 = fin del flip)
float PNGShape::getFlipProgress() const {
auto PNGShape::getFlipProgress() const -> float {
if (!is_flipping_) {
return 0.0f; // No está flipping, progreso = 0
}

View File

@@ -1,104 +1,108 @@
#pragma once
#include "shape.hpp"
#include "defines.hpp" // Para PNG_IDLE_TIME_MIN/MAX constantes
#include <cstdlib> // Para rand()
#include <vector>
#include <cstdlib> // Para rand()
#include "defines.hpp" // Para PNG_IDLE_TIME_MIN/MAX constantes
#include "shape.hpp"
// Figura: Shape generada desde PNG 1-bit (blanco sobre negro)
// Enfoque A: Extrusión 2D (implementado)
// Enfoque B: Voxelización 3D (preparado para futuro)
class PNGShape : public Shape {
private:
// Datos de la imagen cargada
int image_width_ = 0;
int image_height_ = 0;
std::vector<bool> pixel_data_; // Mapa de píxeles blancos (true = blanco)
private:
// Datos de la imagen cargada
int image_width_ = 0;
int image_height_ = 0;
std::vector<bool> pixel_data_; // Mapa de píxeles blancos (true = blanco)
// Puntos generados (Enfoque A: Extrusión 2D)
struct Point2D {
float x, y;
};
std::vector<Point2D> edge_points_; // Contorno (solo bordes) - ORIGINAL sin optimizar
std::vector<Point2D> filled_points_; // Relleno completo - ORIGINAL sin optimizar
std::vector<Point2D> optimized_points_; // Puntos finales optimizados (usado por getPoint3D)
// Puntos generados (Enfoque A: Extrusión 2D)
struct Point2D {
float x, y;
};
std::vector<Point2D> edge_points_; // Contorno (solo bordes) - ORIGINAL sin optimizar
std::vector<Point2D> filled_points_; // Relleno completo - ORIGINAL sin optimizar
std::vector<Point2D> optimized_points_; // Puntos finales optimizados (usado por getPoint3D)
// Parámetros de extrusión
float extrusion_depth_ = 0.0f; // Profundidad de extrusión en Z
int num_layers_ = 0; // Capas de extrusión (más capas = más denso)
// Parámetros de extrusión
float extrusion_depth_ = 0.0f; // Profundidad de extrusión en Z
int num_layers_ = 0; // Capas de extrusión (más capas = más denso)
// Rotación "legible" (de frente con volteretas ocasionales)
float angle_x_ = 0.0f;
float angle_y_ = 0.0f;
float idle_timer_ = 0.0f; // Timer para tiempo de frente
float flip_timer_ = 0.0f; // Timer para voltereta
float next_idle_time_ = 5.0f; // Próximo tiempo de espera (aleatorio)
bool is_flipping_ = false; // Estado: quieto o voltereta
int flip_axis_ = 0; // Eje de voltereta (0=X, 1=Y, 2=ambos)
// Rotación "legible" (de frente con volteretas ocasionales)
float angle_x_ = 0.0f;
float angle_y_ = 0.0f;
float idle_timer_ = 0.0f; // Timer para tiempo de frente
float flip_timer_ = 0.0f; // Timer para voltereta
float next_idle_time_ = 5.0f; // Próximo tiempo de espera (aleatorio)
bool is_flipping_ = false; // Estado: quieto o voltereta
int flip_axis_ = 0; // Eje de voltereta (0=X, 1=Y, 2=ambos)
// Pivoteo sutil en estado IDLE
float tilt_x_ = 0.0f; // Oscilación sutil en eje X
float tilt_y_ = 0.0f; // Oscilación sutil en eje Y
// Pivoteo sutil en estado IDLE
float tilt_x_ = 0.0f; // Oscilación sutil en eje X
float tilt_y_ = 0.0f; // Oscilación sutil en eje Y
// Modo LOGO (intervalos de flip más largos)
bool is_logo_mode_ = false; // true = usar intervalos LOGO (más lentos)
// Modo LOGO (intervalos de flip más largos)
bool is_logo_mode_ = false; // true = usar intervalos LOGO (más lentos)
// Sistema de convergencia (solo relevante en modo LOGO)
float current_convergence_ = 0.0f; // Porcentaje actual de convergencia (0.0-1.0)
bool convergence_threshold_reached_ = false; // true si ha alcanzado umbral mínimo (80%)
// Sistema de convergencia (solo relevante en modo LOGO)
float current_convergence_ = 0.0f; // Porcentaje actual de convergencia (0.0-1.0)
bool convergence_threshold_reached_ = false; // true si ha alcanzado umbral mínimo (80%)
// Sistema de tracking de flips (para modo LOGO - espera de flips)
int flip_count_ = 0; // Contador de flips completados (reset al entrar a LOGO)
bool was_flipping_last_frame_ = false; // Estado previo para detectar transiciones
// Sistema de tracking de flips (para modo LOGO - espera de flips)
int flip_count_ = 0; // Contador de flips completados (reset al entrar a LOGO)
bool was_flipping_last_frame_ = false; // Estado previo para detectar transiciones
// Dimensiones normalizadas
float scale_factor_ = 1.0f;
float center_offset_x_ = 0.0f;
float center_offset_y_ = 0.0f;
// Dimensiones normalizadas
float scale_factor_ = 1.0f;
float center_offset_x_ = 0.0f;
float center_offset_y_ = 0.0f;
int num_points_ = 0; // Total de puntos generados (para indexación)
int num_points_ = 0; // Total de puntos generados (para indexación)
// Métodos internos
bool loadPNG(const char* path); // Cargar PNG con stb_image
void detectEdges(); // Detectar contorno (Enfoque A)
void floodFill(); // Rellenar interior (Enfoque B - futuro)
void generateExtrudedPoints(); // Generar puntos con extrusión 2D
// Métodos internos
bool loadPNG(const char* resource_key); // Cargar PNG con stb_image
void detectEdges(); // Detectar contorno (Enfoque A)
void floodFill(); // Rellenar interior (Enfoque B - futuro)
void generateExtrudedPoints(); // Generar puntos con extrusión 2D
// Métodos de distribución adaptativa (funciones puras, no modifican parámetros)
std::vector<Point2D> extractAlternateRows(const std::vector<Point2D>& source, int row_skip); // Extraer filas alternas
std::vector<Point2D> extractCornerVertices(const std::vector<Point2D>& source); // Extraer vértices/esquinas
// Métodos de distribución adaptativa (funciones puras, no modifican parámetros)
static std::vector<Point2D> extractAlternateRows(const std::vector<Point2D>& source, int row_skip); // Extraer filas alternas
static std::vector<Point2D> extractCornerVertices(const std::vector<Point2D>& source); // Extraer vértices/esquinas
public:
// Constructor: recibe path relativo al PNG
PNGShape(const char* png_path = "data/shapes/jailgames.png");
public:
// Constructor: recibe path relativo al PNG
PNGShape(const char* png_path = "data/shapes/jailgames.png");
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 "PNG SHAPE"; }
float getScaleFactor(float screen_height) const override;
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 "PNG SHAPE"; }
float getScaleFactor(float screen_height) const override;
// Consultar estado de flip
bool isFlipping() const { return is_flipping_; }
// Consultar estado de flip
bool isFlipping() const { return is_flipping_; }
// Obtener progreso del flip actual (0.0 = inicio, 1.0 = fin)
float getFlipProgress() const;
// Obtener progreso del flip actual (0.0 = inicio, 1.0 = fin)
float getFlipProgress() const;
// Obtener número de flips completados (para modo LOGO)
int getFlipCount() const { return flip_count_; }
// Obtener número de flips completados (para modo LOGO)
int getFlipCount() const { return flip_count_; }
// Resetear contador de flips (llamar al entrar a LOGO MODE)
void resetFlipCount() { flip_count_ = 0; was_flipping_last_frame_ = false; }
// Resetear contador de flips (llamar al entrar a LOGO MODE)
void resetFlipCount() {
flip_count_ = 0;
was_flipping_last_frame_ = false;
}
// Control de modo LOGO (flip intervals más largos)
void setLogoMode(bool enable) {
is_logo_mode_ = enable;
// Recalcular next_idle_time_ con el rango apropiado
float idle_min = enable ? PNG_IDLE_TIME_MIN_LOGO : PNG_IDLE_TIME_MIN;
float idle_max = enable ? PNG_IDLE_TIME_MAX_LOGO : PNG_IDLE_TIME_MAX;
next_idle_time_ = idle_min + (rand() % 1000) / 1000.0f * (idle_max - idle_min);
}
// Control de modo LOGO (flip intervals más largos)
void setLogoMode(bool enable) {
is_logo_mode_ = enable;
// Recalcular next_idle_time_ con el rango apropiado
float idle_min = enable ? PNG_IDLE_TIME_MIN_LOGO : PNG_IDLE_TIME_MIN;
float idle_max = enable ? PNG_IDLE_TIME_MAX_LOGO : PNG_IDLE_TIME_MAX;
next_idle_time_ = idle_min + (rand() % 1000) / 1000.0f * (idle_max - idle_min);
}
// Sistema de convergencia (override de Shape::setConvergence)
void setConvergence(float convergence) override;
// Sistema de convergencia (override de Shape::setConvergence)
void setConvergence(float convergence) override;
};

View File

@@ -2,34 +2,34 @@
// Interfaz abstracta para todas las figuras 3D
class Shape {
public:
virtual ~Shape() = default;
public:
virtual ~Shape() = default;
// Generar distribución inicial de puntos en la figura
// num_points: cantidad de pelotas a distribuir
// screen_width/height: dimensiones del área de juego (para escalar)
virtual void generatePoints(int num_points, float screen_width, float screen_height) = 0;
// Generar distribución inicial de puntos en la figura
// num_points: cantidad de pelotas a distribuir
// screen_width/height: dimensiones del área de juego (para escalar)
virtual void generatePoints(int num_points, float screen_width, float screen_height) = 0;
// Actualizar animación de la figura (rotación, deformación, etc.)
// delta_time: tiempo transcurrido desde último frame
// screen_width/height: dimensiones actuales (puede cambiar con F4)
virtual void update(float delta_time, float screen_width, float screen_height) = 0;
// Actualizar animación de la figura (rotación, deformación, etc.)
// delta_time: tiempo transcurrido desde último frame
// screen_width/height: dimensiones actuales (puede cambiar con F4)
virtual void update(float delta_time, float screen_width, float screen_height) = 0;
// Obtener posición 3D del punto i después de transformaciones (rotación, etc.)
// index: índice del punto (0 a num_points-1)
// x, y, z: coordenadas 3D en espacio mundo (centradas en 0,0,0)
virtual void getPoint3D(int index, float& x, float& y, float& z) const = 0;
// Obtener posición 3D del punto i después de transformaciones (rotación, etc.)
// index: índice del punto (0 a num_points-1)
// x, y, z: coordenadas 3D en espacio mundo (centradas en 0,0,0)
virtual void getPoint3D(int index, float& x, float& y, float& z) const = 0;
// Obtener nombre de la figura para debug display
virtual const char* getName() const = 0;
// Obtener nombre de la figura para debug display
virtual const char* getName() const = 0;
// Obtener factor de escala para ajustar física según tamaño de figura
// screen_height: altura actual de pantalla
// Retorna: factor multiplicador para constantes de física (spring_k, damping, etc.)
virtual float getScaleFactor(float screen_height) const = 0;
// Obtener factor de escala para ajustar física según tamaño de figura
// screen_height: altura actual de pantalla
// Retorna: factor multiplicador para constantes de física (spring_k, damping, etc.)
virtual float getScaleFactor(float screen_height) const = 0;
// Notificar a la figura sobre el porcentaje de convergencia (pelotas cerca del objetivo)
// convergence: valor de 0.0 (0%) a 1.0 (100%) indicando cuántas pelotas están en posición
// Default: no-op (la mayoría de figuras no necesitan esta información)
virtual void setConvergence(float convergence) {}
// Notificar a la figura sobre el porcentaje de convergencia (pelotas cerca del objetivo)
// convergence: valor de 0.0 (0%) a 1.0 (100%) indicando cuántas pelotas están en posición
// Default: no-op (la mayoría de figuras no necesitan esta información)
virtual void setConvergence(float convergence) {}
};

View File

@@ -1,7 +1,9 @@
#include "sphere_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include "defines.hpp"
void SphereShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
radius_ = screen_height * ROTOBALL_RADIUS_FACTOR;
@@ -19,12 +21,12 @@ void SphereShape::update(float delta_time, float screen_width, float screen_heig
void SphereShape::getPoint3D(int index, float& x, float& y, float& z) const {
// Algoritmo Fibonacci Sphere para distribución uniforme
const float golden_ratio = (1.0f + sqrtf(5.0f)) / 2.0f;
const float angle_increment = PI * 2.0f * golden_ratio;
const float GOLDEN_RATIO = (1.0f + sqrtf(5.0f)) / 2.0f;
const float ANGLE_INCREMENT = PI * 2.0f * GOLDEN_RATIO;
float t = static_cast<float>(index) / static_cast<float>(num_points_);
float phi = acosf(1.0f - 2.0f * t); // Latitud
float theta = angle_increment * static_cast<float>(index); // Longitud
float phi = acosf(1.0f - (2.0f * t)); // Latitud
float theta = ANGLE_INCREMENT * static_cast<float>(index); // Longitud
// Convertir coordenadas esféricas a cartesianas
float x_base = cosf(theta) * sinf(phi) * radius_;
@@ -34,14 +36,14 @@ void SphereShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
float x_rot = (x_base * cos_y) - (z_base * sin_y);
float z_rot = (x_base * sin_y) + (z_base * cos_y);
// Aplicar rotación en eje X
float cos_x = cosf(angle_x_);
float sin_x = sinf(angle_x_);
float y_rot = y_base * cos_x - z_rot * sin_x;
float z_final = y_base * sin_x + z_rot * cos_x;
float y_rot = (y_base * cos_x) - (z_rot * sin_x);
float z_final = (y_base * sin_x) + (z_rot * cos_x);
// Retornar coordenadas finales rotadas
x = x_rot;
@@ -49,7 +51,7 @@ void SphereShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_final;
}
float SphereShape::getScaleFactor(float screen_height) const {
auto SphereShape::getScaleFactor(float screen_height) const -> float {
// Factor de escala para física: proporcional al radio
// Radio base = 80px (resolución 320x240)
const float BASE_RADIUS = 80.0f;

View File

@@ -6,16 +6,16 @@
// Comportamiento: Rotación dual en ejes X e Y
// Uso anterior: RotoBall
class SphereShape : 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 radius_ = 0.0f; // Radio de la esfera (píxeles)
int num_points_ = 0; // Cantidad de puntos generados
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 radius_ = 0.0f; // Radio de la esfera (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 "SPHERE"; }
float getScaleFactor(float screen_height) const override;
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 "SPHERE"; }
float getScaleFactor(float screen_height) const override;
};

View File

@@ -1,7 +1,10 @@
#include "torus_shape.hpp"
#include "defines.hpp"
#include <algorithm>
#include <cmath>
#include "defines.hpp"
void TorusShape::generatePoints(int num_points, float screen_width, float screen_height) {
num_points_ = num_points;
major_radius_ = screen_height * TORUS_MAJOR_RADIUS_FACTOR;
@@ -26,10 +29,10 @@ void TorusShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
num_rings = std::max(num_rings, 2);
int points_per_ring = num_points_ / num_rings;
if (points_per_ring < 3) points_per_ring = 3;
points_per_ring = std::max(points_per_ring, 3);
// Obtener parámetros u y v del índice
int ring = index / points_per_ring;
@@ -57,7 +60,7 @@ void TorusShape::getPoint3D(int index, float& x, float& y, float& z) const {
float cos_u = cosf(u);
float sin_u = sinf(u);
float radius_at_v = major_radius_ + minor_radius_ * cos_v;
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;
@@ -66,20 +69,20 @@ void TorusShape::getPoint3D(int index, float& x, float& y, float& z) const {
// 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;
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;
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;
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;
@@ -87,7 +90,7 @@ void TorusShape::getPoint3D(int index, float& x, float& y, float& z) const {
z = z_rot_y;
}
float TorusShape::getScaleFactor(float screen_height) const {
auto TorusShape::getScaleFactor(float screen_height) const -> float {
// 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;

View File

@@ -6,18 +6,18 @@
// 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
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;
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;
};