├── .gitignore ├── PathFollowing_2017 ├── Path.pde ├── PathFollowing_2017.pde ├── Vehicle.pde └── data │ ├── paths.csv │ ├── texture.png │ ├── texture16.png │ └── texture24.png ├── Processing_2017 ├── Circle.pde ├── Processing_2017.pde └── data │ ├── 2017.png │ └── 2017.psd ├── README.md ├── data ├── 2017.png └── 2017.psd ├── font_paths ├── OpenSans-Regular.ttf ├── index.html ├── libraries │ ├── p5.dom.js │ ├── p5.js │ └── p5.sound.js ├── path.js ├── pathhelper.js ├── sketch.js └── vehicle.js ├── index.html └── sketch.js /.gitignore: -------------------------------------------------------------------------------- 1 | output/ 2 | -------------------------------------------------------------------------------- /PathFollowing_2017/Path.pde: -------------------------------------------------------------------------------- 1 | // The Nature of Code 2 | // Daniel Shiffman 3 | // http://natureofcode.com 4 | 5 | // Path Following 6 | 7 | class Path { 8 | 9 | // A Path is an arraylist of points (PVector objects) 10 | ArrayList points; 11 | // A path has a radius, i.e how far is it ok for the boid to wander off 12 | float radius; 13 | 14 | Path() { 15 | // Arbitrary radius of 20 16 | radius = 5; 17 | points = new ArrayList(); 18 | } 19 | 20 | // Add a point to the path 21 | void addPoint(float x, float y) { 22 | PVector point = new PVector(x, y); 23 | points.add(point); 24 | } 25 | 26 | // Draw the path 27 | void display() { 28 | strokeJoin(ROUND); 29 | 30 | // Draw thick line for radius 31 | stroke(175); 32 | strokeWeight(radius*2); 33 | noFill(); 34 | beginShape(); 35 | for (PVector v : points) { 36 | vertex(v.x, v.y); 37 | } 38 | endShape(CLOSE); 39 | // Draw thin line for center of path 40 | stroke(0); 41 | strokeWeight(1); 42 | noFill(); 43 | beginShape(); 44 | for (PVector v : points) { 45 | vertex(v.x, v.y); 46 | } 47 | endShape(CLOSE); 48 | } 49 | } -------------------------------------------------------------------------------- /PathFollowing_2017/PathFollowing_2017.pde: -------------------------------------------------------------------------------- 1 | // The Nature of Code 2 | // Daniel Shiffman 3 | // http://natureofcode.com 4 | 5 | // Crowd Path Following 6 | // Via Reynolds: http://www.red3d.com/cwr/steer/CrowdPath.html 7 | 8 | // Using this variable to decide whether to draw all the stuff 9 | boolean debug = false; 10 | 11 | 12 | // A path object (series of connected points) 13 | Path path; 14 | 15 | Path[] paths = new Path[5]; 16 | 17 | // Two vehicles 18 | ArrayList vehicles; 19 | 20 | Table table; 21 | 22 | PImage texture; 23 | 24 | int totalParticles = 1000; 25 | int often = 60; 26 | 27 | void setup() { 28 | size(1280, 720, P2D); 29 | frameRate(60); 30 | blendMode(ADD); 31 | //pixelDensity(2); 32 | // Call a function to generate new Path object 33 | newPath(); 34 | table = loadTable("paths.csv"); 35 | texture = loadImage("texture24.png"); 36 | 37 | for (int i = 0; i < paths.length; i++) { 38 | paths[i] = new Path(); 39 | } 40 | 41 | 42 | 43 | for (TableRow row : table.rows()) { 44 | int i = row.getInt(0); 45 | float x = row.getFloat(1) + 24; 46 | float y = row.getFloat(2) + 100; 47 | paths[i].addPoint(x, y); 48 | } 49 | 50 | // We are now making random vehicles and storing them in an ArrayList 51 | vehicles = new ArrayList(); 52 | newVehicle(0, 200, 0); 53 | } 54 | 55 | void draw() { 56 | background(0); 57 | if (frameCount % often == 0) { 58 | if (vehicles.size() < totalParticles) { 59 | newVehicle(-500, 200, 0); 60 | } 61 | often--; 62 | if (often < 1) { 63 | often = 1; 64 | } 65 | } 66 | // Display the path 67 | // paths[0].display(); 68 | 69 | for (Vehicle v : vehicles) { 70 | // Path following and separation are worked on in this function 71 | // Call the generic run method (update, borders, display, etc.) 72 | v.run(); 73 | v.applyBehaviors(vehicles, paths); 74 | } 75 | //saveFrame("output/render####.png"); 76 | } 77 | 78 | void newPath() { 79 | // A path is a series of connected points 80 | // A more sophisticated path might be a curve 81 | path = new Path(); 82 | float offset = 30; 83 | path.addPoint(offset, offset); 84 | path.addPoint(width-offset, offset); 85 | path.addPoint(width-offset, height-offset); 86 | path.addPoint(width/2, height-offset*3); 87 | path.addPoint(offset, height-offset); 88 | } 89 | 90 | void newVehicle(float x, float y, int wp) { 91 | float maxspeed = random(3, 8); 92 | float maxforce = 0.5; 93 | vehicles.add(new Vehicle(new PVector(x, y), maxspeed, maxforce, wp)); 94 | } -------------------------------------------------------------------------------- /PathFollowing_2017/Vehicle.pde: -------------------------------------------------------------------------------- 1 | // The Nature of Code 2 | // Daniel Shiffman 3 | // http://natureofcode.com 4 | 5 | // Path Following 6 | // Vehicle class 7 | 8 | class Vehicle { 9 | 10 | // All the usual stuff 11 | PVector position; 12 | PVector velocity; 13 | PVector acceleration; 14 | float r; 15 | float maxforce; // Maximum steering force 16 | float maxspeed; // Maximum speed 17 | int whichPath = -1; 18 | int counter = 0; 19 | int dir = 1; 20 | 21 | // Constructor initialize all values 22 | Vehicle( PVector l, float ms, float mf, int wp) { 23 | position = l.copy(); 24 | r = 6; 25 | maxspeed = ms; 26 | maxforce = mf; 27 | acceleration = new PVector(0, 0); 28 | velocity = new PVector(maxspeed, 0); 29 | //whichPath = wp; 30 | } 31 | 32 | // A function to deal with path following and separation 33 | void applyBehaviors(ArrayList vehicles, Path[] paths) { 34 | // Follow path force 35 | PVector f = follow(paths[whichPath]); 36 | // Separate from other boids force 37 | PVector s = separate(vehicles); 38 | // Arbitrary weighting 39 | f.mult(2); 40 | s.mult(2); 41 | // Accumulate in acceleration 42 | applyForce(f); 43 | applyForce(s); 44 | } 45 | 46 | void applyForce(PVector force) { 47 | // We could add mass here if we want A = F / M 48 | acceleration.add(force); 49 | } 50 | 51 | 52 | 53 | // Main "run" function 54 | public void run() { 55 | if (counter % (totalParticles/(paths.length-2)) == 0) { 56 | whichPath = whichPath + dir; 57 | if (whichPath == paths.length-1) dir = -1; 58 | else if (whichPath == 0) dir = 1; 59 | } 60 | counter++; 61 | 62 | update(); 63 | borders(); 64 | render(); 65 | } 66 | 67 | 68 | // This function implements Craig Reynolds' path following algorithm 69 | // http://www.red3d.com/cwr/steer/PathFollow.html 70 | PVector follow(Path p) { 71 | 72 | // Predict position 25 (arbitrary choice) frames ahead 73 | PVector predict = velocity.get(); 74 | predict.normalize(); 75 | predict.mult(25); 76 | PVector predictpos = PVector.add(position, predict); 77 | 78 | // Now we must find the normal to the path from the predicted position 79 | // We look at the normal for each line segment and pick out the closest one 80 | PVector normal = null; 81 | PVector target = null; 82 | float worldRecord = 1000000; // Start with a very high worldRecord distance that can easily be beaten 83 | 84 | // Loop through all points of the path 85 | for (int i = 0; i < p.points.size(); i++) { 86 | 87 | // Look at a line segment 88 | PVector a = p.points.get(i); 89 | PVector b = p.points.get((i+1)%p.points.size()); // Note Path has to wraparound 90 | 91 | // Get the normal point to that line 92 | PVector normalPoint = getNormalPoint(predictpos, a, b); 93 | 94 | // Check if normal is on line segment 95 | PVector dir = PVector.sub(b, a); 96 | // If it's not within the line segment, consider the normal to just be the end of the line segment (point b) 97 | //if (da + db > line.mag()+1) { 98 | if (normalPoint.x < min(a.x, b.x) || normalPoint.x > max(a.x, b.x) || normalPoint.y < min(a.y, b.y) || normalPoint.y > max(a.y, b.y)) { 99 | normalPoint = b.get(); 100 | // If we're at the end we really want the next line segment for looking ahead 101 | a = p.points.get((i+1)%p.points.size()); 102 | b = p.points.get((i+2)%p.points.size()); // Path wraps around 103 | dir = PVector.sub(b, a); 104 | } 105 | 106 | // How far away are we from the path? 107 | float d = PVector.dist(predictpos, normalPoint); 108 | // Did we beat the worldRecord and find the closest line segment? 109 | if (d < worldRecord) { 110 | worldRecord = d; 111 | normal = normalPoint; 112 | 113 | // Look at the direction of the line segment so we can seek a little bit ahead of the normal 114 | dir.normalize(); 115 | // This is an oversimplification 116 | // Should be based on distance to path & velocity 117 | dir.mult(25); 118 | target = normal.get(); 119 | target.add(dir); 120 | } 121 | } 122 | 123 | // Draw the debugging stuff 124 | if (debug) { 125 | // Draw predicted future position 126 | stroke(0); 127 | fill(0); 128 | line(position.x, position.y, predictpos.x, predictpos.y); 129 | ellipse(predictpos.x, predictpos.y, 4, 4); 130 | 131 | // Draw normal position 132 | stroke(0); 133 | fill(0); 134 | ellipse(normal.x, normal.y, 4, 4); 135 | // Draw actual target (red if steering towards it) 136 | line(predictpos.x, predictpos.y, target.x, target.y); 137 | if (worldRecord > p.radius) fill(255, 0, 0); 138 | noStroke(); 139 | ellipse(target.x, target.y, 8, 8); 140 | } 141 | 142 | // Only if the distance is greater than the path's radius do we bother to steer 143 | if (worldRecord > p.radius) { 144 | return seek(target); 145 | } else { 146 | return new PVector(0, 0); 147 | } 148 | } 149 | 150 | 151 | // A function to get the normal point from a point (p) to a line segment (a-b) 152 | // This function could be optimized to make fewer new Vector objects 153 | PVector getNormalPoint(PVector p, PVector a, PVector b) { 154 | // Vector from a to p 155 | PVector ap = PVector.sub(p, a); 156 | // Vector from a to b 157 | PVector ab = PVector.sub(b, a); 158 | ab.normalize(); // Normalize the line 159 | // Project vector "diff" onto line by using the dot product 160 | ab.mult(ap.dot(ab)); 161 | PVector normalPoint = PVector.add(a, ab); 162 | return normalPoint; 163 | } 164 | 165 | // Separation 166 | // Method checks for nearby boids and steers away 167 | PVector separate (ArrayList boids) { 168 | float desiredseparation = r*2; 169 | PVector steer = new PVector(0, 0, 0); 170 | int count = 0; 171 | // For every boid in the system, check if it's too close 172 | for (int i = 0; i < boids.size(); i++) { 173 | Vehicle other = (Vehicle) boids.get(i); 174 | float d = PVector.dist(position, other.position); 175 | // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself) 176 | if ((d > 0) && (d < desiredseparation)) { 177 | // Calculate vector pointing away from neighbor 178 | PVector diff = PVector.sub(position, other.position); 179 | diff.normalize(); 180 | diff.div(d); // Weight by distance 181 | steer.add(diff); 182 | count++; // Keep track of how many 183 | } 184 | } 185 | // Average -- divide by how many 186 | if (count > 0) { 187 | steer.div((float)count); 188 | } 189 | 190 | // As long as the vector is greater than 0 191 | if (steer.mag() > 0) { 192 | // Implement Reynolds: Steering = Desired - Velocity 193 | steer.normalize(); 194 | steer.mult(maxspeed); 195 | steer.sub(velocity); 196 | steer.limit(maxforce); 197 | } 198 | return steer; 199 | } 200 | 201 | 202 | // Method to update position 203 | void update() { 204 | // Update velocity 205 | velocity.add(acceleration); 206 | // Limit speed 207 | velocity.limit(maxspeed); 208 | position.add(velocity); 209 | // Reset accelertion to 0 each cycle 210 | acceleration.mult(0); 211 | } 212 | 213 | // A method that calculates and applies a steering force towards a target 214 | // STEER = DESIRED MINUS VELOCITY 215 | PVector seek(PVector target) { 216 | PVector desired = PVector.sub(target, position); // A vector pointing from the position to the target 217 | 218 | // Normalize desired and scale to maximum speed 219 | desired.normalize(); 220 | desired.mult(maxspeed); 221 | // Steering = Desired minus Vepositionity 222 | PVector steer = PVector.sub(desired, velocity); 223 | steer.limit(maxforce); // Limit to maximum steering force 224 | 225 | return steer; 226 | } 227 | 228 | 229 | void render() { 230 | // Simpler boid is just a circle 231 | //fill(75); 232 | //stroke(0); 233 | //pushMatrix(); 234 | //translate(position.x, position.y); 235 | //ellipse(0, 0, r, r); 236 | //popMatrix(); 237 | //strokeWeight(r); 238 | //stroke(255, 150); 239 | //point(position.x,position.y); 240 | imageMode(CENTER); 241 | image(texture, position.x, position.y); 242 | } 243 | 244 | // Wraparound 245 | void borders() { 246 | if (position.x < -r) position.x = width+r; 247 | //if (position.y < -r) position.y = height+r; 248 | if (position.x > width+r) position.x = -r; 249 | //if (position.y > height+r) position.y = -r; 250 | } 251 | } -------------------------------------------------------------------------------- /PathFollowing_2017/data/paths.csv: -------------------------------------------------------------------------------- 1 | 0,292.7472541369498,498.625 2 | 0,242.75119130484381,498.625 3 | 0,192.75410840857433,498.625 4 | 0,142.7506523624146,498.625 5 | 0,92.7462892654139,498.625 6 | 0,52.5,488.8797744392068 7 | 0,69.43077787786024,445.85627002926776 8 | 0,104.69180506270732,410.41206867722667 9 | 0,139.95329794241115,374.96739921113476 10 | 0,174.86059761047363,339.1745467185974 11 | 0,207.6181640625,301.4384765625 12 | 0,230.57312870025635,257.2912151813507 13 | 0,232.56833863258362,207.85601246356964 14 | 0,202.16661788523197,170.25921750813723 15 | 0,153.16185140609738,164.17291069030762 16 | 0,106.03189184516668,179.7132460027933 17 | 0,68.51584613072919,184.34137059968634 18 | 0,83.01098847389221,151.53077173233032 19 | 0,129.0340786576271,132.43068087100983 20 | 0,178.65635691583157,128.21492137759924 21 | 0,226.9077966213226,140.02460515499115 22 | 0,263.6886475533247,172.92757207900283 23 | 0,277.16865825653076,220.48204469680786 24 | 0,269.1799550354481,269.52469666302204 25 | 0,245.06130492687225,313.11286556720734 26 | 0,212.83874511718747,351.3165283203125 27 | 0,177.65053367614746,386.81916549801826 28 | 0,141.89571270014858,421.79184976516717 29 | 0,106.15547810122371,456.75026673224056 30 | 0,150.785091713964,460.125 31 | 0,200.7808653924585,460.125 32 | 0,250.7785149903484,460.125 33 | 0,292.75,468.1489978572354 34 | 1,587.4999999721767,315.38078303635115 35 | 1,585.09356607683,365.2912495136261 36 | 1,576.0207524970174,414.39729785919184 37 | 1,555.6666187047958,459.839631319046 38 | 1,518.611088655889,492.5568173676729 39 | 1,470.2400787770748,503.5749652981758 40 | 1,421.3702530004084,495.0499515403062 41 | 1,382.5791742801666,464.3904801607132 42 | 1,359.8284547738731,420.1216840352863 43 | 1,349.0730581395328,371.3685532268136 44 | 1,345.784400627017,321.51282127946615 45 | 1,347.5338935703039,271.5574163943529 46 | 1,355.48030095919967,222.25035719946027 47 | 1,374.00843715667725,176.0053834915161 48 | 1,409.05963717773557,141.10231575742364 49 | 1,456.7665661126375,127.66617800295353 50 | 1,506.0260873492807,133.85877737775445 51 | 1,546.6050263866782,162.10358108580112 52 | 1,571.228673638776,205.3187889046967 53 | 1,583.2983874753118,253.75260300934315 54 | 1,587.3668345287442,303.5432289391756 55 | 2,387.7500000698492,315.38473494583735 56 | 2,389.9876608103514,365.29925266653294 57 | 2,399.45303225517273,414.31321585178375 58 | 2,426.1705102920532,455.6351532936096 59 | 2,473.56090143322945,467.57410457730293 60 | 2,516.9354255869985,445.6067847684026 61 | 2,537.068153463304,400.32424653321505 62 | 2,543.9218240436167,350.84550540335476 63 | 2,544.8337359651923,300.87006539851427 64 | 2,540.9437474217266,251.0479271132499 65 | 2,527.8278656229377,202.9816886857152 66 | 2,493.54537919163704,168.2737058699131 67 | 2,444.4397845864296,166.38331225514412 68 | 2,407.3535101413727,197.8954221010208 69 | 2,392.61772768199444,245.43190298229456 70 | 2,388.072877895087,295.1957482788712 71 | 3,791.7426228523254,498.625 72 | 3,751.25,489.12238953763153 73 | 3,751.25,439.12638164581654 74 | 3,751.25,389.1167852833894 75 | 3,751.25,339.1207168659457 76 | 3,751.25,289.1254324571928 77 | 3,751.25,239.12073307763785 78 | 3,752.488313794136,189.14580890536308 79 | 3,725.0782527923584,201.29124927520752 80 | 3,686.3193143606186,232.86982995271683 81 | 3,666.6690937847379,202.72239265726967 82 | 3,706.2409036269804,172.1488238127464 83 | 3,745.8044492382332,141.58163999423323 84 | 3,791.75,134.2899499687337 85 | 3,791.75,184.30697431949625 86 | 3,791.75,234.30020031357685 87 | 3,791.75,284.29413315407874 88 | 3,791.75,334.30170212833434 89 | 3,791.75,384.2942888212874 90 | 3,791.75,434.3058166686503 91 | 3,791.75,484.296966843307 92 | 4,977.0017315223813,498.6212597973645 93 | 4,998.0057241332051,453.25124935583244 94 | 4,1019.0125735151453,407.8750681001235 95 | 4,1040.0156371430985,362.507064322911 96 | 4,1061.0227720446528,317.13026632598906 97 | 4,1082.0283272487652,271.75688057981245 98 | 4,1103.0355420347823,226.37991002717808 99 | 4,1124.0377235548058,181.01381166131887 100 | 4,1089.1197369398683,171.375 101 | 4,1039.1108544813424,171.375 102 | 4,989.1159749990937,171.375 103 | 4,939.1138283971059,171.375 104 | 4,931.1374517786317,133.125 105 | 4,981.1348668201827,133.125 106 | 4,1031.132501415822,133.125 107 | 4,1081.1440343840877,133.125 108 | 4,1131.1326324265092,133.125 109 | 4,1172.5,141.76341515055537 110 | 4,1162.0839337063574,189.5237493382125 111 | 4,1141.5688543115539,235.11662645475735 112 | 4,1121.050580918789,280.71660193800926 113 | 4,1100.5279194378925,326.32632954354665 114 | 4,1080.0127043804462,371.91950815783787 115 | 4,1059.5000897747104,417.5069075073743 116 | 4,1038.9813990046587,463.1078105732586 117 | 4,1011.9525968893431,498.625 118 | -------------------------------------------------------------------------------- /PathFollowing_2017/data/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/PathFollowing_2017/data/texture.png -------------------------------------------------------------------------------- /PathFollowing_2017/data/texture16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/PathFollowing_2017/data/texture16.png -------------------------------------------------------------------------------- /PathFollowing_2017/data/texture24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/PathFollowing_2017/data/texture24.png -------------------------------------------------------------------------------- /Processing_2017/Circle.pde: -------------------------------------------------------------------------------- 1 | // 8 nights of Hanukkah 2016 Examples 2 | // Night 7: Animated Circle Packing 3 | // Expansion of: https://youtu.be/XATr_jdh-44 4 | // Daniel Shiffman 5 | // http://codingrainbow.com/ 6 | 7 | // Circle class 8 | class Circle { 9 | float x, y, r; 10 | 11 | boolean growing = true; 12 | 13 | Circle(float x_, float y_, float r_) { 14 | x = x_; 15 | y = y_; 16 | r = r_; 17 | } 18 | 19 | // Check stuck to an edge 20 | 21 | boolean edges() { 22 | return (r > width - x || r > x || r > height-y || r > y); 23 | } 24 | 25 | // Grow 26 | void grow() { 27 | r += 0.5; 28 | } 29 | 30 | // Show 31 | void show() { 32 | stroke(255); 33 | noFill(); 34 | strokeWeight(2); 35 | ellipse(x, y, r*2, r*2); 36 | } 37 | } -------------------------------------------------------------------------------- /Processing_2017/Processing_2017.pde: -------------------------------------------------------------------------------- 1 | // 8 nights of Hanukkah 2016 Examples 2 | // Night 7: Animated Circle Packing 3 | // Expansion of: https://youtu.be/XATr_jdh-44 4 | // Daniel Shiffman 5 | // http://codingrainbow.com/ 6 | 7 | // All the circles 8 | ArrayList circles = new ArrayList(); 9 | ArrayList spots = new ArrayList(); 10 | 11 | void setup() { 12 | size(900, 400); 13 | PImage txt = loadImage("2017.png"); 14 | txt.loadPixels(); 15 | for (int x = 0; x < txt.width; x++) { 16 | for (int y = 0; y < txt.height; y++) { 17 | color c = txt.pixels[x + y * txt.width]; 18 | if (brightness(c) > 10) { 19 | spots.add(new PVector(x,y)); 20 | } 21 | } 22 | } 23 | pixelDensity(2); 24 | } 25 | 26 | void draw() { 27 | background(0); 28 | // All the circles 29 | for (Circle c : circles) { 30 | c.show(); 31 | 32 | // Is it a growing one? 33 | if (c.growing) { 34 | c.grow(); 35 | // Does it overlap any previous circles? 36 | for (Circle other : circles) { 37 | if (other != c) { 38 | float d = dist(c.x, c.y, other.x, other.y); 39 | if (d-1 < c.r + other.r) { 40 | c.growing = false; 41 | } 42 | } 43 | } 44 | 45 | // Is it stuck to an edge? 46 | if (c.growing) { 47 | c.growing = !c.edges(); 48 | } 49 | } 50 | } 51 | 52 | // Let's try to make a certain number of new circles each frame 53 | // More the longer it has been running 54 | int target = 1;// + constrain(frameCount / 120, 0, 20); 55 | int count = 0; 56 | // Try N times 57 | for (int i = 0; i < 2000; i++) { 58 | if (addCircle()) { 59 | count++; 60 | } 61 | // We made enough 62 | 63 | if (count == target) { 64 | break; 65 | } 66 | } 67 | // We can't make any more 68 | if (count < 1) { 69 | noLoop(); 70 | println("finished"); 71 | } 72 | //tint(50); 73 | //image(canvas, 0, 0); 74 | } 75 | 76 | // Add one circle 77 | 78 | boolean addCircle() { 79 | // Here's a new circle 80 | 81 | int index = int(random(spots.size())); 82 | PVector spot = spots.get(index); 83 | Circle newCircle = new Circle(spot.x, spot.y, 1); 84 | // Is it in an ok spot? 85 | for (Circle other : circles) { 86 | float d = dist(newCircle.x, newCircle.y, other.x, other.y); 87 | if (d < other.r+2) { 88 | newCircle = null; 89 | break; 90 | } 91 | } 92 | // If it is, add it 93 | if (newCircle != null) { 94 | circles.add(newCircle); 95 | return true; 96 | } else { 97 | return false; 98 | } 99 | } -------------------------------------------------------------------------------- /Processing_2017/data/2017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/Processing_2017/data/2017.png -------------------------------------------------------------------------------- /Processing_2017/data/2017.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/Processing_2017/data/2017.psd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2017 2 | Happy 2017 from [Redacted] Rainbow 3 | -------------------------------------------------------------------------------- /data/2017.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/data/2017.png -------------------------------------------------------------------------------- /data/2017.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/data/2017.psd -------------------------------------------------------------------------------- /font_paths/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodingTrain/2017/a2c2e729909cc0c2aaf1a87c649b654c8062b455/font_paths/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /font_paths/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /font_paths/libraries/p5.dom.js: -------------------------------------------------------------------------------- 1 | /*! p5.dom.js v0.2.13 Oct 1, 2016 */ 2 | /** 3 | *

