├── .gitignore ├── 0.selfSign.cert.sh ├── 01.run.p5.serialserver.sh ├── 02.run.http-server.sh ├── 2.ssl.http-server.sh ├── LICENSE ├── README.md ├── ginscope.ino ├── html ├── favicon.ico ├── gin.js ├── p5 │ ├── p5.dom.js │ ├── p5.js │ ├── p5.serialport.js │ └── p5.sound.js └── scope.html ├── http-server.js ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | ssl 2 | node_modules 3 | -------------------------------------------------------------------------------- /0.selfSign.cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | #./ginscope/0.selfSign.cert.sh 3 | mkdir -p ssl 4 | openssl req -subj "/CN=neris.io/O=FUND ME!/C=GB/OU=ginscope" -new -newkey rsa:2048 -sha256 -days 3650 -nodes -x509 -keyout ssl/ginscope.key -out ssl/ginscope.crt 5 | -------------------------------------------------------------------------------- /01.run.p5.serialserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo 'starting serial server in insecure mode' 3 | echo 'todo: wss' 4 | cd node_modules/p5.serialserver && node startserver.js 5 | cd ../../ 6 | -------------------------------------------------------------------------------- /02.run.http-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo 'todo https' 3 | cd html && ../node_modules/http-server/bin/http-server 4 | cd ../ 5 | -------------------------------------------------------------------------------- /2.ssl.http-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd html && ../node_modules/http-server/bin/http-server \ 3 | -p 8080 \ 4 | -a 0.0.0.0 \ 5 | --ssl --key ../ssl/ginscope.key --cert ../ssl/ginscope.crt --cors 6 | cd ../ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © Gintaras Valatka 2 | FREE for personal use 3 | FREE for EDUCATION orgs around the world, except Lewisham and England educational institutions 4 | If you use it and ,make money, donate https://neris.io/ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ginscope - [Lepus Timidus] https://hackaday.io/project/19374-ginscope 2 | ## Html5 Sci Toolbox, containing variuos instruments* 3 | There are quite a few options out there, speaking various/proprietary protocols etc. 4 | We are long past the point of being able to watch videos, we can run things inside web browsers too :D 5 | 6 | Nowadays anyone can make Soundcard oscilloscope - just get some probes (or make your own) and bingo! - although it will never replace pro equipment, but if you are getting more advanced and in need of extra tools or IO and spare Arduino is at hand with some spare parts - you can do even more great stuff! 7 | 8 | #deepTech - is the new black, let's liberate some tools :D 9 | 10 | There are many Arduino based scopes, some with software for Windows some for macOS some for Linux but sort of never 'a complete package'. Also many electronics enthusiasts are being created around the world every day, sadly not all can afford Arduino/parts, let alone oscilloscopes.. 11 | 12 | Long story short: I need one and I needed one long ago, but I still can not afford it [...] 13 | 14 | ...As the saying goes..., make it! 15 | 16 | The aim is to make the best with the least parts possible, hopefully progressing to more advanced acquisition systems. 17 | 18 | External components required. eg: to analyse analog signal with Arduino one would need dual power supply or other clever tricks.* ² 19 | 20 | ``` 21 | Can this be 'match made in heaven' ? 'Data Acquisition' : system 22 | 23 | Arduino --> Serial --> Bluetooth/USB --> \ | /--> VGA/DESKTOP/TABLET 24 | Arduino --> SPI -----> Raspberry Pi --> | -> HDD 25 | FPGA --> SPI -----> Raspberry Pi ---> / | \--> HTTPS/WSS 26 | ``` 27 | 28 | ##### Currently: 29 | * Arduino/Soundcard Oscilloscope 30 | * Soundcard Function generator 31 | * Real time plotting of analog & digital signals in HTML from Serial data 32 | 33 | ##### Nice: / to have / to do / to come /: 34 | * schematics (pcb, kit, scope, tools) 35 | * taxometer 36 | * fft 37 | * x/y plotting - i/v tracing 38 | * logic decoding 39 | * db recording 40 | * more advanced functions 41 | * calc bandwidth, freq, count, etc 42 | * UI, Serial port and Soundcard dropdown selections(IO control) 43 | * tablet/phone app via tcp, - serial streaming via bluetooth easy 44 | * voltmeter 45 | * ampmeter etc 46 | ###### Easy adding extra hardware via i2c like like lux/rgb sensing* 47 | ###### Also possible to increase i2c speed to 400 kHz [http://forum.arduino.cc/index.php?topic=16793.0] 48 | 49 | (click to play the video) 50 | 51 | [![working prototype](https://img.youtube.com/vi/jmDLRDejVgY/0.jpg)](https://youtu.be/jmDLRDejVgY) 52 | 53 | Working prototype based on https://p5js.org/ 54 | 55 | For this example, NXP PCF8591 adc converter was used. Arduino analog pins can be used too. As you can see from the sine wave, only 'half job done' - or nearly all 56 | The advantage of using I2C, is easier to adapt sketches across Arduino world. 57 | 58 | NXP PCF8591 spec: http://www.nxp.com/documents/data_sheet/PCF8591.pdf 59 | 60 | A very nice write-up http://tronixstuff.com/2013/06/17/tutorial-arduino-and-pcf8591-adc-dac-ic/ 61 | 62 | **_Hint:_ ** make use of PCF8591 dac 63 | 64 | **_Idea:_** I trust it is possible on Arduino to sample full analog signal @24bit resolution.., by using an op-amp and 2 analog inputs to obtain one anlalog channel².. Invert the phase by 180° and then capture the negative portion of the signal as if it was positive.., then flip it back before using it ;) 65 | This would give 12bit + 12bit = 24bit resolution, (also possible 8x2=16bit, which makes working with it easier/faster) by merging/alternating aquired buffers* (have not come accros this method on making scopes tutorials :D) 66 | 67 | 68 | ##### Current requirenments depending on needs: 69 | * Moderm web browser 70 | * Soundcard - for function generator, also as analog input, if available 71 | * node.js p5.serialserver - for Serial real time Arduino communications if Arduino is used 72 | * http-server for https, extra/advanced/future functionality 73 | 74 | ##### Running: 75 | * install the required software 76 | * connect PCF8591 and upload the sketch to Arduino or 77 | * (comment out PCF8591 and edit the first 4 Serial.print(0) lines by replacing a0-a3 with 0) 78 | * edit the serial port in gin.js (dropdown selection planned for later) 79 | * run p5serial and open scope.html or start http-server if needed 80 | 81 | ##### Operation: 82 | Reading the values and printing them to serial, no buffering, all real time, then decoding it on the client and displaying it in real time 83 | 84 | Encoding bits and sending them via serial as a characters... then serving it via ws/wss to web browser/aqusition systems(could do binary I suppose?), then decoding them on the client side(in js). The first 4 characters are 8 bit analog values from PCF8591 and the last one is d0-d7 pins of Arduino - Decoding single ASCII character to it's DEC representation - gives analog values.. 0-255, if digital, then DEC to BIN gives a string with on and off values for each of the d0-d7 pins. 85 | 86 | ```javascript 87 | // Might not be the best/fastest solution, 88 | // but for now it works and looks good :D 89 | // We are expecting 5 byte string containing encoded values for: 90 | // analog a0, a1, a2, a3 and digital d0-d7 91 | // terminated by \r\n, which could be further reduced to \n 92 | 93 | var data = 'gins!'; 94 | 95 | // Decoding routine: 96 | // (wonder if anyone use this technique? is there a name for it? 97 | // I thought of it to reduce the payload size = 98 | // faster and more samples transfered) 99 | 100 | // "extracting" data from string 101 | var a0 = data[0]; // a0 = g 102 | var a1 = data[1]; // a1 = i 103 | var a2 = data[2]; // a2 = n 104 | var a3 = data[3]; // a3 = s 105 | var digital = data[4]; // ! 106 | 107 | // then ASCII -> DEC 108 | a0 = a0.charCodeAt(); // g = 103 - Analog 1 value 109 | a1 = a1.charCodeAt(); // i = 105 - Analog 2 value 110 | a2 = a2.charCodeAt(); // n = 110 - Analog 3 value 111 | a3 = a3.charCodeAt(); // s = 115 - Analog 4 value 112 | digital = digital.charCodeAt(); // ! = 33 - all 8 digital pins 113 | 114 | // then DEC -> BIN 115 | digital = digital.toString(2); // = '00100001' 116 | 117 | // state of digital pins 118 | d0 = digital[0]; // - Digital pin 0 (Serial Rx) - unusable 119 | d1 = digital[1]; // - Digital pin 1 (Serial Tx) - unusable 120 | d2 = digital[2]; // - Digital pin 2 121 | d3 = digital[3]; // - Digital pin 3 122 | d4 = digital[4]; // - Digital pin 4 123 | d5 = digital[5]; // - Digital pin 5 124 | d6 = digital[6]; // - Digital pin 6 125 | d7 = digital[7]; // - Digital pin 7 126 | 127 | ``` 128 | 129 | ###### Just some of the resources and other projects/scopes, in no particular order: 130 | https://sigrok.org/ 131 | 132 | http://playground.arduino.cc/Main/LogicAnalyzer 133 | 134 | https://duckduckgo.com/?q=arduino+logic+analyzer&ia=software 135 | 136 | http://elinux.org/Logic_Analyzers 137 | 138 | http://www.practicalarduino.com/projects/scope-logic-analyzer 139 | 140 | http://www.instructables.com/id/Girino-Fast-Arduino-Oscilloscope/?ALLSTEPS 141 | 142 | http://www.instructables.com/id/DIY-Oscilloscopes/ 143 | 144 | Probe http://www.instructables.com/id/DIY-Oscilloscopes/ 145 | 146 | (...the list to be expanded) 147 | Looking forward to open source hardware revolution https://www.sifive.com/ 148 | Wishing for reference equipment and more, - 149 | 150 | Gin-Neris 151 | -------------------------------------------------------------------------------- /ginscope.ino: -------------------------------------------------------------------------------- 1 | //#include ; 2 | #include "Wire.h" 3 | #define PCF8591 (0x90 >> 1) // I2C bus address 4 | char a, a0, a1, a2, a3; 5 | 6 | /* https://www.arduino.cc/en/Reference/PortManipulation 7 | // B (digital pin 8 to 13) 8 | // C (analog input pins) 9 | // D (digital pins 0 to 7) 10 | 11 | PORTD maps to Arduino digital pins 0 to 7 12 | 13 | DDRD - The Port D Data Direction Register - read/write 14 | PORTD - The Port D Data Register - read/write 15 | PIND - The Port D Input Pins Register - read only 16 | -- 17 | PORTB maps to Arduino digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable 18 | 19 | DDRB - The Port B Data Direction Register - read/write 20 | PORTB - The Port B Data Register - read/write 21 | PINB - The Port B Input Pins Register - read only 22 | -- 23 | PORTC maps to Arduino analog pins 0 to 5. Pins 6 & 7 are only accessible on the Arduino Mini 24 | 25 | DDRC - The Port C Data Direction Register - read/write 26 | PORTC - The Port C Data Register - read/write 27 | PINC - The Port C Input Pins Register - read only 28 | */ 29 | 30 | // Create a variable to store the data read from PORT 31 | //char b = 0; 32 | //char c = 0; 33 | //byte d = 0; 34 | char digital; 35 | 36 | // delay(ms) 37 | int ms = 20; // 20 = 1000 / 50 fps 38 | 39 | // delayMicroseconds(16383) 40 | // Pauses the program for the amount of time (in microseconds) specified as parameter. 41 | // There are a thousand microseconds in a millisecond, and a million microseconds in a second. 42 | // Currently, the largest value that will produce an accurate delay is 16383. 43 | // This could change in future Arduino releases. For delays longer than a few thousand microseconds, you should use delay() instead. 44 | int us = 10000; // max 16383; 45 | 46 | char payload[5] = ""; 47 | 48 | void setup() { 49 | 50 | Wire.begin(); 51 | 52 | // Since we’re transferring at 9600 bps, the time spent holding each of those bits high or low is 1/(9600 bps) or 104 µs per bit. 53 | // For every byte of data transmitted, there are actually 10 bits being sent: a start bit, 8 data bits, and a stop bit. 54 | // So, at 9600 bps, we’re actually sending 9600 bits per second or 960 (9600/10) bytes per second. https://learn.sparkfun.com/tutorials/serial-communication/all 55 | 56 | //Serial.begin(9600); // 960 bytes per sec 57 | //Serial.println("--==Started Serial==-- @9600"); 58 | 59 | //Serial.begin(57600); // 5760 bytes per sec 60 | //Serial.println("--==Started Serial==-- @57600"); 61 | 62 | Serial.begin(115200);// 11520 bytes 63 | Serial.println("--==Started Serial==-- @115200"); 64 | 65 | // MAYBE REDUCE DEFAULT SERIAL TIMEOUT? 66 | 67 | while (!Serial) { 68 | ; // wait for serial port to connect. Needed for native USB port only 69 | } 70 | 71 | // Arduino (Atmega) pins default to inputs, so they don't need to be explicitly declared as inputs 72 | //DDRB = 0b00000000; //All pins in PORTB are input // "The two high bits (6 & 7) map to the crystal pins and are not usable" 73 | //DDRC = 0b00000000; //All pins in PORTC are input 74 | //DDRD = 0b00000000; //All pins in PORTD are input // "0 & 1 - serial - poss are not usable?" 75 | 76 | 77 | //Serial.println("format: PORTD bit in 'dec',a0,a1,a2,a3,this message is 6th in "); 78 | Serial.println("Connected via USB"); 79 | Serial.println("PORTD,PCF8591 TO SERIAL"); 80 | 81 | //Serial.print("ms "); 82 | // Serial.print(ms); 83 | Serial.print("delay us: "); 84 | Serial.println(us); 85 | 86 | Serial.println("Begin"); 87 | } 88 | 89 | void loop() { 90 | 91 | if (Serial.available() > 0) { 92 | // read the incoming byte: 93 | char mode = Serial.read(); 94 | int _delay = Serial.parseInt(); 95 | 96 | switch (mode) { 97 | case 'm': 98 | ms = _delay; 99 | delay(_delay); 100 | break; 101 | case 'u': 102 | us = _delay; 103 | delayMicroseconds(_delay); 104 | Serial.println("Setting delay:" + _delay); 105 | break; 106 | } 107 | } 108 | 109 | digital = PIND; 110 | 111 | Wire.beginTransmission(PCF8591); // wake up PCF8591 112 | Wire.write(0x04); // control byte - read ADC0 then auto-increment 113 | Wire.endTransmission(); // end tranmission 114 | Wire.requestFrom(PCF8591, 5); 115 | a = Wire.read(); 116 | a0 = Wire.read(); 117 | a1 = Wire.read(); 118 | a2 = Wire.read(); 119 | a3 = Wire.read(); 120 | 121 | Serial.print(a0); 122 | Serial.print(a1); 123 | Serial.print(a2); 124 | Serial.print(a3); 125 | Serial.println(digital); 126 | 127 | //delay(ms); 128 | delayMicroseconds(us); 129 | 130 | } 131 | -------------------------------------------------------------------------------- /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soholt/GinScope/368417e29de59f5fce21b7193f84eff81d482cbf/html/favicon.ico -------------------------------------------------------------------------------- /html/gin.js: -------------------------------------------------------------------------------- 1 | // Declare a "SerialPort" object 2 | var Serial; // variable to hold an instance of the Serialport library 3 | var portName = '/dev/ttyACM0'; // fill in your Serial port name here 4 | var inData; // for incoming Serial data 5 | var inByte; 6 | var byteCount = 0; 7 | var output = 0; 8 | var options = { 9 | //baudrate: 9600 10 | baudrate: 115200 11 | //baudrate: 230400 12 | //baudrate: 250000 13 | }; 14 | 15 | var delayMicroSec = 10000;//16383; // MAX 16 | //var defaultDelay = 20; 17 | var payload = 'waiting for data'; // payload 18 | 19 | var dataPoints = 1024 - 1; // less 1 'cos of array 20 | 21 | var data = []; 22 | 23 | var a0 = 0; 24 | var a1 = 0; 25 | var a2 = 0; 26 | var a3 = 0; 27 | 28 | var _a0 = []; 29 | var _a1 = []; 30 | var _a2 = []; 31 | var _a3 = []; 32 | 33 | var digital = '00000000'; 34 | var d0,d1,d2,d3,d4,d5,d6,d7; 35 | var _d0 = []; 36 | var _d1 = []; 37 | var _d2 = []; 38 | var _d3 = []; 39 | var _d4 = []; 40 | var _d5 = []; 41 | var _d6 = []; 42 | var _d7 = []; 43 | 44 | 45 | var fps = 50; 46 | var w, h, ch, heightOffset; // window width, height, canvas height & height offset 47 | var padLeft = 0; 48 | var padRight = 10; 49 | 50 | var fft; 51 | var amp = 50; // var amp = 500; 52 | var freq = 1000; 53 | var sin = false; 54 | var tri = false; 55 | var sqr = false; 56 | var saw = false; 57 | var sinAmp = 500; 58 | var triAmp = 500; 59 | var sqrAmp = 500; 60 | var sawAmp = 500; 61 | var sinFreq = 1000; 62 | var triFreq = 1000; 63 | var sqrFreq = 1000; 64 | var sawFreq = 1000; 65 | var sinOn = false; 66 | var triOn = false; 67 | var sqrOn = false; 68 | var sawOn = false; 69 | 70 | var Noise = { 71 | white: new p5.Noise(), 72 | pink: new p5.Noise('pink'), 73 | brow: new p5.Noise('brown') 74 | }; 75 | 76 | var debug = false; 77 | 78 | function setup() { 79 | 80 | // Instantiate our SerialPort object 81 | Serial = new p5.SerialPort(); 82 | 83 | // Get a list the ports available 84 | // You should have a callback defined to see the results 85 | //Serial.list(); 86 | 87 | // Assuming our Arduino is connected, let's open the connection to it 88 | // Change this to the name of your arduino's Serial port 89 | //Serial.open("/dev/ttyUSB0"); 90 | Serial.open(portName, options); 91 | 92 | // Here are the callbacks that you can register 93 | 94 | // When we connect to the underlying server 95 | Serial.on('connected', serverConnected); 96 | 97 | // When we get a list of Serial ports that are available 98 | //Serial.on('list', gotList); 99 | // OR 100 | //Serial.onList(gotList); 101 | 102 | // When we some data from the Serial port 103 | Serial.on('data', gotData); 104 | // OR 105 | //Serial.onData(gotData); 106 | 107 | // When or if we get an error 108 | Serial.on('error', gotError); 109 | // OR 110 | //Serial.onError(gotError); 111 | 112 | // When our Serial port is opened and ready for read/write 113 | Serial.on('open', gotOpen); 114 | // OR 115 | //Serial.onOpen(gotOpen); 116 | 117 | // Callback to get the raw data, as it comes in for handling yourself 118 | //Serial.on('rawdata', gotRawData); 119 | // OR 120 | //Serial.onRawData(gotRawData); 121 | 122 | // -------------------------------------------------------------------------------------------------------------- 123 | 124 | w = windowWidth - padRight; 125 | h = windowHeight; 126 | canvasHeight = 1200; 127 | createCanvas(w, canvasHeight); 128 | //var canvas = p.createCanvas(850, 650); 129 | frameRate(fps); 130 | 131 | delayMicroSec = 10000; 132 | setDelay(); 133 | 134 | //create buffer multiple of 16? 135 | createBuffer(); 136 | 137 | //pg = createGraphics(400, 250); 138 | 139 | sin = new p5.SinOsc(freq); 140 | tri = new p5.TriOsc(freq); 141 | sqr = new p5.SqrOsc(freq); 142 | saw = new p5.SawOsc(freq); 143 | //Noise.Brown = new p5.Noise(brown); 144 | //Noise.Pink = new p5.Noise(pink); 145 | //Noise.White = new p5.Noise(white); 146 | 147 | x = 480; 148 | y = 575; 149 | freqBtn = createButton('Sin'); 150 | freqBtn.position(x + 0, y); 151 | freqBtn.mousePressed(sinState); 152 | 153 | freqBtn = createButton('Tri'); 154 | freqBtn.position(x + 50, y); 155 | freqBtn.mousePressed(triState); 156 | 157 | freqBtn = createButton('Sqr'); 158 | freqBtn.position(x + 100, y); 159 | freqBtn.mousePressed(sqrState); 160 | 161 | freqBtn = createButton('Saw'); 162 | freqBtn.position(x + 150, y); 163 | freqBtn.mousePressed(sawState); 164 | 165 | freqBtn = createButton('White'); 166 | freqBtn.position(x + 210, y); 167 | freqBtn.mousePressed(sawState); 168 | 169 | freqBtn = createButton('Pink'); 170 | freqBtn.position(x + 300, y); 171 | freqBtn.mousePressed(sawState); 172 | 173 | freqBtn = createButton('Brown'); 174 | freqBtn.position(x + 390, y); 175 | freqBtn.mousePressed(sawState); 176 | 177 | x = 480; 178 | y += 50; 179 | freq_xxxx = createButton('xxxx').position(x, y);x += 50; 180 | freq_xxx = createButton('xxx').position(x, y);x += 40; 181 | freq_xx = createButton('xx').position(x, y);x += 32; 182 | freq_x = createButton('x').position(x, y);x += 26; 183 | freq_d = createButton('-').position(x, y);x += 22; 184 | freqDisplay = createInput(freq).position(x, y).size(50);x += 52; 185 | freq_i = createButton('+').position(x, y);x += 22; 186 | freq_y = createButton('x').position(x, y);x += 26; 187 | freq_yy = createButton('xx').position(x, y);x += 32; 188 | freq_yyy = createButton('xxx').position(x, y);x += 40; 189 | freq_yyyy = createButton('xxxx').position(x, y);x += 50; 190 | 191 | freq_xxxx.mousePressed(function(){if(freq > 10000){freq -= 10000;frequency(),freqDisplay.value(freq)}}); 192 | freq_xxx.mousePressed(function(){if(freq > 1000){freq -= 1000;frequency(),freqDisplay.value(freq)}}); 193 | freq_xx.mousePressed(function(){if(freq > 100){freq -= 100;frequency(),freqDisplay.value(freq)}}); 194 | freq_x.mousePressed(function(){if(freq > 10){freq -= 10;frequency(),freqDisplay.value(freq)}}); 195 | freq_d.mousePressed(function(){if(freq > 1){freq -= 1;frequency(),freqDisplay.value(freq)}}); 196 | freq_i.mousePressed(function(){freq += 1;frequency(),freqDisplay.value(freq)}); 197 | freq_y.mousePressed(function(){freq += 10;frequency(),freqDisplay.value(freq)}); 198 | freq_yy.mousePressed(function(){freq += 100;frequency(),freqDisplay.value(freq)}); 199 | freq_yyy.mousePressed(function(){freq += 1000;frequency(),freqDisplay.value(freq)}); 200 | freq_yyyy.mousePressed(function(){freq += 10000;frequency(),freqDisplay.value(freq)}); 201 | 202 | 203 | 204 | x = 480; 205 | y += 50; 206 | delay_xxx = createButton('xxx').position(x, y);x += 40; 207 | delay_xx = createButton('xx').position(x, y);x += 32; 208 | delay_x = createButton('x').position(x, y);x += 26; 209 | delay_d = createButton('-').position(x, y);x += 22; 210 | delayDisplay = createInput(delayMicroSec).position(x, y).size(50);x += 52; 211 | delay_i = createButton('+').position(x, y);x += 22; 212 | delay_y = createButton('x').position(x, y);x += 26; 213 | delay_yy = createButton('xx').position(x, y);x += 32; 214 | delay_yyy = createButton('xxx').position(x, y);x += 40; 215 | 216 | delay_xxx.mousePressed(function(){if(delayMicroSec > 1000){delayMicroSec -= 1000;setDelay();delayDisplay.value(delayMicroSec)}}); 217 | delay_xx.mousePressed(function(){if(delayMicroSec > 100){delayMicroSec -= 100;setDelay();delayDisplay.value(delayMicroSec)}}); 218 | delay_x.mousePressed(function(){if(delayMicroSec > 10){delayMicroSec -= 10;setDelay();delayDisplay.value(delayMicroSec)}}); 219 | delay_d.mousePressed(function(){if(delayMicroSec > 1){delayMicroSec -= 1;setDelay();delayDisplay.value(delayMicroSec)}}); 220 | delay_i.mousePressed(function(){delayMicroSec += 1;setDelay();delayDisplay.value(delayMicroSec)}); 221 | delay_y.mousePressed(function(){delayMicroSec += 10;setDelay();delayDisplay.value(delayMicroSec)}); 222 | delay_yy.mousePressed(function(){delayMicroSec += 100;setDelay();delayDisplay.value(delayMicroSec)}); 223 | delay_yyy.mousePressed(function(){delayMicroSec += 1000;setDelay();delayDisplay.value(delayMicroSec)}); 224 | 225 | 226 | 227 | x = 480; 228 | y += 50; 229 | //fps_xx = createButton('xx').position(x, y);x += 32; 230 | fps_x = createButton('x').position(x, y);x += 26; 231 | fps_d = createButton('-').position(x, y);x += 22; 232 | fpsDisplay = createInput(fps).position(x, y).size(50);x += 52; 233 | fps_i = createButton('+').position(x, y);x += 22; 234 | fps_y = createButton('x').position(x, y);x += 26; 235 | //fps_yy = createButton('xx').position(x, y);x += 32; 236 | 237 | //fps_xx.mousePressed(function(){fps -= 100;frameRate(fps)}); 238 | fps_x.mousePressed(function(){if(fps > 10){fps -= 10;frameRate(fps);fpsDisplay.value(fps)}}); 239 | fps_d.mousePressed(function(){if(fps > 2){fps -= 1;frameRate(fps);fpsDisplay.value(fps)}}); 240 | fps_i.mousePressed(function(){fps += 1;frameRate(fps);fpsDisplay.value(fps)}); 241 | fps_y.mousePressed(function(){fps += 10;frameRate(fps);fpsDisplay.value(fps)}); 242 | //fps_yy.mousePressed(function(){fps += 100;frameRate(fps)}); 243 | 244 | 245 | x = 480; 246 | y += 50; 247 | 248 | amp_x = createButton('x').position(x, y);x += 26; 249 | amp_d = createButton('-').position(x, y);x += 22; 250 | ampDisplay = createInput(amp).position(x, y).size(50);x += 52; 251 | amp_i = createButton('+').position(x, y);x += 22; 252 | amp_y = createButton('x').position(x, y);x += 26; 253 | 254 | amp_x.mousePressed(function(){if(amp > 10){amp -= 10;amplitude();ampDisplay.value(amp)}}); 255 | amp_d.mousePressed(function(){if(amp > 0){amp -= 1;amplitude();ampDisplay.value(amp)}}); 256 | amp_i.mousePressed(function(){if((amp + 1) < 100){amp += 1;amplitude();ampDisplay.value(amp)}}); 257 | amp_y.mousePressed(function(){if((amp + 10) < 100){amp += 10;amplitude();ampDisplay.value(amp)}}); 258 | 259 | 260 | 261 | //freq_y.mousePressed(freq *= 10); 262 | //freq_yy.mousePressed(freq *= 100); 263 | //freq_yyy.mousePressed(freq *= 1000); 264 | 265 | 266 | //mic = new p5.AudioIn(); 267 | //mic.start(); 268 | //fft = new p5.FFT(); 269 | //fft.setInput(mic); 270 | /* 271 | ampSlider = createSlider(0, 1000, amp); 272 | ampSlider.position(10, h - 40); 273 | ampSlider.style('width', w - 40 + 'px'); 274 | 275 | ampBtn = createButton('Amplitude'); 276 | ampBtn.position(10, 1640); 277 | ampBtn.mousePressed(amplitude); 278 | 279 | freqSlider = createSlider(0, 1000, freq); 280 | freqSlider.position(10, h - 20); 281 | freqSlider.style('width', w - 40 + 'px'); 282 | 283 | freqBtn = createButton('Frequency'); 284 | freqBtn.position(10, 1720); 285 | freqBtn.mousePressed(frequency); 286 | 287 | 288 | 289 | 290 | // Delay slider in mils 291 | delaySlider = createSlider(0, 16383, delayMicroSec); 292 | delaySlider.position(10, 10); 293 | delaySlider.style('width', w - 40 + 'px'); 294 | 295 | delayBtn = createButton('Set Delay'); 296 | delayBtn.position(10, 35); 297 | delayBtn.mousePressed(setDelay); 298 | */ 299 | } 300 | 301 | // We are connected and ready to go 302 | function serverConnected() { 303 | println("Connected to Server"); 304 | 305 | //delay(1000); 306 | //delayMicroSec = 10000; 307 | //setDelay(); 308 | 309 | } 310 | 311 | // Got the list of ports 312 | function gotList(thelist) { 313 | println("List of Serial Ports:"); 314 | // theList is an array of their names 315 | for (var i = 0; i < thelist.length; i++) { 316 | // Display in the console 317 | println(i + " " + thelist[i]); 318 | } 319 | } 320 | 321 | // Connected to our Serial device 322 | function gotOpen() { 323 | println("Serial Port is Open"); 324 | //delay(500); 325 | //Serial.write('m555'); 326 | } 327 | 328 | // Ut oh, here is an error, let's log it 329 | function gotError(theerror) { 330 | println(theerror); 331 | } 332 | 333 | function serverConnected() { 334 | println("Serial Connected"); 335 | } 336 | 337 | // There is data available to work with from the Serial port 338 | function gotData() { 339 | 340 | var incoming = Serial.readLine().trim(); // read the incoming string and trim it 341 | 342 | //trim(incoming); 343 | // remove any trailing whitespace 344 | //gotRawData(incoming); 345 | //console.log('incoming',incoming); 346 | 347 | if (!incoming) return; // if the string is empty, do no more 348 | 349 | //__data = incoming.trim(); 350 | 351 | 352 | if(incoming.length === 5) { 353 | 354 | //console.log(incoming.length, incoming); 355 | //a0 = data[0]; 356 | //a1 = data[1]; 357 | //a2 = data[2]; 358 | //a3 = data[3]; 359 | //console.log('data',_a); 360 | 361 | //payload = incoming; 362 | 363 | //data = payload; 364 | //if(data.length === 5) { // Draw 365 | 366 | //console.log('data', data); 367 | 368 | a0 = incoming[0]; 369 | a1 = incoming[1]; 370 | a2 = incoming[2]; 371 | a3 = incoming[3]; 372 | 373 | a0 = a0.charCodeAt() || 0; 374 | a1 = a1.charCodeAt() || 0; 375 | a2 = a2.charCodeAt() || 0; 376 | a3 = a3.charCodeAt() || 0; 377 | 378 | digital = incoming[4]; 379 | digital = digital.charCodeAt() || '0'; 380 | digital = Number(digital); 381 | digital = digital.toString(2); 382 | //console.log(digital); 383 | 384 | d0 = Number(digital[0]) || 0; 385 | d1 = Number(digital[1]) || 0; 386 | d2 = Number(digital[2]) || 0; 387 | d3 = Number(digital[3]) || 0; 388 | d4 = Number(digital[4]) || 0; 389 | d5 = Number(digital[5]) || 0; 390 | d6 = Number(digital[6]) || 0; 391 | d7 = Number(digital[7]) || 0; 392 | 393 | 394 | //data.push({a0:a0,a1:a1,a2:a2,a3:a3}); 395 | data.push(); 396 | _a0.push(a0); 397 | _a1.push(a1); 398 | _a2.push(a2); 399 | _a3.push(a3); 400 | _d1.push(d1); 401 | _d2.push(d2); 402 | _d3.push(d3); 403 | _d4.push(d4); 404 | _d5.push(d5); 405 | _d6.push(d6); 406 | _d7.push(d7); 407 | 408 | 409 | //console.log('_a0.length', _a0.length) 410 | //var i = data.length; 411 | var i = _a0.length; 412 | if(dataPoints < i) { 413 | data.shift(); 414 | _a0.shift(); 415 | _a1.shift(); 416 | _a2.shift(); 417 | _a3.shift(); 418 | _d1.shift(); 419 | _d2.shift(); 420 | _d3.shift(); 421 | _d4.shift(); 422 | _d5.shift(); 423 | _d6.shift(); 424 | _d7.shift(); 425 | } 426 | 427 | if (debug) console.log(data); 428 | 429 | //console.log(data.length); 430 | 431 | 432 | 433 | 434 | } else { 435 | // to see info msgs from arduino 436 | if(incoming.length > 5) { 437 | //text(incoming, 25, 15); 438 | console.info(incoming); 439 | } else { 440 | // TO SEE DROPPED PACKETS AND MSGS 441 | if(debug) console.error(incoming); 442 | } 443 | 444 | } 445 | 446 | //console.log('data.length', data.length); 447 | 448 | } 449 | 450 | // We got raw from the Serial port 451 | function gotRawData(payload) { 452 | println("gotRawData: " + payload); 453 | 454 | } 455 | 456 | 457 | function setDelay() { 458 | //var _delay = ms.value(); 459 | console.info('attempt to set delay', delayMicroSec); 460 | Serial.write('u' + delayMicroSec); 461 | } 462 | 463 | // Methods available 464 | // Serial.read() returns a single byte of data (first in the buffer) 465 | // Serial.readChar() returns a single char 'A', 'a' 466 | // Serial.readBytes() returns all of the data available as an array of bytes 467 | // Serial.readBytesUntil('\n') returns all of the data available until a '\n' (line break) is encountered 468 | // Serial.readString() retunrs all of the data available as a string 469 | // Serial.readStringUntil('\n') returns all of the data available as a string until a specific string is encountered 470 | // Serial.readLine() calls readStringUntil with "\r\n" typical linebreak carriage return combination 471 | // Serial.last() returns the last byte of data from the buffer 472 | // Serial.lastChar() returns the last byte of data from the buffer as a char 473 | // Serial.clear() clears the underlying Serial buffer 474 | // Serial.available() returns the number of bytes available in the buffer 475 | // Serial.write(somevar) writes out the value of somevar to the Serial device 476 | /* 477 | function draw() { 478 | noStroke(); 479 | background(220, 180, 200); 480 | fill(180, 200, 40); 481 | strokeWeight(1); 482 | stroke(180, 100, 240); 483 | for (var i = 0; i < width; i += 2) { 484 | line(i, 300 - a0, i, 300); 485 | line(i, a1, i, 0); 486 | } 487 | }//dataPoints; data.length 488 | */ 489 | var _size = 10; 490 | 491 | function draw() { 492 | background(255); 493 | //background(39,43,48); // #272b30 494 | noStroke(); 495 | fill(33); 496 | 497 | heightOffset = 15; 498 | text('x ' + mouseX, w - 30, heightOffset); 499 | 500 | 501 | 502 | heightOffset += 15; 503 | text('y ', mouseY, w - 30, heightOffset); 504 | 505 | //heightOffset += 15; 506 | 507 | 508 | //stroke('#fae'); 509 | //strokeWeight(.5); 510 | stroke(200); 511 | line(0, mouseY, windowWidth, mouseY); 512 | line(mouseX, 0, mouseX, windowHeight); 513 | 514 | stroke(0,0,150); 515 | line(0, heightOffset, w, heightOffset); 516 | 517 | heightOffset += 256; 518 | 519 | //stroke(0,0,150); 520 | stroke(200); 521 | line(0, heightOffset - 128, w, heightOffset - 128); 522 | stroke(0,0,150); 523 | line(0, heightOffset, w, heightOffset); 524 | //stroke(200);//stroke(0,0,150); 525 | //line(0, heightOffset + 128, w, heightOffset + 128); 526 | //noStroke(); 527 | stroke(0); 528 | 529 | // strokeWeight(1); 530 | noFill(); 531 | beginShape(); 532 | 533 | //for (var i = 0; i < waveform.length; i++){ 534 | // var x = map(i, 0, waveform.length, 0, width); 535 | // var y = map(waveform[i], -1, 1, height, 0); 536 | 537 | //for (var i = 0; i < data.length; i++){ 538 | //var x = map(i, 0, data.length, 0, width) - 20; 539 | for (var i = 0; i < _a0.length; i++){ 540 | var x = map(i, 0, _a0.length, 0, width) - 20; 541 | var y = heightOffset - _a0[i]; 542 | vertex(x, y); 543 | } 544 | endShape(); 545 | fill(a0, 255 - a0, 0); 546 | ellipse(w - 20, heightOffset - a0, _size, _size); 547 | noFill(); 548 | 549 | 550 | 551 | heightOffset += 260; 552 | 553 | //stroke(200); 554 | //line(0, heightOffset, w, heightOffset); 555 | 556 | //heightOffset = heightOffset + 260; 557 | 558 | 559 | stroke(150,0,0); 560 | line(0, heightOffset - 256, w, heightOffset - 256); 561 | stroke(200); 562 | line(0, heightOffset - 128, w, heightOffset - 128); 563 | stroke(150,0,0); 564 | line(0, heightOffset, w, heightOffset); 565 | //stroke(200); 566 | //line(0, heightOffset + 128, w, heightOffset + 128); 567 | stroke(0);//noStroke(); 568 | 569 | beginShape(); 570 | //strokeWeight(1); 571 | //if(debug) console.log(waveform); 572 | 573 | //for (var i = 0; i < waveform.length; i++){ 574 | // var x = map(i, 0, waveform.length, 0, width); 575 | // var y = map(waveform[i], -1, 1, height, 0); 576 | 577 | //for (var i = 0; i < data.length; i++){ 578 | //var x = map(i, 0, data.length, 0, width) - 20; 579 | for (var i = 0; i < _a1.length; i++){ 580 | var x = map(i, 0, _a1.length, 0, width) - 20; 581 | var y = heightOffset - _a1[i]; 582 | vertex(x, y); 583 | } 584 | endShape(); 585 | fill(a1, 255 - a1, 0); 586 | ellipse(w - 20, heightOffset - a1, _size, _size); 587 | noFill(); 588 | //console.log(Math.log(50 * (a0+1))); 589 | 590 | 591 | 592 | 593 | 594 | /* 595 | heightOffset = heightOffset + 256; 596 | beginShape(); 597 | for (var i = 0; i < _a2.length; i++){ 598 | 599 | var x = map(i, 0, _a2.length, 0, width) - 20; 600 | var y = heightOffset - _a2[i]; 601 | //stroke(_a2[i], 255 - _a2[i], 0); 602 | vertex(x, y); 603 | } 604 | endShape(); 605 | //stroke(0, 0, 0); 606 | 607 | heightOffset = heightOffset + 256; 608 | beginShape(LINES); 609 | for (var i = 0; i < _a3.length; i++){ 610 | fill(_a3[i], 255 - _a3[i], 0); 611 | var x = map(i, 0, _a3.length, 0, width) - 20; 612 | var y = heightOffset - _a3[i]; 613 | //stroke(_a3[i], 255 - _a3[i], 0); 614 | vertex(x, y); 615 | } 616 | endShape(); 617 | //stroke(0, 0, 0); 618 | */ 619 | 620 | //console.log(data[0][0]) 621 | // change oscillator frequency based on mouseX 622 | //var freq = map(mouseX, 0, width, 40, 880); 623 | //osc.freq(freq); 624 | 625 | //var amp = map(mouseY, 0, height, 1, .01); 626 | //osc.amp(amp); 627 | /* 628 | var spectrum = fft.analyze(); 629 | //console.log('spectrum',spectrum) 630 | beginShape(); 631 | //for (i = 0; iThe 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 | // ============================================================================= 38 | // p5 additions 39 | // ============================================================================= 40 | 41 | /** 42 | * Searches the page for an element with the given ID, class, or tag name (using the '#' or '.' 43 | * prefixes to specify an ID or class respectively, and none for a tag) and returns it as 44 | * a p5.Element. If a class or tag name is given with more than 1 element, 45 | * only the first element will be returned. 46 | * The DOM node itself can be accessed with .elt. 47 | * Returns null if none found. You can also specify a container to search within. 48 | * 49 | * @method select 50 | * @param {String} name id, class, or tag name of element to search for 51 | * @param {String} [container] id, p5.Element, or HTML element to search within 52 | * @return {Object/p5.Element|Null} p5.Element containing node found 53 | * @example 54 | *

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