๐ŸŽ‡ Gravity Comparision Data Vis

BamgasiJMยท2026๋…„ 1์›” 23์ผ

p5.js Art

๋ชฉ๋ก ๋ณด๊ธฐ
50/65
post-thumbnail

๐Ÿ“ main.c

#include <stdio.h>
#include <string.h>
#include <math.h>

// ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์ฒด ์ •์˜: ์ฒœ์ฒด ์ด๋ฆ„๊ณผ ์ค‘๋ ฅ ๊ฐ€์†๋„๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
typedef struct
{
    const char *name;
    double gravity;
} CelestialBody;

// ์ดˆ๊ธฐ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ƒ์ˆ˜ ์„ค์ •
#define INITIAL_VELOCITY 20.0 // ์ดˆ๊ธฐ ์†๋„ (m/s)
#define TIME_STEP 0.01        // ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ (s)

/**
 * @brief ์ค‘๋ ฅ ๊ฐ€์†๋„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ CSV ํŒŒ์ผ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
 * * @param v0 ์ดˆ๊ธฐ ์†๋„ (m/s)
 * @param dt ์‹œ๊ฐ„ ๊ฐ„๊ฒฉ (s)
 * @param g ์ค‘๋ ฅ ๊ฐ€์†๋„ (m/s^2)
 * @param filename ์ถœ๋ ฅ ํŒŒ์ผ ์ด๋ฆ„
 */
void run_simulation(double v0, double dt, double g, const char *filename)
{
    FILE *fp = fopen(filename, "w");
    if (fp == NULL)
    {
        // ํŒŒ์ผ ์ƒ์„ฑ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
        fprintf(stderr, "Error: Could not open file %s\n", filename);
        return;
    }

    // CSV ํ—ค๋” ์ž‘์„ฑ (์‹œ๊ฐ„, ์œ„์น˜)
    fprintf(fp, "Time (s),Position (m)\n");

    double t = 0.0;
    double y_initial = 0.0;
    double y = 0.0;

    // ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ฃจํ”„
    // ๋ฌผ์ฒด๊ฐ€ ๋‹ค์‹œ ๋•…(y=0)์œผ๋กœ ๋Œ์•„์˜ฌ ๋•Œ๊นŒ์ง€ ๊ณ„์‚ฐ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    while (1)
    {
        // ์œ„์น˜ ๊ณ„์‚ฐ ๊ณต์‹: y(t) = y_initial + v0 * t - 0.5 * g * t^2
        y = y_initial + v0 * t - 0.5 * g * t * t;

        // ๋ฌผ์ฒด๊ฐ€ ๋•…์— ๋‹ฟ๊ฑฐ๋‚˜ ๋•… ์•„๋ž˜๋กœ ๋‚ด๋ ค๊ฐ€๋ ค๊ณ  ํ•˜๋ฉด (์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์ข…๋ฃŒ ์กฐ๊ฑด)
        if (y < 0.0)
        {
            // ์ด์ „์— ๋•…์„ ๋šซ๊ณ  ๋‚ด๋ ค๊ฐ”์œผ๋ฏ€๋กœ, ๋งˆ์ง€๋ง‰ ์œ„์น˜๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
            y = 0.0;
            fprintf(fp, "%.6f,%.6f\n", t, y);
            break;
        }

        // CSV์— ๋ฐ์ดํ„ฐ ์ž‘์„ฑ (์‹œ๊ฐ„, ์œ„์น˜)
        fprintf(fp, "%.6f,%.6f\n", t, y);

        // ๋‹ค์Œ ์‹œ๊ฐ„ ๋‹จ๊ณ„๋กœ ์ด๋™
        t += dt;

        // ์ตœ๋Œ€ ๋น„ํ–‰ ์‹œ๊ฐ„ (2 * v0 / g)์˜ 2๋ฐฐ๋ฅผ ๋„˜์–ด๊ฐ€๋ฉด ์•ˆ์ „์„ ์œ„ํ•ด ๋ฃจํ”„๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค.
        // ํƒœ์–‘์˜ ๊ฒฝ์šฐ ๋น„ํ–‰ ์‹œ๊ฐ„์ด ๋งค์šฐ ์งง๊ธฐ ๋•Œ๋ฌธ์— ์ด ์กฐ๊ฑด์ด ๊ณผ๋„ํ•˜๊ฒŒ ๋А๋ฆฐ ๋ฃจํ”„๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
        if (t > (2.0 * v0 / g) + 1.0)
        {
            break;
        }
    }

    fclose(fp);
    printf("-> Simulation results for %s (G=%.2f) saved to %s\n",
           filename, g, filename);
}

