// shape_renderer.cpp - Implementació del renderizado de formes // © 2026 JailDesigner #include "core/rendering/shape_renderer.hpp" #include #include #include "core/defaults/effects.hpp" #include "core/graphics/shape.hpp" #include "core/rendering/line_renderer.hpp" namespace Rendering { // Helper: transformar un point con rotación, scale i traslación static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 { const float CENTERED_X = point.x - shape_centre.x; const float CENTERED_Y = point.y - shape_centre.y; const float SCALED_X = CENTERED_X * scale; const float SCALED_Y = CENTERED_Y * scale; const float COS_A = std::cos(angle); const float SIN_A = std::sin(angle); const float ROTATED_X = (SCALED_X * COS_A) - (SCALED_Y * SIN_A); const float ROTATED_Y = (SCALED_X * SIN_A) + (SCALED_Y * COS_A); return {.x = ROTATED_X + position.x, .y = ROTATED_Y + position.y}; } // Una passada de renderitzat: itera primitives de la shape i emet línies // amb el thickness/alpha indicats. Es crida N vegades en glow mode (una // per pass de halo + core), o 1 vegada quan glow=false. static void renderSinglePass(Rendering::Renderer* renderer, const std::shared_ptr& shape, const Vec2& position, float angle, float scale, float brightness, SDL_Color color, float thickness, float alpha) { const Vec2& shape_centre = shape->getCenter(); // Petita extensió a línies gruixudes per tapar forats entre segments. // A vèrtex aguts (~108°) un valor alt produeix "espigues" — 15%. const float EFFECTIVE_T = (thickness > 0.0F) ? thickness : getLineThickness(); const float EXTEND = (EFFECTIVE_T > 2.0F) ? (EFFECTIVE_T * 0.15F) : 0.0F; for (const auto& primitive : shape->getPrimitives()) { if (primitive.type == Graphics::PrimitiveType::POLYLINE) { for (size_t i = 0; i < primitive.points.size() - 1; i++) { Vec2 p1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale); Vec2 p2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale); if (EXTEND > 0.0F) { const float DX = p2.x - p1.x; const float DY = p2.y - p1.y; const float LEN = std::sqrt((DX * DX) + (DY * DY)); if (LEN > 1e-6F) { const float UX = (DX / LEN) * EXTEND; const float UY = (DY / LEN) * EXTEND; p1.x -= UX; p1.y -= UY; p2.x += UX; p2.y += UY; } } linea(renderer, static_cast(p1.x), static_cast(p1.y), static_cast(p2.x), static_cast(p2.y), brightness, thickness, color, alpha); } } else if (primitive.points.size() >= 2) { // LINE const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale); const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale); linea(renderer, static_cast(P1.x), static_cast(P1.y), static_cast(P2.x), static_cast(P2.y), brightness, thickness, color, alpha); } } } void renderShape(Rendering::Renderer* renderer, const std::shared_ptr& shape, const Vec2& position, float angle, float scale, float progress, float brightness, SDL_Color color, float thickness, float alpha, bool glow) { if (!shape || !shape->isValid()) { return; } if (progress < 1.0F) { return; } if (!glow) { renderSinglePass(renderer, shape, position, angle, scale, brightness, color, thickness, alpha); return; } // Glow: multi-pass amb halos translúcids proporcionals al tamany de // la shape. Cada pass amb thickness_ratio<0 usa el thickness/alpha // que ha passat el caller (és el "core" / línia real). Saturem la // mida de referència a MAX_REFERENCE_RADIUS perquè shapes molt // grans (logos) no tinguin halo desproporcionat. const float RAW_REF = shape->getBoundingRadius() * scale; const float REFERENCE_SIZE = std::min(RAW_REF, Defaults::FX::Glow::MAX_REFERENCE_RADIUS); for (const auto& pass : Defaults::FX::Glow::PASSES) { float pass_thickness = thickness; float pass_alpha = alpha; if (pass.thickness_ratio > 0.0F) { pass_thickness = REFERENCE_SIZE * pass.thickness_ratio; pass_alpha = pass.alpha * alpha; // respecta el master alpha del caller } renderSinglePass(renderer, shape, position, angle, scale, brightness, color, pass_thickness, pass_alpha); } } } // namespace Rendering