Sun

ANAR+ provides a Sun object which you can use to visualize the sun position in the sky hemisphere at given latitude, longitude, date and local time. Using projections it enables the computation of simple shadows.

Position on Earth

The position on the Earth is specified using two angles: latitude and longitude. The latitude is the angle on the North-South direction with zero value on the Equator (in ANAR+ south direction is negative). The longitude is the angle on the East-West direction from the Greenwich meridian. A meridian is the set of position with the same longitude which corresponds to a half great-circle on the Earth surface.

Local Time and Solar Time

Local time is actually dependent on time zones, which can be defined through the corresponding reference meridian. This is the meridian on which Sun zenith happens exactly at noon. For Central European Time (CET), such reference meridian is 15 degree East which corresponds to most eastern place in Germany, Görlitz, right next to Poland. As a result, the day in Switzerland is shifted towards the evening, e.g. sunrise and sunset times are not symmetrical to noon. Note that the Sun object currently does not take into account Daylight Saving time changes.

Since the Earth travels on an ellipse on the ecliptic plane, solar time is also shifting from a mechanical clock along the year. This shift is computed using the equation of time.

Julian day

The date is described using the julian day which corresponds to the number of day from the beginning of the year. January 1st is Julian day 1 while December 31st is Julian day 365 (or 366 in leap year - not considered in the Sun object).

Azimuth and Altitude

Sun position is described using two angles: altitude and azimuth. The altitude is the angle of the sun above the horizon at a given local time, while the azimuth is the angle departing from geographical South with negative values being East of South. Changing the latitude has an influence on the altitude, while changing longitude and meridian affects the azimuth.

Since the Earth axis makes an angle with the normal to the ecliptic plane (23.45 degrees), the sun altitude for a given time and position changes along the year. This change more dramatic as latitude increase and is responsible for seasons and midnight sun.

some links

For illustrations and explanations of concepts and equations see http://www.usc.edu/dept/architecture/mbs/tools/vrsolar/Help/solar_concepts.html where most computation are extracted from, except from equation of time and reference meridian.

http://www.srrb.noaa.gov/highlights/sunrise/calcdetails.html for a more official source of solar computations

Using the Sun object

The most basic way of creating a sun is

Sun sun = new Sun();
...
sun.draw();

It is possible to extract the parametric values from it

Sun sun = new Sun();
 
Anar.sliders(sun);

Another way is to create a set of Param (a Sliders object) from date, time and position and get a Sun object out of it

Sliders dateTime = Sun.getDateTime(1,5,8,27,42,-6,-15); // day, month, hour, minutes, latitude, longitude, ref. meridian
Sun sun = new Sun(dateTime);
...
sun.draw();

The following code does the same in one line

Sun sun = new Sun(1,5,8,27,42,-6,-15); // day, month, hour, minutes, latitude, longitude, ref. meridian

The set of Param is organised as follows:

  1. Julian day from 1 to 365
  2. time (in float)
  3. latitude
  4. longitude
  5. reference meridian

These Param can be set to change parametrically the position of the sun

dateTime.get(0).set(123) // sets the date
dateTime.get(1).set(12.3) // sets the time
...

There are various functions to help in the date and time conversion

Sun.hourFloat(12,30) // return 12.5
Sun.minutesFloat(12.5) // returns 30
Sun.julianDay(31,12) // returns 365 (no leap year)
Sun.month(365) //returns 12
Sun.day(365) // returns 31

Note that for display purposes, the Sun is given a distance which can be specified and/or made parametric.

float distSun = 1000;
Sun sun0 = new Sun(distSun); 
 
Sliders dateTime = Sun.getDateTime(1,5,8,27,42,-6,-15); // day, month, hour, minutes, latitude, longitude, ref. meridian
Sun sun1 = new Sun(dateTime,distSun);
 
Param distSunParam = new Param(1000,100,3000);
Sun sun2 = new Sun(dateTime,distSunParam);
 
Sun sun3 = new Sun(1,5,8,27,42,-6,-15,distSun); // day, month, hour, minutes, latitude, longitude, ref. meridian, distance 

The Sun object is a Pt so you can change its appearance using Renders relevant to Pt objects.

Sun sun = new Sun(300);
sun.render = new RenderPtShapeOriented(new Circle(10), new AColor(255,0,0), new AColor(255,100,0),Anar.scene);

Cast shadows using projections

The position of the sun can be used to define a projection direction on a given plane to compute shadows.

Sun sun = new Sun();
Pt a,b,c // points defining a plane
...
Pt p
Pts l;
Face f;
Obj o;
Group g;
...
Pt shadowPt = p.project(sun,a,b,c);
Pts shadowPt = l.project(sun,a,b,c);
Face shadowPt = f.project(sun,a,b,c);
Obj shadowPt = o.project(sun,a,b,c);
Group shadowPt = g.project(sun,a,b,c);

Note that a complex scene would require quite a lot of work to accurately display all shadows. It is deliberately left to the user to take care of it in order to limit computation to what is effectively required. Carefully considering what design question has to be answered most of the time results in simple computations.

The following code presents an example of shadow casting using a projection on XY plane.

import processing.opengl.*;
import anar.*;
 
