STAR TRAILS // SEBASTIAN WOLF

Inspired by photos and videos of star trails my aim was to recreate the look of these in an interactive processing sketch.

In this version of the code you are able to randomly add new stars and show/hide/fade their trails, and switch to »Vortex Mode«


Implementation

So, what does a starry sky consist of and how can we code something similar? At first you have to create the stars, they are all basically just little dots and circles with different size, color and brightness. Does not seem to be a problem for the size, just let it be random. Same for brightness and... oh wait – stars do not have random colors, there are no green stars for example. There are mostly white, blue, yellow and some red stars. It turns out that the color of a star depends on its energy level, its temperature ... astronomycafe The point is, I can not use random() for my colors, so I create an array of colors I want to have and pick my colors out of that. To get the right amount of stars of each color I simply say »15 out of 20 colors in my array are blueish-white, 4 are yellow, 1 is red«. After thinking about that I do the same for size and brightness, so I get the right distribution of values – I do not want all my stars to be maximum sized and ultra bright, I need a little more control. Well, now there is a star field. To get some traces the stars have to be moved and drawn again every frame. You could do this by assigning every single star a vector changing over time but there is a much simpler way for this particular idea. Since it is not the stars that are moving but our earth, they all move the same. To move all the stars at once I simply move the whole coordinate system with rotate() and translate(). In this version of the sketch there is only one kind of movement – rotation – but you could easily write other types of movement, too. There is the »Vortex Mode« though, it works not by moving the whole system but by moving every single star. With the function moveSystem(int mode) the coordinate system can be moved, thereby new motion patterns can be created. The patterns can be selected via the MODE value.

Program

Processing einfuehrung final sebastian.jpg


// import processing.opengl.*;

/////////////////////////////////////////////
// TRY PRESSING f, t, and v
/////////////////////////////////////////////
int winWidth   = 800;   // width of the window
int count      = 100;    // how many "stars" to draw at once
float maxSize  = 3;     // maximum size of "stars"
float centerX  = 66;    // percent
float centerY  = 62;    // percent
boolean trace  = true;  // if "stars" traces should be drawn
boolean fade   = false; // if traces should fade out
boolean vortex = false; // if "stars" move to one point
/////////////////////////////////////////////

int winHeight = int((winWidth/16)*9);    // aspect ratio 16/9
ArrayList circles = new ArrayList();     // ArrayList to store objects of the Circle class
float step = 0;                          // initial state of rotation steps (360steps)
color colors[] = new color[20];          // Array of possible colors
float sizes[] = new float[20];           // Array of possible sizes


void setup()
{
  size(winWidth, winHeight, JAVA2D);     // JAVA2D (standard) renderer for smooth rendering
  frameRate(24);
  noStroke();
  smooth();
  //hint( ENABLE_OPENGL_2X_SMOOTH );     // 2x "antialiasing" for OPENGL renderer
  //hint( ENABLE_OPENGL_4X_SMOOTH );     // 4x "antialiasing" for OPENGL renderer

  colorMode(HSB, 360);
  background(0);

  createSizes();
  createColors();
  createCircles();
}

void draw() {
  if (!trace) {
    background(0);
  }
  pushMatrix();
  translate((centerX/100)*width, (centerY/100)*height);
  moveSystem(2);
  drawCircles();
  popMatrix();
  step += .005;
  if (step >= 360) {
    step = 0;
  }
  if (fade) {
    fill(0, 10);
    rect(0, 0, width, height);
  }
}

void mouseReleased() {
  if (circles.size() >= 1) {
    createCircles();
  }
}

void keyReleased() {
  if (key == 't' || key == 'T') {
    trace = !trace;
  }
  if (key == 'f' || key == 'F') {
    fade = !fade;
  }
  if (key == 'v' || key == 'V') {
    vortex = !vortex;
  }
}

void createCircles() {
  for (int i = 0; i < count; i++) {
    float sz = sizes[int(random(sizes.length))];
    color clr = colors[int(random(colors.length))];
    circles.add(new Circle(new PVector(random(-width, width), random(-height, height), sz), clr));
  }
}

void drawCircles() { 
  for (int i = 0; i < circles.size(); i++) {
    Circle crcl = (Circle)circles.get(i);
    PVector target = new PVector(0, 0, 0);
    if (vortex) {
      crcl.vortex(target);
    }
    crcl.display();
  }
}

void createSizes() {
  for (int i = 0; i < sizes.length; i++) {
    sizes[i] = random(maxSize/3);      // most "stars" are not larger than a third of maxSize
    if (i <= sizes.length/5) {         // every 5th "star"
      // is
      sizes[i] = random(maxSize);    // between 0 and maxSize
    }
  }
}

void createColors() {
  for (int i = 0; i < colors.length; i++) {
    colors[i] = color(random(350, 360));
    if (i <= colors.length/2) {                                                                // half of all "stars"                                                                                           // are
      colors[i] = color(random(180, 240), random(60, 120), random(330, 360), random(60, 300));  // BLUE
    }
    if (i <= colors.length/4) {                                                                // every 4th                                                                                          // is
      colors[i] = color(random(50, 60), random(60, 120), random(330, 360), random(60, 180));    // YELLOW
    }    
    if (i <= random(1)) {                                                                // of those one or two
      // are
      colors[i] = color(random(0, 15), random(180, 240), random(330, 360), random(300, 360));   // RED
    }
  }
}

void moveSystem(int mode) {
  switch(mode) {
  case 1: 
    rotate(step);
  case 2: 
    //    translate(100*sin(step), 100*cos(step));
  }
}

class Circle { 
  PVector p;
  color c;

  Circle (PVector tempP, color tempC) {  
    p = tempP;
    c = tempC;
  }

  void vortex(PVector target){
    target = PVector.sub(target, p);  // find vector pointing towards target
    target.normalize();               // normalize it
    p.add(target);                    // add it to position vector
  }

  void display(){
    fill(c);
    ellipse(p.x, p.y, p.z, p.z);
  }
}