I originally wanted to make a water droplet fall into the screen each second, filling the screen each hour, but I quickly ran into trouble with the memory restrictions on the Pebble. I took a long time considering possibilities of what I could do with little to no memory. I wanted to make something visually pleasing. I thought about what could be easier than water and remembered a simple snow screensaver I had made in my Introduction to Computer Programming class in high school. I was probably reminded of this due to the weather conditions. After making the snow work on the screen, I realized what I’d made did not tell time. For this reason, I added a ground which would accumulate and be relieved of snow on an hourly basis. I then added a sun and moon that would pass through the sky telling the time of day. I based the sun and moon motion based on a sunrise and sunset at 6 AM and 6 PM with the moon taking the other half of the day.
I am happy with the final project. I enjoy the simplicity of how I’ve shown the progress through the day because it is divorced from our numeric concept of time.
At 2:10 PM:
At 3:40 AM:
At 6:50 AM:
These are sketches for my final and original idea.
Here is the final code:
//Snow Clock
// by: Matthew Kellogg
// date: January 27, 2015
// Copyright 2015 Matthew Kellogg
#include "pebble.h"
static Window *window;
static GRect window_frame;
static Layer *root_layer;
#define WIDTH 144
#define HEIGHT 168
//An array of the x positions of the snowflakes
// there is one snowflake at each y coordinate
static uint8_t flakes_x[HEIGHT];
//This is called whenever the layer needs redrawn.
//This draws all of the scene
static void layer_update_callback(Layer *me, GContext *ctx) {
//draw
//clear
graphics_context_set_fill_color(ctx, GColorBlack);
graphics_fill_rect(ctx, window_frame, 0, GCornerNone);
//get time
time_t t = time(NULL);
struct tm* time = localtime(&t);
int buffer = 76;
int done = ((HEIGHT-buffer)*((60*time->tm_min) + time->tm_sec)) / 3600;
//draw snow base
GRect snow_rect = GRect(0, HEIGHT-done, WIDTH, done);
graphics_context_set_fill_color(ctx, GColorWhite);
graphics_fill_rect(ctx, snow_rect,0 , GCornerNone);
//draw sun/ moon
// sun rise at 6AM set at 6PM, moon rise and set at 6 PM and 6 AM
int sunx = (WIDTH*(((time->tm_hour+6)%12))*60 +time->tm_min)/(12*60);
graphics_fill_circle(ctx,GPoint(sunx,56), 20);
if (time->tm_hour >= 18 || time->tm_hour<6){
graphics_context_set_fill_color(ctx, GColorBlack);
graphics_fill_circle(ctx, GPoint(sunx+10,56), 14);
graphics_context_set_fill_color(ctx, GColorWhite);
}
//draw flakes
graphics_context_set_stroke_color(ctx, GColorWhite);
int i,y;
for (i=0;i<HEIGHT; i++){ y = i%HEIGHT; graphics_fill_circle(ctx,GPoint(flakes_x[y],y),2); } } //This is called once per second //It updates snowflake positions and marks the layer for redraw static void timer_callback(struct tm *tick_time, TimeUnits units_changed){ //update int i; for (i = HEIGHT -1; i >= 0; i--){
flakes_x[(i+1)%HEIGHT] = (int)flakes_x[i]-1+(rand()%3);
}
layer_mark_dirty(root_layer);
}
//This initializes the snowflake array with random coordinates
static void flakes_init(){
int i;
for (i = 0; i< HEIGHT; i++){
flakes_x[i] = rand()%HEIGHT;
}
}
//This is called when the window is available
// It gets the layer and sets the update function
static void window_load(Window *window) {
root_layer = window_get_root_layer(window);
window_frame = layer_get_frame(root_layer);
layer_set_update_proc(root_layer, layer_update_callback);
}
//This function is empty because no dynamic memory is used
static void window_unload(Window *window) {
}
//This method is called when the app is being initialized
static void init(void) {
window = window_create();
window_set_window_handlers(window, (WindowHandlers) {
.load = window_load,
.unload = window_unload
});
window_stack_push(window, true /* Animated */);
window_set_background_color(window, GColorBlack);
//initialize flakes
flakes_init();
tick_timer_service_subscribe(SECOND_UNIT, timer_callback);
}
static void deinit(void) {
window_destroy(window);
}
int main(void) {
init();
app_event_loop();
deinit();
}