Skip to content

Instantly share code, notes, and snippets.

@uwcc
Forked from dribnet/.block
Last active October 15, 2018 02:00
Show Gist options
  • Save uwcc/d02410a84de1c7fb006fb0488da00019 to your computer and use it in GitHub Desktop.
Save uwcc/d02410a84de1c7fb006fb0488da00019 to your computer and use it in GitHub Desktop.
PS4 MDDN 342 2018
license: mit
height: 720

PS4 MDDN 342 2018

Adding animation based on z.

const max_thickness = 64;
const max_movement = 0;
const ball_radius = 32;
const line_width = 8;
const grid_size = 64;
let do_animation = true;
/* the random number seed for the tour */
var tourSeed = 150;
/* triplets of locations: zoom, x, y */
var tourPath = [
[2, -146, 1132],
[2, -149, 1257]
]
/* this function takes a coordinate and aligns to a grid of size gsize */
function snap_to_grid(num, gsize) {
return (num - (num % gsize));
}
/* this function returns a point offset by noise at that location */
function getOffsetPoint(p5, x, y, z, noiseScale) {
let offsetX = getRandomValue(p5, x, y, z, "offsetX", -max_movement, max_movement, noiseScale);
let offsetY = getRandomValue(p5, x, y, z, "offsetY", -max_movement, max_movement, noiseScale);
return [x+offsetX, y+offsetY]
}
function drawPetals(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2, z) {
const sqrt2 = 1.4142/2;
let offsets = [
[sqrt2, sqrt2],
[-sqrt2, sqrt2],
[-sqrt2, -sqrt2],
[sqrt2, -sqrt2]
]
let phase = getRandomValue(p5, pos_x, pos_y, z, "phase", 0, 2*p5.PI, 0.1);
let freq = getRandomValue(p5, pos_x, pos_y, z, "freq", 10, 50, 0.1);
let sineWave = p5.sin(phase + (p5.globalFrameCount / freq));
let radiusScale = p5.map(sineWave, -1, 1, 0.80, 1.0);
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
pixel_radius = radiusScale * pixel_radius;
for(let i=0; i<offsets.length; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
}
}
function drawStamens(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2, drawLines, z) {
const offsets = [
[1, 1],
[1, -1],
[-1, 1],
[-1, -1]
]
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
let z_fraction = z % 1.0;
let num_stamens = p5.map(z_fraction, 0, 1, 0, offsets.length)
for(var i=0; i<offsets.length; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
if(drawLines) {
p5.strokeWeight(pixel_radius / 20);
p5.line(pixel_x-pixel_radius, pixel_y, pixel_x+pixel_radius, pixel_y);
p5.line(pixel_x, pixel_y-pixel_radius, pixel_x, pixel_y+pixel_radius);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius / 12);
}
}
}
/*
* This is the funciton to implement to make your own abstract design.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
/* max_shift is the amount of overlap a tile can spill over into its neighbors */
let max_shift = max_thickness + max_movement;
/* For animation: updated z based on global frame count */
let dz = p5.globalFrameCount / 100.0;
z = z + dz;
/* this rectangle defines the region that will be drawn and includes a margin */
let min_x = snap_to_grid(x1 - max_shift, grid_size);
let max_x = snap_to_grid(x2 + max_shift + grid_size, grid_size);
let min_y = snap_to_grid(y1 - max_shift, grid_size);
let max_y = snap_to_grid(y2 + max_shift + grid_size, grid_size);
// debug version: draw one
// let half_x = (x1 + x2) / 2;
// let half_y = (y1 + y2) / 2;
// min_x = snap_to_grid(half_x, grid_size);
// max_x = snap_to_grid(half_x + grid_size, grid_size);
// min_y = snap_to_grid(half_y, grid_size);
// max_y = snap_to_grid(half_y + grid_size, grid_size);
let c_p00 = p5.map(0, x1, x2, 0, 256);
let c_plwidth = p5.map(line_width, x1, x2, 0, 256);
let c_pball = p5.map(ball_radius, x1, x2, 0, 256);
let cur_line_width = c_plwidth - c_p00;
let cur_ball_radius = c_pball - c_p00;
p5.background(255);
for(let x=min_x; x<max_x; x+=grid_size) {
for(let y=min_y; y<max_y; y+=grid_size) {
// First compute shifted point in grid
let offsetX = getRandomValue(p5, x, y, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY = getRandomValue(p5, x, y, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x = x + offsetX;
let shifted_y = y + offsetY;
let x_pos = p5.map(shifted_x, x1, x2, 0, 256);
let y_pos = p5.map(shifted_y, y1, y2, 0, 256);
// now compute shifted point one step to the left
let x_left = x + grid_size;
let y_left = y;
let offsetX_left = getRandomValue(p5, x_left, y_left, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_left = getRandomValue(p5, x_left, y_left, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_left = x_left + offsetX_left;
let shifted_y_left = y_left + offsetY_left;
let x_pos_left = p5.map(shifted_x_left, x1, x2, 0, 256);
let y_pos_left = p5.map(shifted_y_left, y1, y2, 0, 256);
// lastly compute shifted point one step down
let x_down = x;
let y_down = y + grid_size;
let offsetX_down = getRandomValue(p5, x_down, y_down, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_down = getRandomValue(p5, x_down, y_down, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_down = x_down + offsetX_down;
let shifted_y_down = y_down + offsetY_down;
let x_pos_down = p5.map(shifted_x_down, x1, x2, 0, 256);
let y_pos_down = p5.map(shifted_y_down, y1, y2, 0, 256);
/* now draw all elements from back to front */
if (zoom < 2) {
p5.strokeWeight(cur_line_width);
p5.stroke(150, 0, 0);
p5.line(x_pos, y_pos, x_pos_left, y_pos_left);
p5.stroke(0, 150, 0);
p5.line(x_pos, y_pos, x_pos_down, y_pos_down);
}
if (zoom >= 2) {
p5.fill(0, 0, 255);
p5.noStroke();
drawPetals(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius, 2*line_width, z);
}
p5.stroke(0, 0, 150);
p5.fill(0, 0, 128);
p5.noStroke();
p5.ellipse(x_pos, y_pos, cur_ball_radius);
if(zoom >= 3) {
// now if we are super zoomed, draw lines in the stamen
var drawLines = false;
if (zoom >= 5) drawLines = true;
p5.fill(0, 0, 255);
p5.stroke(0, 0, 128);
drawStamens(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius/3, line_width/2, drawLines, z);
}
}
}
// debug - show border
// p5.noFill();
// p5.stroke(0, 200, 200)
// p5.strokeWeight(1);
// p5.rect(0, 0, 255, 255);
// p5.text("corner: (" + x1 + "," + y1 + ")", 10, 20);
// let sizex = x2 - x1;
// p5.text("width: " + sizex, 10, 40);
}
const max_thickness = 64;
const ball_radius = 32;
const line_width = 8;
const grid_size = 64;
const max_movement = 16;
/* the random number seed for the tour */
var tourSeed = 301;
/* triplets of locations: zoom, x, y */
var tourPath = [
[1, 356.500000000000, 665.750000000000],
[3, 353.250000000000, 668.187500000000],
[4, 322.562500000000, 645.093750000000],
[5, 322.562500000000, 645.109375000000],
[7, 317.984375000000, 643.636718750000],
[3, 317.984375000000, 643.636718750000]
]
/* this function takes a coordinate and aligns to a grid of size gsize */
function snap_to_grid(num, gsize) {
return (num - (num % gsize));
}
/* this function returns a point offset by noise at that location */
function getOffsetPoint(p5, x, y, z, noiseScale) {
let noiseX = p5.noise(x * noiseScale,
y * noiseScale, z);
let noiseY = p5.noise(x * noiseScale,
y * noiseScale, z+50);
let offsetX = p5.map(noiseX, 0, 1, -max_movement, max_movement);
let offsetY = p5.map(noiseY, 0, 1, -max_movement, max_movement);
return [x+offsetX, y+offsetY]
}
/*
* This is the funciton to implement to make your own abstract design.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
/* max_shift is the amount of overlap a tile can spill over into its neighbors */
let max_shift = max_thickness + max_movement;
/* this rectangle defines the region that will be drawn and includes a margin */
let min_x = snap_to_grid(x1 - max_shift, grid_size);
let max_x = snap_to_grid(x2 + max_shift + grid_size, grid_size);
let min_y = snap_to_grid(y1 - max_shift, grid_size);
let max_y = snap_to_grid(y2 + max_shift + grid_size, grid_size);
// debug version: draw one
// let half_x = (x1 + x2) / 2;
// let half_y = (y1 + y2) / 2;
// min_x = snap_to_grid(half_x, grid_size);
// max_x = snap_to_grid(half_x + grid_size, grid_size);
// min_y = snap_to_grid(half_y, grid_size);
// max_y = snap_to_grid(half_y + grid_size, grid_size);
let c_p00 = p5.map(0, x1, x2, 0, 256);
let c_plwidth = p5.map(line_width, x1, x2, 0, 256);
let c_pball = p5.map(ball_radius, x1, x2, 0, 256);
let cur_line_width = c_plwidth - c_p00;
let cur_ball_radius = c_pball - c_p00;
p5.background(255);
p5.fill(0, 0, 128);
for(let x=min_x; x<max_x; x+=grid_size) {
for(let y=min_y; y<max_y; y+=grid_size) {
/* first compute all three points with offsets */
let shift_point = getOffsetPoint(p5, x, y, z, 0.1);
let x_pos = p5.map(shift_point[0], x1, x2, 0, 256);
let y_pos = p5.map(shift_point[1], y1, y2, 0, 256);
let shift_point_left = getOffsetPoint(p5, x+grid_size, y, z, 0.1);
let x_pos_left = p5.map(shift_point_left[0], x1, x2, 0, 256);
let y_pos_left = p5.map(shift_point_left[1], y1, y2, 0, 256);
let shift_point_down = getOffsetPoint(p5, x, y+grid_size, z, 0.1);
let x_pos_down = p5.map(shift_point_down[0], x1, x2, 0, 256);
let y_pos_down = p5.map(shift_point_down[1], y1, y2, 0, 256);
/* now draw all elements from back to front */
p5.strokeWeight(cur_line_width);
p5.stroke(150, 0, 0);
p5.line(x_pos, y_pos, x_pos_left, y_pos_left);
p5.stroke(0, 150, 0);
p5.line(x_pos, y_pos, x_pos_down, y_pos_down);
p5.stroke(0, 0, 150);
p5.noStroke();
p5.ellipse(x_pos, y_pos, cur_ball_radius);
}
}
// debug - show border
// p5.noFill();
// p5.stroke(0, 200, 200)
// p5.strokeWeight(1);
// p5.rect(0, 0, 255, 255);
// p5.text("corner: (" + x1 + "," + y1 + ")", 10, 20);
// let sizex = x2 - x1;
// p5.text("width: " + sizex, 10, 40);
}
const max_thickness = 64;
const max_movement = 16;
const ball_radius = 32;
const line_width = 8;
const grid_size = 64;
/* the random number seed for the tour */
var tourSeed = 301;
/* triplets of locations: zoom, x, y */
var tourPath = [
[1, 356.500000000000, 665.750000000000],
[3, 353.250000000000, 668.187500000000],
[4, 322.562500000000, 645.093750000000],
[5, 322.562500000000, 645.109375000000],
[7, 317.984375000000, 643.636718750000],
[3, 317.984375000000, 643.636718750000]
]
/* this function takes a coordinate and aligns to a grid of size gsize */
function snap_to_grid(num, gsize) {
return (num - (num % gsize));
}
/*
* This is the funciton to implement to make your own abstract design.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
/* max_shift is the amount of overlap a tile can spill over into its neighbors */
let max_shift = max_thickness + max_movement;
/* this rectangle defines the region that will be drawn and includes a margin */
let min_x = snap_to_grid(x1 - max_shift, grid_size);
let max_x = snap_to_grid(x2 + max_shift + grid_size, grid_size);
let min_y = snap_to_grid(y1 - max_shift, grid_size);
let max_y = snap_to_grid(y2 + max_shift + grid_size, grid_size);
// debug version: draw one
// let half_x = (x1 + x2) / 2;
// let half_y = (y1 + y2) / 2;
// min_x = snap_to_grid(half_x, grid_size);
// max_x = snap_to_grid(half_x + grid_size, grid_size);
// min_y = snap_to_grid(half_y, grid_size);
// max_y = snap_to_grid(half_y + grid_size, grid_size);
let c_p00 = p5.map(0, x1, x2, 0, 256);
let c_plwidth = p5.map(line_width, x1, x2, 0, 256);
let c_pball = p5.map(ball_radius, x1, x2, 0, 256);
let cur_line_width = c_plwidth - c_p00;
let cur_ball_radius = c_pball - c_p00;
p5.background(255);
p5.fill(0, 0, 128);
for(let x=min_x; x<max_x; x+=grid_size) {
for(let y=min_y; y<max_y; y+=grid_size) {
// First compute shifted point in grid
let offsetX = getRandomValue(p5, x, y, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY = getRandomValue(p5, x, y, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x = x + offsetX;
let shifted_y = y + offsetY;
let x_pos = p5.map(shifted_x, x1, x2, 0, 256);
let y_pos = p5.map(shifted_y, y1, y2, 0, 256);
// now compute shifted point one step to the left
let x_left = x + grid_size;
let y_left = y;
let offsetX_left = getRandomValue(p5, x_left, y_left, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_left = getRandomValue(p5, x_left, y_left, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_left = x_left + offsetX_left;
let shifted_y_left = y_left + offsetY_left;
let x_pos_left = p5.map(shifted_x_left, x1, x2, 0, 256);
let y_pos_left = p5.map(shifted_y_left, y1, y2, 0, 256);
// lastly compute shifted point one step down
let x_down = x;
let y_down = y + grid_size;
let offsetX_down = getRandomValue(p5, x_down, y_down, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_down = getRandomValue(p5, x_down, y_down, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_down = x_down + offsetX_down;
let shifted_y_down = y_down + offsetY_down;
let x_pos_down = p5.map(shifted_x_down, x1, x2, 0, 256);
let y_pos_down = p5.map(shifted_y_down, y1, y2, 0, 256);
/* now draw all elements from back to front */
p5.strokeWeight(cur_line_width);
p5.stroke(150, 0, 0);
p5.line(x_pos, y_pos, x_pos_left, y_pos_left);
p5.stroke(0, 150, 0);
p5.line(x_pos, y_pos, x_pos_down, y_pos_down);
p5.stroke(0, 0, 150);
p5.noStroke();
p5.ellipse(x_pos, y_pos, cur_ball_radius);
}
}
// debug - show border
// p5.noFill();
// p5.stroke(0, 200, 200)
// p5.strokeWeight(1);
// p5.rect(0, 0, 255, 255);
// p5.text("corner: (" + x1 + "," + y1 + ")", 10, 20);
// let sizex = x2 - x1;
// p5.text("width: " + sizex, 10, 40);
}
const max_thickness = 64;
const max_movement = 16;
const ball_radius = 32;
const line_width = 8;
const grid_size = 64;
/* the random number seed for the tour */
var tourSeed = 301;
/* triplets of locations: zoom, x, y */
var tourPath = [
[1, 356.500000000000, 665.750000000000],
[3, 353.250000000000, 668.187500000000],
[4, 322.562500000000, 645.093750000000],
[5, 322.562500000000, 645.109375000000],
[7, 317.984375000000, 643.636718750000],
[3, 317.984375000000, 643.636718750000]
]
/* this function takes a coordinate and aligns to a grid of size gsize */
function snap_to_grid(num, gsize) {
return (num - (num % gsize));
}
/* this function returns a point offset by noise at that location */
function getOffsetPoint(p5, x, y, z, noiseScale) {
let offsetX = getRandomValue(p5, x, y, z, "offsetX", -max_movement, max_movement, noiseScale);
let offsetY = getRandomValue(p5, x, y, z, "offsetY", -max_movement, max_movement, noiseScale);
return [x+offsetX, y+offsetY]
}
function drawPetals(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2) {
const sqrt2 = 1.4142/2;
let offsets = [
[sqrt2, sqrt2],
[-sqrt2, sqrt2],
[-sqrt2, -sqrt2],
[sqrt2, -sqrt2]
]
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
for(let i=0; i<offsets.length; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
}
}
function drawStamens(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2, drawLines) {
const offsets = [
[1, 1],
[1, -1],
[-1, 1],
[-1, -1]
]
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
for(var i=0; i<offsets.length; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
if(drawLines) {
p5.strokeWeight(pixel_radius / 20);
p5.line(pixel_x-pixel_radius, pixel_y, pixel_x+pixel_radius, pixel_y);
p5.line(pixel_x, pixel_y-pixel_radius, pixel_x, pixel_y+pixel_radius);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius / 12);
}
}
}
/*
* This is the funciton to implement to make your own abstract design.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
/* max_shift is the amount of overlap a tile can spill over into its neighbors */
let max_shift = max_thickness + max_movement;
/* this rectangle defines the region that will be drawn and includes a margin */
let min_x = snap_to_grid(x1 - max_shift, grid_size);
let max_x = snap_to_grid(x2 + max_shift + grid_size, grid_size);
let min_y = snap_to_grid(y1 - max_shift, grid_size);
let max_y = snap_to_grid(y2 + max_shift + grid_size, grid_size);
// debug version: draw one
// let half_x = (x1 + x2) / 2;
// let half_y = (y1 + y2) / 2;
// min_x = snap_to_grid(half_x, grid_size);
// max_x = snap_to_grid(half_x + grid_size, grid_size);
// min_y = snap_to_grid(half_y, grid_size);
// max_y = snap_to_grid(half_y + grid_size, grid_size);
let c_p00 = p5.map(0, x1, x2, 0, 256);
let c_plwidth = p5.map(line_width, x1, x2, 0, 256);
let c_pball = p5.map(ball_radius, x1, x2, 0, 256);
let cur_line_width = c_plwidth - c_p00;
let cur_ball_radius = c_pball - c_p00;
p5.background(255);
for(let x=min_x; x<max_x; x+=grid_size) {
for(let y=min_y; y<max_y; y+=grid_size) {
// First compute shifted point in grid
let offsetX = getRandomValue(p5, x, y, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY = getRandomValue(p5, x, y, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x = x + offsetX;
let shifted_y = y + offsetY;
let x_pos = p5.map(shifted_x, x1, x2, 0, 256);
let y_pos = p5.map(shifted_y, y1, y2, 0, 256);
// now compute shifted point one step to the left
let x_left = x + grid_size;
let y_left = y;
let offsetX_left = getRandomValue(p5, x_left, y_left, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_left = getRandomValue(p5, x_left, y_left, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_left = x_left + offsetX_left;
let shifted_y_left = y_left + offsetY_left;
let x_pos_left = p5.map(shifted_x_left, x1, x2, 0, 256);
let y_pos_left = p5.map(shifted_y_left, y1, y2, 0, 256);
// lastly compute shifted point one step down
let x_down = x;
let y_down = y + grid_size;
let offsetX_down = getRandomValue(p5, x_down, y_down, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_down = getRandomValue(p5, x_down, y_down, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_down = x_down + offsetX_down;
let shifted_y_down = y_down + offsetY_down;
let x_pos_down = p5.map(shifted_x_down, x1, x2, 0, 256);
let y_pos_down = p5.map(shifted_y_down, y1, y2, 0, 256);
/* now draw all elements from back to front */
if (zoom < 2) {
p5.strokeWeight(cur_line_width);
p5.stroke(150, 0, 0);
p5.line(x_pos, y_pos, x_pos_left, y_pos_left);
p5.stroke(0, 150, 0);
p5.line(x_pos, y_pos, x_pos_down, y_pos_down);
}
if (zoom >= 2) {
p5.fill(0, 0, 255);
p5.noStroke();
drawPetals(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius, 2*line_width);
}
p5.stroke(0, 0, 150);
p5.fill(0, 0, 128);
p5.noStroke();
p5.ellipse(x_pos, y_pos, cur_ball_radius);
if(zoom >= 3) {
// now if we are super zoomed, draw lines in the stamen
var drawLines = false;
if (zoom >= 5) drawLines = true;
p5.fill(0, 0, 255);
p5.stroke(0, 0, 128);
drawStamens(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius/3, line_width/2, drawLines);
}
}
}
// debug - show border
// p5.noFill();
// p5.stroke(0, 200, 200)
// p5.strokeWeight(1);
// p5.rect(0, 0, 255, 255);
// p5.text("corner: (" + x1 + "," + y1 + ")", 10, 20);
// let sizex = x2 - x1;
// p5.text("width: " + sizex, 10, 40);
}
const max_thickness = 64;
const max_movement = 16;
const ball_radius = 32;
const line_width = 8;
const grid_size = 64;
let do_animation = true;
/* the random number seed for the tour */
var tourSeed = 301;
/* triplets of locations: zoom, x, y */
var tourPath = [
[1, 356.500000000000, 665.750000000000],
[3, 353.250000000000, 668.187500000000],
[4, 322.562500000000, 645.093750000000],
[5, 322.562500000000, 645.109375000000],
[7, 317.984375000000, 643.636718750000],
[3, 317.984375000000, 643.636718750000]
]
/* this function takes a coordinate and aligns to a grid of size gsize */
function snap_to_grid(num, gsize) {
return (num - (num % gsize));
}
/* this function returns a point offset by noise at that location */
function getOffsetPoint(p5, x, y, z, noiseScale) {
let offsetX = getRandomValue(p5, x, y, z, "offsetX", -max_movement, max_movement, noiseScale);
let offsetY = getRandomValue(p5, x, y, z, "offsetY", -max_movement, max_movement, noiseScale);
return [x+offsetX, y+offsetY]
}
function drawPetals(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2) {
const sqrt2 = 1.4142/2;
let offsets = [
[sqrt2, sqrt2],
[-sqrt2, sqrt2],
[-sqrt2, -sqrt2],
[sqrt2, -sqrt2]
]
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
for(let i=0; i<offsets.length; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
}
}
function drawStamens(p5, x1, x2, y1, y2, pos_x, pos_y, rad1, rad2, drawLines, z) {
const offsets = [
[1, 1],
[1, -1],
[-1, 1],
[-1, -1]
]
let pixel_posx1 = p5.map(pos_x, x1, x2, 0, 256);
let pixel_posx2 = p5.map(pos_x+rad2, x1, x2, 0, 256);
let pixel_radius = pixel_posx2 - pixel_posx1;
let z_fraction = z % 1.0;
let num_stamens = p5.map(z_fraction, 0, 1, 0, offsets.length)
for(var i=0; i<num_stamens; i++) {
let offset = offsets[i];
let pixel_x = p5.map(pos_x+0.5*rad1*offset[0], x1, x2, 0, 256);
let pixel_y = p5.map(pos_y+0.5*rad1*offset[1], y1, y2, 0, 256);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius);
if(drawLines) {
p5.strokeWeight(pixel_radius / 20);
p5.line(pixel_x-pixel_radius, pixel_y, pixel_x+pixel_radius, pixel_y);
p5.line(pixel_x, pixel_y-pixel_radius, pixel_x, pixel_y+pixel_radius);
p5.strokeWeight(0);
p5.ellipse(pixel_x, pixel_y, pixel_radius / 12);
}
}
}
/*
* This is the funciton to implement to make your own abstract design.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
/* max_shift is the amount of overlap a tile can spill over into its neighbors */
let max_shift = max_thickness + max_movement;
/* For animation: updated z based on global frame count */
let dz = p5.globalFrameCount / 100.0;
z = z + dz;
/* this rectangle defines the region that will be drawn and includes a margin */
let min_x = snap_to_grid(x1 - max_shift, grid_size);
let max_x = snap_to_grid(x2 + max_shift + grid_size, grid_size);
let min_y = snap_to_grid(y1 - max_shift, grid_size);
let max_y = snap_to_grid(y2 + max_shift + grid_size, grid_size);
// debug version: draw one
// let half_x = (x1 + x2) / 2;
// let half_y = (y1 + y2) / 2;
// min_x = snap_to_grid(half_x, grid_size);
// max_x = snap_to_grid(half_x + grid_size, grid_size);
// min_y = snap_to_grid(half_y, grid_size);
// max_y = snap_to_grid(half_y + grid_size, grid_size);
let c_p00 = p5.map(0, x1, x2, 0, 256);
let c_plwidth = p5.map(line_width, x1, x2, 0, 256);
let c_pball = p5.map(ball_radius, x1, x2, 0, 256);
let cur_line_width = c_plwidth - c_p00;
let cur_ball_radius = c_pball - c_p00;
p5.background(255);
for(let x=min_x; x<max_x; x+=grid_size) {
for(let y=min_y; y<max_y; y+=grid_size) {
// First compute shifted point in grid
let offsetX = getRandomValue(p5, x, y, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY = getRandomValue(p5, x, y, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x = x + offsetX;
let shifted_y = y + offsetY;
let x_pos = p5.map(shifted_x, x1, x2, 0, 256);
let y_pos = p5.map(shifted_y, y1, y2, 0, 256);
// now compute shifted point one step to the left
let x_left = x + grid_size;
let y_left = y;
let offsetX_left = getRandomValue(p5, x_left, y_left, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_left = getRandomValue(p5, x_left, y_left, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_left = x_left + offsetX_left;
let shifted_y_left = y_left + offsetY_left;
let x_pos_left = p5.map(shifted_x_left, x1, x2, 0, 256);
let y_pos_left = p5.map(shifted_y_left, y1, y2, 0, 256);
// lastly compute shifted point one step down
let x_down = x;
let y_down = y + grid_size;
let offsetX_down = getRandomValue(p5, x_down, y_down, z, "shiftX", -max_movement, max_movement, 0.1);
let offsetY_down = getRandomValue(p5, x_down, y_down, z, "shiftY", -max_movement, max_movement, 0.1);
let shifted_x_down = x_down + offsetX_down;
let shifted_y_down = y_down + offsetY_down;
let x_pos_down = p5.map(shifted_x_down, x1, x2, 0, 256);
let y_pos_down = p5.map(shifted_y_down, y1, y2, 0, 256);
/* now draw all elements from back to front */
if (zoom < 2) {
p5.strokeWeight(cur_line_width);
p5.stroke(150, 0, 0);
p5.line(x_pos, y_pos, x_pos_left, y_pos_left);
p5.stroke(0, 150, 0);
p5.line(x_pos, y_pos, x_pos_down, y_pos_down);
}
if (zoom >= 2) {
p5.fill(0, 0, 255);
p5.noStroke();
drawPetals(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius, 2*line_width);
}
p5.stroke(0, 0, 150);
p5.fill(0, 0, 128);
p5.noStroke();
p5.ellipse(x_pos, y_pos, cur_ball_radius);
if(zoom >= 3) {
// now if we are super zoomed, draw lines in the stamen
var drawLines = false;
if (zoom >= 5) drawLines = true;
p5.fill(0, 0, 255);
p5.stroke(0, 0, 128);
drawStamens(p5, x1, x2, y1, y2, shifted_x, shifted_y, ball_radius/3, line_width/2, drawLines, z);
}
}
}
// debug - show border
// p5.noFill();
// p5.stroke(0, 200, 200)
// p5.strokeWeight(1);
// p5.rect(0, 0, 255, 255);
// p5.text("corner: (" + x1 + "," + y1 + ")", 10, 20);
// let sizex = x2 - x1;
// p5.text("width: " + sizex, 10, 40);
}
/*
* This is the a class example of the abstract design framework.
*
* arguments:
* p5: the p5.js object - all draw commands should be prefixed with this object
* x1, x2, y1, y2: draw the pattern contained in the rectangle x1,y1 to x2, y2
* z: use this as the noise z offset (can be shifted)
* zoom: current zoom level (starts at 0), useful to decide how much detail to draw
*
* The destination drawing should be in the square 0, 0, 255, 255.
*/
/* the random number seed for the tour */
var tourSeed = 301;
/* triplets of locations: zoom, x, y */
var tourPath = [
[2, 512, 512],
[2, 420, 400],
[4, 420, 400]
]
// This version draws two rectangles and two ellipses.
// The rectangles are 960x720 and centered at 512,512.
function drawGrid(p5, x1, x2, y1, y2, z, zoom) {
// temporary variables used for object placement
let cx=0, cy=0, cx2=0, cy2=0;
p5.background(255);
p5.rectMode(p5.CORNERS);
// The first red rectangle fills the entire space
cx = p5.map(512-960/2, x1, x2, 0, 256);
cy = p5.map(512-720/2, y1, y2, 0, 256);
cx2 = p5.map(512+960/2, x1, x2, 0, 256);
cy2 = p5.map(512+720/2, y1, y2, 0, 256);
p5.fill(255, 0, 0);
p5.rect(cx, cy, cx2, cy2);
// The second black rectangle is inset to form a frame inset by 20 units
cx = p5.map(512-940/2, x1, x2, 0, 256);
cy = p5.map(512-700/2, y1, y2, 0, 256);
cx2 = p5.map(512+940/2, x1, x2, 0, 256);
cy2 = p5.map(512+700/2, y1, y2, 0, 256);
p5.fill(0);
p5.rect(cx, cy, cx2, cy2);
// Two ellipses with a radius of 50 units are then added.
cx = p5.map(512, x1, x2, 0, 256);
cy = p5.map(512, y1, y2, 0, 256);
cx2 = p5.map(512+50, x1, x2, 0, 256);
p5.fill(0, 0, 255);
p5.ellipse(cx, cy, (cx2-cx));
// The second green ellipse is above and to the left of the first one.
cx = p5.map(412, x1, x2, 0, 256);
cy = p5.map(412, y1, y2, 0, 256);
cx2 = p5.map(412+50, x1, x2, 0, 256);
p5.fill(0, 255, 0);
p5.ellipse(cx, cy, (cx2-cx));
// debug - show border
// p5.noFill();
// p5.stroke(255, 0, 0)
// p5.rect(0, 0, 255, 255);
}
<!DOCTYPE html>
<meta charset="utf-8">
<title>framed version</title>
<div class="column">
<h3>960x720 preview</h3>
<div class="index">
<iframe sandbox="allow-popups allow-scripts allow-forms allow-same-origin" src="index.html" marginwidth="0" marginheight="0" style="width:960px;height:720px;" scrolling="no"></iframe>
</div>
</div>
<!DOCTYPE html>
<html>
<head>
<title>17.2.MDDN342 PS4</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.2.0/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 100%;
width: 100%;
background-color: white;
}
.leaflet-control-attribution {
font-size: 24px !important;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.js"></script>
<script src="https://unpkg.com/leaflet@1.2.0/dist/leaflet.js"></script>
<script src="leaflet-hash.js"></script>
<script src="drawgrid.js"></script>
<script src="map.js"></script>
</body>
</html>
(function(window) {
var HAS_HASHCHANGE = (function() {
var doc_mode = window.documentMode;
return ('onhashchange' in window) &&
(doc_mode === undefined || doc_mode > 7);
})();
L.Hash = function(map) {
this.onHashChange = L.Util.bind(this.onHashChange, this);
if (map) {
this.init(map);
}
};
L.Hash.parseHash = function(hash) {
if(hash.indexOf('#') === 0) {
hash = hash.substr(1);
}
var args = hash.split("/");
if (args.length == 5) {
var seed = parseInt(args[0], 10),
zoom = parseInt(args[1], 10),
lat = parseFloat(args[2]),
lon = parseFloat(args[3]);
depth = parseFloat(args[4]);
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
return false;
} else {
return {
center: new L.LatLng(lat, lon),
zoom: zoom,
seed: seed,
depth: depth
};
}
} else {
return false;
}
};
L.Hash.formatHash = function(map) {
var seed = map._p5_seed,
depth = map._p5_depth,
center = map.getCenter(),
zoom = map.getZoom(),
precision = 12;
// precision = Math.max(0, Math.ceil(Math.log(zoom*zoom) / Math.LN2));
return "#" + [seed, zoom,
center.lat.toFixed(precision),
center.lng.toFixed(precision),
depth.toFixed(precision)
].join("/");
},
L.Hash.prototype = {
map: null,
lastHash: null,
parseHash: L.Hash.parseHash,
formatHash: L.Hash.formatHash,
init: function(map) {
this.map = map;
// reset the hash
this.lastHash = null;
this.onHashChange();
if (!this.isListening) {
this.startListening();
}
},
removeFrom: function(map) {
if (this.changeTimeout) {
clearTimeout(this.changeTimeout);
}
if (this.isListening) {
this.stopListening();
}
this.map = null;
},
onMapMove: function() {
// bail if we're moving the map (updating from a hash),
// or if the map is not yet loaded
if (this.movingMap || !this.map._loaded) {
return false;
}
var hash = this.formatHash(this.map);
if (this.lastHash != hash) {
location.replace(hash);
this.lastHash = hash;
}
},
movingMap: false,
update: function() {
var hash = location.hash;
if (hash === this.lastHash) {
return;
}
var parsed = this.parseHash(hash);
if (parsed) {
var do_reset = false;
if (!("_hash_parsed" in this.map)) {
do_reset = true;
}
this.map._hash_parsed = true;
this.map._p5_seed = parsed.seed;
this.map._p5_depth = parsed.depth;
this.movingMap = true;
this.map.setView(parsed.center, parsed.zoom, {reset: do_reset});
this.movingMap = false;
}
else if (!("_hash_parsed" in this.map)) {
this.map._hash_parsed = true;
var center = this.map.getCenter();
var zoom = this.map.getZoom();
this.map.setView(center, zoom, {reset: true});
}
else {
this.onMapMove(this.map);
}
},
// defer hash change updates every 100ms
changeDefer: 100,
changeTimeout: null,
onHashChange: function() {
// throttle calls to update() so that they only happen every
// `changeDefer` ms
if (!this.changeTimeout) {
var that = this;
this.changeTimeout = setTimeout(function() {
that.update();
that.changeTimeout = null;
}, this.changeDefer);
}
},
isListening: false,
hashChangeInterval: null,
startListening: function() {
this.map.on("moveend", this.onMapMove, this);
if (HAS_HASHCHANGE) {
L.DomEvent.addListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
this.hashChangeInterval = setInterval(this.onHashChange, 50);
}
this.isListening = true;
},
stopListening: function() {
this.map.off("moveend", this.onMapMove, this);
if (HAS_HASHCHANGE) {
L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
}
this.isListening = false;
}
};
L.hash = function(map) {
return new L.Hash(map);
};
L.Map.prototype.addHash = function() {
this._hash = L.hash(this);
};
L.Map.prototype.removeHash = function() {
this._hash.removeFrom();
};
})(window);
String.prototype.hashCode = function() {
var hash = 0, i, chr;
if (this.length === 0) return hash;
for (i = 0; i < this.length; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
function getRandomValue(p5, x, y, z, name, min, max, scale) {
let hashNumber = name.hashCode();
let noiseVal = p5.noise(x * scale, y * scale, (z + hashNumber));
return p5.map(noiseVal, 0, 1, min, max);
}
var myCRS = L.extend({}, L.CRS.Simple, {
transformation: new L.Transformation(1, 0,
// -1, // works like expected
1, // image travels while zooming
0)
});
if (typeof initialZoomLevel === 'undefined') {
var initialZoomLevel = 0;
}
if (typeof maxZoomLevel === 'undefined') {
var maxZoomLevel = 16;
}
var worldMap = new L.Map('map', {
continuousWorld:true,
minZoom: 0,
maxZoom: maxZoomLevel,
crs: myCRS,
attributionControl: false,
center: [512, 512],
zoom: initialZoomLevel});
worldMap._p5_seed = Math.floor(Math.random() * 1000);
worldMap._p5_depth = 0.0;
// console.log("Seed start", worldMap._p5_seed)
// Assuming your map instance is in a variable called map
var hash = new L.Hash(worldMap);
// console.log("Seed now", worldMap._p5_seed)
// sloppy way to set tile size
var g_tileSize = null;
var s = function( p ) {
p.setup = function() {
canvas = p.createCanvas(g_tileSize.x, g_tileSize.y);
p.noLoop();
};
p.draw = function() {
if ("_L_size" in p && "_L_nw" in p) {
var nw = p._L_nw;
var t_size = p._L_size;
var zoom = p._L_zoom;
var m_x1 = nw.lng;
var m_y1 = nw.lat;
var m_x2 = m_x1 + t_size;
var m_y2 = m_y1 + t_size;
var depth = p._L_depth;
p.noiseSeed(p._L_seed)
drawGrid(p, m_x1, m_x2, m_y1, m_y2, depth, zoom);
}
};
};
var tiles = new L.GridLayer({continuousWorld: true});
tiles.createTile = function(coords) {
if (!("_hash_parsed" in worldMap)) {
return L.DomUtil.create('canvas', 'leaflet-tile');
}
var size = this.getTileSize();
g_tileSize = size;
var myp5 = new p5(s);
myp5._L_width = size.x;
myp5._L_height = size.y;
myp5._L_zoom = coords.z;
myp5._L_seed = worldMap._p5_seed;
myp5._L_depth = worldMap._p5_depth;
myp5._L_coords = coords;
// calculate projection coordinates of top left tile pixel
myp5._L_nwPoint = coords.scaleBy(size);
myp5._L_size = 256.0 / Math.pow(2, coords.z)
// calculate geographic coordinates of top left tile pixel
myp5._L_nw = worldMap.unproject(myp5._L_nwPoint, coords.z)
var tile = myp5.canvas;
tile.rendering = true;
if(typeof do_animation !== 'undefined' && do_animation) {
(function doAnimate(){
if(tile.rendering){
myp5.globalFrameCount = worldMap.globalFrameCount;
myp5.redraw()
requestAnimationFrame(doAnimate)
}
})()
}
myp5._start();
tile.p5 = myp5
L.DomUtil.addClass(tile, 'leaflet-tile');
return tile;
}
tiles.on('tileunload', function(e){
e.tile.rendering = false;
})
// tiles.on('tileload', function(e){
// /** @type {HTMLCanvasElement} */
// var tile = e.tile
// /** @type {p5} */
// var p5 = tile.p5;
// if(p5){
// console.log(e)
// p5.redraw()
// }
// })
// setInterval(function(){
// if(!tiles.isLoading()){
// tiles.redraw()
// }
// }, 1000)
tiles.addTo(worldMap)
var curLinkIndex = 0;
linkHome = "#0/0/512/512/0"
if (typeof tourPath === 'undefined') {
var tourPath = [
[2, 512, 512],
[4, 512, 512],
[6, 512, 512],
[8, 512, 512]
]
}
tourPath.unshift([initialZoomLevel, 512, 512]);
if (typeof tourSeed === 'undefined') {
var tourSeed = 0;
}
function clickHome() {
worldMap.flyTo([tourPath[0][1], tourPath[0][2]], tourPath[0][0]);
}
worldMap.globalFrameCount = 0;
if(typeof do_animation !== 'undefined' && do_animation) {
(function doCounter() {
worldMap.globalFrameCount = worldMap.globalFrameCount + 1;
requestAnimationFrame(doCounter);
})()
}
function clickDemo() {
if(worldMap._p5_seed != tourSeed) {
var center = worldMap.getCenter();
var zoom = worldMap.getZoom();
worldMap._p5_seed = tourSeed;
tiles.redraw();
// worldMap.setView(center, zoom, {reset: true});
curLinkIndex = 0;
}
else {
curLinkIndex = (curLinkIndex + 1) % tourPath.length
}
var curDest = tourPath[curLinkIndex]
worldMap.flyTo([curDest[1], curDest[2]], curDest[0]);
}
function clickReset() {
window.location.reload();
}
attrib = new L.Control.Attribution
attrib.setPrefix("")
attrStr = '<a href="#" onclick="javascript:clickHome();">home</a> | '
attrStr += '<a href="#" onclick="javascript:clickReset();">reset</a> | '
attrStr += '<a href="#" onclick="javascript:clickDemo();">tour</a>'
attrib.addAttribution(attrStr)
worldMap.addControl(attrib)
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.js"></script>
<script language="javascript" type="text/javascript" src="owl.js"></script>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
</body>
function setup () {
createCanvas(960, 500);
}
function keyTyped() {
if (key == '!') {
saveBlocksImages();
}
else if (key == '@') {
saveBlocksImages(true);
}
}
function draw() {
background(204);
owl(110, 110);
owl(180, 110);
owl(400, 110);
}
function owl(x, y) {
push();
translate(x, y);
stroke(0);
strokeWeight(70);
line(0, -35, 0, -65); // Body
noStroke();
fill(255);
ellipse(-17.5, -65, 35, 35); // Left eye dome
ellipse(17.5, -65, 35, 35); // Right eye dome
arc(0, -65, 70, 70, 0, PI); // Chin
fill(0);
ellipse(-14, -65, 8, 8); // Left eye
ellipse(14, -65, 8, 8); // Right eye
quad(0, -58, 4, -51, 0, -44, -4, -51); // Beak
pop();
}
{
"commits": [
{
"name": "p5.globalFrameCount animation",
"sha": "04f772f7e3e304fcb365b3f1f542973c1178d278"
},
{
"name": "tourPath and tourSeed",
"sha": "c5125f80061df144941ef86007013c7b4cccd1ba"
},
{
"name": "animation example",
"sha": "f3f6acf5d777346fa69f1b45602d71de0a73fb94"
},
{
"name": "zoom details example",
"sha": "dfcfe7027c22c19021e6c6174a6bfe4719720e82"
},
{
"name": "randomness example",
"sha": "b28b6e3e495f118d37a2d940eb77fe2fbec67c98"
},
{
"name": "first grid example",
"sha": "be74e2a4628b630d67d0abe12a637df3bcfadb53"
},
{
"name": "adapting owl example",
"sha": "45fac8111292984d0b605792173574beb0d0b229"
},
{
"name": "first class example",
"sha": "445b5ba0d2554f3fa7b1a6bfdff5439f2d2a6b66"
},
{
"name": "starting code",
"sha": "fc7fe100e8dc0cd48f5bda374669ecb56ccb8513"
}
]
}
<head>
<style> body {padding: 0; margin: 0;} </style>
</head>
<body style="background-color:white">
<img src="sketch.png" width="960" height="720"/>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment