KAZ-SYED-ABUTHAHIR_PlanetarySystemSimulation/PlanetarySystemSimulation.pde

/**
 
 @ author  Dr. KAZ. Syed Abuthahir
 Refactoring On from Iteration 8: 15.06.2018
 Primary aim of refactoring is to cast SideBar class in a more reusable form
 Emphasis is on Code Reusability...And Don't forget the famous Arrow class
 Grand Success Need not to explicitly draw SideBar. Handled in the SideBar class itself.
 Refactoring after Nearly 3  Years. 22.10.2021 Friday Iteration 25.
 Implemented Image Feature For Showing Planets and the Atrractor. PNG  images are used for the Sun [as the Attractor] and the Planets. [21.07.2024]
 Also added Foci to the Orbital Points to be shown.[[21.07.2024]
 Areal Sweep Added On 28.07.2024. Refactoring Due Within : 31.08.2024
 Kepler's Broom Added On 03.08.2024 11.01 PM
 
 */

import gifAnimation.*;

Attractor sun;
PImage attractorImage ;
Gif attractorGif;
PImage planetImage;

ArrayList<Planet> planets;
SideBar sb;

SideBar SB;
TextBar txtBar, txtBarSB, txtBarsb;
int highlightedPlanetIndex = -1;

float zoomFactor = 0.575f; //0.575; //This is the optimal zoom factor for speed...
float zoomStep = 0.01;

float panX = 0, panY = 0;
float panStep = 5;

float pMouseX, pMouseY;

boolean showOrbitPoints = false;
boolean showTrail = false;

boolean isStopped = false;
boolean showKeplersBroom = false;
boolean sweepRadial = true;


PVector velocityAnchor  = new PVector(600, 0);
PVector accelerationAnchor  = new PVector(600, 100);

final int FRAME_RATE = 60;

PImage sbBkImg, SBBkImg;

PImage imgKeplersBroom ;


void settings() {
  fullScreen();
  smooth();
}


void setup() {
  SBBkImg = loadImage("JupiterSaturn.jpg");
  sbBkImg = loadImage("outer-space-stars-galaxies-planets-background-images.jpg");

  frameRate(FRAME_RATE);
  surface.setTitle("PLANETARY ADVENTURES: KEPLER'S PARADISE");
  sb = new SideBar(this, 0, 2.5*width/3, 2*height/3);
  SB = new SideBar(this, 2*height/3, 2*width/3, height/2);
  sb.setImage(sbBkImg);
  SB.setImage(SBBkImg);

  txtBar = new TextBar(10, 10, (sb.getDrawWidth()-20), sb.barHeight/1.8-10);
  txtBarsb = new TextBar(10, sb.barHeight/1.8+10, (sb.getDrawWidth()-20), sb.barHeight-(sb.barHeight/1.8+20));
  txtBarSB = new TextBar(10, 10, (SB.getDrawWidth()-20), SB.barHeight/2-20);

  txtBar.setTextSize(22);
  txtBarSB.setTextSize(22);


  sb.addChild(txtBarsb);
  sb.addChild(txtBar);
  SB.addChild(txtBarSB);

  String message = "Interactivity\n\n" +
    "Click and drag to add a Planet. The arrow represents the velocity vector.\n" +
    "Press \'h\' or \'H\' to cycle through planets for selection.\n" +
    "Press \'d\' or \'D\' to delete the selected planet.\n" +
    "Press 'a','s','w' or 'z' for panning\n" +
    "Press \'p\' or \'P\' to Toggle Showing Foci, Orbit Center, Apoapsis and Periapsis Points\n" +
    "Orbital parameters for the selected planet are shown in the above text field";

  txtBarsb.setText(message);
  txtBarsb.setTextSize(16);

  imgKeplersBroom = loadImage("KeplersBroom01.png");
  attractorImage = loadImage("EarthPolar.png");
  planetImage = loadImage("MoonPolarMini.png");
  attractorGif =  new Gif(this, "sun-7.gif");
  attractorGif.loop();

  sun = new Attractor(attractorImage);
  planets = new ArrayList<Planet>();

  //  thread("calculateForceandUpdate");
  //  background(0);
}

void draw() {
  if (!isStopped) {
    if (!mousePressed || sb.inFocus() || SB.inFocus()) {
      if (showTrail) {
        fill(0, 8);
        noStroke();
        rect(0, 0, width, height);
      } else {
        background(0);
      }
      //image(attractorGif, (width - attractorGif.width) / 2, (height - attractorGif.height) / 2);
      pushMatrix();

      scale(zoomFactor);
      translate(width/2*(1/zoomFactor), height/2*(1/zoomFactor));
      pan(panX, panY);

      sun.show();
      // Draw Kepler's Broom

      for (Planet p : planets) {
        //p.showVelocity(velocityAnchor);
        p.update();
        p.showOrbit(showOrbitPoints);
        p.show();
        p.calcForce(sun);
        p.showVelocity();
        p.showAcceleration();
        //p.showAcceleration(accelerationAnchor);
      }

      if (showKeplersBroom && highlightedPlanetIndex > -1) {
        //sun.setRotAngle(27.3*planets.get(highlightedPlanetIndex).rotAngle);
        //planets.get(highlightedPlanetIndex).showRadial(sun);
        PVector startPoint = planets.get(highlightedPlanetIndex).position;
        PVector endPoint = sun.position;
        drawKplersBroom(startPoint, endPoint, imgKeplersBroom);
      }


      popMatrix();

      if (frameCount%10 == 0) {
        String debugMsg = "Debug Parameters"+
          "\nFrame Rate   : " + String.format("%.02f", frameRate) +
          "\nZoom Factor : " + String.format("%.02f", zoomFactor)+
          "\nhighlightedPlanetIndex : " + highlightedPlanetIndex;
        txtBarSB.setText(debugMsg, ':');
      }
    }
  }
}

void drawKplersBroom(PVector startPoint, PVector endPoint, PImage imgKeplersBroom) {
  // Calculate the angle of the line joining the two points
  float angle = atan2(endPoint.y - startPoint.y, endPoint.x - startPoint.x) + 1.57;

  // Calculate the midpoint of the line to place the image
  PVector midPoint = PVector.lerp(startPoint, endPoint, 0.5);

  // Calculate the distance between the two points
  float distance = PVector.dist(startPoint, endPoint);

  // Calculate the new dimensions of the image to fit between the points
  float aspectRatio = (float) imgKeplersBroom.width / imgKeplersBroom.height;
  float imgHeight = distance;
  float imgWidth = imgHeight * aspectRatio;

  // Draw the image oriented along the line
  pushMatrix();
  translate(midPoint.x, midPoint.y);
  rotate(angle);
  translate(-imgWidth / 2, -imgHeight/2); // Translate so the rotation happens at the bottom center of the image
  imageMode(CORNER);
  image(imgKeplersBroom, 0, 0, imgWidth, imgHeight); // Scale the image to fit between the points
  popMatrix();
}



void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      zoomFactor += zoomStep;
      if (zoomFactor>1) zoomFactor = 1;
      background(0);
    } else if (keyCode == DOWN) {
      zoomFactor -= zoomStep;
      if (zoomFactor<0.15) zoomFactor = 0.15;
      background(0);
    } else if (keyCode == RIGHT) {
      sb.slideOut();
    } else if (keyCode == LEFT) {
      sb.slideIn();
    }
  }

  if (key == 'D'||key == 'd') {
    if (planets.size()>0) {
      planets.remove(planets.get(highlightedPlanetIndex++));
      if (highlightedPlanetIndex > (planets.size()-1)) highlightedPlanetIndex = 0;
      if (planets.size()>0) {
        selectPlanet(highlightedPlanetIndex);
      } else {
        txtBar.setText("NO PLANETS AVAILABLE !");
        highlightedPlanetIndex = -1;
      }
      background(0);
    }
  }

  if (key == 'H'||key == 'h') {
    if (planets.size()>0) {
      planets.get(highlightedPlanetIndex).setHighlighted(false);
      planets.get(highlightedPlanetIndex).setSweepRadial(sweepRadial);
      highlightedPlanetIndex++;
      if (highlightedPlanetIndex > (planets.size()-1)) highlightedPlanetIndex = 0;
      planets.get(highlightedPlanetIndex).setSweepRadial(sweepRadial);
      selectPlanet(highlightedPlanetIndex);
    }
  }

  if (key == 'p' || key == 'P') {
    showOrbitPoints = !showOrbitPoints;
  }

  if (key == 'k' || key == 'K') {
    sweepRadial = !sweepRadial;
    planets.get(highlightedPlanetIndex).setSweepRadial(sweepRadial);
  }

  // Reset Zoom and Pan
  if (key == 'r' || key == 'R') {
    zoomFactor = 1.0f;
    panX = 0;
    panY = 0;
  }



  if (key == 'a' || key == 'A') {
    panX -= panStep;
  } else if (key == 's' || key == 'S') {
    panX += panStep;
  } else if (key == 'w' || key == 'W') {
    panY -= panStep;
  } else if (key == 'z' || key == 'Z') {
    panY += panStep;
  }

  if (keyPressed && key == ' ') {
    isStopped = !isStopped;
  }

  if (key == 'b' || key == 'B') {
    showKeplersBroom = !showKeplersBroom;
  }
}

void selectPlanet(int index) {
  planets.get(index).setHighlighted(true);
  txtBar.setText(planets.get(index).orbitDescriptor, ':');
  sb.setHandleColor(planets.get(index).planetColor);
}

void mouseDragged() {
  if (sb.inFocus() || SB.inFocus()) {
    return;
  }
  background(0);
  drawScene();
  Arrow arrow  = new Arrow(pMouseX, pMouseY, mouseX, mouseY);
  arrow.show();
}

void mousePressed() {
  if (SB.inFocus() || sb.inFocus()) {
    return;
  }
  pMouseX = mouseX;
  pMouseY = mouseY;

  noCursor();
}

void mouseReleased() {
  if (sb.inFocus() || SB.inFocus()) {
    return;
  }
  addNewPlanet((mouseX - pMouseX)/Planet.velocityArrowScale, (mouseY - pMouseY)/Planet.velocityArrowScale);
  background(0);
  cursor();
}

void addNewPlanet(float vx, float vy) {
  pushMatrix();
  //Important piece of transformation , I understand?
  float x = (pMouseX-width/2)/zoomFactor;
  float y = (pMouseY-height/2)/zoomFactor;
  float mass = 1.0;
  Planet p = new Planet(new PVector(x, y), new PVector(vx, vy), mass, sun, planetImage);
  p.setSweepRadial(sweepRadial);
  try {
    planets.add(p);
  }
  catch(Exception e) {
    e.printStackTrace();
  }

  if (planets.size()==1) {
    highlightedPlanetIndex = 0;
    selectPlanet(highlightedPlanetIndex);
  }
  popMatrix();
}

void drawScene() {
  background(0);
  pushMatrix();
  scale(zoomFactor);
  translate(width/2*(1/zoomFactor), height/2*(1/zoomFactor));
  pan(panX, panY);
  sun.show();
  popMatrix();
}

void pan(float shiftX, float shiftY) {
  translate(shiftX, shiftY);
}
