forked from FOSS/BangleApps
solar clock: First Commit
parent
29f34ad567
commit
03716d5b80
25
apps.json
25
apps.json
|
@ -265,6 +265,31 @@
|
|||
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "solarclock",
|
||||
"name": "Solar Clock",
|
||||
"icon": "solar_clock.png",
|
||||
"version":"0.01",
|
||||
"description": "The solar clock will use your current or chosen location to work out the the current sun phase of the day",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"allow_emulator":false,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"solarclock.app.js","url":"solar_clock.js"},
|
||||
{"name":"solarclock.img","url":"solar_clock-icon.js","evaluate":true},
|
||||
{"name":"solar_colors.js","url":"solar_colors.js"},
|
||||
{"name":"solar_controller.js","url":"solar_controller.js"},
|
||||
{"name":"solar_date_utils.js","url":"solar_date_utils.js"},
|
||||
{"name":"solar_graphic_utils.js","url":"solar_graphic_utils.js"},
|
||||
{"name":"solar_location.js","url":"solar_location.js"},
|
||||
{"name":"solar_math_utils.js","url":"solar_math_utils.js"},
|
||||
{"name":"solar_loc.Iceland.json","url":"solar_loc.Iceland.json"},
|
||||
{"name":"solar_loc.Kauai.json","url":"solar_loc.Kauai.json"},
|
||||
{"name":"solar_loc.Tokyo.json","url":"solar_loc.Tokyo.json"},
|
||||
{"name":"solar_loc.local.json","url":"solar_loc.local.json"}
|
||||
{"name":"solar_locations.json","url":"solar_locations.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "sweepclock",
|
||||
"name": "Sweep Clock",
|
||||
"icon": "sweepclock.png",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial Release
|
|
@ -0,0 +1 @@
|
|||
# Solar Clock
|
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("lEowkA/4AGmYIHABHzmVCCaE0kUin4TPmUimQTQ+UzmcvJ6EjCaP/kYABCaEymYTl+Q7SMgITTmQTQPAK0RMgITm+QTS+ciPCcikQpPY4MjmYTO+czmcyHh4TCmcvJ54nCPCBjBJx4oECc8zJ6ATTn48RE4YTTHh4SDH4ImRFBwTGFBgTGFBgSGFBYmHUgITRmcyFBASFAoUjE5PzkQLBHJxiDAQP/GxAA=="))
|
|
@ -0,0 +1,466 @@
|
|||
const DateUtils = require("solar_date_utils.js");
|
||||
const Math2 = require("solar_math_utils.js");
|
||||
const GraphicUtils = require("solar_graphic_utils.js");
|
||||
const Colors = require("solar_colors.js");
|
||||
const LocationUtils = require("solar_location.js");
|
||||
|
||||
var screen_info = {
|
||||
screen_width : g.getWidth(),
|
||||
screen_start_x : 0,
|
||||
screen_centre_x: g.getWidth()/2,
|
||||
screen_height : (g.getHeight()-100),
|
||||
screen_start_y : 100,
|
||||
screen_centre_y : 90 + (g.getHeight()-100)/2,
|
||||
screen_bg_color : Colors.BLACK,
|
||||
sun_radius: 8,
|
||||
sun_x : null,
|
||||
sun_y : null,
|
||||
sunrise_y : null,
|
||||
}
|
||||
const img_width=40;
|
||||
const img_height=30;
|
||||
var img_buffer = Graphics.createArrayBuffer(img_width,img_height,8);
|
||||
var img = {width:img_width,height:img_height,bpp:8,transparent:0,buffer:img_buffer.buffer};
|
||||
var img_info = {
|
||||
x: null,
|
||||
y: null,
|
||||
img: img,
|
||||
img_buffer: img_buffer
|
||||
}
|
||||
const COSINE_COLOUR= Colors.GREY;
|
||||
const HORIZON_COLOUR = Colors.GREY;
|
||||
const SolarController = require("solar_controller.js");
|
||||
var controller = new SolarController();
|
||||
var curr_mode = null;
|
||||
var last_sun_draw_time = null;
|
||||
var draw_full_cosine = true;
|
||||
|
||||
function draw_sun(now, day_info) {
|
||||
|
||||
var now_fraction = (now.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS;
|
||||
var now_x = now_fraction * screen_info.screen_width;
|
||||
if(screen_info.sun_x != null && Math.abs(now_x- screen_info.sun_x) < 1){
|
||||
console.log("no sun movement");
|
||||
return false;
|
||||
}
|
||||
// now calculate thew new sun coordinates
|
||||
var now_radians = Math2.TWO_PI *(now_x - screen_info.screen_centre_x)/screen_info.screen_width;
|
||||
var now_y = screen_info.screen_centre_y - (screen_info.screen_height * Math.cos(now_radians) / 2);
|
||||
if(Math.abs(now_x - screen_info.sun_x) > 5){
|
||||
clear_sun();
|
||||
}
|
||||
// update the screen info with the new sun info
|
||||
screen_info.sun_x = now_x;
|
||||
screen_info.sun_y = now_y;
|
||||
last_sun_draw_time = now;
|
||||
|
||||
if(draw_full_cosine){
|
||||
//console.log("drawing full cosine");
|
||||
GraphicUtils.draw_cosine(screen_info.screen_start_x,
|
||||
screen_info.screen_width,
|
||||
COSINE_COLOUR,
|
||||
screen_info);
|
||||
draw_full_cosine = false;
|
||||
}
|
||||
if(curr_mode == null) {
|
||||
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||
}
|
||||
// decide on the new sun drawing mode and draw
|
||||
curr_mode = controller.mode(now,day_info,screen_info);
|
||||
img_info.img_buffer.clear();
|
||||
img_info.img_buffer.setColor(screen_info.screen_bg_color[0],
|
||||
screen_info.screen_bg_color[1],
|
||||
screen_info.screen_bg_color[2],
|
||||
);
|
||||
img_info.img_buffer.fillRect(0,0,img_width, img_height);
|
||||
img_info.x = screen_info.sun_x - img_info.img.width/2;
|
||||
img_info.y = screen_info.sun_y - img_info.img.height/2;
|
||||
|
||||
var cosine_dist = screen_info.sun_radius/Math.sqrt(2);
|
||||
GraphicUtils.draw_cosine(img_info.x,
|
||||
screen_info.sun_x - cosine_dist,
|
||||
COSINE_COLOUR,
|
||||
screen_info,
|
||||
img_info);
|
||||
GraphicUtils.draw_cosine(screen_info.sun_x + cosine_dist,
|
||||
screen_info.sun_x + img_width,
|
||||
COSINE_COLOUR,
|
||||
screen_info,
|
||||
img_info);
|
||||
|
||||
curr_mode.draw(now,day_info,screen_info,img_info);
|
||||
|
||||
var sunrise_dist = Math.abs(screen_info.sunrise_y-screen_info.sun_y);
|
||||
if( sunrise_dist <= img_height) {
|
||||
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info,img_info);
|
||||
} else if(sunrise_dist <= img_height*2.5) {
|
||||
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||
}
|
||||
// we draw a blank where the image is going to be drawn to clear out the area
|
||||
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||
g.fillRect(img_info.x,img_info.y-2,img_info.x+img_width,img_info.y + img_height + 2);
|
||||
g.drawImage(img,img_info.x,img_info.y);
|
||||
// paint the cosine curve back to the normal color where it just came from
|
||||
GraphicUtils.draw_cosine(img_info.x - 3,
|
||||
img_info.x,
|
||||
COSINE_COLOUR,
|
||||
screen_info);
|
||||
GraphicUtils.draw_cosine(img_info.x + img_width,
|
||||
img_info.x + img_width + 3,
|
||||
COSINE_COLOUR,
|
||||
screen_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
function clear_sun(){
|
||||
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||
g.fillRect(img_info.x,img_info.y,img_info.x+img_width,img_info.y + img_width);
|
||||
GraphicUtils.draw_cosine(img_info.x - 4,
|
||||
img_info.x + img_width + 4,
|
||||
COSINE_COLOUR,
|
||||
screen_info);
|
||||
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||
screen_info.sun_x = null;
|
||||
screen_info.sun_y = null;
|
||||
}
|
||||
|
||||
var last_time = null;
|
||||
var last_offset = null;
|
||||
var last_date = null;
|
||||
|
||||
const time_color = Colors.WHITE;
|
||||
const date_color = Colors.YELLOW;
|
||||
const DATE_Y_COORD = 35;
|
||||
const DATE_X_COORD = 10;
|
||||
const TIME_X_COORD = 140;
|
||||
const TIME_Y_COORD = 35;
|
||||
const OFFSET_Y_COORD = 70;
|
||||
const LOCATION_Y_COORD = 55;
|
||||
|
||||
function write_date(now){
|
||||
var new_date = require('locale').dow(now,1) + " " + Math2.format00(now.getDate());
|
||||
//console.log("writing date:" + new_date)
|
||||
g.setFont("Vector",15);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
if(last_date != null){
|
||||
if(new_date == last_date){
|
||||
return;
|
||||
}
|
||||
g.setColor(screen_info.screen_bg_color[0],
|
||||
screen_info.screen_bg_color[1],
|
||||
screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_date, DATE_X_COORD,DATE_Y_COORD);
|
||||
}
|
||||
g.setColor(date_color[0],date_color[1],date_color[2]);
|
||||
g.drawString(new_date, DATE_X_COORD,DATE_Y_COORD);
|
||||
last_date = new_date;
|
||||
}
|
||||
|
||||
var last_status_msg = ""
|
||||
var last_gps_coords_msg_n = "";
|
||||
var last_gps_coords_msg_e = "";
|
||||
const GPS_MSG_X_COORD = 70;
|
||||
const GPS_MSG_Y = 220;
|
||||
const GPS_MSG_COORDS_Y_E = 80;
|
||||
const GPS_MSG_COORDS_Y_N = 90;
|
||||
|
||||
function write_GPS_status(){
|
||||
var gps_coords = location.getCoordinates();
|
||||
var gps_coords_msg_n;
|
||||
var gps_coords_msg_e;
|
||||
if(gps_coords != null){
|
||||
gps_coords_msg_n = "N: " + gps_coords[0];
|
||||
gps_coords_msg_n = gps_coords_msg_n.substr(0,Math.min(gps_coords_msg_n.length - 1,10));
|
||||
gps_coords_msg_e = "E: " + gps_coords[1];
|
||||
gps_coords_msg_e = gps_coords_msg_e.substr(0,Math.min(gps_coords_msg_e.length - 1,10));
|
||||
} else {
|
||||
gps_coords_msg_n = "";
|
||||
gps_coords_msg_e = "";
|
||||
}
|
||||
var status_msg = "";
|
||||
if(location.isGPSLocation()) {
|
||||
if(gps_coords == null) {
|
||||
if (location.getGPSPower() > 0) {
|
||||
status_msg = "Finding GPS Position";
|
||||
} else {
|
||||
status_msg = "ERROR GPS Position not found";
|
||||
}
|
||||
} else {
|
||||
if (location.getGPSPower() > 0) {
|
||||
status_msg = "Updating GPS Position";
|
||||
}
|
||||
}
|
||||
}
|
||||
g.setFont("Vector",11);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
if(last_status_msg != status_msg) {
|
||||
g.setColor(screen_info.screen_bg_color[0],
|
||||
screen_info.screen_bg_color[1],
|
||||
screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_status_msg, GPS_MSG_X_COORD, GPS_MSG_Y);
|
||||
g.setColor(Colors.YELLOW[0],Colors.YELLOW[1],Colors.YELLOW[2]);
|
||||
g.drawString(status_msg, GPS_MSG_X_COORD, GPS_MSG_Y);
|
||||
last_status_msg = status_msg;
|
||||
}
|
||||
|
||||
if(last_gps_coords_msg_e != gps_coords_msg_e) {
|
||||
g.setColor(screen_info.screen_bg_color[0],
|
||||
screen_info.screen_bg_color[1],
|
||||
screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E);
|
||||
g.drawString(last_gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N);
|
||||
g.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]);
|
||||
|
||||
g.drawString(gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E);
|
||||
g.drawString(gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N);
|
||||
last_gps_coords_msg_e = gps_coords_msg_e;
|
||||
last_gps_coords_msg_n = gps_coords_msg_n;
|
||||
}
|
||||
}
|
||||
|
||||
function write_time(now){
|
||||
var new_time = format_time(now);
|
||||
g.setFont("Vector",35);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
if(last_time != null){
|
||||
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_time, TIME_X_COORD,TIME_Y_COORD);
|
||||
}
|
||||
g.setColor(time_color[0],time_color[1],time_color[2]);
|
||||
g.drawString(new_time, TIME_X_COORD,TIME_Y_COORD);
|
||||
last_time = new_time;
|
||||
}
|
||||
|
||||
function format_time(now){
|
||||
var time = new Date(now.getTime() - time_offset);
|
||||
var hours = time.getHours() % 12;
|
||||
if(hours < 1){
|
||||
hours = 12;
|
||||
}
|
||||
return Math2.format00(hours) + ":" + Math2.format00(time.getMinutes());
|
||||
}
|
||||
|
||||
function write_offset(){
|
||||
var new_offset = format_offset();
|
||||
g.setFont("Vector",15);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
if(last_offset != null){
|
||||
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_offset, TIME_X_COORD,OFFSET_Y_COORD);
|
||||
}
|
||||
g.setColor(time_color[0],time_color[1],time_color[2]);
|
||||
g.drawString(new_offset, TIME_X_COORD,OFFSET_Y_COORD);
|
||||
last_offset = new_offset;
|
||||
}
|
||||
|
||||
function format_offset(){
|
||||
if(time_offset == 0)
|
||||
return "";
|
||||
|
||||
var hours_offset = Math.abs(time_offset) / DateUtils.HOUR_MILLIS;
|
||||
var mins_offset = Math.abs(time_offset) / DateUtils.MIN_MILLIS;
|
||||
var mins_offset_from_hour = mins_offset % 60;
|
||||
//console.log("mins offset=" + mins_offset + " mins_offset_from_hour=" + mins_offset_from_hour);
|
||||
var sign = "+";
|
||||
if(time_offset < 0)
|
||||
sign = "-";
|
||||
|
||||
return sign + Math2.format00(hours_offset) + ":" + Math2.format00(mins_offset_from_hour);
|
||||
}
|
||||
|
||||
let time_offset = 0;
|
||||
let last_draw_time = null;
|
||||
var day_info = null;
|
||||
var location = LocationUtils.load_locations();
|
||||
var last_location_name = null;
|
||||
|
||||
function write_location_name() {
|
||||
var new_location_name = location.getName();
|
||||
g.setFont("Vector", 20);
|
||||
g.setFontAlign(-1, -1, 0);
|
||||
if (last_location_name != null) {
|
||||
g.setColor(screen_info.screen_bg_color[0], screen_info.screen_bg_color[1], screen_info.screen_bg_color[2]);
|
||||
g.drawString(last_location_name, DATE_X_COORD, LOCATION_Y_COORD);
|
||||
}
|
||||
g.setColor(time_color[0], time_color[1], time_color[2]);
|
||||
if (new_location_name != "local") {
|
||||
g.drawString(new_location_name, DATE_X_COORD, LOCATION_Y_COORD);
|
||||
}
|
||||
last_location_name = new_location_name;
|
||||
}
|
||||
|
||||
location.addUpdateListener(
|
||||
(loc)=>{
|
||||
console.log("location update:" + JSON.stringify(loc));
|
||||
clear_sun();
|
||||
GraphicUtils.draw_sunrise_line(screen_info.screen_bg_color, day_info, screen_info);
|
||||
day_info = null;
|
||||
screen_info.sunrise_y = null;
|
||||
curr_mode = null;
|
||||
draw_clock();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
function dayInfo(now) {
|
||||
if (day_info == null || now > day_info.day_end) {
|
||||
var coords = location.getCoordinates();
|
||||
if(coords != null) {
|
||||
day_info = DateUtils.sunrise_sunset(now, coords[0], coords[1], location.getUTCOffset());
|
||||
//console.log("day info:" + JSON.stringify(day_info));
|
||||
} else {
|
||||
day_info = null;
|
||||
}
|
||||
}
|
||||
return day_info;
|
||||
}
|
||||
|
||||
function time_now() {
|
||||
var timezone_offset_hours = location.getUTCOffset();
|
||||
if(timezone_offset_hours != null) {
|
||||
var local_offset_hours = -new Date().getTimezoneOffset()/60;
|
||||
var timezone_offset_millis =
|
||||
(timezone_offset_hours - local_offset_hours) * DateUtils.HOUR_MILLIS;
|
||||
return new Date(Date.now() + time_offset + timezone_offset_millis);
|
||||
} else {
|
||||
return new Date(Date.now() + time_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function draw_clock(){
|
||||
var start_time = Date.now();
|
||||
var now = time_now();
|
||||
|
||||
var day_info = dayInfo(now);
|
||||
if(day_info != null) {
|
||||
draw_sun(now, day_info);
|
||||
}
|
||||
write_time(now);
|
||||
write_date(now);
|
||||
write_offset();
|
||||
write_location_name();
|
||||
write_GPS_status();
|
||||
last_draw_time = now;
|
||||
log_memory_used();
|
||||
var time_taken = Date.now() - start_time;
|
||||
console.log("drawing clock:" + now.toISOString() + " time taken:" + time_taken );
|
||||
}
|
||||
|
||||
function log_memory_used() {
|
||||
var memory = process.memory();
|
||||
console.log("memory used:" + memory.usage +
|
||||
" total:" + memory.total + "->" +
|
||||
" ->" + memory.usage/memory.total
|
||||
);
|
||||
}
|
||||
|
||||
function button1pressed(){
|
||||
console.log("button 1 pressed");
|
||||
time_offset = 0;
|
||||
clear_sun();
|
||||
day_info = null;
|
||||
draw_clock();
|
||||
}
|
||||
|
||||
function button3pressed(){
|
||||
console.log("button 3 pressed");
|
||||
time_offset = 0;
|
||||
location.nextLocation();
|
||||
}
|
||||
|
||||
function button4pressed(){
|
||||
time_offset -= DateUtils.HOUR_MILLIS/4;
|
||||
draw_clock();
|
||||
setTimeout(()=>{
|
||||
if(BTN4.read()){
|
||||
button4pressed();
|
||||
}
|
||||
},
|
||||
50
|
||||
)
|
||||
}
|
||||
|
||||
function button5pressed(){
|
||||
time_offset += DateUtils.HOUR_MILLIS/4;
|
||||
draw_clock();
|
||||
setTimeout(()=>{
|
||||
if(BTN5.read()){
|
||||
button5pressed();
|
||||
}
|
||||
},
|
||||
50
|
||||
)
|
||||
}
|
||||
|
||||
// The interval reference for updating the clock
|
||||
let interval_ref = null;
|
||||
function clear_timers(){
|
||||
if(interval_ref != null) {
|
||||
clearInterval(interval_ref);
|
||||
interval_ref = null;
|
||||
}
|
||||
}
|
||||
|
||||
function start_timers(){
|
||||
var date = new Date();
|
||||
var secs = date.getSeconds();
|
||||
var nextMinuteStart = 60 - secs;
|
||||
setTimeout(schedule_draw_clock,nextMinuteStart * 1000);
|
||||
draw_clock();
|
||||
}
|
||||
function schedule_draw_clock(){
|
||||
clear_timers();
|
||||
if (Bangle.isLCDOn()) {
|
||||
interval_ref = setInterval(() => {
|
||||
if (!Bangle.isLCDOn()) {
|
||||
console.log("draw clock callback - skipped redraw");
|
||||
} else {
|
||||
draw_clock();
|
||||
}
|
||||
}, DateUtils.MIN_MILLIS
|
||||
);
|
||||
draw_clock();
|
||||
} else {
|
||||
console.log("scheduleDrawClock - skipped not visible");
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
console.log("lcdPower: on");
|
||||
draw_clock();
|
||||
start_timers();
|
||||
} else {
|
||||
console.log("lcdPower: off");
|
||||
clear_timers();
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('faceUp',function(up){
|
||||
if (up && !Bangle.isLCDOn()) {
|
||||
clear_timers();
|
||||
Bangle.setLCDPower(true);
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
start_timers();
|
||||
|
||||
function button2pressed(){
|
||||
controller = null;
|
||||
|
||||
location.shutdown();
|
||||
location = null;
|
||||
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
setWatch(button2pressed, BTN2,{repeat:false,edge:"falling"});
|
||||
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||
setWatch(button4pressed, BTN4,{repeat:true,edge:"rising"});
|
||||
setWatch(button5pressed, BTN5,{repeat:true,edge:"rising"});
|
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
|
@ -0,0 +1,14 @@
|
|||
const _BLACK= [0.0,0.0,0.0];
|
||||
const _GREY= [0.5,0.5,0.5];
|
||||
const _WHITE= [1.0,1.0,1.0];
|
||||
const _YELLOW= [1.0,1.0,0.0];
|
||||
const _RED= [1.0,0.0,0.0];
|
||||
|
||||
const Colors = {
|
||||
BLACK: _BLACK,
|
||||
GREY: _GREY,
|
||||
WHITE: _WHITE,
|
||||
YELLOW: _YELLOW,
|
||||
RED: _RED
|
||||
};
|
||||
module.exports = Colors;
|
|
@ -0,0 +1,247 @@
|
|||
const Math2 = require("solar_math_utils.js");
|
||||
const DateUtils = require("solar_date_utils.js");
|
||||
const GraphicUtils = require("solar_graphic_utils.js");
|
||||
const Colors = require("solar_colors.js");
|
||||
|
||||
const CORONA_GAP = 2;
|
||||
const CORONA_MIN_LENGTH = 2;
|
||||
const CORONA_LINES = 12;
|
||||
const CORONA_RADIUS = 14;
|
||||
const SUNSET_START_HEIGHT = 10;
|
||||
const SUNSET_COLOUR = Colors.RED;
|
||||
const SUNRISE_COLOUR = [1,0.6,0];
|
||||
const MIDDAY_COLOUR = [1,1,0.7];
|
||||
const NIGHT_COLOUR = Colors.BLACK;
|
||||
|
||||
function daytime_sun_color(now,day_info){
|
||||
var now_fraction_of_day =DateUtils.now_fraction_of_day(now,day_info);
|
||||
if(now > day_info.sunset_date){
|
||||
return SUNSET_COLOUR;
|
||||
} else if(now < day_info.sunrise_date){
|
||||
return SUNRISE_COLOUR;
|
||||
} else if(now < day_info.solar_noon) {
|
||||
var sunrise_fraction = (day_info.sunrise_date - day_info.day_start) / DateUtils.DAY_MILLIS;
|
||||
var rise_to_midday_fraction = (now_fraction_of_day - sunrise_fraction) / (0.5 - sunrise_fraction);
|
||||
return Math2.interpolate(SUNRISE_COLOUR, MIDDAY_COLOUR, rise_to_midday_fraction);
|
||||
} else {
|
||||
var sunset_fraction = (day_info.day_end - day_info.sunset_date) / DateUtils.DAY_MILLIS;
|
||||
var midday_to_sunset_fraction = (now_fraction_of_day - 0.5)/(0.5 - sunset_fraction);
|
||||
//console.log("sunset_fraction=" + sunset_fraction + " midday_to_sunset_fraction=" + midday_to_sunset_fraction)
|
||||
return Math2.interpolate(MIDDAY_COLOUR,SUNSET_COLOUR,midday_to_sunset_fraction);
|
||||
}
|
||||
}
|
||||
|
||||
function draw_night_sun(sun_x,sun_y,sun_radius,img_info){
|
||||
var draw_info = GraphicUtils.draw_info(img_info);
|
||||
draw_info.buff.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]);
|
||||
draw_info.buff.fillCircle(sun_x - draw_info.offset_x,
|
||||
sun_y - draw_info.offset_y,
|
||||
sun_radius);
|
||||
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||
draw_info.buff.fillCircle(sun_x - draw_info.offset_x,
|
||||
sun_y - draw_info.offset_y,
|
||||
sun_radius-1);
|
||||
}
|
||||
|
||||
function draw_partial_sun(time, day_info, screen_info,img_info){
|
||||
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||
if(sun_height > screen_info.sun_radius){
|
||||
var sun_color = daytime_sun_color(time,day_info);
|
||||
var draw_info = GraphicUtils.draw_info(img_info);
|
||||
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||
screen_info.sun_y - draw_info.offset_y,
|
||||
screen_info.sun_radius
|
||||
);
|
||||
} else if(sun_height < -screen_info.sun_radius){
|
||||
draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info);
|
||||
} else {
|
||||
var draw_info = GraphicUtils.draw_info(img_info);
|
||||
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||
screen_info.sun_y - draw_info.offset_y,
|
||||
screen_info.sun_radius-1);
|
||||
var sun_color = daytime_sun_color(time,day_info);
|
||||
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||
draw_info.buff.drawCircle(screen_info.sun_x - draw_info.offset_x,
|
||||
screen_info.sun_y - draw_info.offset_y,
|
||||
screen_info.sun_radius);
|
||||
GraphicUtils.fill_circle_partial_y(screen_info.sun_x,
|
||||
screen_info.sun_y,
|
||||
screen_info.sun_radius,
|
||||
screen_info.sun_y - screen_info.sun_radius,
|
||||
screen_info.sunrise_y,
|
||||
img_info
|
||||
);
|
||||
}
|
||||
}
|
||||
function draw_random_background(screen_info,
|
||||
img_info,
|
||||
rgb_init,
|
||||
rgb_step
|
||||
){
|
||||
var draw_info = GraphicUtils.draw_info(img_info);
|
||||
var rgb = rgb_init;
|
||||
var sky_to = Math.min(screen_info.sunrise_y-1, img_info.y + img_info.img.height - 3);
|
||||
for(var sky_y=img_info.y+3;sky_y<sky_to ; sky_y++){
|
||||
for(var i=0;i<rgb.length;i++){
|
||||
if(rgb_step[i]>0)
|
||||
rgb[i] = Math2.random_walk(rgb[i],rgb_step[i],1,0);
|
||||
}
|
||||
draw_info.buff.setColor(rgb[0],rgb[1],rgb[2]);
|
||||
draw_info.buff.moveTo(screen_info.sun_x +
|
||||
Math.random()*img_info.img.width/8 -
|
||||
0.4*img_info.img.width -
|
||||
draw_info.offset_x,
|
||||
sky_y - draw_info.offset_y);
|
||||
draw_info.buff.lineTo(screen_info.sun_x +
|
||||
0.4*img_info.img.width -
|
||||
Math.random()*img_info.img.width/8 -
|
||||
draw_info.offset_x,
|
||||
sky_y - draw_info.offset_y);
|
||||
}
|
||||
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||
screen_info.sun_y - draw_info.offset_y,
|
||||
screen_info.sun_radius+1);
|
||||
}
|
||||
class SolarMode {
|
||||
test(time, day_info, screen_info){ throw "test undefined";}
|
||||
draw(time, day_info, screen_info, img_buffer_info){
|
||||
throw "sun drawing undefined";
|
||||
}
|
||||
}
|
||||
class NightMode extends SolarMode {
|
||||
toString(){return "NightMode";}
|
||||
test(time, day_info, screen_info, img_info) {
|
||||
return (time < day_info.sunrise_date || time > day_info.sunset_date);
|
||||
}
|
||||
draw(time, day_info, screen_info, img_info){
|
||||
draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info);
|
||||
}
|
||||
}
|
||||
class DayLightMode extends SolarMode {
|
||||
toString(){
|
||||
return "DayLightMode";
|
||||
}
|
||||
test(time, day_info, screen_info){
|
||||
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||
/*console.log("DayLightMode " +
|
||||
"time=" + time.toISOString() +
|
||||
" sunset_date=" + day_info.sunset_date.toISOString() +
|
||||
" sunrise_date=" + day_info.sunrise_date.toISOString()
|
||||
);*/
|
||||
return time < day_info.sunset_date &&
|
||||
time > day_info.sunrise_date &&
|
||||
sun_height >= screen_info.sun_radius * 2 + SUNSET_START_HEIGHT;
|
||||
}
|
||||
_calc_corona_radius(now, day_info){
|
||||
if(now < day_info.sunset_date &&
|
||||
now > day_info.sunrise_date){
|
||||
var now_fraction_of_day =DateUtils.now_fraction_of_day(now,day_info);
|
||||
var sunset_fraction = (day_info.sunset_date.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS;
|
||||
var now_fraction_from_midday =
|
||||
1 - Math.abs(now_fraction_of_day-0.5)/(sunset_fraction-0.5);
|
||||
return CORONA_RADIUS * now_fraction_from_midday;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
_drawCorona(corona_radius,sun_x,sun_y,sun_radius, draw_info){
|
||||
var thickness_rads = (Math2.TWO_PI/CORONA_LINES)/3;
|
||||
var from_radius = sun_radius + CORONA_GAP;
|
||||
if(corona_radius > from_radius + CORONA_MIN_LENGTH) {
|
||||
for (var i = 0; i < CORONA_LINES; i++) {
|
||||
var to_x1 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES + thickness_rads);
|
||||
var to_y1 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES + thickness_rads);
|
||||
var to_x2 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES - thickness_rads);
|
||||
var to_y2 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES - thickness_rads);
|
||||
var to_x3 = sun_x - draw_info.offset_x + corona_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES);
|
||||
var to_y3 = sun_y - draw_info.offset_y + corona_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES);
|
||||
draw_info.buff.fillPoly([to_x1, to_y1, to_x2, to_y2, to_x3, to_y3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
draw(now, day_info, screen_info, img_info){
|
||||
var sun_color = daytime_sun_color(now,day_info);
|
||||
var corona_radius = this._calc_corona_radius(now, day_info);
|
||||
var draw_info = GraphicUtils.draw_info(img_info);
|
||||
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||
if(corona_radius > screen_info.sun_radius){
|
||||
this._drawCorona(corona_radius,
|
||||
screen_info.sun_x,
|
||||
screen_info.sun_y,
|
||||
screen_info.sun_radius,
|
||||
draw_info);
|
||||
}
|
||||
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||
screen_info.sun_y - draw_info.offset_y,
|
||||
screen_info.sun_radius);
|
||||
}
|
||||
}
|
||||
class TwiLightMode extends SolarMode {
|
||||
toString(){
|
||||
return "TwilightMode";
|
||||
}
|
||||
test(time, day_info, screen_info){
|
||||
if(screen_info.sunrise_y == null) {
|
||||
console.log("warning no sunrise_defined");
|
||||
return false;
|
||||
}
|
||||
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||
/*console.log("TwilightMode " +
|
||||
"time=" + time.toISOString() +
|
||||
" sun_height=" + sun_height +
|
||||
" sun_radius=" + screen_info.sun_radius
|
||||
);*/
|
||||
if(sun_height > -screen_info.sun_radius &&
|
||||
sun_height < screen_info.sun_radius * 2 + SUNSET_START_HEIGHT
|
||||
){
|
||||
//console.log("selected TwilightMode");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
draw(time, day_info, screen_info, img_info){
|
||||
if(time < day_info.solar_noon) {
|
||||
draw_random_background(screen_info,
|
||||
img_info,
|
||||
[0,0.8,1],
|
||||
[0.05,0.05,0.0]);
|
||||
} else {
|
||||
draw_random_background(screen_info,
|
||||
img_info,
|
||||
[1,0.75,Math.random()],
|
||||
[0,0.05,0.05]);
|
||||
}
|
||||
draw_partial_sun(time,day_info,screen_info,img_info);
|
||||
}
|
||||
}
|
||||
class SolarControllerImpl {
|
||||
constructor(){
|
||||
this.solar_modes = [new TwiLightMode(), new DayLightMode()];
|
||||
this.default_mode = new NightMode();
|
||||
this.last = null;
|
||||
}
|
||||
toString(){
|
||||
return "SolarControllerImpl";
|
||||
}
|
||||
mode(time, day_info, screen_info){
|
||||
if(this.last != null){
|
||||
if(this.last.test(time,day_info,screen_info)){
|
||||
return this.last;
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.solar_modes.length; i++){
|
||||
if(this.solar_modes[i].test(time,day_info,screen_info) ){
|
||||
this.last = this.solar_modes[i];
|
||||
return this.last;
|
||||
}
|
||||
}
|
||||
//console.log("defaulting");
|
||||
this.last = this.default_mode;
|
||||
return this.last;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SolarControllerImpl;
|
|
@ -0,0 +1,132 @@
|
|||
var Math2 = require("solar_math_utils.js");
|
||||
|
||||
const _MIN_MILLIS = 1000 * 60 ;
|
||||
const _HOUR_MILLIS = _MIN_MILLIS * 60;
|
||||
const _DAY_MILLIS = _HOUR_MILLIS * 24;
|
||||
|
||||
function _start_of_julian_day(now){
|
||||
var sod_of_day = new Date(now.getTime());
|
||||
var local_offset_hours = sod_of_day.getTimezoneOffset()/60;
|
||||
//console.log("local_offset_hours=" + local_offset_hours);
|
||||
sod_of_day.setHours(12 + local_offset_hours,0,0,0);
|
||||
return sod_of_day;
|
||||
}
|
||||
function _date_to_julian_date(now){
|
||||
var year = now.getFullYear();
|
||||
var month = now.getMonth() + 1;
|
||||
var day = now.getDate();
|
||||
|
||||
var julian_date_full = (1461 * (year + 4800 + (month-14)/12))/4 +(367 * (month-2 - 12*((month - 14)/12)))/12-(3 * ((year + 4900 + (month - 14)/12)/100))/4 + day - 32075;
|
||||
var julian_date = (julian_date_full| 0);
|
||||
return julian_date;
|
||||
}
|
||||
function _to_time(now,day_fraction){
|
||||
var datetime = new Date(now.getTime());
|
||||
var hours = (day_fraction * 24) |0;
|
||||
var remainder = day_fraction - hours/24;
|
||||
var mins = (remainder * 24 * 60 | 0);
|
||||
var remainder = remainder - mins/(24*60);
|
||||
var secs = (remainder * 24 * 60 * 60 | 0);
|
||||
var remainder = remainder - secs /(24 * 60 * 60);
|
||||
var millis = remainder * 24 * 60 * 60 * 1000;
|
||||
datetime.setHours(hours, mins, secs,millis);
|
||||
return datetime;
|
||||
}
|
||||
const DateUtils = {
|
||||
DAY_MILLIS : _DAY_MILLIS,
|
||||
HOUR_MILLIS : _HOUR_MILLIS,
|
||||
MIN_MILLIS: _MIN_MILLIS,
|
||||
sunrise_sunset: (now,longitude,latitude, utc_offset)=>{
|
||||
|
||||
var sod_julian = _start_of_julian_day(now);
|
||||
var julian_date = _date_to_julian_date(sod_julian);
|
||||
//console.log("julian date=" + julian_date);
|
||||
//var n = julian_date - 2451545.0 + 0.0008;
|
||||
var julian_century = (julian_date-2451545)/36525;
|
||||
//console.log("julian_century=" + julian_century);
|
||||
var geom_mean_long_sun_degrees = (280.46646+julian_century*(36000.76983 + julian_century*0.0003032)) % 360;
|
||||
//console.log("geom_mean_long_sun=" + geom_mean_long_sun_degrees);
|
||||
var geom_mean_anomaly_sun_degrees = 357.52911+julian_century*(35999.05029 - 0.0001537*julian_century);
|
||||
//console.log("solar_mean_anomaly_sun=" + geom_mean_anomaly_sun_degrees);
|
||||
var eccent_earth_orbit = 0.016708634-julian_century*(0.000042037+0.0000001267*julian_century);
|
||||
//console.log("eccent_earth_orbit=" + eccent_earth_orbit);
|
||||
var sun_eq_of_ctr = Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))*
|
||||
(1.914602-julian_century*(0.004817+0.000014*julian_century))+
|
||||
Math.sin(Math2.to_radians(2*geom_mean_anomaly_sun_degrees))*(0.019993-0.000101*julian_century)+
|
||||
Math.sin(Math2.to_radians(3*geom_mean_anomaly_sun_degrees))*0.000289;
|
||||
//console.log("sun_eq_of_ctr=" + sun_eq_of_ctr);
|
||||
var sun_true_long_degrees = geom_mean_long_sun_degrees + sun_eq_of_ctr;
|
||||
//console.log("sun_true_long_degrees=" + sun_true_long_degrees);
|
||||
var sun_true_anom_degrees = geom_mean_anomaly_sun_degrees + sun_eq_of_ctr;
|
||||
//console.log("sun_true_anom_degrees=" + sun_true_anom_degrees);
|
||||
var sun_rad_vector_AUs = (1.000001018*(1-eccent_earth_orbit*eccent_earth_orbit))/(1+eccent_earth_orbit*Math.cos(Math2.to_radians(sun_true_anom_degrees)))
|
||||
//console.log("sun_rad_vector_AUs=" + sun_rad_vector_AUs);
|
||||
var sun_app_long_degress = sun_true_long_degrees-0.00569-0.00478*Math.sin(Math2.to_radians(125.04-1934.136*julian_century));
|
||||
//console.log("sun_app_long_degress=" + sun_app_long_degress);
|
||||
var mean_obliq_ecliptic_degrees = 23+(26+((21.448-julian_century*(46.815+julian_century*(0.00059-julian_century*0.001813))))/60)/60;
|
||||
//console.log("mean_obliq_ecliptic_degrees=" + mean_obliq_ecliptic_degrees);
|
||||
var obliq_corr_degrees = mean_obliq_ecliptic_degrees+0.00256*Math.cos(Math2.to_radians(125.04-1934.136*julian_century))
|
||||
//console.log("obliq_corr_degrees=" + obliq_corr_degrees);
|
||||
|
||||
var sun_declin_degrees = Math2.to_degrees(
|
||||
Math.asin(Math.sin(Math2.to_radians(obliq_corr_degrees))*Math.sin(Math2.to_radians(sun_app_long_degress)))
|
||||
);
|
||||
//console.log("sun_declin_degrees=" + sun_declin_degrees);
|
||||
var var_y = Math.tan(Math2.to_radians(obliq_corr_degrees/2))*Math.tan(Math2.to_radians(obliq_corr_degrees/2));
|
||||
//console.log("var_y=" + var_y);
|
||||
var eq_of_time = 4*Math2.to_degrees(
|
||||
var_y*Math.sin(2*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||
2*eccent_earth_orbit*Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))+
|
||||
4*eccent_earth_orbit*var_y*Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))*Math.cos(2*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||
0.5*var_y*var_y*Math.sin(4*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||
1.25*eccent_earth_orbit*eccent_earth_orbit*Math.sin(2*Math2.to_radians(geom_mean_anomaly_sun_degrees))
|
||||
);
|
||||
//console.log("eq_of_time=" + eq_of_time);
|
||||
|
||||
var HA_sunrise_degrees = Math2.to_degrees(
|
||||
Math.acos(
|
||||
Math.cos(Math2.to_radians(90.833))/(Math.cos(Math2.to_radians(latitude))*Math.cos(Math2.to_radians(sun_declin_degrees)))-
|
||||
Math.tan(Math2.to_radians(latitude))*Math.tan(Math2.to_radians(sun_declin_degrees))
|
||||
)
|
||||
);
|
||||
//console.log("HA_sunrise_degrees=" + HA_sunrise_degrees);
|
||||
|
||||
var local_offset_hours = new Date().getTimezoneOffset()/60;
|
||||
if(utc_offset == null){
|
||||
utc_offset = -local_offset_hours;
|
||||
}
|
||||
var timezone_offset_hours = (utc_offset - local_offset_hours);
|
||||
console.log("timezone_offset_hours=" + timezone_offset_hours +
|
||||
" longitude" + longitude +
|
||||
" utc_offset=" + utc_offset
|
||||
);
|
||||
|
||||
var solar_noon = (720-4*longitude-eq_of_time+timezone_offset_hours*60)/1440;
|
||||
var solar_noon_datetime = _to_time(now,solar_noon);
|
||||
|
||||
console.log("solar_noon=" + solar_noon + "->" + solar_noon_datetime.toISOString());
|
||||
|
||||
var sunrise_time_LST = (solar_noon*1440-HA_sunrise_degrees*4)/1440;
|
||||
var sunrise_time_LST_datetime = _to_time(now,sunrise_time_LST);
|
||||
console.log("sunrise_time_LST=" + sunrise_time_LST +
|
||||
"->" + sunrise_time_LST_datetime.toISOString());
|
||||
|
||||
var sunset_time_LST =(solar_noon*1440+HA_sunrise_degrees*4)/1440;
|
||||
var sunset_time_LST_datetime = _to_time(now,sunset_time_LST);
|
||||
console.log("sunset_time_LST=" + sunset_time_LST +
|
||||
"->" + sunset_time_LST_datetime.toISOString());
|
||||
return {
|
||||
day_start: new Date(solar_noon_datetime.getTime() - _DAY_MILLIS / 2),
|
||||
sunrise_date: sunrise_time_LST_datetime,
|
||||
//sunrise_fraction: sunrise_time_LST,
|
||||
sunset_date: sunset_time_LST_datetime,
|
||||
//sunset_fraction: sunset_time_LST,
|
||||
solar_noon: solar_noon_datetime,
|
||||
day_end: new Date(solar_noon_datetime.getTime() + _DAY_MILLIS / 2)
|
||||
};
|
||||
},
|
||||
now_fraction_of_day: (now,day_info)=>{
|
||||
return (now.getTime() - day_info.day_start.getTime())/_DAY_MILLIS;
|
||||
},
|
||||
}
|
||||
module.exports = DateUtils;
|
|
@ -0,0 +1,94 @@
|
|||
var DateUtils = require("solar_date_utils.js");
|
||||
var Math2 = require("solar_math_utils.js");
|
||||
|
||||
function _draw_info(img_info){
|
||||
if (img_info == null) {
|
||||
return {
|
||||
buff: g,
|
||||
offset_x: 0,
|
||||
offset_y: 0
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
buff: img_info.img_buffer,
|
||||
offset_x: img_info.x,
|
||||
offset_y: img_info.y
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
const GraphicUtils = {
|
||||
draw_info : (img_info)=>_draw_info(img_info),
|
||||
draw_cosine : (from_x,to_x, line_colour, screen_info, img_info)=>{
|
||||
//console.log("draw_cosine from_x=" + from_x + " to_x=" + to_x);
|
||||
var draw_info = _draw_info(img_info);
|
||||
|
||||
draw_info.buff.reset();
|
||||
draw_info.buff.setColor(line_colour[0],line_colour[1],line_colour[2]);
|
||||
first = true;
|
||||
for(var x=from_x; x<to_x;x++){
|
||||
var radians = Math2.TWO_PI *((x-screen_info.screen_start_x) - screen_info.screen_centre_x)/(screen_info.screen_width);
|
||||
var y = screen_info.screen_centre_y - screen_info.screen_height * Math.cos(radians)/2;
|
||||
if(first) {
|
||||
draw_info.buff.moveTo(x-draw_info.offset_x, y-draw_info.offset_y);
|
||||
first = false;
|
||||
} else {
|
||||
draw_info.buff.lineTo(x-draw_info.offset_x,y - draw_info.offset_y);
|
||||
}
|
||||
//console.log("(x,y)=" + x +"," + y + " radians=" +radians );
|
||||
}
|
||||
draw_info.buff.reset();
|
||||
},
|
||||
|
||||
draw_sunrise_line : (sunrise_colour, day_info, screen_info, img_info)=> {
|
||||
var rise_y = screen_info.sunrise_y;
|
||||
if (rise_y == null && day_info != null) {
|
||||
var rise_fraction = (day_info.sunrise_date.getTime() - day_info.day_start.getTime()) / DateUtils.DAY_MILLIS;
|
||||
var rise_radians = Math2.TWO_PI * (rise_fraction - 0.5);
|
||||
var rise_y = screen_info.screen_centre_y - (screen_info.screen_height * Math.cos(rise_radians) / 2);
|
||||
//console.log("rise_y=" + rise_y + " rise_fraction=" + rise_fraction + " rise_radian=" + rise_radians);
|
||||
screen_info.sunrise_y = rise_y;
|
||||
}
|
||||
if(rise_y != null) {
|
||||
var draw_info = _draw_info(img_info);
|
||||
draw_info.buff.setColor(sunrise_colour[0], sunrise_colour[1], sunrise_colour[2]);
|
||||
draw_info.buff.drawLine(screen_info.screen_start_x - draw_info.offset_x,
|
||||
rise_y - draw_info.offset_y,
|
||||
screen_info.screen_width,
|
||||
rise_y - draw_info.offset_y);
|
||||
}
|
||||
},
|
||||
|
||||
fill_circle_partial_y : (center_x,center_y,radius,from_y,to_y, img_info)=>{
|
||||
from_y = (from_y | 0);
|
||||
to_y = (to_y | 0);
|
||||
if(from_y > to_y){
|
||||
var tmp_y = from_y;
|
||||
from_y = to_y;
|
||||
to_y = tmp_y;
|
||||
}
|
||||
var draw_info = _draw_info(img_info);
|
||||
for(var y=from_y; y<=to_y; y++){
|
||||
// now (y-center_y)^2 + (x-center_x)^2 = radius^2
|
||||
// so (x-center_x)^2 = radius^2 - (y-center_y)^2
|
||||
// so (x-center_x) = sqrt( radius^2 - (y-center_y)^2 )
|
||||
// so x = center_x +/- sqrt( radius^2 - (y-center_y)^2 )
|
||||
var x_dist2 =radius*radius - (y-center_y)*(y-center_y);
|
||||
if(x_dist2 >= 0) {
|
||||
var x_dist = Math.sqrt(x_dist2);
|
||||
var from_x = (center_x - x_dist | 0);
|
||||
var to_x = center_x + x_dist ;
|
||||
var to_x_rounded = (to_x | 0);
|
||||
if(to_x - to_x_rounded > 0.01)
|
||||
to_x_rounded += 1;
|
||||
|
||||
draw_info.buff.drawLine(from_x-draw_info.offset_x,
|
||||
y-draw_info.offset_y,
|
||||
to_x_rounded-draw_info.offset_x,
|
||||
y-draw_info.offset_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = GraphicUtils;
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"coordinates": [-21.9,64.133],
|
||||
"utc_offset": 0
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"coordinates": [-159.4879501,22.2224610],
|
||||
"utc_offset": -10
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"coordinates": [139.6503,35.6762],
|
||||
"utc_offset": 9
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"coordinates": [-0.12574, 51.50853]
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
const storage = require("Storage");
|
||||
class LocationManager {
|
||||
constructor(locations) {
|
||||
this.idx=0;
|
||||
this.locations = locations;
|
||||
this.listeners = [];
|
||||
this.in_use = true;
|
||||
this.gps_queried = false;
|
||||
this.gpsPower = 0;
|
||||
this.location_info = null;
|
||||
}
|
||||
init(){
|
||||
this.location_info = storage.readJSON("solar_loc." + this.getName() + ".json");
|
||||
if(this.isGPSLocation() && !this.gps_queried) {
|
||||
console.log("updating local location");
|
||||
this._gpsUpdate();
|
||||
this.gps_queried = true;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
setGPSPower(power){
|
||||
this.gpsPower = power;
|
||||
Bangle.setGPSPower(this.gpsPower);
|
||||
}
|
||||
getGPSPower(){return this.gpsPower;}
|
||||
_gpsUpdate(){
|
||||
this.setGPSPower(1);
|
||||
Bangle.on('GPS', (g) => {
|
||||
if (!this.in_use)
|
||||
return;
|
||||
|
||||
if (g.fix) {
|
||||
var loc_info = {
|
||||
last_update: new Date(),
|
||||
coordinates: [g.lon, g.lat]
|
||||
};
|
||||
console.log("Received gps fixing:" + JSON.stringify(loc_info));
|
||||
storage.writeJSON("solar_loc.local.json", this.location_info);
|
||||
if(this.isGPSLocation()){
|
||||
this.location_info = loc_info;
|
||||
this.notifyUpdate();
|
||||
}
|
||||
this.setGPSPower(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
isGPSLocation(){return this.getName() == 'local';}
|
||||
addUpdateListener(listener){this.listeners.push(listener);}
|
||||
nextLocation() {
|
||||
if(this.locations.length > 1) {
|
||||
this.idx += 1;
|
||||
this.idx = this.idx % this.locations.length;
|
||||
console.log("location now:" + this.getName());
|
||||
this.init();
|
||||
this.notifyUpdate();
|
||||
} else {
|
||||
console.log("no extra locations found");
|
||||
}
|
||||
}
|
||||
notifyUpdate(){
|
||||
for(var i=0; i<this.listeners.length; i++){
|
||||
this.listeners[i](this);
|
||||
}
|
||||
}
|
||||
getUTCOffset(){return this.location_info.utc_offset;}
|
||||
getName(){return this.locations[this.idx];}
|
||||
getCoordinates(){return this.location_info.coordinates;}
|
||||
shutdown(){
|
||||
this.in_use=false;
|
||||
this.setGPSPower(0);
|
||||
}
|
||||
}
|
||||
const LOCATIONS_FILE = "solar_locations.json";
|
||||
const LocationUtils = {
|
||||
load_locations : ()=>{
|
||||
var locations = storage.readJSON(LOCATIONS_FILE);
|
||||
console.log("loaded locations:" + locations);
|
||||
var mgr = new LocationManager(locations);
|
||||
mgr.init();
|
||||
return mgr;
|
||||
}
|
||||
}
|
||||
module.exports = LocationUtils;
|
|
@ -0,0 +1 @@
|
|||
["local","Tokyo","Iceland","Kauai"]
|
|
@ -0,0 +1,34 @@
|
|||
const _TWO_PI = 2 * Math.PI;
|
||||
const Maths2 = {
|
||||
TWO_PI: _TWO_PI,
|
||||
to_radians: (degrees)=> _TWO_PI * degrees / 360,
|
||||
to_degrees: (radians)=> 360 * radians/ (_TWO_PI),
|
||||
interpolate: (vector1, vector2, fraction)=>{
|
||||
var result = [];
|
||||
for(var i=0; i< vector1.length; i++){
|
||||
var value = vector1[i] + (vector2[i] - vector1[i]) * fraction;
|
||||
result.push(value);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
format00: (num)=>{
|
||||
var value = (num | 0);
|
||||
if(value > 99 || value < 0)
|
||||
throw "must be between in range 0-99";
|
||||
if(value < 10)
|
||||
return "0" + value.toString();
|
||||
else
|
||||
return value.toString();
|
||||
},
|
||||
random_walk: (value,step,max,min)=>{
|
||||
if(Math.random()>0.5){
|
||||
value -= step;
|
||||
} else {
|
||||
value += step;
|
||||
}
|
||||
value = Math.min(value,max);
|
||||
value = Math.max(value,min);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
module.exports = Maths2;
|
Loading…
Reference in New Issue