Sun sun;
 
Obj box;
Obj shade;
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis();
 
  Anar.drawReferenceFrame();
 
  initForm();  
}
 
void initForm(){
 
  box = new Box(10,20,30);
  box.translate(70,30,0);
 
  // init sun
  sun = new Sun(300);
  sun.render = new RenderPtShapeOriented(new Circle(10), new AColor(255,0,0), new AColor(255,100,0),Anar.scene);
 
  // find shadows  
  shade = box.project(sun,Pt.ORIGIN,Pt.xAxis,Pt.yAxis);
 
  Anar.sliders(sun);
  Anar.camTarget(box);  
}
 
void draw(){
  background(155);
  box.draw();
  sun.draw();
  shade.draw();
}

More objects in the scene, grouped in a Group. The code is compacted compared to the above version.

import processing.opengl.*;
import anar.*;
 
Sun sun;
 
Group group;
Group shade;
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis();
 
  initForm();  
}
 
void initForm(){
 
  group = new Group();
 
  group.add(new Box(10,20,30).translate(70,30,0));
  group.add(new Cylinder(50,24,50).translate(-100,0,0));
  group.add(new SwissCross3D(25,25).translate(50,-50,37.5));
 
  // init sun
  sun = new Sun(300);
  sun.render = new RenderPtShapeOriented(new Circle(10), new AColor(255,0,0), new AColor(255,100,0),Anar.scene);
 
  shade = group.project(sun,Pt.ORIGIN,Pt.xAxis,Pt.yAxis);
 
  Anar.sliders(sun);
  Anar.camTarget(group);  
}
 
void draw(){
  background(155);
  sun.draw();
  group.draw();
  shade.draw();
}

Sun paths

ANAR+ can provide sun paths for a given day and position. It distributes the requested number of sun positions evenly between local sunrise and sunset time.

Pts path = Sun.getPath(21, 3, 42, 6, -15, 20, 300); // day, month, latitude, longitude, ref. meridian, nb of points, distance

Displaying equinox paths results in a visualization of possible solar positions over the whole year.

import processing.opengl.*;
import anar.*;
 
Obj paths;
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis();
 
  initForm();  
}
 
void initForm(){
 
  paths = new Obj();
 
  // add equinox sun paths in Geneva
  paths.add( Sun.getPath(21, 3, 42, -6, -15,40,300));
  paths.add( Sun.getPath(21, 6, 42, -6, -15,40,300));  
  //paths.add( Sun.getPath(21, 9, 42, -6, -15,40,300)); // not needed, same as first one
  paths.add( Sun.getPath(21, 12, 42, -6, -15,40,300));
 
}
 
void draw(){
  background(155);
  paths.draw();
}

The sun path can be used to compute a simple solar envelope.

import processing.opengl.*;
import anar.*;
 
Pts path;
 
Obj box;
Obj shade;
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis();
 
  initForm();  
}
 
void initForm(){
 
  box = new Box(10,20,30);
  box.translate(70,30,0);
 
  // get path
  path = Sun.getPath(21, 3, 42, 6, -15, 20, 300); // day, month, latitude, longitude, ref. meridian, nb of points, distance
 
  // initialize envelope
  shade = new Obj();
 
  for (int i=0; i<path.numOfPts(); i++) {
    shade.add(box.project(path.pt(i),Pt.ORIGIN,Pt.xAxis,Pt.yAxis));
  } 
 
  Anar.sliders(path);
  Anar.camTarget(box);  
}
 
void draw(){
  background(155);
  box.draw();
  path.draw();
  shade.draw();
}

Analemmas

Considering all positions of the sun over the year at a given local time results in a specific figure 8 pattern on the sky hemisphere. It is called an analemma. This pattern is due to the conjunction of the declination (the angle of the Earth axis with the ecliptic plane) and the equation of time.

http://en.wikipedia.org/wiki/Analemma

Pts path = Sun.getAnalemma(8.25,42,-6,-15,20,300); // time (in float), lat., lon., ref. mer., nb of points, distance

Note that when the Sun is below the horizon the analemma path jumps to the point where it comes again above the horizon.

import processing.opengl.*;
import anar.*;
 
Obj paths = new Obj();
 
void setup(){
  size(800,400,OPENGL);
  Anar.init(this);
  Anar.drawAxis();
 
  initForm();  
}
 
void initForm(){
 
  // add analemmas at 7:15, 10:24 and 16:54 in Geneva
  paths.add( Sun.getAnalemma(7.25, 42, -6, -15,20,300));
  paths.add( Sun.getAnalemma(10.4, 42, -6, -15,20,300));
  paths.add( Sun.getAnalemma(16.9, 42, -6, -15,20,300));
 
}
 
void draw(){
  background(155);
  paths.draw();
}

Going further

Using the Sun object is useful for geometric computations. If you want numeric information about light availability or the amount of solar energy that hits a surface, you should use a ray tracing simulation. Radiance is one of such simulations which has been extensively used and tested in daylighting computations. The ANAR+ library provides a bridge to use this simulation within the processing.org environment.

http://radsite.lbl.gov/radiance/ the official Radiance website

a tutorial on using radiance from ANAR+/processing.org