int main()
{
    // ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•  ์ฒœ์ฒด ๋ชฉ๋ก ์ •์˜
    CelestialBody bodies[] = {
        {"Earth", 9.80665},
        {"Moon", 1.62},
        {"Mars", 3.71},
        {"Jupiter", 24.79},
        {"Neptune", 11.15},
        {"Sun", 274.0}};
    int num_bodies = sizeof(bodies) / sizeof(bodies[0]);
    char filename_buffer[256];

    printf("Starting Gravity Simulation for %d bodies (V0=%.1fm/s, dT=%.2fs):\n",
           num_bodies, INITIAL_VELOCITY, TIME_STEP);
    printf("------------------------------------------------------------------\n");

    // ๋ชจ๋“  ์ฒœ์ฒด์— ๋Œ€ํ•ด ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ๋ฐ˜๋ณต ์‹คํ–‰
    for (int i = 0; i < num_bodies; i++)
    {

        // ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ (์˜ˆ: Earth_data.csv)
        // snprintf๋Š” ๋ฌธ์ž์—ด ๋ฒ„ํผ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ์•ˆ์ „ํ•œ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
        snprintf(filename_buffer, sizeof(filename_buffer), "%s_data.csv", bodies[i].name);

        // ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ์‹คํ–‰ ๋ฐ ํŒŒ์ผ ์ €์žฅ
        run_simulation(INITIAL_VELOCITY, TIME_STEP, bodies[i].gravity, filename_buffer);
    }

    printf("------------------------------------------------------------------\n");
    printf("All simulations completed successfully. Check the generated CSV files.\n");

    return 0;
}

๐Ÿ“ p5.js

// p5.js ์ฝ”๋“œ
// ํ”„๋กœ์ ํŠธ ํด๋”์— CSV ํŒŒ์ผ๋“ค์„ ์—…๋กœ๋“œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

let tables = {};
let planets = [
  { name: "Sun", file: "Sun_data.csv", color: "#FFD700" }, // ๋…ธ๋ž‘
  { name: "Jupiter", file: "Jupiter_data.csv", color: "#D2691E" }, // ๊ฐˆ์ƒ‰
  { name: "Neptune", file: "Neptune_data.csv", color: "#4169E1" }, // ํŒŒ๋ž‘
  { name: "Earth", file: "Earth_data.csv", color: "#32CD32" }, // ์ดˆ๋ก
  { name: "Mars", file: "Mars_data.csv", color: "#FF4500" }, // ์ฃผํ™ฉ
  { name: "Moon", file: "Moon_data.csv", color: "#C0C0C0" }, // ํšŒ์ƒ‰
];

let maxTime = 0;
let maxPos = 0;
let dataPoints = []; // ํŒŒ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋ฐฐ์—ด
let currentIndex = 0; // ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„ ์ธ๋ฑ์Šค

function preload() {
  // ๋ชจ๋“  CSV ํŒŒ์ผ ๋กœ๋“œ
  for (let p of planets) {
    tables[p.name] = loadTable(p.file, "csv", "header");
  }
}

