Something went wrong. Try again.
google balls
Something went wrong. Try again.
14 kB · 422 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422#include <3ds.h>#include <citro2d.h>#include <citro3d.h>#include <vector>#include <cmath>#include <string>#include <algorithm>
// 3DS screen dimensions#define TOP_SCREEN_WIDTH 400#define TOP_SCREEN_HEIGHT 240#define BOTTOM_SCREEN_WIDTH 320#define BOTTOM_SCREEN_HEIGHT 240
// Target FPS and frame timing#define TARGET_FPS 33#define FRAME_TIME_NS (1000000000LL / TARGET_FPS)
struct Vector3 { double x, y, z; Vector3(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {} void set(double newX, double newY, double newZ = 0) { x = newX; y = newY; z = newZ; } void addX(double dx) { x += dx; } void addY(double dy) { y += dy; } void addZ(double dz) { z += dz; }};
struct Color { u8 r, g, b, a; Color(u8 r = 255, u8 g = 255, u8 b = 255, u8 a = 255) : r(r), g(g), b(b), a(a) {} // Convert hex string to color static Color fromHex(const std::string& hex) { if (hex[0] == '#') { unsigned int value = std::stoul(hex.substr(1), nullptr, 16); return Color((value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, 255); } return Color(); } u32 to3DS() const { return C2D_Color32(r, g, b, a); }};
class Point {public: Vector3 curPos, originalPos, targetPos, velocity; Color color; double radius, size; // Fixed: Declare in correct order double friction = 0.8; double springStrength = 0.1; // Fixed: Initialize in declaration order Point(double x, double y, double z, double size, const std::string& colorHex) : curPos(x, y, z), originalPos(x, y, z), targetPos(x, y, z), velocity(0, 0, 0), radius(size), size(size) { color = Color::fromHex(colorHex); } void update() { // X axis spring physics double dx = targetPos.x - curPos.x; double ax = dx * springStrength; velocity.x += ax; velocity.x *= friction; // Stop small oscillations if (std::abs(dx) < 0.1 && std::abs(velocity.x) < 0.01) { curPos.x = targetPos.x; velocity.x = 0; } else { curPos.x += velocity.x; } // Y axis spring physics double dy = targetPos.y - curPos.y; double ay = dy * springStrength; velocity.y += ay; velocity.y *= friction; // Stop small oscillations if (std::abs(dy) < 0.1 && std::abs(velocity.y) < 0.01) { curPos.y = targetPos.y; velocity.y = 0; } else { curPos.y += velocity.y; } // Z axis (depth) based on distance from original position double dox = originalPos.x - curPos.x; double doy = originalPos.y - curPos.y; double dd = (dox * dox) + (doy * doy); double d = std::sqrt(dd); targetPos.z = d / 100.0 + 1.0; double dz = targetPos.z - curPos.z; double az = dz * springStrength; velocity.z += az; velocity.z *= friction; // Stop small Z oscillations if (std::abs(dz) < 0.01 && std::abs(velocity.z) < 0.001) { curPos.z = targetPos.z; velocity.z = 0; } else { curPos.z += velocity.z; } // Update radius based on depth radius = size * curPos.z; if (radius < 1) radius = 1; } void draw() { // Draw filled circle using citro2d C2D_DrawCircleSolid(curPos.x, curPos.y, 0.0f, radius, color.to3DS()); }};
// Original point data (scaled for 3DS top screen)struct PointData { int x, y; int size; std::string color;};
static void computeBounds(const std::vector<PointData>& data, double& w, double& h) { int minX = 99999, maxX = -99999; int minY = 99999, maxY = -99999; for (const auto& p : data) { if (p.x < minX) minX = p.x; if (p.x > maxX) maxX = p.x; if (p.y < minY) minY = p.y; if (p.y > maxY) maxY = p.y; } w = maxX - minX; h = maxY - minY;}
class PointCollection {public: Vector3 mousePos; std::vector<Point> points; PointCollection() : mousePos(0, 0, 0) {} void addPoint(double x, double y, double z, double size, const std::string& color) { points.emplace_back(x, y, z, size, color); } void update() { for (auto& point : points) { double dx = mousePos.x - point.curPos.x; double dy = mousePos.y - point.curPos.y; double dd = (dx * dx) + (dy * dy); double d = std::sqrt(dd); if (d < 75) { // Reduced interaction distance for 3DS screen // Fixed: Match JavaScript logic exactly point.targetPos.x = point.curPos.x - dx; point.targetPos.y = point.curPos.y - dy; } else { point.targetPos.x = point.originalPos.x; point.targetPos.y = point.originalPos.y; } point.update(); } } void draw() { for (auto& point : points) { point.draw(); } }};
class App {private: C3D_RenderTarget* topTarget; C3D_RenderTarget* bottomTarget; PointCollection pointCollection; bool running; touchPosition touch; bool touching; C2D_TextBuf textBuf; C2D_Text instructionText; C2D_Font font; u64 lastFrameTime; public: App() : topTarget(nullptr), bottomTarget(nullptr), running(false), touching(false), lastFrameTime(0) {} bool init() { // Initialize services romfsInit(); gfxInitDefault(); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); C2D_Init(C2D_DEFAULT_MAX_OBJECTS); C2D_Prepare(); // Create render targets topTarget = C2D_CreateScreenTarget(GFX_TOP, GFX_LEFT); bottomTarget = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); if (!topTarget || !bottomTarget) { return false; } // Initialize text rendering textBuf = C2D_TextBufNew(4096); font = C2D_FontLoadSystem(CFG_REGION_USA); // Create instruction text C2D_TextFontParse(&instructionText, font, textBuf, "Touch the screen or use the Circle Pad\nto interact with the points!\n\nPress START to exit"); C2D_TextOptimize(&instructionText); initPoints(); running = true; lastFrameTime = osGetTime(); return true; } void initPoints() { // Scaled down point data for 3DS top screen (400x240) std::vector<PointData> pointData = { // Scaled coordinates (roughly 0.6x scale factor to fit 3DS screen) {121, 47, 5, "#ed9d33"}, {209, 50, 5, "#d44d61"}, {154, 41, 5, "#4f7af2"}, {128, 35, 5, "#ef9a1e"}, {159, 22, 5, "#4976f3"}, {180, 47, 5, "#269230"}, {176, 35, 5, "#1f9e2c"}, {27, 53, 5, "#1c48dd"}, {161, 31, 5, "#2a56ea"}, {44, 50, 5, "#3355d8"}, {176, 4, 5, "#36b641"}, {141, 37, 5, "#2e5def"}, {212, 25, 4, "#d53747"}, {202, 31, 4, "#eb676f"}, {125, 25, 4, "#f9b125"}, {193, 42, 4, "#de3646"}, {5, 36, 4, "#2a59f0"}, {108, 49, 4, "#eb9c31"}, {88, 39, 4, "#c41731"}, {87, 29, 4, "#d82038"}, {148, 20, 4, "#5f8af8"}, {101, 41, 4, "#efa11e"}, {164, 59, 4, "#2e55e2"}, {149, 72, 4, "#4167e4"}, {176, 25, 4, "#0b991a"}, {160, 68, 4, "#4869e3"}, {47, 40, 4, "#3059e3"}, {176, 14, 4, "#10a11d"}, {70, 50, 4, "#cf4055"}, {82, 48, 4, "#cd4359"}, {8, 43, 4, "#2855ea"}, {199, 48, 4, "#ca273c"}, {15, 49, 4, "#2650e1"}, {140, 28, 4, "#4a7bf9"}, {44, 8, 4, "#3d65e7"}, {196, 21, 3, "#f47875"}, {191, 28, 3, "#f36764"}, {154, 49, 3, "#1d4eeb"}, {146, 53, 3, "#698bf1"}, {116, 19, 3, "#fac652"}, {58, 34, 3, "#ee5257"}, {63, 45, 3, "#cf2a3f"}, {25, 2, 3, "#5681f5"}, {6, 16, 3, "#4577f6"}, {100, 33, 3, "#f7b326"}, {160, 53, 3, "#2b58e8"}, {107, 20, 3, "#facb5e"}, {60, 39, 3, "#e02e3d"}, {206, 19, 3, "#f16d6f"}, {35, 3, 3, "#507bf2"}, {16, 5, 3, "#5683f7"}, {140, 70, 3, "#3158e2"}, {74, 19, 3, "#f0696c"}, {4, 23, 3, "#3769f6"}, {38, 37, 3, "#6084ef"}, {4, 29, 3, "#2a5cf4"}, {65, 22, 3, "#f4716e"}, {101, 26, 3, "#f8c247"}, {82, 22, 3, "#e74653"}, {191, 35, 3, "#ec4147"}, {136, 60, 3, "#4876f1"}, {61, 28, 3, "#ef5c5c"}, {136, 65, 3, "#2552ea"}, {10, 10, 3, "#4779f7"}, {139, 56, 3, "#4b78f1"} }; double logoW, logoH; computeBounds(pointData, logoW, logoH); double offsetX = (TOP_SCREEN_WIDTH / 2.0) - (logoW / 2.0); double offsetY = (TOP_SCREEN_HEIGHT / 2.0) - (logoH / 2.0); // Center the points on the top screen for (const auto& data : pointData) { double x = offsetX + data.x; double y = offsetY + data.y; pointCollection.addPoint(x, y, 0.0, static_cast<double>(data.size), data.color); } } void handleInput() { hidScanInput(); u32 kDown = hidKeysDown(); if (kDown & KEY_START) { running = false; } // Improved touch input handling for real hardware u32 kHeld = hidKeysHeld(); if (kHeld & KEY_TOUCH) { hidTouchRead(&touch); // Less restrictive validation - allow edge touches and zero coordinates if (touch.px >= 0 && touch.px <= TOP_SCREEN_WIDTH && touch.py >= 0 && touch.py <= TOP_SCREEN_HEIGHT) { touching = true; pointCollection.mousePos.set(touch.px, touch.py); } } else { touching = false; } // Handle circle pad input (alternative to touch) circlePosition circlePos; hidCircleRead(&circlePos); // Use proper threshold constants instead of magic numbers const int CIRCLE_THRESHOLD = 20; if (abs(circlePos.dx) > CIRCLE_THRESHOLD || abs(circlePos.dy) > CIRCLE_THRESHOLD) { static double padX = TOP_SCREEN_WIDTH / 2.0; static double padY = TOP_SCREEN_HEIGHT / 2.0; // Smoother circle pad movement padX += circlePos.dx / 25.0; padY -= circlePos.dy / 25.0; // Invert Y axis // Clamp to screen bounds padX = std::max(0.0, std::min((double)TOP_SCREEN_WIDTH, padX)); padY = std::max(0.0, std::min((double)TOP_SCREEN_HEIGHT, padY)); pointCollection.mousePos.set(padX, padY); }} void update() { pointCollection.update(); } void limitFrameRate() { u64 currentTime = osGetTime(); u64 deltaTime = currentTime - lastFrameTime; u64 targetTime = FRAME_TIME_NS / 1000000; // Convert to milliseconds if (deltaTime < targetTime) { u64 sleepTime = targetTime - deltaTime; svcSleepThread(sleepTime * 1000000); // Convert to nanoseconds } lastFrameTime = osGetTime(); } void render() { // Render top screen C3D_FrameBegin(C3D_FRAME_SYNCDRAW); C2D_TargetClear(topTarget, C2D_Color32f(1.0f, 1.0f, 1.0f, 1.0f)); // White background C2D_SceneBegin(topTarget); pointCollection.draw(); // Fixed: Check circle pad input properly circlePosition circlePos; hidCircleRead(&circlePos); bool circlepadActive = (circlePos.dx > 20 || circlePos.dx < -20 || circlePos.dy > 20 || circlePos.dy < -20); // Draw pointer when interacting if (touching) { // Pulsing red pointer when dragging with touch static u32 pulseCounter = 0; pulseCounter++; float pulseIntensity = 0.5f + 0.5f * sinf(pulseCounter * 0.3f); u8 red = 255; u8 green = (u8)(100 * pulseIntensity); u8 blue = (u8)(100 * pulseIntensity); C2D_DrawCircleSolid(pointCollection.mousePos.x, pointCollection.mousePos.y, 0.0f, 4.0f, C2D_Color32(red, green, blue, 200)); // Outer ring C2D_DrawCircleSolid(pointCollection.mousePos.x, pointCollection.mousePos.y, 0.0f, 6.0f, C2D_Color32(red, green, blue, 100)); } else if (circlepadActive) { // Blue pointer when using circle pad C2D_DrawCircleSolid(pointCollection.mousePos.x, pointCollection.mousePos.y, 0.0f, 3.0f, C2D_Color32(100, 150, 255, 180)); } else { // Gray pointer when idle C2D_DrawCircleSolid(pointCollection.mousePos.x, pointCollection.mousePos.y, 0.0f, 2.0f, C2D_Color32(100, 100, 100, 100)); } // Render bottom screen (instructions) C2D_TargetClear(bottomTarget, C2D_Color32f(0.9f, 0.9f, 0.9f, 1.0f)); // Light gray background C2D_SceneBegin(bottomTarget); // Draw instruction text centered on bottom screen float textWidth, textHeight; C2D_TextGetDimensions(&instructionText, 0.6f, 0.6f, &textWidth, &textHeight); float textX = (BOTTOM_SCREEN_WIDTH - textWidth) / 2.0f; float textY = (BOTTOM_SCREEN_HEIGHT - textHeight) / 2.0f; C2D_DrawText(&instructionText, C2D_WithColor, textX, textY, 0.0f, 0.6f, 0.6f, C2D_Color32(50, 50, 50, 255)); C3D_FrameEnd(0); } void run() { while (running && aptMainLoop()) { handleInput(); update(); render(); limitFrameRate(); // Lock to 33 FPS } } void cleanup() { C2D_TextBufDelete(textBuf); C2D_Fini(); C3D_Fini(); gfxExit(); romfsExit(); }};
int main(int argc, char* argv[]) { App app; if (!app.init()) { return -1; } app.run(); app.cleanup(); return 0;}