├── frontier
├── sketch.properties
├── frontier.pde
├── Sky.pde
├── Tree.pde
├── Mountains.pde
└── Building.pde
├── img
├── tree.png
├── building.png
├── landscape.png
├── buildings-day.png
└── buildings-night.png
├── .gitignore
├── README.md
└── js
├── src
├── Sky.js
├── helpers.js
├── Tree.js
├── Mountains.js
├── noise.js
├── setup.js
└── Building.js
├── build.pl
├── frontier.min.js
└── frontier.js
/frontier/sketch.properties:
--------------------------------------------------------------------------------
1 | mode.id=processing.mode.java.JavaMode
2 | mode=Java
3 |
--------------------------------------------------------------------------------
/img/tree.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davepagurek/Frontier/HEAD/img/tree.png
--------------------------------------------------------------------------------
/img/building.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davepagurek/Frontier/HEAD/img/building.png
--------------------------------------------------------------------------------
/img/landscape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davepagurek/Frontier/HEAD/img/landscape.png
--------------------------------------------------------------------------------
/img/buildings-day.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davepagurek/Frontier/HEAD/img/buildings-day.png
--------------------------------------------------------------------------------
/img/buildings-night.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davepagurek/Frontier/HEAD/img/buildings-night.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | .DS_STORE
3 |
4 | # Mobile Tools for Java (J2ME)
5 | .mtj.tmp/
6 |
7 | # Package Files #
8 | *.jar
9 | *.war
10 | *.ear
11 |
12 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
13 | hs_err_pid*
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frontier
2 | Procedural landscape generation in Processing. Read about how it works
3 |
4 | ## Screenshots
5 | Landscape in the day:
6 |
7 |
8 | Landscape at night:
9 |
10 |
--------------------------------------------------------------------------------
/frontier/frontier.pde:
--------------------------------------------------------------------------------
1 | void setup() {
2 | size(1280, 720);
3 | drawScene();
4 | }
5 |
6 | void draw() {
7 | }
8 |
9 | void mousePressed() {
10 | drawScene();
11 | }
12 |
13 | void drawScene() {
14 | background(255);
15 |
16 | float time = random(1);
17 |
18 | Sky sky = new Sky(time);
19 | sky.draw();
20 |
21 | Mountains mountains = new Mountains(height*0.2, height*0.7, time);
22 | mountains.draw();
23 | }
24 |
--------------------------------------------------------------------------------
/js/src/Sky.js:
--------------------------------------------------------------------------------
1 | function Sky(time) {
2 | var sky, horizon;
3 | if (time < 0.5) {
4 | sky = lerpColor("#B9F7D5", "#57C8F0", map(time, 0, 0.5, 0, 1));
5 | horizon = lerpColor("#FCED42", "#E365ED", map(time, 0, 0.5, 0, 1));
6 | } else {
7 | sky = lerpColor("#C282E5", "#1A285A", map(time, 0.5, 1, 0, 1));
8 | horizon = lerpColor("#82E3E5", "#BFE1FC", map(time, 0.5, 1, 0, 1));
9 | }
10 |
11 | this.draw = function() {
12 | setGradient(0, 0, width, height, sky, horizon);
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/js/src/helpers.js:
--------------------------------------------------------------------------------
1 | function throttle(fn, threshhold, scope) {
2 | threshhold || (threshhold = 250);
3 | var last,
4 | deferTimer;
5 | return function () {
6 | var context = scope || this;
7 |
8 | var now = +new Date,
9 | args = arguments;
10 | if (last && now < last + threshhold) {
11 | // hold on to it
12 | clearTimeout(deferTimer);
13 | deferTimer = setTimeout(function () {
14 | last = now;
15 | fn.apply(context, args);
16 | }, threshhold);
17 | } else {
18 | last = now;
19 | fn.apply(context, args);
20 | }
21 | };
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/js/build.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 |
3 | use File::Slurp;
4 | use autodie;
5 |
6 | my @files = qw(
7 | helpers.js
8 | noise.js
9 | Building.js
10 | Tree.js
11 | Mountains.js
12 | Sky.js
13 | setup.js
14 | );
15 |
16 | my $content = "";
17 |
18 | for my $file (@files) {
19 | $content .= read_file("src/$file") . "\n\n";
20 | }
21 |
22 |
23 | open my $condensed, ">", "frontier.js";
24 | print $condensed $content;
25 | close $condensed;
26 |
27 | #remove comments
28 | $content =~ s|//.*$||mg;
29 | $content =~ s|/\*.*?\*/||mg;
30 |
31 | #condense whitespace
32 | $content =~ s/\s+/ /mg;
33 |
34 | open my $minified, ">", "frontier.min.js";
35 | print $minified $content;
36 | close $minified;
37 |
38 |
39 |
--------------------------------------------------------------------------------
/frontier/Sky.pde:
--------------------------------------------------------------------------------
1 | class Sky {
2 | color sky, horizon;
3 | final int Y_AXIS = 1,
4 | X_AXIS = 2;
5 |
6 | Sky(float time) {
7 | if (time < 0.5) {
8 | sky = lerpColor(#B9F7D5, #57C8F0, map(time, 0, 0.5, 0, 1));
9 | horizon = lerpColor(#FCED42, #E365ED, map(time, 0, 0.5, 0, 1));
10 | } else {
11 | sky = lerpColor(#C282E5, #1A285A, map(time, 0.5, 1, 0, 1));
12 | horizon = lerpColor(#82E3E5, #BFE1FC, map(time, 0.5, 1, 0, 1));
13 | }
14 | }
15 |
16 | void draw() {
17 | setGradient(0, 0, width, height, sky, horizon, Y_AXIS);
18 | }
19 |
20 | private void setGradient(int x, int y, float w, float h, color c1, color c2, int axis) {
21 | noFill();
22 |
23 | if (axis == Y_AXIS) { // Top to bottom gradient
24 | for (int i = y; i <= y+h; i++) {
25 | float inter = map(i, y, y+h, 0, 1);
26 | color c = lerpColor(c1, c2, inter);
27 | stroke(c);
28 | line(x, i, x+w, i);
29 | }
30 | } else if (axis == X_AXIS) { // Left to right gradient
31 | for (int i = x; i <= x+w; i++) {
32 | float inter = map(i, x, x+w, 0, 1);
33 | color c = lerpColor(c1, c2, inter);
34 | stroke(c);
35 | line(i, y, i, y+h);
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/js/src/Tree.js:
--------------------------------------------------------------------------------
1 | function Tree(x, y, w, h, time) {
2 | var root = new Branch(x, y, w, h, BRANCH_ROOT, 1, time);
3 | this.draw = function() {
4 | root.drawLeaves();
5 | root.drawBranches();
6 | };
7 | }
8 |
9 | function Branch(x, y, w, h, side, level, time) {
10 | var branches = [];
11 | var segmentX = 0;
12 | var segmentY = 0;
13 | var leafWidth = 0;
14 | var leafHeight = 0;
15 |
16 | if (side == BRANCH_ROOT) {
17 | segmentX = x + (random(w * 0.6) - w * 0.3);
18 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
19 | } else if (side == BRANCH_LEFT) {
20 | segmentX = x - random(w * 0.3);
21 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
22 | } else if (side == BRANCH_RIGHT) {
23 | segmentX = x + random(w * 0.3);
24 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
25 | }
26 |
27 | leafWidth = random(w * 0.6) + w * 0.3;
28 | leafHeight = random(h * 0.6) + h * 0.3;
29 |
30 | if (level < 3) {
31 | for (var i = 0; i < BRANCH_MAX_LEVELS; i++) {
32 | if (random(10) >= 7) {
33 | var branchY = random(segmentY, y);
34 | var branchX = x + ((segmentX - x) / (segmentY - y)) * (branchY - y);
35 | var branchDir = random(10) > 5 ? BRANCH_LEFT : BRANCH_RIGHT;
36 | branches.push(new Branch(branchX, branchY, w * 0.6, h * 0.5, branchDir, level + 1, time));
37 | }
38 | }
39 | }
40 |
41 | this.drawLeaves = function() {
42 | var leafColor;
43 | if (time < 0.5) {
44 | leafColor = lerpColor("#FFD1D1", "#F2C975", map(time, 0, 0.5, 0, 1));
45 | } else {
46 | leafColor = lerpColor("#D5BCE0", "#56627C", map(time, 0.5, 1, 0, 1));
47 | }
48 | fill(leafColor);
49 | ellipse(segmentX, segmentY, leafWidth, leafHeight);
50 |
51 | branches.forEach(function(b) {
52 | b.drawLeaves();
53 | });
54 | };
55 |
56 | this.drawBranches = function() {
57 | var branchColor;
58 | if (time < 0.5) {
59 | branchColor = lerpColor("#79504E", "#866A59", map(time, 0, 0.5, 0, 1));
60 | } else {
61 | branchColor = lerpColor("#635774", "#635774", map(time, 0.5, 1, 0, 1));
62 | }
63 |
64 | strokeWeight((y - segmentY) / 20);
65 | stroke(branchColor);
66 | line(x, y, segmentX, segmentY);
67 |
68 | branches.forEach(function(b) {
69 | b.drawBranches();
70 | });
71 | };
72 | }
73 |
--------------------------------------------------------------------------------
/frontier/Tree.pde:
--------------------------------------------------------------------------------
1 | final int BRANCH_ROOT = 0,
2 | BRANCH_LEFT = -1,
3 | BRANCH_RIGHT = 1,
4 |
5 | BRANCH_MAX_LEVELS = 3;
6 |
7 | class Tree {
8 | Branch root;
9 |
10 | Tree(float x, float y, float w, float h, float time) {
11 | root = new Branch(x, y, w, h, BRANCH_ROOT, 1, time);
12 | }
13 |
14 | void draw() {
15 | root.drawLeaves();
16 | root.drawBranches();
17 | }
18 | }
19 |
20 | class Branch {
21 | float segmentX = 0, segmentY = 0;
22 | float leafWidth = 0, leafHeight = 0;
23 | float rootX = 0, rootY = 0;
24 | float time;
25 | ArrayList branches;
26 |
27 | Branch(float x, float y, float w, float h, int side, int level, float t) {
28 | rootX = x;
29 | rootY = y;
30 |
31 | time = t;
32 |
33 | branches = new ArrayList();
34 |
35 | if (side == BRANCH_ROOT) {
36 | segmentX = x+(random(w*0.6)-w*0.3);
37 | segmentY = y-h*0.75+(random(h*0.3)-h*0.15);
38 | } else if (side == BRANCH_LEFT) {
39 | segmentX = x-random(w*0.3);
40 | segmentY = y-h*0.75+(random(h*0.3)-h*0.15);
41 | } else if (side == BRANCH_RIGHT) {
42 | segmentX = x+random(w*0.3);
43 | segmentY = y-h*0.75+(random(h*0.3)-h*0.15);
44 | }
45 |
46 | leafWidth = random(w*0.6)+w*0.3;
47 | leafHeight = random(h*0.6)+h*0.3;
48 |
49 | if (level < 3) {
50 | for (int i=0; i= 7) {
52 | float branchY = random(segmentY, y);
53 | float branchX = x + ((segmentX - x)/(segmentY - y)) * (branchY - y);
54 | int branchDir = random(10) > 5 ? BRANCH_LEFT : BRANCH_RIGHT;
55 | branches.add(new Branch(branchX, branchY, w*0.6, h*0.5, branchDir, level+1, time));
56 | }
57 | }
58 | }
59 | }
60 |
61 | void drawLeaves() {
62 | noStroke();
63 | color leafColor;
64 | if (time < 0.5) {
65 | leafColor = lerpColor(#FFD1D1, #F2C975, map(time, 0, 0.5, 0, 1));
66 | } else {
67 | leafColor = lerpColor(#D5BCE0, #56627C, map(time, 0.5, 1, 0, 1));
68 | }
69 | fill(leafColor);
70 | ellipse(segmentX, segmentY, leafWidth, leafHeight);
71 |
72 | for (Branch b : branches) {
73 | b.drawLeaves();
74 | }
75 | }
76 |
77 | void drawBranches() {
78 | color branchColor;
79 | if (time < 0.5) {
80 | branchColor = lerpColor(#79504E, #866A59, map(time, 0, 0.5, 0, 1));
81 | } else {
82 | branchColor = lerpColor(#635774, #635774, map(time, 0.5, 1, 0, 1));
83 | }
84 |
85 | strokeWeight((rootY - segmentY)/20);
86 | stroke(branchColor);
87 | line(rootX, rootY, segmentX, segmentY);
88 |
89 | for (Branch b : branches) {
90 | b.drawBranches();
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/js/src/Mountains.js:
--------------------------------------------------------------------------------
1 | function Mountains(top, bottom, time) { //#D36F6F, #AD1515
2 | var ranges = [];
3 | var start, end;
4 | if (time < 0.5) {
5 | start = lerpColor("#D36F6F", "#F2C178", map(time, 0, 0.5, 0, 1));
6 | end = lerpColor("#AD1515", "#E85B57", map(time, 0, 0.5, 0, 1));
7 | } else {
8 | start = lerpColor("#654183", "#2D3A5F", map(time, 0.5, 1, 0, 1));
9 | end = lerpColor("#B198C6", "#6172A5", map(time, 0.5, 1, 0, 1));
10 | }
11 |
12 | var layers = int(random(2, 5));
13 | var heightUnit = (bottom - top) / (layers + 1);
14 |
15 | for (var l = 0; l < layers; l++) {
16 | var y = top + random(heightUnit * l, heightUnit * (l + 1));
17 | var spread = random(1, 3) * heightUnit;
18 | ranges.push(new MountainRange(
19 | y,
20 | spread,
21 | lerpColor(start, end, l/layers), (l + 1) / layers,
22 | time
23 | ));
24 | }
25 |
26 | this.draw = function() {
27 | ranges.forEach(function(range) {
28 | range.draw();
29 | });
30 | };
31 | }
32 |
33 | function MountainRange(y, spread, c, foreground, time) {
34 | var elevation = [];
35 | var trees = [];
36 | var buildings = [];
37 |
38 | var noiseElevation = random(1, 100);
39 | var spikiness = random(40, 90);
40 |
41 | for (var x = 0; x < 150; x++) {
42 | elevation.push(map(noise(x / spikiness, noiseElevation), 0, 1, -1, 1) * spread);
43 | }
44 |
45 | var numTrees = int(random(1, map(1.0 / foreground, 1, 100, 5, 40)));
46 | for (var i = 0; i < numTrees; i++) {
47 | var location = int(random(elevation.length));
48 | trees.push(new Tree(
49 | (location / (elevation.length - 1)) * width,
50 | y + elevation[location],
51 | foreground * 100,
52 | foreground * 80,
53 | time
54 | ));
55 | }
56 |
57 | var numBuildings = int(random(1, map(1.0 / foreground, 1, 100, 0, 10)));
58 | for (var i = 0; i < numTrees; i++) {
59 | var location = int(random(elevation.length));
60 | buildings.push(new Building(
61 | (location / (elevation.length - 1)) * width,
62 | y + elevation[location] + spread * 0.1,
63 | foreground * 80,
64 | foreground * 120,
65 | time
66 | ));
67 | }
68 |
69 | this.draw = function() {
70 | buildings.forEach(function(building) {
71 | building.draw();
72 | });
73 |
74 | fill(c);
75 | beginShape();
76 | for (var i = 0; i < elevation.length; i++) {
77 | vertex((i / (elevation.length - 1)) * width, y + elevation[i]);
78 | }
79 | vertex(width, height);
80 | vertex(0, height);
81 | endShape();
82 |
83 | trees.forEach(function(tree) {
84 | tree.draw();
85 | });
86 | };
87 | }
88 |
--------------------------------------------------------------------------------
/frontier/Mountains.pde:
--------------------------------------------------------------------------------
1 | class Mountains {
2 | int layers = 0;
3 | float heightUnit = 0;
4 | color start, end;
5 | ArrayList ranges;
6 |
7 | Mountains(float top, float bottom, float time) { //#D36F6F, #AD1515
8 | if (time < 0.5) {
9 | start = lerpColor(#D36F6F, #F2C178, map(time, 0, 0.5, 0, 1));
10 | end = lerpColor(#AD1515, #E85B57, map(time, 0, 0.5, 0, 1));
11 | } else {
12 | start = lerpColor(#654183, #2D3A5F, map(time, 0.5, 1, 0, 1));
13 | end = lerpColor(#B198C6, #6172A5, map(time, 0.5, 1, 0, 1));
14 | }
15 |
16 | layers = int(random(2, 5));
17 | heightUnit = (bottom - top)/(layers+1);
18 |
19 | ranges = new ArrayList();
20 |
21 | for (int l = 0; l < layers; l++) {
22 | float y = top + random(heightUnit*l, heightUnit*(l+1));
23 | float spread = random(2, 4)*heightUnit;
24 | ranges.add(new MountainRange(
25 | y,
26 | spread,
27 | lerpColor(start, end, float(l)/layers),
28 | float(l+1)/layers,
29 | time
30 | ));
31 | }
32 | }
33 |
34 | void draw() {
35 | for (MountainRange range : ranges) {
36 | range.draw();
37 | }
38 | }
39 | }
40 |
41 | class MountainRange {
42 | ArrayList elevation;
43 | ArrayList trees;
44 | ArrayList buildings;
45 | float rangeY = 0;
46 | color rangeColor;
47 |
48 | MountainRange(float y, float spread, color c, float foreground, float time) {
49 | rangeY = y;
50 | rangeColor = c;
51 | elevation = new ArrayList();
52 | trees = new ArrayList();
53 | buildings = new ArrayList();
54 |
55 | float noiseElevation = random(1, 100);
56 | float spikiness = random(40, 90);
57 |
58 | for (int x = 0; x < 150; x++) {
59 | elevation.add(map(noise(float(x)/spikiness, noiseElevation), 0, 1, -1, 1) * spread);
60 | }
61 |
62 | int numTrees = int(random(1, map(1.0/foreground, 1, 100, 5, 40)));
63 | for (int i = 0; i < numTrees; i++) {
64 | int location = int(random(elevation.size()));
65 | trees.add(new Tree(
66 | (float(location)/(elevation.size()-1))*width,
67 | rangeY + elevation.get(location),
68 | foreground*100,
69 | foreground*80,
70 | time
71 | ));
72 | }
73 |
74 | int numBuildings = int(random(1, map(1.0/foreground, 1, 100, 0, 10)));
75 | for (int i = 0; i < numTrees; i++) {
76 | int location = int(random(elevation.size()));
77 | buildings.add(new Building(
78 | (float(location)/(elevation.size()-1))*width,
79 | rangeY + elevation.get(location) + spread*0.1,
80 | foreground*80,
81 | foreground*120,
82 | time
83 | ));
84 | }
85 | }
86 |
87 | void draw() {
88 | for (Building building : buildings) {
89 | building.draw();
90 | }
91 |
92 | noStroke();
93 | fill(rangeColor);
94 | beginShape();
95 | for (int i = 0; i < elevation.size(); i++) {
96 | vertex((float(i)/(elevation.size()-1))*width, rangeY + elevation.get(i));
97 | }
98 | vertex(width, height);
99 | vertex(0, height);
100 | endShape(CLOSE);
101 |
102 | for (Tree tree : trees) {
103 | tree.draw();
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/js/src/noise.js:
--------------------------------------------------------------------------------
1 | var ClassicalNoise = function(r) { // Classic Perlin noise in 3D, for comparison
2 | if (r == undefined) r = Math;
3 | this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
4 | [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
5 | [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
6 | this.p = [];
7 | for (var i=0; i<256; i++) {
8 | this.p[i] = Math.floor(r.random()*256);
9 | }
10 | // To remove the need for index wrapping, double the permutation table length
11 | this.perm = [];
12 | for(var i=0; i<512; i++) {
13 | this.perm[i]=this.p[i & 255];
14 | }
15 | };
16 |
17 | ClassicalNoise.prototype.dot = function(g, x, y, z) {
18 | return g[0]*x + g[1]*y + g[2]*z;
19 | };
20 |
21 | ClassicalNoise.prototype.mix = function(a, b, t) {
22 | return (1.0-t)*a + t*b;
23 | };
24 |
25 | ClassicalNoise.prototype.fade = function(t) {
26 | return t*t*t*(t*(t*6.0-15.0)+10.0);
27 | };
28 |
29 | // Classic Perlin noise, 3D version
30 | ClassicalNoise.prototype.noise = function(x, y, z) {
31 | // Find unit grid cell containing point
32 | var X = Math.floor(x);
33 | var Y = Math.floor(y);
34 | var Z = Math.floor(z);
35 |
36 | // Get relative xyz coordinates of point within that cell
37 | x = x - X;
38 | y = y - Y;
39 | z = z - Z;
40 |
41 | // Wrap the integer cells at 255 (smaller integer period can be introduced here)
42 | X = X & 255;
43 | Y = Y & 255;
44 | Z = Z & 255;
45 |
46 | // Calculate a set of eight hashed gradient indices
47 | var gi000 = this.perm[X+this.perm[Y+this.perm[Z]]] % 12;
48 | var gi001 = this.perm[X+this.perm[Y+this.perm[Z+1]]] % 12;
49 | var gi010 = this.perm[X+this.perm[Y+1+this.perm[Z]]] % 12;
50 | var gi011 = this.perm[X+this.perm[Y+1+this.perm[Z+1]]] % 12;
51 | var gi100 = this.perm[X+1+this.perm[Y+this.perm[Z]]] % 12;
52 | var gi101 = this.perm[X+1+this.perm[Y+this.perm[Z+1]]] % 12;
53 | var gi110 = this.perm[X+1+this.perm[Y+1+this.perm[Z]]] % 12;
54 | var gi111 = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]] % 12;
55 |
56 | // The gradients of each corner are now:
57 | // g000 = grad3[gi000];
58 | // g001 = grad3[gi001];
59 | // g010 = grad3[gi010];
60 | // g011 = grad3[gi011];
61 | // g100 = grad3[gi100];
62 | // g101 = grad3[gi101];
63 | // g110 = grad3[gi110];
64 | // g111 = grad3[gi111];
65 | // Calculate noise contributions from each of the eight corners
66 | var n000= this.dot(this.grad3[gi000], x, y, z);
67 | var n100= this.dot(this.grad3[gi100], x-1, y, z);
68 | var n010= this.dot(this.grad3[gi010], x, y-1, z);
69 | var n110= this.dot(this.grad3[gi110], x-1, y-1, z);
70 | var n001= this.dot(this.grad3[gi001], x, y, z-1);
71 | var n101= this.dot(this.grad3[gi101], x-1, y, z-1);
72 | var n011= this.dot(this.grad3[gi011], x, y-1, z-1);
73 | var n111= this.dot(this.grad3[gi111], x-1, y-1, z-1);
74 | // Compute the fade curve value for each of x, y, z
75 | var u = this.fade(x);
76 | var v = this.fade(y);
77 | var w = this.fade(z);
78 | // Interpolate along x the contributions from each of the corners
79 | var nx00 = this.mix(n000, n100, u);
80 | var nx01 = this.mix(n001, n101, u);
81 | var nx10 = this.mix(n010, n110, u);
82 | var nx11 = this.mix(n011, n111, u);
83 | // Interpolate the four results along y
84 | var nxy0 = this.mix(nx00, nx10, v);
85 | var nxy1 = this.mix(nx01, nx11, v);
86 | // Interpolate the two last results along z
87 | var nxyz = this.mix(nxy0, nxy1, w);
88 |
89 | return nxyz;
90 | };
91 |
--------------------------------------------------------------------------------
/js/src/setup.js:
--------------------------------------------------------------------------------
1 | var width = 720;
2 | var height = 405;
3 | var canvas = document.getElementById("landscape");
4 | var container = document.getElementById("header");
5 | var overlay = document.getElementById("overlay");
6 | var buffer, buffer_canvas;
7 | var pathCleared = true;
8 | var transitioning = false;
9 |
10 | var clearBackground = function() {
11 | buffer.clearRect(0, 0, width, height);
12 | };
13 |
14 | var random = function(low, high) {
15 | if (high === undefined) {
16 | high = low;
17 | low = 0;
18 | }
19 | return Math.random() * (high - low) + low;
20 | };
21 |
22 | var int = function(n) {
23 | return Math.floor(n);
24 | };
25 |
26 | var strokeWeight = function(w) {
27 | buffer.lineWidth = w;
28 | };
29 |
30 | var stroke = function(c) {
31 | buffer.strokeStyle = c;
32 | };
33 |
34 | var line = function(x1, y1, x2, y2) {
35 | buffer.beginPath();
36 | buffer.moveTo(x1, y1);
37 | buffer.lineTo(x2, y2);
38 | buffer.stroke();
39 | };
40 |
41 | var fill = function(c) {
42 | buffer.fillStyle = c;
43 | };
44 |
45 | var beginShape = function() {
46 | buffer.beginPath();
47 | pathCleared = true;
48 | };
49 |
50 | var vertex = function(x, y) {
51 | if (pathCleared) {
52 | buffer.moveTo(x, y);
53 | } else {
54 | buffer.lineTo(x, y);
55 | }
56 | pathCleared = false;
57 | };
58 |
59 | var endShape = function() {
60 | buffer.closePath();
61 | buffer.fill();
62 | };
63 |
64 | var rect = function(x, y, w, h) {
65 | buffer.fillRect(x, y, w, h);
66 | };
67 |
68 | var ellipse = function(x, y, w, h) {
69 | buffer.beginPath();
70 | buffer.ellipse(x, y, w/2, h/2, 0, 0, 2 * Math.PI);
71 | buffer.fill();
72 | };
73 |
74 | var setGradient = function(x, y, w, h, c1, c2) {
75 | var gradient = buffer.createLinearGradient(0, 0, 0, h);
76 | gradient.addColorStop(0, c1);
77 | gradient.addColorStop(1, c2);
78 | buffer.fillStyle = gradient;
79 | buffer.fillRect(x, y, w, h);
80 | };
81 |
82 | var hexToRGB = function(hex) {
83 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
84 | return result ? {
85 | r: parseInt(result[1], 16),
86 | g: parseInt(result[2], 16),
87 | b: parseInt(result[3], 16)
88 | } : null;
89 | };
90 |
91 | var componentToHex = function(c) {
92 | var hex = int(c).toString(16);
93 | return hex.length == 1 ? "0" + hex : hex;
94 | };
95 |
96 | var rgbToHex = function(r, g, b) {
97 | return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
98 | };
99 |
100 | var lerp = function(a, b, n) {
101 | return Math.abs((b - a) * n + a);
102 | };
103 |
104 | var lerpColor = function(beginning, end, percent) {
105 | var c1 = hexToRGB(beginning);
106 | var c2 = hexToRGB(end);
107 | return rgbToHex(
108 | lerp(c1.r, c2.r, percent),
109 | lerp(c1.g, c2.g, percent),
110 | lerp(c1.b, c2.b, percent)
111 | );
112 | };
113 |
114 | var map = function(v, a1, b1, a2, b2) {
115 | return (((v-a1) / (b1 - a1)) * (b2 - a2) + a2);
116 | };
117 |
118 | var perlin = new ClassicalNoise();
119 | var noise = function(x, y) {
120 | y = y || 0;
121 | return map(perlin.noise(x, y, 0), -1, 1, 0, 1);
122 | };
123 |
124 | var BUILDING_TOP = 0,
125 | BUILDING_LEFT = -1,
126 | BUILDING_RIGHT = 1,
127 | BUILDING_MAX_LEVELS = 2;
128 |
129 | var BRANCH_ROOT = 0,
130 | BRANCH_LEFT = -1,
131 | BRANCH_RIGHT = 1,
132 | BRANCH_MAX_LEVELS = 3;
133 |
134 | var time, sky, mountains;
135 |
136 | var drawScene = function(offsetX, offsetY) {
137 | sky.draw();
138 | mountains.draw();
139 | stage.drawImage(buffer_canvas, offsetX, offsetY);
140 | };
141 |
142 | var regenScene = function() {
143 | width = canvas.width;
144 | height = canvas.height;
145 | time = random(1);
146 | sky = new Sky(time);
147 | mountains = new Mountains(height * 0.2, height * 0.8, time);
148 | };
149 |
150 | var redrawCanvas = function(regen) {
151 | canvas.width = container.offsetWidth;
152 | canvas.height = container.offsetHeight;
153 | if (regen) regenScene();
154 | var scale;
155 | if (canvas.width > (width/height)*canvas.height) {
156 | scale = canvas.width/width;
157 | } else {
158 | scale = canvas.height/height;
159 | }
160 |
161 | buffer_canvas.width = width*scale;
162 | buffer_canvas.height = height*scale;
163 | buffer.scale(scale, scale);
164 | drawScene(
165 | (canvas.width - buffer_canvas.width)/2,
166 | (canvas.height - buffer_canvas.height)/2
167 | );
168 | };
169 |
170 | var transition = function() {
171 | if (transitioning) return;
172 | overlay.classList.remove("transparent");
173 | transitioning = true;
174 | setTimeout(function() {
175 | redrawCanvas(true);
176 | overlay.classList.add("transparent");
177 | setTimeout(function() {
178 | transitioning = false;
179 | }, 700);
180 | }, 700);
181 | };
182 |
183 | if (canvas.getContext) {
184 | stage = canvas.getContext("2d");
185 | buffer_canvas = document.createElement("canvas");
186 | buffer = buffer_canvas.getContext("2d");
187 | if (CanvasRenderingContext2D.prototype.ellipse == undefined) {
188 | CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
189 | this.save();
190 | this.translate(x, y);
191 | this.rotate(rotation);
192 | this.scale(radiusX, radiusY);
193 | this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
194 | this.restore();
195 | };
196 | }
197 | window.addEventListener("resize", throttle(function() { redrawCanvas() }, 200));
198 | container.addEventListener("click", transition);
199 | setTimeout(function() {
200 | redrawCanvas(true);
201 | overlay.classList.add("transparent");
202 | }, 200);
203 | } else {
204 | console.log("canvas not supported");
205 | document.getElementById("header").classList.add("static");
206 | }
207 |
--------------------------------------------------------------------------------
/js/src/Building.js:
--------------------------------------------------------------------------------
1 | function BuildingUnit(x, y, w, h, side, level, time) {
2 | var WINDOW_LIGHT = "#FFE76F";
3 | var WINDOW_DARK = "#1B5E81";
4 | var LIGHTS_ON = 0.6;
5 | var windows = 0;
6 | var windowHeight = 0;
7 | var alignment = 0;
8 | var units = [];
9 | var lights = [];
10 | var offsetLeft = random(-0.1 * w, 0.1 * w);
11 | var offsetRight = random(-0.1 * w, 0.1 * w);
12 | var offsetRoof = random(0.1 * w, 0.3 * w);
13 | var stiltLength = 0;
14 | if (level > 1) {
15 | stiltLength = random(h * 0.2, h * 0.5);
16 | }
17 | if (level <= 2) {
18 | windows = int(random(0, 5 - level));
19 | if (time > LIGHTS_ON) {
20 | for (var i=0; i 0.5);
22 | }
23 | }
24 | windowHeight = random((h - stiltLength) * 0.1, (h - stiltLength) * 0.5);
25 | if (random(0, 1) > 0.5) {
26 | alignment = BUILDING_LEFT;
27 | } else {
28 | alignment = BUILDING_RIGHT;
29 | }
30 | for (var i = 0; i < BUILDING_MAX_LEVELS; i++) {
31 | if (random(10) >= 5) {
32 | var branchWidth = random(0.2 * w, 0.8 * w);
33 | var branchHeight = random(0.2 * h, 0.8 * h);
34 | var branchX = 0;
35 | var branchY = 0;
36 |
37 | var branchSide = int(random(-2, 2));
38 | if (branchSide == BUILDING_TOP) {
39 | branchX = random(x - w / 2 + branchWidth / 2, x + w / 2 - branchWidth / 2);
40 | branchY = y - h;
41 | } else if (branchSide == BUILDING_LEFT) {
42 | branchX = x - w / 2 - branchWidth / 2 + w * 0.2;
43 | branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength);
44 | } else if (branchSide == BUILDING_RIGHT) {
45 | branchX = x + w / 2 + branchWidth / 2 - w * 0.2;
46 | branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength);
47 | }
48 |
49 | units.push(new BuildingUnit(branchX, branchY, branchWidth, branchHeight, branchSide, level + 1, time));
50 | }
51 | }
52 | }
53 |
54 | this.draw = function() {
55 | units.forEach(function(b) {
56 | b.draw();
57 | });
58 |
59 | var stiltColor, roofColor, houseColor, windowDark;
60 | if (time < 0.5) {
61 | stiltColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
62 | houseColor = lerpColor("#AD9592", "#BFABA9", map(time, 0, 0.5, 0, 1));
63 | roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
64 | windowDark = lerpColor("#1B5E81", "#383E5A", map(time, 0, 0.5, 0, 1));
65 | } else {
66 | stiltColor = lerpColor("#352B48", "#1D263E", map(time, 0.5, 1, 0, 1));
67 | houseColor = lerpColor("#C3B7D1", "#8788A7", map(time, 0, 0.5, 0, 1));
68 | roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
69 | windowDark = lerpColor("#644C6F", "#3D435F", map(time, 0, 0.5, 0, 1));
70 | }
71 |
72 | if (stiltLength > 0) {
73 | strokeWeight(w / 20);
74 | stroke(stiltColor);
75 | if (side == BUILDING_TOP) {
76 | line(x - w * 0.3, y, x - w * 0.3, y - stiltLength);
77 | line(x + w * 0.3, y, x + w * 0.3, y - stiltLength);
78 | } else if (side == BUILDING_LEFT) {
79 | line(x - w * 0.4, y - stiltLength, x + w / 2, y);
80 | } else if (side == BUILDING_RIGHT) {
81 | line(x + w * 0.4, y - stiltLength, x - w / 2, y);
82 | }
83 | }
84 |
85 | //Main building
86 | fill(level % 2 == 1 ? houseColor : lerpColor(houseColor, "#1C1443", 0.2));
87 | beginShape();
88 | vertex(x - w / 2, y - stiltLength);
89 | vertex(x + w / 2, y - stiltLength);
90 | vertex(x + w / 2 + offsetRight, y - h * 0.85);
91 | vertex(x - w / 2 + offsetLeft, y - h * 0.85);
92 | endShape();
93 |
94 | //Windows
95 | for (var i = 0; i < windows; i++) {
96 | if (time > LIGHTS_ON && lights[i]) {
97 | fill(WINDOW_LIGHT);
98 | } else {
99 | fill(windowDark);
100 | }
101 | if (alignment == BUILDING_LEFT) {
102 | rect(
103 | x + offsetLeft - w / 2 + w * 0.05 + w * 0.25 * i,
104 | y - h * 0.8,
105 | w * 0.2,
106 | windowHeight
107 | );
108 | } else if (alignment == BUILDING_RIGHT) {
109 | rect(
110 | x + offsetRight + w / 2 - w * 0.05 - w * 0.25 * (i + 1),
111 | y - h * 0.8,
112 | w * 0.2,
113 | windowHeight
114 | );
115 | }
116 | }
117 |
118 | //Roof
119 | fill("#685D71");
120 | beginShape();
121 | vertex(x - w / 2 + offsetLeft - offsetRoof, y - h * 0.85);
122 | vertex(x + w / 2 + offsetRight + offsetRoof, y - h * 0.85);
123 | vertex(x + w / 2 + offsetRight, y - h);
124 | vertex(x - w / 2 + offsetLeft, y - h);
125 | endShape();
126 | };
127 | }
128 |
129 | function Building(x, y, w, h, time) {
130 | var units = [];
131 | var numUnits = int(random(1, 2));
132 | for (var i = 0; i < numUnits; i++) {
133 | var unitWidth = random(0.5, 1.5) * (w / numUnits);
134 | units.push(new BuildingUnit(
135 | x - w / 2 + i * (w / numUnits) + unitWidth / 2,
136 | y,
137 | unitWidth,
138 | random(h * 0.2, h),
139 | BUILDING_TOP,
140 | 1,
141 | time
142 | ));
143 | }
144 |
145 | this.draw = function() {
146 | units.forEach(function(b) {
147 | b.draw();
148 | });
149 | };
150 | }
151 |
--------------------------------------------------------------------------------
/frontier/Building.pde:
--------------------------------------------------------------------------------
1 | final int BUILDING_TOP = 0,
2 | BUILDING_LEFT = -1,
3 | BUILDING_RIGHT = 1,
4 | BUILDING_MAX_LEVELS = 2;
5 |
6 | class Building {
7 | ArrayList units;
8 |
9 | Building(float x, float y, float w, float h, float time) {
10 | units = new ArrayList();
11 |
12 | int numUnits = int(random(1, 2));
13 | for (int i = 0; i < numUnits; i++) {
14 | float unitWidth = random(0.5, 1.5)*(w/numUnits);
15 | units.add(new BuildingUnit(
16 | x - w/2 + i*(w/numUnits) + unitWidth/2,
17 | y,
18 | unitWidth,
19 | random(h*0.2, h),
20 | BUILDING_TOP,
21 | 1,
22 | time
23 | ));
24 | }
25 | }
26 |
27 | void draw() {
28 | for (BuildingUnit b: units) {
29 | b.draw();
30 | }
31 | }
32 | }
33 |
34 | class BuildingUnit {
35 | float unitX = 0,
36 | unitY = 0,
37 | unitWidth = 0,
38 | unitHeight = 0,
39 | offsetLeft = 0,
40 | offsetRight = 0,
41 | offsetRoof = 0,
42 | stiltLength = 0,
43 | windowHeight = 0,
44 | time = 0;
45 | int unitLevel = 0,
46 | unitSide = 0,
47 | windows = 0,
48 | alignment;
49 | ArrayList units;
50 |
51 | color WINDOW_LIGHT = #FFE76F,
52 | WINDOW_DARK = #1B5E81;
53 |
54 | BuildingUnit(float x, float y, float w, float h, int side, int level, float t) {
55 | units = new ArrayList();
56 | unitX = x;
57 | unitY = y;
58 | unitWidth = w;
59 | unitHeight = h;
60 | unitSide = side;
61 | offsetLeft = random(-0.1*w, 0.1*w);
62 | offsetRight = random(-0.1*w, 0.1*w);
63 | offsetRoof = random(0.1*w, 0.3*w);
64 | unitLevel = level;
65 | time = t;
66 | if (level > 1) {
67 | stiltLength = random(h * 0.2, h * 0.5);
68 | }
69 |
70 | if (level <= 2) {
71 | windows = int(random(0, 5 - level));
72 | windowHeight = random((unitHeight-stiltLength)*0.1, (unitHeight-stiltLength)*0.5);
73 | if (random(0, 1) > 0.5) {
74 | alignment = BUILDING_LEFT;
75 | } else {
76 | alignment = BUILDING_RIGHT;
77 | }
78 | }
79 |
80 | if (level < 3) {
81 | for (int i=0; i= 5) {
83 | float branchWidth = random(0.2*unitWidth, 0.8*unitWidth);
84 | float branchHeight = random(0.2*unitHeight, 0.8*unitHeight);
85 | float branchX = 0;
86 | float branchY = 0;
87 |
88 | int branchSide = int(random(-2, 2));
89 | if (branchSide == BUILDING_TOP) {
90 | branchX = random(x - w/2 + branchWidth/2, x + w/2 - branchWidth/2);
91 | branchY = y - h;
92 | } else if (branchSide == BUILDING_LEFT) {
93 | branchX = x - w/2 - branchWidth/2 + w*0.2;
94 | branchY = random(y - 0.2*(h-stiltLength) - stiltLength, y - 0.8*(h-stiltLength) - stiltLength);
95 | } else if (branchSide == BUILDING_RIGHT) {
96 | branchX = x + w/2 + branchWidth/2 - w*0.2;
97 | branchY = random(y - 0.2*(h-stiltLength) - stiltLength, y - 0.8*(h-stiltLength) - stiltLength);
98 | } else {
99 | return;
100 | }
101 |
102 | units.add(new BuildingUnit(branchX, branchY, branchWidth, branchHeight, branchSide, level+1, time));
103 | }
104 | }
105 | }
106 | }
107 |
108 | void draw() {
109 | for (BuildingUnit b: units) {
110 | b.draw();
111 | }
112 |
113 | color stiltColor, roofColor, houseColor, windowDark;
114 | if (time < 0.5) {
115 | stiltColor = lerpColor(#776F7E, #4B433C, map(time, 0, 0.5, 0, 1));
116 | houseColor = lerpColor(#AD9592, #BFABA9, map(time, 0, 0.5, 0, 1));
117 | roofColor = lerpColor(#776F7E, #4B433C, map(time, 0, 0.5, 0, 1));
118 | windowDark = lerpColor(#1B5E81, #383E5A, map(time, 0, 0.5, 0, 1));
119 | } else {
120 | stiltColor = lerpColor(#352B48, #1D263E, map(time, 0.5, 1, 0, 1));
121 | houseColor = lerpColor(#C3B7D1, #8788A7, map(time, 0, 0.5, 0, 1));
122 | roofColor = lerpColor(#776F7E, #4B433C, map(time, 0, 0.5, 0, 1));
123 | windowDark = lerpColor(#644C6F, #3D435F, map(time, 0, 0.5, 0, 1));
124 | }
125 |
126 | if (stiltLength > 0) {
127 | strokeWeight(unitWidth/20);
128 | stroke(stiltColor);
129 | if (unitSide == BUILDING_TOP) {
130 | line(unitX - unitWidth*0.3, unitY, unitX - unitWidth*0.3, unitY - stiltLength);
131 | line(unitX + unitWidth*0.3, unitY, unitX + unitWidth*0.3, unitY - stiltLength);
132 | } else if (unitSide == BUILDING_LEFT) {
133 | line(unitX - unitWidth*0.4, unitY - stiltLength, unitX + unitWidth/2, unitY);
134 | } else if (unitSide == BUILDING_RIGHT) {
135 | line(unitX + unitWidth*0.4, unitY - stiltLength, unitX - unitWidth/2, unitY);
136 | }
137 | }
138 |
139 | noStroke();
140 |
141 | //Main building
142 | fill(unitLevel % 2 == 1 ? houseColor : lerpColor(houseColor, #1C1443, 0.2));
143 | beginShape();
144 | vertex(unitX - unitWidth/2, unitY - stiltLength);
145 | vertex(unitX + unitWidth/2, unitY - stiltLength);
146 | vertex(unitX + unitWidth/2 + offsetRight, unitY - unitHeight * 0.85);
147 | vertex(unitX - unitWidth/2 + offsetLeft, unitY - unitHeight * 0.85);
148 | endShape(CLOSE);
149 |
150 | //Windows
151 | for (int i = 0; i < windows; i++) {
152 | if (time > 0.6 && random(1)>0.5) {
153 | fill(WINDOW_LIGHT);
154 | } else {
155 | fill(windowDark);
156 | }
157 | if (alignment == BUILDING_LEFT) {
158 | rect(
159 | unitX + offsetLeft - unitWidth/2 + unitWidth*0.05 + unitWidth*0.25*i,
160 | unitY - unitHeight*0.8,
161 | unitWidth*0.2,
162 | windowHeight
163 | );
164 | } else if (alignment == BUILDING_RIGHT) {
165 | rect(
166 | unitX + offsetRight + unitWidth/2 - unitWidth*0.05 - unitWidth*0.25*(i+1),
167 | unitY - unitHeight*0.8,
168 | unitWidth*0.2,
169 | windowHeight
170 | );
171 | }
172 | }
173 |
174 | //Roof
175 | fill(#685D71);
176 | beginShape();
177 | vertex(unitX - unitWidth/2 + offsetLeft - offsetRoof, unitY - unitHeight * 0.85);
178 | vertex(unitX + unitWidth/2 + offsetRight + offsetRoof, unitY - unitHeight * 0.85);
179 | vertex(unitX + unitWidth/2 + offsetRight, unitY - unitHeight);
180 | vertex(unitX - unitWidth/2 + offsetLeft, unitY - unitHeight);
181 | endShape(CLOSE);
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/js/frontier.min.js:
--------------------------------------------------------------------------------
1 | function throttle(fn, threshhold, scope) { threshhold || (threshhold = 250); var last, deferTimer; return function () { var context = scope || this; var now = +new Date, args = arguments; if (last && now < last + threshhold) { clearTimeout(deferTimer); deferTimer = setTimeout(function () { last = now; fn.apply(context, args); }, threshhold); } else { last = now; fn.apply(context, args); } }; } var ClassicalNoise = function(r) { if (r == undefined) r = Math; this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]]; this.p = []; for (var i=0; i<256; i++) { this.p[i] = Math.floor(r.random()*256); } this.perm = []; for(var i=0; i<512; i++) { this.perm[i]=this.p[i & 255]; } }; ClassicalNoise.prototype.dot = function(g, x, y, z) { return g[0]*x + g[1]*y + g[2]*z; }; ClassicalNoise.prototype.mix = function(a, b, t) { return (1.0-t)*a + t*b; }; ClassicalNoise.prototype.fade = function(t) { return t*t*t*(t*(t*6.0-15.0)+10.0); }; ClassicalNoise.prototype.noise = function(x, y, z) { var X = Math.floor(x); var Y = Math.floor(y); var Z = Math.floor(z); x = x - X; y = y - Y; z = z - Z; X = X & 255; Y = Y & 255; Z = Z & 255; var gi000 = this.perm[X+this.perm[Y+this.perm[Z]]] % 12; var gi001 = this.perm[X+this.perm[Y+this.perm[Z+1]]] % 12; var gi010 = this.perm[X+this.perm[Y+1+this.perm[Z]]] % 12; var gi011 = this.perm[X+this.perm[Y+1+this.perm[Z+1]]] % 12; var gi100 = this.perm[X+1+this.perm[Y+this.perm[Z]]] % 12; var gi101 = this.perm[X+1+this.perm[Y+this.perm[Z+1]]] % 12; var gi110 = this.perm[X+1+this.perm[Y+1+this.perm[Z]]] % 12; var gi111 = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]] % 12; var n000= this.dot(this.grad3[gi000], x, y, z); var n100= this.dot(this.grad3[gi100], x-1, y, z); var n010= this.dot(this.grad3[gi010], x, y-1, z); var n110= this.dot(this.grad3[gi110], x-1, y-1, z); var n001= this.dot(this.grad3[gi001], x, y, z-1); var n101= this.dot(this.grad3[gi101], x-1, y, z-1); var n011= this.dot(this.grad3[gi011], x, y-1, z-1); var n111= this.dot(this.grad3[gi111], x-1, y-1, z-1); var u = this.fade(x); var v = this.fade(y); var w = this.fade(z); var nx00 = this.mix(n000, n100, u); var nx01 = this.mix(n001, n101, u); var nx10 = this.mix(n010, n110, u); var nx11 = this.mix(n011, n111, u); var nxy0 = this.mix(nx00, nx10, v); var nxy1 = this.mix(nx01, nx11, v); var nxyz = this.mix(nxy0, nxy1, w); return nxyz; }; function BuildingUnit(x, y, w, h, side, level, time) { var WINDOW_LIGHT = "#FFE76F"; var WINDOW_DARK = "#1B5E81"; var LIGHTS_ON = 0.6; var windows = 0; var windowHeight = 0; var alignment = 0; var units = []; var lights = []; var offsetLeft = random(-0.1 * w, 0.1 * w); var offsetRight = random(-0.1 * w, 0.1 * w); var offsetRoof = random(0.1 * w, 0.3 * w); var stiltLength = 0; if (level > 1) { stiltLength = random(h * 0.2, h * 0.5); } if (level <= 2) { windows = int(random(0, 5 - level)); if (time > LIGHTS_ON) { for (var i=0; i 0.5); } } windowHeight = random((h - stiltLength) * 0.1, (h - stiltLength) * 0.5); if (random(0, 1) > 0.5) { alignment = BUILDING_LEFT; } else { alignment = BUILDING_RIGHT; } for (var i = 0; i < BUILDING_MAX_LEVELS; i++) { if (random(10) >= 5) { var branchWidth = random(0.2 * w, 0.8 * w); var branchHeight = random(0.2 * h, 0.8 * h); var branchX = 0; var branchY = 0; var branchSide = int(random(-2, 2)); if (branchSide == BUILDING_TOP) { branchX = random(x - w / 2 + branchWidth / 2, x + w / 2 - branchWidth / 2); branchY = y - h; } else if (branchSide == BUILDING_LEFT) { branchX = x - w / 2 - branchWidth / 2 + w * 0.2; branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength); } else if (branchSide == BUILDING_RIGHT) { branchX = x + w / 2 + branchWidth / 2 - w * 0.2; branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength); } units.push(new BuildingUnit(branchX, branchY, branchWidth, branchHeight, branchSide, level + 1, time)); } } } this.draw = function() { units.forEach(function(b) { b.draw(); }); var stiltColor, roofColor, houseColor, windowDark; if (time < 0.5) { stiltColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1)); houseColor = lerpColor("#AD9592", "#BFABA9", map(time, 0, 0.5, 0, 1)); roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1)); windowDark = lerpColor("#1B5E81", "#383E5A", map(time, 0, 0.5, 0, 1)); } else { stiltColor = lerpColor("#352B48", "#1D263E", map(time, 0.5, 1, 0, 1)); houseColor = lerpColor("#C3B7D1", "#8788A7", map(time, 0, 0.5, 0, 1)); roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1)); windowDark = lerpColor("#644C6F", "#3D435F", map(time, 0, 0.5, 0, 1)); } if (stiltLength > 0) { strokeWeight(w / 20); stroke(stiltColor); if (side == BUILDING_TOP) { line(x - w * 0.3, y, x - w * 0.3, y - stiltLength); line(x + w * 0.3, y, x + w * 0.3, y - stiltLength); } else if (side == BUILDING_LEFT) { line(x - w * 0.4, y - stiltLength, x + w / 2, y); } else if (side == BUILDING_RIGHT) { line(x + w * 0.4, y - stiltLength, x - w / 2, y); } } fill(level % 2 == 1 ? houseColor : lerpColor(houseColor, "#1C1443", 0.2)); beginShape(); vertex(x - w / 2, y - stiltLength); vertex(x + w / 2, y - stiltLength); vertex(x + w / 2 + offsetRight, y - h * 0.85); vertex(x - w / 2 + offsetLeft, y - h * 0.85); endShape(); for (var i = 0; i < windows; i++) { if (time > LIGHTS_ON && lights[i]) { fill(WINDOW_LIGHT); } else { fill(windowDark); } if (alignment == BUILDING_LEFT) { rect( x + offsetLeft - w / 2 + w * 0.05 + w * 0.25 * i, y - h * 0.8, w * 0.2, windowHeight ); } else if (alignment == BUILDING_RIGHT) { rect( x + offsetRight + w / 2 - w * 0.05 - w * 0.25 * (i + 1), y - h * 0.8, w * 0.2, windowHeight ); } } fill("#685D71"); beginShape(); vertex(x - w / 2 + offsetLeft - offsetRoof, y - h * 0.85); vertex(x + w / 2 + offsetRight + offsetRoof, y - h * 0.85); vertex(x + w / 2 + offsetRight, y - h); vertex(x - w / 2 + offsetLeft, y - h); endShape(); }; } function Building(x, y, w, h, time) { var units = []; var numUnits = int(random(1, 2)); for (var i = 0; i < numUnits; i++) { var unitWidth = random(0.5, 1.5) * (w / numUnits); units.push(new BuildingUnit( x - w / 2 + i * (w / numUnits) + unitWidth / 2, y, unitWidth, random(h * 0.2, h), BUILDING_TOP, 1, time )); } this.draw = function() { units.forEach(function(b) { b.draw(); }); }; } function Tree(x, y, w, h, time) { var root = new Branch(x, y, w, h, BRANCH_ROOT, 1, time); this.draw = function() { root.drawLeaves(); root.drawBranches(); }; } function Branch(x, y, w, h, side, level, time) { var branches = []; var segmentX = 0; var segmentY = 0; var leafWidth = 0; var leafHeight = 0; if (side == BRANCH_ROOT) { segmentX = x + (random(w * 0.6) - w * 0.3); segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15); } else if (side == BRANCH_LEFT) { segmentX = x - random(w * 0.3); segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15); } else if (side == BRANCH_RIGHT) { segmentX = x + random(w * 0.3); segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15); } leafWidth = random(w * 0.6) + w * 0.3; leafHeight = random(h * 0.6) + h * 0.3; if (level < 3) { for (var i = 0; i < BRANCH_MAX_LEVELS; i++) { if (random(10) >= 7) { var branchY = random(segmentY, y); var branchX = x + ((segmentX - x) / (segmentY - y)) * (branchY - y); var branchDir = random(10) > 5 ? BRANCH_LEFT : BRANCH_RIGHT; branches.push(new Branch(branchX, branchY, w * 0.6, h * 0.5, branchDir, level + 1, time)); } } } this.drawLeaves = function() { var leafColor; if (time < 0.5) { leafColor = lerpColor("#FFD1D1", "#F2C975", map(time, 0, 0.5, 0, 1)); } else { leafColor = lerpColor("#D5BCE0", "#56627C", map(time, 0.5, 1, 0, 1)); } fill(leafColor); ellipse(segmentX, segmentY, leafWidth, leafHeight); branches.forEach(function(b) { b.drawLeaves(); }); }; this.drawBranches = function() { var branchColor; if (time < 0.5) { branchColor = lerpColor("#79504E", "#866A59", map(time, 0, 0.5, 0, 1)); } else { branchColor = lerpColor("#635774", "#635774", map(time, 0.5, 1, 0, 1)); } strokeWeight((y - segmentY) / 20); stroke(branchColor); line(x, y, segmentX, segmentY); branches.forEach(function(b) { b.drawBranches(); }); }; } function Mountains(top, bottom, time) { var ranges = []; var start, end; if (time < 0.5) { start = lerpColor("#D36F6F", "#F2C178", map(time, 0, 0.5, 0, 1)); end = lerpColor("#AD1515", "#E85B57", map(time, 0, 0.5, 0, 1)); } else { start = lerpColor("#654183", "#2D3A5F", map(time, 0.5, 1, 0, 1)); end = lerpColor("#B198C6", "#6172A5", map(time, 0.5, 1, 0, 1)); } var layers = int(random(2, 5)); var heightUnit = (bottom - top) / (layers + 1); for (var l = 0; l < layers; l++) { var y = top + random(heightUnit * l, heightUnit * (l + 1)); var spread = random(1, 3) * heightUnit; ranges.push(new MountainRange( y, spread, lerpColor(start, end, l/layers), (l + 1) / layers, time )); } this.draw = function() { ranges.forEach(function(range) { range.draw(); }); }; } function MountainRange(y, spread, c, foreground, time) { var elevation = []; var trees = []; var buildings = []; var noiseElevation = random(1, 100); var spikiness = random(40, 90); for (var x = 0; x < 150; x++) { elevation.push(map(noise(x / spikiness, noiseElevation), 0, 1, -1, 1) * spread); } var numTrees = int(random(1, map(1.0 / foreground, 1, 100, 5, 40))); for (var i = 0; i < numTrees; i++) { var location = int(random(elevation.length)); trees.push(new Tree( (location / (elevation.length - 1)) * width, y + elevation[location], foreground * 100, foreground * 80, time )); } var numBuildings = int(random(1, map(1.0 / foreground, 1, 100, 0, 10))); for (var i = 0; i < numTrees; i++) { var location = int(random(elevation.length)); buildings.push(new Building( (location / (elevation.length - 1)) * width, y + elevation[location] + spread * 0.1, foreground * 80, foreground * 120, time )); } this.draw = function() { buildings.forEach(function(building) { building.draw(); }); fill(c); beginShape(); for (var i = 0; i < elevation.length; i++) { vertex((i / (elevation.length - 1)) * width, y + elevation[i]); } vertex(width, height); vertex(0, height); endShape(); trees.forEach(function(tree) { tree.draw(); }); }; } function Sky(time) { var sky, horizon; if (time < 0.5) { sky = lerpColor("#B9F7D5", "#57C8F0", map(time, 0, 0.5, 0, 1)); horizon = lerpColor("#FCED42", "#E365ED", map(time, 0, 0.5, 0, 1)); } else { sky = lerpColor("#C282E5", "#1A285A", map(time, 0.5, 1, 0, 1)); horizon = lerpColor("#82E3E5", "#BFE1FC", map(time, 0.5, 1, 0, 1)); } this.draw = function() { setGradient(0, 0, width, height, sky, horizon); }; } var width = 720; var height = 405; var canvas = document.getElementById("landscape"); var container = document.getElementById("header"); var overlay = document.getElementById("overlay"); var buffer, buffer_canvas; var pathCleared = true; var transitioning = false; var clearBackground = function() { buffer.clearRect(0, 0, width, height); }; var random = function(low, high) { if (high === undefined) { high = low; low = 0; } return Math.random() * (high - low) + low; }; var int = function(n) { return Math.floor(n); }; var strokeWeight = function(w) { buffer.lineWidth = w; }; var stroke = function(c) { buffer.strokeStyle = c; }; var line = function(x1, y1, x2, y2) { buffer.beginPath(); buffer.moveTo(x1, y1); buffer.lineTo(x2, y2); buffer.stroke(); }; var fill = function(c) { buffer.fillStyle = c; }; var beginShape = function() { buffer.beginPath(); pathCleared = true; }; var vertex = function(x, y) { if (pathCleared) { buffer.moveTo(x, y); } else { buffer.lineTo(x, y); } pathCleared = false; }; var endShape = function() { buffer.closePath(); buffer.fill(); }; var rect = function(x, y, w, h) { buffer.fillRect(x, y, w, h); }; var ellipse = function(x, y, w, h) { buffer.beginPath(); buffer.ellipse(x, y, w/2, h/2, 0, 0, 2 * Math.PI); buffer.fill(); }; var setGradient = function(x, y, w, h, c1, c2) { var gradient = buffer.createLinearGradient(0, 0, 0, h); gradient.addColorStop(0, c1); gradient.addColorStop(1, c2); buffer.fillStyle = gradient; buffer.fillRect(x, y, w, h); }; var hexToRGB = function(hex) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; var componentToHex = function(c) { var hex = int(c).toString(16); return hex.length == 1 ? "0" + hex : hex; }; var rgbToHex = function(r, g, b) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); }; var lerp = function(a, b, n) { return Math.abs((b - a) * n + a); }; var lerpColor = function(beginning, end, percent) { var c1 = hexToRGB(beginning); var c2 = hexToRGB(end); return rgbToHex( lerp(c1.r, c2.r, percent), lerp(c1.g, c2.g, percent), lerp(c1.b, c2.b, percent) ); }; var map = function(v, a1, b1, a2, b2) { return (((v-a1) / (b1 - a1)) * (b2 - a2) + a2); }; var perlin = new ClassicalNoise(); var noise = function(x, y) { y = y || 0; return map(perlin.noise(x, y, 0), -1, 1, 0, 1); }; var BUILDING_TOP = 0, BUILDING_LEFT = -1, BUILDING_RIGHT = 1, BUILDING_MAX_LEVELS = 2; var BRANCH_ROOT = 0, BRANCH_LEFT = -1, BRANCH_RIGHT = 1, BRANCH_MAX_LEVELS = 3; var time, sky, mountains; var drawScene = function(offsetX, offsetY) { sky.draw(); mountains.draw(); stage.drawImage(buffer_canvas, offsetX, offsetY); }; var regenScene = function() { width = canvas.width; height = canvas.height; time = random(1); sky = new Sky(time); mountains = new Mountains(height * 0.2, height * 0.8, time); }; var redrawCanvas = function(regen) { canvas.width = container.offsetWidth; canvas.height = container.offsetHeight; if (regen) regenScene(); var scale; if (canvas.width > (width/height)*canvas.height) { scale = canvas.width/width; } else { scale = canvas.height/height; } buffer_canvas.width = width*scale; buffer_canvas.height = height*scale; buffer.scale(scale, scale); drawScene( (canvas.width - buffer_canvas.width)/2, (canvas.height - buffer_canvas.height)/2 ); }; var transition = function() { if (transitioning) return; overlay.classList.remove("transparent"); transitioning = true; setTimeout(function() { redrawCanvas(true); overlay.classList.add("transparent"); setTimeout(function() { transitioning = false; }, 700); }, 700); }; if (canvas.getContext) { stage = canvas.getContext("2d"); buffer_canvas = document.createElement("canvas"); buffer = buffer_canvas.getContext("2d"); if (CanvasRenderingContext2D.prototype.ellipse == undefined) { CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) { this.save(); this.translate(x, y); this.rotate(rotation); this.scale(radiusX, radiusY); this.arc(0, 0, 1, startAngle, endAngle, antiClockwise); this.restore(); }; } window.addEventListener("resize", throttle(function() { redrawCanvas() }, 200)); container.addEventListener("click", transition); setTimeout(function() { redrawCanvas(true); overlay.classList.add("transparent"); }, 200); } else { console.log("canvas not supported"); document.getElementById("header").classList.add("static"); }
--------------------------------------------------------------------------------
/js/frontier.js:
--------------------------------------------------------------------------------
1 | function throttle(fn, threshhold, scope) {
2 | threshhold || (threshhold = 250);
3 | var last,
4 | deferTimer;
5 | return function () {
6 | var context = scope || this;
7 |
8 | var now = +new Date,
9 | args = arguments;
10 | if (last && now < last + threshhold) {
11 | // hold on to it
12 | clearTimeout(deferTimer);
13 | deferTimer = setTimeout(function () {
14 | last = now;
15 | fn.apply(context, args);
16 | }, threshhold);
17 | } else {
18 | last = now;
19 | fn.apply(context, args);
20 | }
21 | };
22 | }
23 |
24 |
25 |
26 | var ClassicalNoise = function(r) { // Classic Perlin noise in 3D, for comparison
27 | if (r == undefined) r = Math;
28 | this.grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0],
29 | [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1],
30 | [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
31 | this.p = [];
32 | for (var i=0; i<256; i++) {
33 | this.p[i] = Math.floor(r.random()*256);
34 | }
35 | // To remove the need for index wrapping, double the permutation table length
36 | this.perm = [];
37 | for(var i=0; i<512; i++) {
38 | this.perm[i]=this.p[i & 255];
39 | }
40 | };
41 |
42 | ClassicalNoise.prototype.dot = function(g, x, y, z) {
43 | return g[0]*x + g[1]*y + g[2]*z;
44 | };
45 |
46 | ClassicalNoise.prototype.mix = function(a, b, t) {
47 | return (1.0-t)*a + t*b;
48 | };
49 |
50 | ClassicalNoise.prototype.fade = function(t) {
51 | return t*t*t*(t*(t*6.0-15.0)+10.0);
52 | };
53 |
54 | // Classic Perlin noise, 3D version
55 | ClassicalNoise.prototype.noise = function(x, y, z) {
56 | // Find unit grid cell containing point
57 | var X = Math.floor(x);
58 | var Y = Math.floor(y);
59 | var Z = Math.floor(z);
60 |
61 | // Get relative xyz coordinates of point within that cell
62 | x = x - X;
63 | y = y - Y;
64 | z = z - Z;
65 |
66 | // Wrap the integer cells at 255 (smaller integer period can be introduced here)
67 | X = X & 255;
68 | Y = Y & 255;
69 | Z = Z & 255;
70 |
71 | // Calculate a set of eight hashed gradient indices
72 | var gi000 = this.perm[X+this.perm[Y+this.perm[Z]]] % 12;
73 | var gi001 = this.perm[X+this.perm[Y+this.perm[Z+1]]] % 12;
74 | var gi010 = this.perm[X+this.perm[Y+1+this.perm[Z]]] % 12;
75 | var gi011 = this.perm[X+this.perm[Y+1+this.perm[Z+1]]] % 12;
76 | var gi100 = this.perm[X+1+this.perm[Y+this.perm[Z]]] % 12;
77 | var gi101 = this.perm[X+1+this.perm[Y+this.perm[Z+1]]] % 12;
78 | var gi110 = this.perm[X+1+this.perm[Y+1+this.perm[Z]]] % 12;
79 | var gi111 = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]] % 12;
80 |
81 | // The gradients of each corner are now:
82 | // g000 = grad3[gi000];
83 | // g001 = grad3[gi001];
84 | // g010 = grad3[gi010];
85 | // g011 = grad3[gi011];
86 | // g100 = grad3[gi100];
87 | // g101 = grad3[gi101];
88 | // g110 = grad3[gi110];
89 | // g111 = grad3[gi111];
90 | // Calculate noise contributions from each of the eight corners
91 | var n000= this.dot(this.grad3[gi000], x, y, z);
92 | var n100= this.dot(this.grad3[gi100], x-1, y, z);
93 | var n010= this.dot(this.grad3[gi010], x, y-1, z);
94 | var n110= this.dot(this.grad3[gi110], x-1, y-1, z);
95 | var n001= this.dot(this.grad3[gi001], x, y, z-1);
96 | var n101= this.dot(this.grad3[gi101], x-1, y, z-1);
97 | var n011= this.dot(this.grad3[gi011], x, y-1, z-1);
98 | var n111= this.dot(this.grad3[gi111], x-1, y-1, z-1);
99 | // Compute the fade curve value for each of x, y, z
100 | var u = this.fade(x);
101 | var v = this.fade(y);
102 | var w = this.fade(z);
103 | // Interpolate along x the contributions from each of the corners
104 | var nx00 = this.mix(n000, n100, u);
105 | var nx01 = this.mix(n001, n101, u);
106 | var nx10 = this.mix(n010, n110, u);
107 | var nx11 = this.mix(n011, n111, u);
108 | // Interpolate the four results along y
109 | var nxy0 = this.mix(nx00, nx10, v);
110 | var nxy1 = this.mix(nx01, nx11, v);
111 | // Interpolate the two last results along z
112 | var nxyz = this.mix(nxy0, nxy1, w);
113 |
114 | return nxyz;
115 | };
116 |
117 |
118 | function BuildingUnit(x, y, w, h, side, level, time) {
119 | var WINDOW_LIGHT = "#FFE76F";
120 | var WINDOW_DARK = "#1B5E81";
121 | var LIGHTS_ON = 0.6;
122 | var windows = 0;
123 | var windowHeight = 0;
124 | var alignment = 0;
125 | var units = [];
126 | var lights = [];
127 | var offsetLeft = random(-0.1 * w, 0.1 * w);
128 | var offsetRight = random(-0.1 * w, 0.1 * w);
129 | var offsetRoof = random(0.1 * w, 0.3 * w);
130 | var stiltLength = 0;
131 | if (level > 1) {
132 | stiltLength = random(h * 0.2, h * 0.5);
133 | }
134 | if (level <= 2) {
135 | windows = int(random(0, 5 - level));
136 | if (time > LIGHTS_ON) {
137 | for (var i=0; i 0.5);
139 | }
140 | }
141 | windowHeight = random((h - stiltLength) * 0.1, (h - stiltLength) * 0.5);
142 | if (random(0, 1) > 0.5) {
143 | alignment = BUILDING_LEFT;
144 | } else {
145 | alignment = BUILDING_RIGHT;
146 | }
147 | for (var i = 0; i < BUILDING_MAX_LEVELS; i++) {
148 | if (random(10) >= 5) {
149 | var branchWidth = random(0.2 * w, 0.8 * w);
150 | var branchHeight = random(0.2 * h, 0.8 * h);
151 | var branchX = 0;
152 | var branchY = 0;
153 |
154 | var branchSide = int(random(-2, 2));
155 | if (branchSide == BUILDING_TOP) {
156 | branchX = random(x - w / 2 + branchWidth / 2, x + w / 2 - branchWidth / 2);
157 | branchY = y - h;
158 | } else if (branchSide == BUILDING_LEFT) {
159 | branchX = x - w / 2 - branchWidth / 2 + w * 0.2;
160 | branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength);
161 | } else if (branchSide == BUILDING_RIGHT) {
162 | branchX = x + w / 2 + branchWidth / 2 - w * 0.2;
163 | branchY = random(y - 0.2 * (h - stiltLength) - stiltLength, y - 0.8 * (h - stiltLength) - stiltLength);
164 | }
165 |
166 | units.push(new BuildingUnit(branchX, branchY, branchWidth, branchHeight, branchSide, level + 1, time));
167 | }
168 | }
169 | }
170 |
171 | this.draw = function() {
172 | units.forEach(function(b) {
173 | b.draw();
174 | });
175 |
176 | var stiltColor, roofColor, houseColor, windowDark;
177 | if (time < 0.5) {
178 | stiltColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
179 | houseColor = lerpColor("#AD9592", "#BFABA9", map(time, 0, 0.5, 0, 1));
180 | roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
181 | windowDark = lerpColor("#1B5E81", "#383E5A", map(time, 0, 0.5, 0, 1));
182 | } else {
183 | stiltColor = lerpColor("#352B48", "#1D263E", map(time, 0.5, 1, 0, 1));
184 | houseColor = lerpColor("#C3B7D1", "#8788A7", map(time, 0, 0.5, 0, 1));
185 | roofColor = lerpColor("#776F7E", "#4B433C", map(time, 0, 0.5, 0, 1));
186 | windowDark = lerpColor("#644C6F", "#3D435F", map(time, 0, 0.5, 0, 1));
187 | }
188 |
189 | if (stiltLength > 0) {
190 | strokeWeight(w / 20);
191 | stroke(stiltColor);
192 | if (side == BUILDING_TOP) {
193 | line(x - w * 0.3, y, x - w * 0.3, y - stiltLength);
194 | line(x + w * 0.3, y, x + w * 0.3, y - stiltLength);
195 | } else if (side == BUILDING_LEFT) {
196 | line(x - w * 0.4, y - stiltLength, x + w / 2, y);
197 | } else if (side == BUILDING_RIGHT) {
198 | line(x + w * 0.4, y - stiltLength, x - w / 2, y);
199 | }
200 | }
201 |
202 | //Main building
203 | fill(level % 2 == 1 ? houseColor : lerpColor(houseColor, "#1C1443", 0.2));
204 | beginShape();
205 | vertex(x - w / 2, y - stiltLength);
206 | vertex(x + w / 2, y - stiltLength);
207 | vertex(x + w / 2 + offsetRight, y - h * 0.85);
208 | vertex(x - w / 2 + offsetLeft, y - h * 0.85);
209 | endShape();
210 |
211 | //Windows
212 | for (var i = 0; i < windows; i++) {
213 | if (time > LIGHTS_ON && lights[i]) {
214 | fill(WINDOW_LIGHT);
215 | } else {
216 | fill(windowDark);
217 | }
218 | if (alignment == BUILDING_LEFT) {
219 | rect(
220 | x + offsetLeft - w / 2 + w * 0.05 + w * 0.25 * i,
221 | y - h * 0.8,
222 | w * 0.2,
223 | windowHeight
224 | );
225 | } else if (alignment == BUILDING_RIGHT) {
226 | rect(
227 | x + offsetRight + w / 2 - w * 0.05 - w * 0.25 * (i + 1),
228 | y - h * 0.8,
229 | w * 0.2,
230 | windowHeight
231 | );
232 | }
233 | }
234 |
235 | //Roof
236 | fill("#685D71");
237 | beginShape();
238 | vertex(x - w / 2 + offsetLeft - offsetRoof, y - h * 0.85);
239 | vertex(x + w / 2 + offsetRight + offsetRoof, y - h * 0.85);
240 | vertex(x + w / 2 + offsetRight, y - h);
241 | vertex(x - w / 2 + offsetLeft, y - h);
242 | endShape();
243 | };
244 | }
245 |
246 | function Building(x, y, w, h, time) {
247 | var units = [];
248 | var numUnits = int(random(1, 2));
249 | for (var i = 0; i < numUnits; i++) {
250 | var unitWidth = random(0.5, 1.5) * (w / numUnits);
251 | units.push(new BuildingUnit(
252 | x - w / 2 + i * (w / numUnits) + unitWidth / 2,
253 | y,
254 | unitWidth,
255 | random(h * 0.2, h),
256 | BUILDING_TOP,
257 | 1,
258 | time
259 | ));
260 | }
261 |
262 | this.draw = function() {
263 | units.forEach(function(b) {
264 | b.draw();
265 | });
266 | };
267 | }
268 |
269 |
270 | function Tree(x, y, w, h, time) {
271 | var root = new Branch(x, y, w, h, BRANCH_ROOT, 1, time);
272 | this.draw = function() {
273 | root.drawLeaves();
274 | root.drawBranches();
275 | };
276 | }
277 |
278 | function Branch(x, y, w, h, side, level, time) {
279 | var branches = [];
280 | var segmentX = 0;
281 | var segmentY = 0;
282 | var leafWidth = 0;
283 | var leafHeight = 0;
284 |
285 | if (side == BRANCH_ROOT) {
286 | segmentX = x + (random(w * 0.6) - w * 0.3);
287 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
288 | } else if (side == BRANCH_LEFT) {
289 | segmentX = x - random(w * 0.3);
290 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
291 | } else if (side == BRANCH_RIGHT) {
292 | segmentX = x + random(w * 0.3);
293 | segmentY = y - h * 0.75 + (random(h * 0.3) - h * 0.15);
294 | }
295 |
296 | leafWidth = random(w * 0.6) + w * 0.3;
297 | leafHeight = random(h * 0.6) + h * 0.3;
298 |
299 | if (level < 3) {
300 | for (var i = 0; i < BRANCH_MAX_LEVELS; i++) {
301 | if (random(10) >= 7) {
302 | var branchY = random(segmentY, y);
303 | var branchX = x + ((segmentX - x) / (segmentY - y)) * (branchY - y);
304 | var branchDir = random(10) > 5 ? BRANCH_LEFT : BRANCH_RIGHT;
305 | branches.push(new Branch(branchX, branchY, w * 0.6, h * 0.5, branchDir, level + 1, time));
306 | }
307 | }
308 | }
309 |
310 | this.drawLeaves = function() {
311 | var leafColor;
312 | if (time < 0.5) {
313 | leafColor = lerpColor("#FFD1D1", "#F2C975", map(time, 0, 0.5, 0, 1));
314 | } else {
315 | leafColor = lerpColor("#D5BCE0", "#56627C", map(time, 0.5, 1, 0, 1));
316 | }
317 | fill(leafColor);
318 | ellipse(segmentX, segmentY, leafWidth, leafHeight);
319 |
320 | branches.forEach(function(b) {
321 | b.drawLeaves();
322 | });
323 | };
324 |
325 | this.drawBranches = function() {
326 | var branchColor;
327 | if (time < 0.5) {
328 | branchColor = lerpColor("#79504E", "#866A59", map(time, 0, 0.5, 0, 1));
329 | } else {
330 | branchColor = lerpColor("#635774", "#635774", map(time, 0.5, 1, 0, 1));
331 | }
332 |
333 | strokeWeight((y - segmentY) / 20);
334 | stroke(branchColor);
335 | line(x, y, segmentX, segmentY);
336 |
337 | branches.forEach(function(b) {
338 | b.drawBranches();
339 | });
340 | };
341 | }
342 |
343 |
344 | function Mountains(top, bottom, time) { //#D36F6F, #AD1515
345 | var ranges = [];
346 | var start, end;
347 | if (time < 0.5) {
348 | start = lerpColor("#D36F6F", "#F2C178", map(time, 0, 0.5, 0, 1));
349 | end = lerpColor("#AD1515", "#E85B57", map(time, 0, 0.5, 0, 1));
350 | } else {
351 | start = lerpColor("#654183", "#2D3A5F", map(time, 0.5, 1, 0, 1));
352 | end = lerpColor("#B198C6", "#6172A5", map(time, 0.5, 1, 0, 1));
353 | }
354 |
355 | var layers = int(random(2, 5));
356 | var heightUnit = (bottom - top) / (layers + 1);
357 |
358 | for (var l = 0; l < layers; l++) {
359 | var y = top + random(heightUnit * l, heightUnit * (l + 1));
360 | var spread = random(1, 3) * heightUnit;
361 | ranges.push(new MountainRange(
362 | y,
363 | spread,
364 | lerpColor(start, end, l/layers), (l + 1) / layers,
365 | time
366 | ));
367 | }
368 |
369 | this.draw = function() {
370 | ranges.forEach(function(range) {
371 | range.draw();
372 | });
373 | };
374 | }
375 |
376 | function MountainRange(y, spread, c, foreground, time) {
377 | var elevation = [];
378 | var trees = [];
379 | var buildings = [];
380 |
381 | var noiseElevation = random(1, 100);
382 | var spikiness = random(40, 90);
383 |
384 | for (var x = 0; x < 150; x++) {
385 | elevation.push(map(noise(x / spikiness, noiseElevation), 0, 1, -1, 1) * spread);
386 | }
387 |
388 | var numTrees = int(random(1, map(1.0 / foreground, 1, 100, 5, 40)));
389 | for (var i = 0; i < numTrees; i++) {
390 | var location = int(random(elevation.length));
391 | trees.push(new Tree(
392 | (location / (elevation.length - 1)) * width,
393 | y + elevation[location],
394 | foreground * 100,
395 | foreground * 80,
396 | time
397 | ));
398 | }
399 |
400 | var numBuildings = int(random(1, map(1.0 / foreground, 1, 100, 0, 10)));
401 | for (var i = 0; i < numTrees; i++) {
402 | var location = int(random(elevation.length));
403 | buildings.push(new Building(
404 | (location / (elevation.length - 1)) * width,
405 | y + elevation[location] + spread * 0.1,
406 | foreground * 80,
407 | foreground * 120,
408 | time
409 | ));
410 | }
411 |
412 | this.draw = function() {
413 | buildings.forEach(function(building) {
414 | building.draw();
415 | });
416 |
417 | fill(c);
418 | beginShape();
419 | for (var i = 0; i < elevation.length; i++) {
420 | vertex((i / (elevation.length - 1)) * width, y + elevation[i]);
421 | }
422 | vertex(width, height);
423 | vertex(0, height);
424 | endShape();
425 |
426 | trees.forEach(function(tree) {
427 | tree.draw();
428 | });
429 | };
430 | }
431 |
432 |
433 | function Sky(time) {
434 | var sky, horizon;
435 | if (time < 0.5) {
436 | sky = lerpColor("#B9F7D5", "#57C8F0", map(time, 0, 0.5, 0, 1));
437 | horizon = lerpColor("#FCED42", "#E365ED", map(time, 0, 0.5, 0, 1));
438 | } else {
439 | sky = lerpColor("#C282E5", "#1A285A", map(time, 0.5, 1, 0, 1));
440 | horizon = lerpColor("#82E3E5", "#BFE1FC", map(time, 0.5, 1, 0, 1));
441 | }
442 |
443 | this.draw = function() {
444 | setGradient(0, 0, width, height, sky, horizon);
445 | };
446 | }
447 |
448 |
449 | var width = 720;
450 | var height = 405;
451 | var canvas = document.getElementById("landscape");
452 | var container = document.getElementById("header");
453 | var overlay = document.getElementById("overlay");
454 | var buffer, buffer_canvas;
455 | var pathCleared = true;
456 | var transitioning = false;
457 |
458 | var clearBackground = function() {
459 | buffer.clearRect(0, 0, width, height);
460 | };
461 |
462 | var random = function(low, high) {
463 | if (high === undefined) {
464 | high = low;
465 | low = 0;
466 | }
467 | return Math.random() * (high - low) + low;
468 | };
469 |
470 | var int = function(n) {
471 | return Math.floor(n);
472 | };
473 |
474 | var strokeWeight = function(w) {
475 | buffer.lineWidth = w;
476 | };
477 |
478 | var stroke = function(c) {
479 | buffer.strokeStyle = c;
480 | };
481 |
482 | var line = function(x1, y1, x2, y2) {
483 | buffer.beginPath();
484 | buffer.moveTo(x1, y1);
485 | buffer.lineTo(x2, y2);
486 | buffer.stroke();
487 | };
488 |
489 | var fill = function(c) {
490 | buffer.fillStyle = c;
491 | };
492 |
493 | var beginShape = function() {
494 | buffer.beginPath();
495 | pathCleared = true;
496 | };
497 |
498 | var vertex = function(x, y) {
499 | if (pathCleared) {
500 | buffer.moveTo(x, y);
501 | } else {
502 | buffer.lineTo(x, y);
503 | }
504 | pathCleared = false;
505 | };
506 |
507 | var endShape = function() {
508 | buffer.closePath();
509 | buffer.fill();
510 | };
511 |
512 | var rect = function(x, y, w, h) {
513 | buffer.fillRect(x, y, w, h);
514 | };
515 |
516 | var ellipse = function(x, y, w, h) {
517 | buffer.beginPath();
518 | buffer.ellipse(x, y, w/2, h/2, 0, 0, 2 * Math.PI);
519 | buffer.fill();
520 | };
521 |
522 | var setGradient = function(x, y, w, h, c1, c2) {
523 | var gradient = buffer.createLinearGradient(0, 0, 0, h);
524 | gradient.addColorStop(0, c1);
525 | gradient.addColorStop(1, c2);
526 | buffer.fillStyle = gradient;
527 | buffer.fillRect(x, y, w, h);
528 | };
529 |
530 | var hexToRGB = function(hex) {
531 | var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
532 | return result ? {
533 | r: parseInt(result[1], 16),
534 | g: parseInt(result[2], 16),
535 | b: parseInt(result[3], 16)
536 | } : null;
537 | };
538 |
539 | var componentToHex = function(c) {
540 | var hex = int(c).toString(16);
541 | return hex.length == 1 ? "0" + hex : hex;
542 | };
543 |
544 | var rgbToHex = function(r, g, b) {
545 | return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
546 | };
547 |
548 | var lerp = function(a, b, n) {
549 | return Math.abs((b - a) * n + a);
550 | };
551 |
552 | var lerpColor = function(beginning, end, percent) {
553 | var c1 = hexToRGB(beginning);
554 | var c2 = hexToRGB(end);
555 | return rgbToHex(
556 | lerp(c1.r, c2.r, percent),
557 | lerp(c1.g, c2.g, percent),
558 | lerp(c1.b, c2.b, percent)
559 | );
560 | };
561 |
562 | var map = function(v, a1, b1, a2, b2) {
563 | return (((v-a1) / (b1 - a1)) * (b2 - a2) + a2);
564 | };
565 |
566 | var perlin = new ClassicalNoise();
567 | var noise = function(x, y) {
568 | y = y || 0;
569 | return map(perlin.noise(x, y, 0), -1, 1, 0, 1);
570 | };
571 |
572 | var BUILDING_TOP = 0,
573 | BUILDING_LEFT = -1,
574 | BUILDING_RIGHT = 1,
575 | BUILDING_MAX_LEVELS = 2;
576 |
577 | var BRANCH_ROOT = 0,
578 | BRANCH_LEFT = -1,
579 | BRANCH_RIGHT = 1,
580 | BRANCH_MAX_LEVELS = 3;
581 |
582 | var time, sky, mountains;
583 |
584 | var drawScene = function(offsetX, offsetY) {
585 | sky.draw();
586 | mountains.draw();
587 | stage.drawImage(buffer_canvas, offsetX, offsetY);
588 | };
589 |
590 | var regenScene = function() {
591 | width = canvas.width;
592 | height = canvas.height;
593 | time = random(1);
594 | sky = new Sky(time);
595 | mountains = new Mountains(height * 0.2, height * 0.8, time);
596 | };
597 |
598 | var redrawCanvas = function(regen) {
599 | canvas.width = container.offsetWidth;
600 | canvas.height = container.offsetHeight;
601 | if (regen) regenScene();
602 | var scale;
603 | if (canvas.width > (width/height)*canvas.height) {
604 | scale = canvas.width/width;
605 | } else {
606 | scale = canvas.height/height;
607 | }
608 |
609 | buffer_canvas.width = width*scale;
610 | buffer_canvas.height = height*scale;
611 | buffer.scale(scale, scale);
612 | drawScene(
613 | (canvas.width - buffer_canvas.width)/2,
614 | (canvas.height - buffer_canvas.height)/2
615 | );
616 | };
617 |
618 | var transition = function() {
619 | if (transitioning) return;
620 | overlay.classList.remove("transparent");
621 | transitioning = true;
622 | setTimeout(function() {
623 | redrawCanvas(true);
624 | overlay.classList.add("transparent");
625 | setTimeout(function() {
626 | transitioning = false;
627 | }, 700);
628 | }, 700);
629 | };
630 |
631 | if (canvas.getContext) {
632 | stage = canvas.getContext("2d");
633 | buffer_canvas = document.createElement("canvas");
634 | buffer = buffer_canvas.getContext("2d");
635 | if (CanvasRenderingContext2D.prototype.ellipse == undefined) {
636 | CanvasRenderingContext2D.prototype.ellipse = function(x, y, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise) {
637 | this.save();
638 | this.translate(x, y);
639 | this.rotate(rotation);
640 | this.scale(radiusX, radiusY);
641 | this.arc(0, 0, 1, startAngle, endAngle, antiClockwise);
642 | this.restore();
643 | };
644 | }
645 | window.addEventListener("resize", throttle(function() { redrawCanvas() }, 200));
646 | container.addEventListener("click", transition);
647 | setTimeout(function() {
648 | redrawCanvas(true);
649 | overlay.classList.add("transparent");
650 | }, 200);
651 | } else {
652 | console.log("canvas not supported");
653 | document.getElementById("header").classList.add("static");
654 | }
655 |
656 |
657 |
--------------------------------------------------------------------------------