#include "icosahedron_shape.hpp" #include #include #include #include #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; // 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 const std::array, 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}, // 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; points_per_face = std::max(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 constexpr std::array, 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]; // Interpolar dentro del triángulo usando coordenadas baricéntricas simples float t = static_cast(point_in_face) / static_cast(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; } 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; float current_radius = screen_height * ICOSAHEDRON_RADIUS_FACTOR; return current_radius / BASE_RADIUS; }