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.
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 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.
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).
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.
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
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:
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);
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(); }
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(); }
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(); }
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