function setup() {
  createCanvas(1000, 600);
  frameRate(60);

  // ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑ ๋ฐ ์ตœ๋Œ€๊ฐ’ ์ฐพ๊ธฐ (์Šค์ผ€์ผ๋ง์šฉ)
  for (let p of planets) {
    let table = tables[p.name];
    let rows = table.getRows();
    let pData = [];

    for (let r of rows) {
      let t = r.getNum("Time (s)");
      let y = r.getNum("Position (m)");

      pData.push({ t: t, y: y });

      if (t > maxTime) maxTime = t;
      if (y > maxPos) maxPos = y;
    }
    dataPoints.push({ meta: p, data: pData });
  }

  // ์—ฌ๋ฐฑ์„ ์œ„ํ•ด ์ตœ๋Œ€๊ฐ’ ์•ฝ๊ฐ„ ์ฆ๊ฐ€
  maxPos *= 1.1;

  textSize(14);
}

function draw() {
  background(30);

  // ์ถ• ๊ทธ๋ฆฌ๊ธฐ
  stroke(255);
  line(50, height - 50, width - 50, height - 50); // X์ถ•
  line(50, height - 50, 50, 50); // Y์ถ•

  // ๋ฒ”๋ก€ ๋ฐ ํ…์ŠคํŠธ ํ‘œ์‹œ
  noStroke();
  fill(255);
  text(`Max Height: ${maxPos.toFixed(1)}m`, 60, 40);
  text(`Max Duration: ${maxTime.toFixed(1)}s`, width - 150, height - 30);

  // ๊ฐ ํ–‰์„ฑ๋ณ„ ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ
  for (let i = 0; i < dataPoints.length; i++) {
    let planet = dataPoints[i];
    let path = planet.data;

    stroke(planet.meta.color);
    strokeWeight(2);
    noFill();

    // ํ˜„์žฌ ํ”„๋ ˆ์ž„๊นŒ์ง€๋งŒ ๊ฒฝ๋กœ ๊ทธ๋ฆฌ๊ธฐ (์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ)
    beginShape();
    let drawLimit = min(currentIndex, path.length - 1);

    for (let j = 0; j <= drawLimit; j++) {
      // ํ™”๋ฉด ์ขŒํ‘œ๋กœ ๋งคํ•‘
      // x: ์‹œ๊ฐ„ (0 ~ maxTime) -> ํ™”๋ฉด ๋„ˆ๋น„ (50 ~ width-50)
      // y: ๋†’์ด (0 ~ maxPos) -> ํ™”๋ฉด ๋†’์ด (height-50 ~ 50)
      let sx = map(path[j].t, 0, maxTime, 50, width - 50);
      let sy = map(path[j].y, 0, maxPos, height - 50, 50);
      vertex(sx, sy);

      // ํ˜„์žฌ ์œ„์น˜์— ์› ๊ทธ๋ฆฌ๊ธฐ (ํ—ค๋“œ)
      if (j === drawLimit) {
        push();
        fill(planet.meta.color);
        noStroke();
        ellipse(sx, sy, 8, 8);

        // ํ–‰์„ฑ ์ด๋ฆ„ ๋ผ๋ฒจ
        text(planet.meta.name, sx + 10, sy);
        pop();
      }
    }
    endShape();
  }

  // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ง„ํ–‰ (์†๋„ ์กฐ์ ˆ ๊ฐ€๋Šฅ)
  // ๋ฐ์ดํ„ฐ๊ฐ€ ๋งŽ์œผ๋ฏ€๋กœ ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ์Šคํ…์”ฉ ์ ํ”„ํ•˜์—ฌ ์†๋„๊ฐ ์žˆ๊ฒŒ ์žฌ์ƒ
  currentIndex += 5;

  // ๋ฃจํ”„ ์ข…๋ฃŒ ์กฐ๊ฑด (๊ฐ€์žฅ ๊ธด ๋ฐ์ดํ„ฐ ๊ธฐ์ค€)
  let longestData = 0;
  for (let p of dataPoints) longestData = max(longestData, p.data.length);

  if (currentIndex > longestData + 100) {
    noLoop(); // ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋
  }
}

profile
Coding Art with Blender / oF / Processing / p5.js / nannou

0๊ฐœ์˜ ๋Œ“๊ธ€