The web is much more than just canvas and p5.dom makes it easy to interact 4 | * with other HTML5 objects, including text, hyperlink, image, input, video, 5 | * audio, and webcam.

6 | *

There is a set of creation methods, DOM manipulation methods, and 7 | * an extended p5.Element that supports a range of HTML elements. See the 8 | * 9 | * beyond the canvas tutorial for a full overview of how this addon works. 10 | * 11 | *

Methods and properties shown in black are part of the p5.js core, items in 12 | * blue are part of the p5.dom library. You will need to include an extra file 13 | * in order to access the blue functions. See the 14 | * using a library 15 | * section for information on how to include this library. p5.dom comes with 16 | * p5 complete or you can download the single file 17 | * 18 | * here.

19 | *

See tutorial: beyond the canvas 20 | * for more info on how to use this libary. 21 | * 22 | * @module p5.dom 23 | * @submodule p5.dom 24 | * @for p5.dom 25 | * @main 26 | */ 27 | 28 | (function (root, factory) { 29 | if (typeof define === 'function' && define.amd) 30 | define('p5.dom', ['p5'], function (p5) { (factory(p5));}); 31 | else if (typeof exports === 'object') 32 | factory(require('../p5')); 33 | else 34 | factory(root['p5']); 35 | }(this, function (p5) { 36 | // ============================================================================= 37 | // p5 additions 38 | // ============================================================================= 39 | 40 | /** 41 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' 42 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as 43 | * a p5.Element. If a class or tag name is given with more than 1 element, 44 | * only the first element will be returned. 45 | * The DOM node itself can be accessed with .elt. 46 | * Returns null if none found. You can also specify a container to search within. 47 | * 48 | * @method select 49 | * @param {String} name id, class, or tag name of element to search for 50 | * @param {String} [container] id, p5.Element, or HTML element to search within 51 | * @return {Object/p5.Element|Null} p5.Element containing node found 52 | * @example 53 | *

54 | * function setup() { 55 | * createCanvas(100,100); 56 | * //translates canvas 50px down 57 | * select('canvas').position(100, 100); 58 | * } 59 | *
60 | *
61 | * // these are all valid calls to select() 62 | * var a = select('#moo'); 63 | * var b = select('#blah', '#myContainer'); 64 | * var c = select('#foo', b); 65 | * var d = document.getElementById('beep'); 66 | * var e = select('p', d); 67 | *
68 | * 69 | */ 70 | p5.prototype.select = function (e, p) { 71 | var res = null; 72 | var container = getContainer(p); 73 | if (e[0] === '.'){ 74 | e = e.slice(1); 75 | res = container.getElementsByClassName(e); 76 | if (res.length) { 77 | res = res[0]; 78 | } else { 79 | res = null; 80 | } 81 | }else if (e[0] === '#'){ 82 | e = e.slice(1); 83 | res = container.getElementById(e); 84 | }else { 85 | res = container.getElementsByTagName(e); 86 | if (res.length) { 87 | res = res[0]; 88 | } else { 89 | res = null; 90 | } 91 | } 92 | if (res) { 93 | return wrapElement(res); 94 | } else { 95 | return null; 96 | } 97 | }; 98 | 99 | /** 100 | * Searches the page for elements with the given class or tag name (using the '.' prefix 101 | * to specify a class and no prefix for a tag) and returns them as p5.Elements 102 | * in an array. 103 | * The DOM node itself can be accessed with .elt. 104 | * Returns an empty array if none found. 105 | * You can also specify a container to search within. 106 | * 107 | * @method selectAll 108 | * @param {String} name class or tag name of elements to search for 109 | * @param {String} [container] id, p5.Element, or HTML element to search within 110 | * @return {Array} Array of p5.Elements containing nodes found 111 | * @example 112 | *
113 | * function setup() { 114 | * createButton('btn'); 115 | * createButton('2nd btn'); 116 | * createButton('3rd btn'); 117 | * var buttons = selectAll('button'); 118 | * 119 | * for (var i = 0; i < buttons.length; i++){ 120 | * buttons[i].size(100,100); 121 | * } 122 | * } 123 | *
124 | *
125 | * // these are all valid calls to selectAll() 126 | * var a = selectAll('.moo'); 127 | * var b = selectAll('div'); 128 | * var c = selectAll('button', '#myContainer'); 129 | * var d = select('#container'); 130 | * var e = selectAll('p', d); 131 | * var f = document.getElementById('beep'); 132 | * var g = select('.blah', f); 133 | *
134 | * 135 | */ 136 | p5.prototype.selectAll = function (e, p) { 137 | var arr = []; 138 | var res; 139 | var container = getContainer(p); 140 | if (e[0] === '.'){ 141 | e = e.slice(1); 142 | res = container.getElementsByClassName(e); 143 | } else { 144 | res = container.getElementsByTagName(e); 145 | } 146 | if (res) { 147 | for (var j = 0; j < res.length; j++) { 148 | var obj = wrapElement(res[j]); 149 | arr.push(obj); 150 | } 151 | } 152 | return arr; 153 | }; 154 | 155 | /** 156 | * Helper function for select and selectAll 157 | */ 158 | function getContainer(p) { 159 | var container = document; 160 | if (typeof p === 'string' && p[0] === '#'){ 161 | p = p.slice(1); 162 | container = document.getElementById(p) || document; 163 | } else if (p instanceof p5.Element){ 164 | container = p.elt; 165 | } else if (p instanceof HTMLElement){ 166 | container = p; 167 | } 168 | return container; 169 | } 170 | 171 | /** 172 | * Helper function for getElement and getElements. 173 | */ 174 | function wrapElement(elt) { 175 | if(elt.tagName === "INPUT" && elt.type === "checkbox") { 176 | var converted = new p5.Element(elt); 177 | converted.checked = function(){ 178 | if (arguments.length === 0){ 179 | return this.elt.checked; 180 | } else if(arguments[0]) { 181 | this.elt.checked = true; 182 | } else { 183 | this.elt.checked = false; 184 | } 185 | return this; 186 | }; 187 | return converted; 188 | } else if (elt.tagName === "VIDEO" || elt.tagName === "AUDIO") { 189 | return new p5.MediaElement(elt); 190 | } else { 191 | return new p5.Element(elt); 192 | } 193 | } 194 | 195 | /** 196 | * Removes all elements created by p5, except any canvas / graphics 197 | * elements created by createCanvas or createGraphics. 198 | * Event handlers are removed, and element is removed from the DOM. 199 | * @method removeElements 200 | * @example 201 | *
202 | * function setup() { 203 | * createCanvas(100, 100); 204 | * createDiv('this is some text'); 205 | * createP('this is a paragraph'); 206 | * } 207 | * function mousePressed() { 208 | * removeElements(); // this will remove the div and p, not canvas 209 | * } 210 | *
211 | * 212 | */ 213 | p5.prototype.removeElements = function (e) { 214 | for (var i=0; i 242 | * var myDiv; 243 | * function setup() { 244 | * myDiv = createDiv('this is some text'); 245 | * } 246 | * 247 | */ 248 | 249 | /** 250 | * Creates a <p></p> element in the DOM with given inner HTML. Used 251 | * for paragraph length text. 252 | * Appends to the container node if one is specified, otherwise 253 | * appends to body. 254 | * 255 | * @method createP 256 | * @param {String} html inner HTML for element created 257 | * @return {Object/p5.Element} pointer to p5.Element holding created node 258 | * @example 259 | *
260 | * var myP; 261 | * function setup() { 262 | * myP = createP('this is some text'); 263 | * } 264 | *
265 | */ 266 | 267 | /** 268 | * Creates a <span></span> element in the DOM with given inner HTML. 269 | * Appends to the container node if one is specified, otherwise 270 | * appends to body. 271 | * 272 | * @method createSpan 273 | * @param {String} html inner HTML for element created 274 | * @return {Object/p5.Element} pointer to p5.Element holding created node 275 | * @example 276 | *
277 | * var mySpan; 278 | * function setup() { 279 | * mySpan = createSpan('this is some text'); 280 | * } 281 | *
282 | */ 283 | var tags = ['div', 'p', 'span']; 284 | tags.forEach(function(tag) { 285 | var method = 'create' + tag.charAt(0).toUpperCase() + tag.slice(1); 286 | p5.prototype[method] = function(html) { 287 | var elt = document.createElement(tag); 288 | elt.innerHTML = typeof html === undefined ? "" : html; 289 | return addElement(elt, this); 290 | } 291 | }); 292 | 293 | /** 294 | * Creates an <img /> element in the DOM with given src and 295 | * alternate text. 296 | * Appends to the container node if one is specified, otherwise 297 | * appends to body. 298 | * 299 | * @method createImg 300 | * @param {String} src src path or url for image 301 | * @param {String} [alt] alternate text to be used if image does not load 302 | * @param {Function} [successCallback] callback to be called once image data is loaded 303 | * @return {Object/p5.Element} pointer to p5.Element holding created node 304 | * @example 305 | *
306 | * var img; 307 | * function setup() { 308 | * img = createImg('http://p5js.org/img/asterisk-01.png'); 309 | * } 310 | *
311 | */ 312 | p5.prototype.createImg = function() { 313 | var elt = document.createElement('img'); 314 | var args = arguments; 315 | var self; 316 | var setAttrs = function(){ 317 | self.width = elt.offsetWidth; 318 | self.height = elt.offsetHeight; 319 | if (args.length > 1 && typeof args[1] === 'function'){ 320 | self.fn = args[1]; 321 | self.fn(); 322 | }else if (args.length > 1 && typeof args[2] === 'function'){ 323 | self.fn = args[2]; 324 | self.fn(); 325 | } 326 | }; 327 | elt.src = args[0]; 328 | if (args.length > 1 && typeof args[1] === 'string'){ 329 | elt.alt = args[1]; 330 | } 331 | elt.onload = function(){ 332 | setAttrs(); 333 | } 334 | self = addElement(elt, this); 335 | return self; 336 | }; 337 | 338 | /** 339 | * Creates an <a></a> element in the DOM for including a hyperlink. 340 | * Appends to the container node if one is specified, otherwise 341 | * appends to body. 342 | * 343 | * @method createA 344 | * @param {String} href url of page to link to 345 | * @param {String} html inner html of link element to display 346 | * @param {String} [target] target where new link should open, 347 | * could be _blank, _self, _parent, _top. 348 | * @return {Object/p5.Element} pointer to p5.Element holding created node 349 | * @example 350 | *
351 | * var myLink; 352 | * function setup() { 353 | * myLink = createA('http://p5js.org/', 'this is a link'); 354 | * } 355 | *
356 | */ 357 | p5.prototype.createA = function(href, html, target) { 358 | var elt = document.createElement('a'); 359 | elt.href = href; 360 | elt.innerHTML = html; 361 | if (target) elt.target = target; 362 | return addElement(elt, this); 363 | }; 364 | 365 | /** INPUT **/ 366 | 367 | 368 | /** 369 | * Creates a slider <input></input> element in the DOM. 370 | * Use .size() to set the display length of the slider. 371 | * Appends to the container node if one is specified, otherwise 372 | * appends to body. 373 | * 374 | * @method createSlider 375 | * @param {Number} min minimum value of the slider 376 | * @param {Number} max maximum value of the slider 377 | * @param {Number} [value] default value of the slider 378 | * @param {Number} [step] step size for each tick of the slider (if step is set to 0, the slider will move continuously from the minimum to the maximum value) 379 | * @return {Object/p5.Element} pointer to p5.Element holding created node 380 | * @example 381 | *
382 | * var slider; 383 | * function setup() { 384 | * slider = createSlider(0, 255, 100); 385 | * slider.position(10, 10); 386 | * slider.style('width', '80px'); 387 | * } 388 | * 389 | * function draw() { 390 | * var val = slider.value(); 391 | * background(val); 392 | * } 393 | *
394 | * 395 | *
396 | * var slider; 397 | * function setup() { 398 | * colorMode(HSB); 399 | * slider = createSlider(0, 360, 60, 40); 400 | * slider.position(10, 10); 401 | * slider.style('width', '80px'); 402 | * } 403 | * 404 | * function draw() { 405 | * var val = slider.value(); 406 | * background(val, 100, 100, 1); 407 | * } 408 | *
409 | */ 410 | p5.prototype.createSlider = function(min, max, value, step) { 411 | var elt = document.createElement('input'); 412 | elt.type = 'range'; 413 | elt.min = min; 414 | elt.max = max; 415 | if (step === 0) { 416 | elt.step = .000000000000000001; // smallest valid step 417 | } else if (step) { 418 | elt.step = step; 419 | } 420 | if (typeof(value) === "number") elt.value = value; 421 | return addElement(elt, this); 422 | }; 423 | 424 | /** 425 | * Creates a <button></button> element in the DOM. 426 | * Use .size() to set the display size of the button. 427 | * Use .mousePressed() to specify behavior on press. 428 | * Appends to the container node if one is specified, otherwise 429 | * appends to body. 430 | * 431 | * @method createButton 432 | * @param {String} label label displayed on the button 433 | * @param {String} [value] value of the button 434 | * @return {Object/p5.Element} pointer to p5.Element holding created node 435 | * @example 436 | *
437 | * var button; 438 | * function setup() { 439 | * createCanvas(100, 100); 440 | * background(0); 441 | * button = createButton('click me'); 442 | * button.position(19, 19); 443 | * button.mousePressed(changeBG); 444 | * } 445 | * 446 | * function changeBG() { 447 | * var val = random(255); 448 | * background(val); 449 | * } 450 | *
451 | */ 452 | p5.prototype.createButton = function(label, value) { 453 | var elt = document.createElement('button'); 454 | elt.innerHTML = label; 455 | elt.value = value; 456 | if (value) elt.value = value; 457 | return addElement(elt, this); 458 | }; 459 | 460 | /** 461 | * Creates a checkbox <input></input> element in the DOM. 462 | * Calling .checked() on a checkbox returns if it is checked or not 463 | * 464 | * @method createCheckbox 465 | * @param {String} [label] label displayed after checkbox 466 | * @param {boolean} [value] value of the checkbox; checked is true, unchecked is false.Unchecked if no value given 467 | * @return {Object/p5.Element} pointer to p5.Element holding created node 468 | * @example 469 | *
470 | * var checkbox; 471 | * 472 | * function setup() { 473 | * checkbox = createCheckbox('label', false); 474 | * checkbox.changed(myCheckedEvent); 475 | * } 476 | * 477 | * function myCheckedEvent() { 478 | * if (this.checked()) { 479 | * console.log("Checking!"); 480 | * } else { 481 | * console.log("Unchecking!"); 482 | * } 483 | * } 484 | *
485 | */ 486 | p5.prototype.createCheckbox = function() { 487 | var elt = document.createElement('div'); 488 | var checkbox = document.createElement('input'); 489 | checkbox.type = 'checkbox'; 490 | elt.appendChild(checkbox); 491 | //checkbox must be wrapped in p5.Element before label so that label appears after 492 | var self = addElement(elt, this); 493 | self.checked = function(){ 494 | var cb = self.elt.getElementsByTagName('input')[0]; 495 | if (cb) { 496 | if (arguments.length === 0){ 497 | return cb.checked; 498 | }else if(arguments[0]){ 499 | cb.checked = true; 500 | }else{ 501 | cb.checked = false; 502 | } 503 | } 504 | return self; 505 | }; 506 | this.value = function(val){ 507 | self.value = val; 508 | return this; 509 | }; 510 | if (arguments[0]){ 511 | var ran = Math.random().toString(36).slice(2); 512 | var label = document.createElement('label'); 513 | checkbox.setAttribute('id', ran); 514 | label.htmlFor = ran; 515 | self.value(arguments[0]); 516 | label.appendChild(document.createTextNode(arguments[0])); 517 | elt.appendChild(label); 518 | } 519 | if (arguments[1]){ 520 | checkbox.checked = true; 521 | } 522 | return self; 523 | }; 524 | 525 | /** 526 | * Creates a dropdown menu <select></select> element in the DOM. 527 | * @method createSelect 528 | * @param {boolean} [multiple] [true if dropdown should support multiple selections] 529 | * @return {Object/p5.Element} pointer to p5.Element holding created node 530 | * @example 531 | *
532 | * var sel; 533 | * 534 | * function setup() { 535 | * textAlign(CENTER); 536 | * background(200); 537 | * sel = createSelect(); 538 | * sel.position(10, 10); 539 | * sel.option('pear'); 540 | * sel.option('kiwi'); 541 | * sel.option('grape'); 542 | * sel.changed(mySelectEvent); 543 | * } 544 | * 545 | * function mySelectEvent() { 546 | * var item = sel.value(); 547 | * background(200); 548 | * text("it's a "+item+"!", 50, 50); 549 | * } 550 | *
551 | */ 552 | p5.prototype.createSelect = function(mult) { 553 | var elt = document.createElement('select'); 554 | if (mult){ 555 | elt.setAttribute('multiple', 'true'); 556 | } 557 | var self = addElement(elt, this); 558 | self.option = function(name, value){ 559 | var opt = document.createElement('option'); 560 | opt.innerHTML = name; 561 | if (arguments.length > 1) 562 | opt.value = value; 563 | else 564 | opt.value = name; 565 | elt.appendChild(opt); 566 | }; 567 | self.selected = function(value){ 568 | var arr = []; 569 | if (arguments.length > 0){ 570 | for (var i = 0; i < this.elt.length; i++){ 571 | if (value.toString() === this.elt[i].value){ 572 | this.elt.selectedIndex = i; 573 | } 574 | } 575 | return this; 576 | }else{ 577 | if (mult){ 578 | for (var i = 0; i < this.elt.selectedOptions.length; i++){ 579 | arr.push(this.elt.selectedOptions[i].value); 580 | } 581 | return arr; 582 | }else{ 583 | return this.elt.value; 584 | } 585 | } 586 | }; 587 | return self; 588 | }; 589 | 590 | /** 591 | * Creates a radio button <input></input> element in the DOM. 592 | * The .option() method can be used to set options for the radio after it is 593 | * created. The .value() method will return the currently selected option. 594 | * 595 | * @method createRadio 596 | * @param {String} [divId] the id and name of the created div and input field respectively 597 | * @return {Object/p5.Element} pointer to p5.Element holding created node 598 | * @example 599 | *
600 | * var radio; 601 | * 602 | * function setup() { 603 | * radio = createRadio(); 604 | * radio.option("black"); 605 | * radio.option("white"); 606 | * radio.option("gray"); 607 | * radio.style('width', '60px'); 608 | * textAlign(CENTER); 609 | * fill(255, 0, 0); 610 | * } 611 | * 612 | * function draw() { 613 | * var val = radio.value(); 614 | * background(val); 615 | * text(val, width/2, height/2); 616 | * } 617 | *
618 | *
619 | * var radio; 620 | * 621 | * function setup() { 622 | * radio = createRadio(); 623 | * radio.option('apple', 1); 624 | * radio.option('bread', 2); 625 | * radio.option('juice', 3); 626 | * radio.style('width', '60px'); 627 | * textAlign(CENTER); 628 | * } 629 | * 630 | * function draw() { 631 | * background(200); 632 | * var val = radio.value(); 633 | * if (val) { 634 | * text('item cost is $'+val, width/2, height/2); 635 | * } 636 | * } 637 | *
638 | */ 639 | p5.prototype.createRadio = function() { 640 | var radios = document.querySelectorAll("input[type=radio]"); 641 | var count = 0; 642 | if(radios.length > 1){ 643 | var length = radios.length; 644 | var prev=radios[0].name; 645 | var current = radios[1].name; 646 | count = 1; 647 | for(var i = 1; i < length; i++) { 648 | current = radios[i].name; 649 | if(prev != current){ 650 | count++; 651 | } 652 | prev = current; 653 | } 654 | } 655 | else if (radios.length == 1){ 656 | count = 1; 657 | } 658 | var elt = document.createElement('div'); 659 | var self = addElement(elt, this); 660 | var times = -1; 661 | self.option = function(name, value){ 662 | var opt = document.createElement('input'); 663 | opt.type = 'radio'; 664 | opt.innerHTML = name; 665 | if (arguments.length > 1) 666 | opt.value = value; 667 | else 668 | opt.value = name; 669 | opt.setAttribute('name',"defaultradio"+count); 670 | elt.appendChild(opt); 671 | if (name){ 672 | times++; 673 | var ran = Math.random().toString(36).slice(2); 674 | var label = document.createElement('label'); 675 | opt.setAttribute('id', "defaultradio"+count+"-"+times); 676 | label.htmlFor = "defaultradio"+count+"-"+times; 677 | label.appendChild(document.createTextNode(name)); 678 | elt.appendChild(label); 679 | } 680 | return opt; 681 | }; 682 | self.selected = function(){ 683 | var length = this.elt.childNodes.length; 684 | if(arguments.length == 1) { 685 | for (var i = 0; i < length; i+=2){ 686 | if(this.elt.childNodes[i].value == arguments[0]) 687 | this.elt.childNodes[i].checked = true; 688 | } 689 | return this; 690 | } else { 691 | for (var i = 0; i < length; i+=2){ 692 | if(this.elt.childNodes[i].checked == true) 693 | return this.elt.childNodes[i].value; 694 | } 695 | } 696 | }; 697 | self.value = function(){ 698 | var length = this.elt.childNodes.length; 699 | if(arguments.length == 1) { 700 | for (var i = 0; i < length; i+=2){ 701 | if(this.elt.childNodes[i].value == arguments[0]) 702 | this.elt.childNodes[i].checked = true; 703 | } 704 | return this; 705 | } else { 706 | for (var i = 0; i < length; i+=2){ 707 | if(this.elt.childNodes[i].checked == true) 708 | return this.elt.childNodes[i].value; 709 | } 710 | return ""; 711 | } 712 | }; 713 | return self 714 | }; 715 | 716 | /** 717 | * Creates an <input></input> element in the DOM for text input. 718 | * Use .size() to set the display length of the box. 719 | * Appends to the container node if one is specified, otherwise 720 | * appends to body. 721 | * 722 | * @method createInput 723 | * @param {Number} [value] default value of the input box 724 | * @return {Object/p5.Element} pointer to p5.Element holding created node 725 | * @example 726 | *
727 | * function setup(){ 728 | * var inp = createInput(''); 729 | * inp.input(myInputEvent); 730 | * } 731 | * 732 | * function myInputEvent(){ 733 | * console.log('you are typing: ', this.value()); 734 | * } 735 | * 736 | *
737 | */ 738 | p5.prototype.createInput = function(value) { 739 | var elt = document.createElement('input'); 740 | elt.type = 'text'; 741 | if (value) elt.value = value; 742 | return addElement(elt, this); 743 | }; 744 | 745 | /** 746 | * Creates an <input></input> element in the DOM of type 'file'. 747 | * This allows users to select local files for use in a sketch. 748 | * 749 | * @method createFileInput 750 | * @param {Function} [callback] callback function for when a file loaded 751 | * @param {String} [multiple] optional to allow multiple files selected 752 | * @return {Object/p5.Element} pointer to p5.Element holding created DOM element 753 | */ 754 | p5.prototype.createFileInput = function(callback, multiple) { 755 | 756 | // Is the file stuff supported? 757 | if (window.File && window.FileReader && window.FileList && window.Blob) { 758 | // Yup, we're ok and make an input file selector 759 | var elt = document.createElement('input'); 760 | elt.type = 'file'; 761 | 762 | // If we get a second argument that evaluates to true 763 | // then we are looking for multiple files 764 | if (multiple) { 765 | // Anything gets the job done 766 | elt.multiple = 'multiple'; 767 | } 768 | 769 | // Function to handle when a file is selected 770 | // We're simplifying life and assuming that we always 771 | // want to load every selected file 772 | function handleFileSelect(evt) { 773 | // These are the files 774 | var files = evt.target.files; 775 | // Load each one and trigger a callback 776 | for (var i = 0; i < files.length; i++) { 777 | var f = files[i]; 778 | var reader = new FileReader(); 779 | function makeLoader(theFile) { 780 | // Making a p5.File object 781 | var p5file = new p5.File(theFile); 782 | return function(e) { 783 | p5file.data = e.target.result; 784 | callback(p5file); 785 | }; 786 | }; 787 | reader.onload = makeLoader(f); 788 | 789 | // Text or data? 790 | // This should likely be improved 791 | if (f.type.indexOf('text') > -1) { 792 | reader.readAsText(f); 793 | } else { 794 | reader.readAsDataURL(f); 795 | } 796 | } 797 | } 798 | 799 | // Now let's handle when a file was selected 800 | elt.addEventListener('change', handleFileSelect, false); 801 | return addElement(elt, this); 802 | } else { 803 | console.log('The File APIs are not fully supported in this browser. Cannot create element.'); 804 | } 805 | }; 806 | 807 | 808 | /** VIDEO STUFF **/ 809 | 810 | function createMedia(pInst, type, src, callback) { 811 | var elt = document.createElement(type); 812 | 813 | // allow src to be empty 814 | var src = src || ''; 815 | if (typeof src === 'string') { 816 | src = [src]; 817 | } 818 | for (var i=0; i