├── .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 | [](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; ithis
855 | * page for further information about supported formats.
856 | *
857 | * @method createVideo
858 | * @param {String|Array} src path to a video file, or array of paths for
859 | * supporting different browsers
860 | * @param {Object} [callback] callback function to be called upon
861 | * 'canplaythrough' event fire, that is, when the
862 | * browser can play the media, and estimates that
863 | * enough data has been loaded to play the media
864 | * up to its end without having to stop for
865 | * further buffering of content
866 | * @return {Object/p5.Element} pointer to video p5.Element
867 | */
868 | p5.prototype.createVideo = function(src, callback) {
869 | return createMedia(this, 'video', src, callback);
870 | };
871 |
872 | /** AUDIO STUFF **/
873 |
874 | /**
875 | * Creates a hidden HTML5 <audio> element in the DOM for simple audio
876 | * playback. Appends to the container node if one is specified,
877 | * otherwise appends to body. The first parameter
878 | * can be either a single string path to a audio file, or an array of string
879 | * paths to different formats of the same audio. This is useful for ensuring
880 | * that your audio can play across different browsers, as each supports
881 | * different formats. See this
882 | * page for further information about supported formats.
883 | *
884 | * @method createAudio
885 | * @param {String|Array} src path to an audio file, or array of paths for
886 | * supporting different browsers
887 | * @param {Object} [callback] callback function to be called upon
888 | * 'canplaythrough' event fire, that is, when the
889 | * browser can play the media, and estimates that
890 | * enough data has been loaded to play the media
891 | * up to its end without having to stop for
892 | * further buffering of content
893 | * @return {Object/p5.Element} pointer to audio p5.Element
894 | */
895 | p5.prototype.createAudio = function(src, callback) {
896 | return createMedia(this, 'audio', src, callback);
897 | };
898 |
899 |
900 | /** CAMERA STUFF **/
901 |
902 | p5.prototype.VIDEO = 'video';
903 | p5.prototype.AUDIO = 'audio';
904 |
905 | navigator.getUserMedia = navigator.getUserMedia ||
906 | navigator.webkitGetUserMedia ||
907 | navigator.mozGetUserMedia ||
908 | navigator.msGetUserMedia;
909 |
910 | /**
911 | * Creates a new <video> element that contains the audio/video feed
912 | * from a webcam. This can be drawn onto the canvas using video().
913 | * More specific properties of the feed can be passing in a Constraints object.
914 | * See the
915 | * W3C
916 | * spec for possible properties. Note that not all of these are supported
917 | * by all browsers.
918 | * Security note: A new browser security specification requires that getUserMedia,
919 | * which is behind createCapture(), only works when you're running the code locally,
920 | * or on HTTPS. Learn more here
921 | * and here.
922 | *
923 | * @method createCapture
924 | * @param {String|Constant|Object} type type of capture, either VIDEO or
925 | * AUDIO if none specified, default both,
926 | * or a Constraints object
927 | * @param {Function} callback function to be called once
928 | * stream has loaded
929 | * @return {Object/p5.Element} capture video p5.Element
930 | * @example
931 | *
932 | * var capture;
933 | *
934 | * function setup() {
935 | * createCanvas(480, 120);
936 | * capture = createCapture(VIDEO);
937 | * }
938 | *
939 | * function draw() {
940 | * image(capture, 0, 0, width, width*capture.height/capture.width);
941 | * filter(INVERT);
942 | * }
943 | *
944 | *
945 | * function setup() {
946 | * createCanvas(480, 120);
947 | * var constraints = {
948 | * video: {
949 | * mandatory: {
950 | * minWidth: 1280,
951 | * minHeight: 720
952 | * },
953 | * optional: [
954 | * { maxFrameRate: 10 }
955 | * ]
956 | * },
957 | * audio: true
958 | * };
959 | * createCapture(constraints, function(stream) {
960 | * console.log(stream);
961 | * });
962 | * }
963 | *
964 | */
965 | p5.prototype.createCapture = function() {
966 | var useVideo = true;
967 | var useAudio = true;
968 | var constraints;
969 | var cb;
970 | for (var i=0; i
1026 | * var h2 = createElement('h2','im an h2 p5.element!');
1027 | *
1028 | */
1029 | p5.prototype.createElement = function(tag, content) {
1030 | var elt = document.createElement(tag);
1031 | if (typeof content !== 'undefined') {
1032 | elt.innerHTML = content;
1033 | }
1034 | return addElement(elt, this);
1035 | };
1036 |
1037 |
1038 | // =============================================================================
1039 | // p5.Element additions
1040 | // =============================================================================
1041 | /**
1042 | *
1043 | * Adds specified class to the element.
1044 | *
1045 | * @for p5.Element
1046 | * @method addClass
1047 | * @param {String} class name of class to add
1048 | * @return {Object/p5.Element}
1049 | * @example
1050 | *
1051 | * var div = createDiv('div');
1052 | * div.addClass('myClass');
1053 | *
1054 | */
1055 | p5.Element.prototype.addClass = function(c) {
1056 | if (this.elt.className) {
1057 | // PEND don't add class more than once
1058 | //var regex = new RegExp('[^a-zA-Z\d:]?'+c+'[^a-zA-Z\d:]?');
1059 | //if (this.elt.className.search(/[^a-zA-Z\d:]?hi[^a-zA-Z\d:]?/) === -1) {
1060 | this.elt.className = this.elt.className+' '+c;
1061 | //}
1062 | } else {
1063 | this.elt.className = c;
1064 | }
1065 | return this;
1066 | }
1067 |
1068 | /**
1069 | *
1070 | * Removes specified class from the element.
1071 | *
1072 | * @method removeClass
1073 | * @param {String} class name of class to remove
1074 | * @return {Object/p5.Element}
1075 | */
1076 | p5.Element.prototype.removeClass = function(c) {
1077 | var regex = new RegExp('(?:^|\\s)'+c+'(?!\\S)');
1078 | this.elt.className = this.elt.className.replace(regex, '');
1079 | this.elt.className = this.elt.className.replace(/^\s+|\s+$/g, ""); //prettify (optional)
1080 | return this;
1081 | }
1082 |
1083 | /**
1084 | *
1085 | * Attaches the element as a child to the parent specified.
1086 | * Accepts either a string ID, DOM node, or p5.Element.
1087 | * If no argument is specified, an array of children DOM nodes is returned.
1088 | *
1089 | * @method child
1090 | * @param {String|Object|p5.Element} [child] the ID, DOM node, or p5.Element
1091 | * to add to the current element
1092 | * @return {p5.Element}
1093 | * @example
1094 | *
1095 | * var div0 = createDiv('this is the parent');
1096 | * var div1 = createDiv('this is the child');
1097 | * div0.child(div1); // use p5.Element
1098 | *
1099 | *
1100 | * var div0 = createDiv('this is the parent');
1101 | * var div1 = createDiv('this is the child');
1102 | * div1.id('apples');
1103 | * div0.child('apples'); // use id
1104 | *
1105 | *
1106 | * var div0 = createDiv('this is the parent');
1107 | * var elt = document.getElementById('myChildDiv');
1108 | * div0.child(elt); // use element from page
1109 | *
1110 | */
1111 | p5.Element.prototype.child = function(c) {
1112 | if (typeof c === 'undefined'){
1113 | return this.elt.childNodes
1114 | }
1115 | if (typeof c === 'string') {
1116 | if (c[0] === '#') {
1117 | c = c.substring(1);
1118 | }
1119 | c = document.getElementById(c);
1120 | } else if (c instanceof p5.Element) {
1121 | c = c.elt;
1122 | }
1123 | this.elt.appendChild(c);
1124 | return this;
1125 | };
1126 |
1127 | /**
1128 | * Centers a p5 Element either vertically, horizontally,
1129 | * or both, relative to its parent or according to
1130 | * the body if the Element has no parent. If no argument is passed
1131 | * the Element is aligned both vertically and horizontally.
1132 | *
1133 | * @param {String} align passing 'vertical', 'horizontal' aligns element accordingly
1134 | * @return {Object/p5.Element} pointer to p5.Element
1135 | * @example
1136 | *
1137 | * function setup() {
1138 | * var div = createDiv('').size(10,10);
1139 | * div.style('background-color','orange');
1140 | * div.center();
1141 | *
1142 | * }
1143 | *
1144 | */
1145 | p5.Element.prototype.center = function(align) {
1146 | var style = this.elt.style.display;
1147 | var hidden = this.elt.style.display === 'none';
1148 | var parentHidden = this.parent().style.display === 'none';
1149 | var pos = { x : this.elt.offsetLeft, y : this.elt.offsetTop };
1150 |
1151 | if (hidden) this.show();
1152 |
1153 | this.elt.style.display = 'block';
1154 | this.position(0,0);
1155 |
1156 | if (parentHidden) this.parent().style.display = 'block';
1157 |
1158 | var wOffset = Math.abs(this.parent().offsetWidth - this.elt.offsetWidth);
1159 | var hOffset = Math.abs(this.parent().offsetHeight - this.elt.offsetHeight);
1160 | var y = pos.y;
1161 | var x = pos.x;
1162 |
1163 | if (align === 'both' || align === undefined){
1164 | this.position(wOffset/2, hOffset/2);
1165 | }else if (align === 'horizontal'){
1166 | this.position(wOffset/2, y);
1167 | }else if (align === 'vertical'){
1168 | this.position(x, hOffset/2);
1169 | }
1170 |
1171 | this.style('display', style);
1172 |
1173 | if (hidden) this.hide();
1174 |
1175 | if (parentHidden) this.parent().style.display = 'none';
1176 |
1177 | return this;
1178 | };
1179 |
1180 | /**
1181 | *
1182 | * If an argument is given, sets the inner HTML of the element,
1183 | * replacing any existing html. If true is included as a second
1184 | * argument, html is appended instead of replacing existing html.
1185 | * If no arguments are given, returns
1186 | * the inner HTML of the element.
1187 | *
1188 | * @for p5.Element
1189 | * @method html
1190 | * @param {String} [html] the HTML to be placed inside the element
1191 | * @param {boolean} [append] whether to append HTML to existing
1192 | * @return {Object/p5.Element|String}
1193 | * @example
1194 | *
1195 | * var div = createDiv('').size(100,100);
1196 | * div.html('hi');
1197 | *
1198 | *
1199 | * var div = createDiv('Hello ').size(100,100);
1200 | * div.html('World', true);
1201 | *
1202 | */
1203 | p5.Element.prototype.html = function() {
1204 | if (arguments.length === 0) {
1205 | return this.elt.innerHTML;
1206 | } else if (arguments[1]) {
1207 | this.elt.innerHTML += arguments[0];
1208 | return this;
1209 | } else {
1210 | this.elt.innerHTML = arguments[0];
1211 | return this;
1212 | }
1213 | };
1214 |
1215 | /**
1216 | *
1217 | * Sets the position of the element relative to (0, 0) of the
1218 | * window. Essentially, sets position:absolute and left and top
1219 | * properties of style. If no arguments given returns the x and y position
1220 | * of the element in an object.
1221 | *
1222 | * @method position
1223 | * @param {Number} [x] x-position relative to upper left of window
1224 | * @param {Number} [y] y-position relative to upper left of window
1225 | * @return {Object/p5.Element}
1226 | * @example
1227 | *
1228 | * function setup() {
1229 | * var cnv = createCanvas(100, 100);
1230 | * // positions canvas 50px to the right and 100px
1231 | * // below upper left corner of the window
1232 | * cnv.position(50, 100);
1233 | * }
1234 | *
1235 | */
1236 | p5.Element.prototype.position = function() {
1237 | if (arguments.length === 0){
1238 | return { 'x' : this.elt.offsetLeft , 'y' : this.elt.offsetTop };
1239 | }else{
1240 | this.elt.style.position = 'absolute';
1241 | this.elt.style.left = arguments[0]+'px';
1242 | this.elt.style.top = arguments[1]+'px';
1243 | this.x = arguments[0];
1244 | this.y = arguments[1];
1245 | return this;
1246 | }
1247 | };
1248 |
1249 | /* Helper method called by p5.Element.style() */
1250 | p5.Element.prototype._translate = function(){
1251 | this.elt.style.position = 'absolute';
1252 | // save out initial non-translate transform styling
1253 | var transform = '';
1254 | if (this.elt.style.transform) {
1255 | transform = this.elt.style.transform.replace(/translate3d\(.*\)/g, '');
1256 | transform = transform.replace(/translate[X-Z]?\(.*\)/g, '');
1257 | }
1258 | if (arguments.length === 2) {
1259 | this.elt.style.transform = 'translate('+arguments[0]+'px, '+arguments[1]+'px)';
1260 | } else if (arguments.length > 2) {
1261 | this.elt.style.transform = 'translate3d('+arguments[0]+'px,'+arguments[1]+'px,'+arguments[2]+'px)';
1262 | if (arguments.length === 3) {
1263 | this.elt.parentElement.style.perspective = '1000px';
1264 | } else {
1265 | this.elt.parentElement.style.perspective = arguments[3]+'px';
1266 | }
1267 | }
1268 | // add any extra transform styling back on end
1269 | this.elt.style.transform += transform;
1270 | return this;
1271 | };
1272 |
1273 | /* Helper method called by p5.Element.style() */
1274 | p5.Element.prototype._rotate = function(){
1275 | // save out initial non-rotate transform styling
1276 | var transform = '';
1277 | if (this.elt.style.transform) {
1278 | var transform = this.elt.style.transform.replace(/rotate3d\(.*\)/g, '');
1279 | transform = transform.replace(/rotate[X-Z]?\(.*\)/g, '');
1280 | }
1281 |
1282 | if (arguments.length === 1){
1283 | this.elt.style.transform = 'rotate('+arguments[0]+'deg)';
1284 | }else if (arguments.length === 2){
1285 | this.elt.style.transform = 'rotate('+arguments[0]+'deg, '+arguments[1]+'deg)';
1286 | }else if (arguments.length === 3){
1287 | this.elt.style.transform = 'rotateX('+arguments[0]+'deg)';
1288 | this.elt.style.transform += 'rotateY('+arguments[1]+'deg)';
1289 | this.elt.style.transform += 'rotateZ('+arguments[2]+'deg)';
1290 | }
1291 | // add remaining transform back on
1292 | this.elt.style.transform += transform;
1293 | return this;
1294 | };
1295 |
1296 | /**
1297 | * Sets the given style (css) property (1st arg) of the element with the
1298 | * given value (2nd arg). If a single argument is given, .style()
1299 | * returns the value of the given property; however, if the single argument
1300 | * is given in css syntax ('text-align:center'), .style() sets the css
1301 | * appropriatly. .style() also handles 2d and 3d css transforms. If
1302 | * the 1st arg is 'rotate', 'translate', or 'position', the following arguments
1303 | * accept Numbers as values. ('translate', 10, 100, 50);
1304 | *
1305 | * @method style
1306 | * @param {String} property property to be set
1307 | * @param {String|Number|p5.Color} [value] value to assign to property
1308 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1309 | * @param {String|Number} [value] value to assign to property (rotate/translate)
1310 | * @param {String|Number} [value] value to assign to property (translate)
1311 | * @return {String|Object/p5.Element} value of property, if no value is specified
1312 | * or p5.Element
1313 | * @example
1314 | *
1315 | * var myDiv = createDiv("I like pandas.");
1316 | * myDiv.style("font-size", "18px");
1317 | * myDiv.style("color", "#ff0000");
1318 | *
1319 | *
1320 | * var col = color(25,23,200,50);
1321 | * var button = createButton("button");
1322 | * button.style("background-color", col);
1323 | * button.position(10, 10);
1324 | *
1325 | *
1326 | * var myDiv = createDiv("I like lizards.");
1327 | * myDiv.style("position", 20, 20);
1328 | * myDiv.style("rotate", 45);
1329 | *
1330 | *
1331 | * var myDiv;
1332 | * function setup() {
1333 | * background(200);
1334 | * myDiv = createDiv("I like gray.");
1335 | * myDiv.position(20, 20);
1336 | * }
1337 | *
1338 | * function draw() {
1339 | * myDiv.style("font-size", mouseX+"px");
1340 | * }
1341 | *
1342 | */
1343 | p5.Element.prototype.style = function(prop, val) {
1344 | var self = this;
1345 |
1346 | if (val instanceof p5.Color) {
1347 | val = 'rgba(' + val.levels[0] + ',' + val.levels[1] + ',' + val.levels[2] + ',' + val.levels[3]/255 + ')'
1348 | }
1349 |
1350 | if (typeof val === 'undefined') {
1351 | if (prop.indexOf(':') === -1) {
1352 | var styles = window.getComputedStyle(self.elt);
1353 | var style = styles.getPropertyValue(prop);
1354 | return style;
1355 | } else {
1356 | var attrs = prop.split(';');
1357 | for (var i = 0; i < attrs.length; i++) {
1358 | var parts = attrs[i].split(':');
1359 | if (parts[0] && parts[1]) {
1360 | this.elt.style[parts[0].trim()] = parts[1].trim();
1361 | }
1362 | }
1363 | }
1364 | } else {
1365 | if (prop === 'rotate' || prop === 'translate' || prop === 'position'){
1366 | var trans = Array.prototype.shift.apply(arguments);
1367 | var f = this[trans] || this['_'+trans];
1368 | f.apply(this, arguments);
1369 | } else {
1370 | this.elt.style[prop] = val;
1371 | if (prop === 'width' || prop === 'height' || prop === 'left' || prop === 'top') {
1372 | var numVal = val.replace(/\D+/g, '');
1373 | this[prop] = parseInt(numVal, 10); // pend: is this necessary?
1374 | }
1375 | }
1376 | }
1377 | return this;
1378 | };
1379 |
1380 |
1381 | /**
1382 | *
1383 | * Adds a new attribute or changes the value of an existing attribute
1384 | * on the specified element. If no value is specified, returns the
1385 | * value of the given attribute, or null if attribute is not set.
1386 | *
1387 | * @method attribute
1388 | * @param {String} attr attribute to set
1389 | * @param {String} [value] value to assign to attribute
1390 | * @return {String|Object/p5.Element} value of attribute, if no value is
1391 | * specified or p5.Element
1392 | * @example
1393 | *
1394 | * var myDiv = createDiv("I like pandas.");
1395 | * myDiv.attribute("align", "center");
1396 | *
1397 | */
1398 | p5.Element.prototype.attribute = function(attr, value) {
1399 | if (typeof value === 'undefined') {
1400 | return this.elt.getAttribute(attr);
1401 | } else {
1402 | this.elt.setAttribute(attr, value);
1403 | return this;
1404 | }
1405 | };
1406 |
1407 |
1408 | /**
1409 | *
1410 | * Removes an attribute on the specified element.
1411 | *
1412 | * @method removeAttribute
1413 | * @param {String} attr attribute to remove
1414 | * @return {Object/p5.Element}
1415 | *
1416 | * @example
1417 | *
1418 | * var button;
1419 | * var checkbox;
1420 | *
1421 | * function setup() {
1422 | * checkbox = createCheckbox('enable', true);
1423 | * checkbox.changed(enableButton);
1424 | * button = createButton('button');
1425 | * button.position(10, 10);
1426 | * }
1427 | *
1428 | * function enableButton() {
1429 | * if( this.checked() ) {
1430 | * // Re-enable the button
1431 | * button.removeAttribute('disabled');
1432 | * } else {
1433 | * // Disable the button
1434 | * button.attribute('disabled','');
1435 | * }
1436 | * }
1437 | *
1438 | */
1439 | p5.Element.prototype.removeAttribute = function(attr) {
1440 | this.elt.removeAttribute(attr);
1441 | return this;
1442 | };
1443 |
1444 |
1445 | /**
1446 | * Either returns the value of the element if no arguments
1447 | * given, or sets the value of the element.
1448 | *
1449 | * @method value
1450 | * @param {String|Number} [value]
1451 | * @return {String|Object/p5.Element} value of element if no value is specified or p5.Element
1452 | * @example
1453 | *
1454 | * // gets the value
1455 | * var inp;
1456 | * function setup() {
1457 | * inp = createInput('');
1458 | * }
1459 | *
1460 | * function mousePressed() {
1461 | * print(inp.value());
1462 | * }
1463 | *
1464 | *
1465 | * // sets the value
1466 | * var inp;
1467 | * function setup() {
1468 | * inp = createInput('myValue');
1469 | * }
1470 | *
1471 | * function mousePressed() {
1472 | * inp.value("myValue");
1473 | * }
1474 | *
1475 | */
1476 | p5.Element.prototype.value = function() {
1477 | if (arguments.length > 0) {
1478 | this.elt.value = arguments[0];
1479 | return this;
1480 | } else {
1481 | if (this.elt.type === 'range') {
1482 | return parseFloat(this.elt.value);
1483 | }
1484 | else return this.elt.value;
1485 | }
1486 | };
1487 |
1488 | /**
1489 | *
1490 | * Shows the current element. Essentially, setting display:block for the style.
1491 | *
1492 | * @method show
1493 | * @return {Object/p5.Element}
1494 | * @example
1495 | *
1496 | * var div = createDiv('div');
1497 | * div.style("display", "none");
1498 | * div.show(); // turns display to block
1499 | *
1500 | */
1501 | p5.Element.prototype.show = function() {
1502 | this.elt.style.display = 'block';
1503 | return this;
1504 | };
1505 |
1506 | /**
1507 | * Hides the current element. Essentially, setting display:none for the style.
1508 | *
1509 | * @method hide
1510 | * @return {Object/p5.Element}
1511 | * @example
1512 | *
1513 | * var div = createDiv('this is a div');
1514 | * div.hide();
1515 | *
1516 | */
1517 | p5.Element.prototype.hide = function() {
1518 | this.elt.style.display = 'none';
1519 | return this;
1520 | };
1521 |
1522 | /**
1523 | *
1524 | * Sets the width and height of the element. AUTO can be used to
1525 | * only adjust one dimension. If no arguments given returns the width and height
1526 | * of the element in an object.
1527 | *
1528 | * @method size
1529 | * @param {Number} [w] width of the element
1530 | * @param {Number} [h] height of the element
1531 | * @return {Object/p5.Element}
1532 | * @example
1533 | *
1534 | * var div = createDiv('this is a div');
1535 | * div.size(100, 100);
1536 | *
1537 | */
1538 | p5.Element.prototype.size = function(w, h) {
1539 | if (arguments.length === 0){
1540 | return { 'width' : this.elt.offsetWidth , 'height' : this.elt.offsetHeight };
1541 | }else{
1542 | var aW = w;
1543 | var aH = h;
1544 | var AUTO = p5.prototype.AUTO;
1545 | if (aW !== AUTO || aH !== AUTO) {
1546 | if (aW === AUTO) {
1547 | aW = h * this.width / this.height;
1548 | } else if (aH === AUTO) {
1549 | aH = w * this.height / this.width;
1550 | }
1551 | // set diff for cnv vs normal div
1552 | if (this.elt instanceof HTMLCanvasElement) {
1553 | var j = {};
1554 | var k = this.elt.getContext('2d');
1555 | for (var prop in k) {
1556 | j[prop] = k[prop];
1557 | }
1558 | this.elt.setAttribute('width', aW * this._pInst._pixelDensity);
1559 | this.elt.setAttribute('height', aH * this._pInst._pixelDensity);
1560 | this.elt.setAttribute('style', 'width:' + aW + 'px; height:' + aH + 'px');
1561 | this._pInst.scale(this._pInst._pixelDensity, this._pInst._pixelDensity);
1562 | for (var prop in j) {
1563 | this.elt.getContext('2d')[prop] = j[prop];
1564 | }
1565 | } else {
1566 | this.elt.style.width = aW+'px';
1567 | this.elt.style.height = aH+'px';
1568 | this.elt.width = aW;
1569 | this.elt.height = aH;
1570 | this.width = aW;
1571 | this.height = aH;
1572 | }
1573 |
1574 | this.width = this.elt.offsetWidth;
1575 | this.height = this.elt.offsetHeight;
1576 |
1577 | if (this._pInst) { // main canvas associated with p5 instance
1578 | if (this._pInst._curElement.elt === this.elt) {
1579 | this._pInst._setProperty('width', this.elt.offsetWidth);
1580 | this._pInst._setProperty('height', this.elt.offsetHeight);
1581 | }
1582 | }
1583 | }
1584 | return this;
1585 | }
1586 | };
1587 |
1588 | /**
1589 | * Removes the element and deregisters all listeners.
1590 | * @method remove
1591 | * @example
1592 | *
1593 | * var myDiv = createDiv('this is some text');
1594 | * myDiv.remove();
1595 | *
1596 | */
1597 | p5.Element.prototype.remove = function() {
1598 | // deregister events
1599 | for (var ev in this._events) {
1600 | this.elt.removeEventListener(ev, this._events[ev]);
1601 | }
1602 | if (this.elt.parentNode) {
1603 | this.elt.parentNode.removeChild(this.elt);
1604 | }
1605 | delete(this);
1606 | };
1607 |
1608 |
1609 |
1610 | // =============================================================================
1611 | // p5.MediaElement additions
1612 | // =============================================================================
1613 |
1614 |
1615 | /**
1616 | * Extends p5.Element to handle audio and video. In addition to the methods
1617 | * of p5.Element, it also contains methods for controlling media. It is not
1618 | * called directly, but p5.MediaElements are created by calling createVideo,
1619 | * createAudio, and createCapture.
1620 | *
1621 | * @class p5.MediaElement
1622 | * @constructor
1623 | * @param {String} elt DOM node that is wrapped
1624 | * @param {Object} [pInst] pointer to p5 instance
1625 | */
1626 | p5.MediaElement = function(elt, pInst) {
1627 | p5.Element.call(this, elt, pInst);
1628 |
1629 | var self = this;
1630 | this.elt.crossOrigin = 'anonymous';
1631 |
1632 | this._prevTime = 0;
1633 | this._cueIDCounter = 0;
1634 | this._cues = [];
1635 | this._pixelDensity = 1;
1636 |
1637 | /**
1638 | * Path to the media element source.
1639 | *
1640 | * @property src
1641 | * @return {String} src
1642 | */
1643 | Object.defineProperty(self, 'src', {
1644 | get: function() {
1645 | var firstChildSrc = self.elt.children[0].src;
1646 | var srcVal = self.elt.src === window.location.href ? '' : self.elt.src;
1647 | var ret = firstChildSrc === window.location.href ? srcVal : firstChildSrc;
1648 | return ret;
1649 | },
1650 | set: function(newValue) {
1651 | for (var i = 0; i < self.elt.children.length; i++) {
1652 | self.elt.removeChild(self.elt.children[i]);
1653 | }
1654 | var source = document.createElement('source');
1655 | source.src = newValue;
1656 | elt.appendChild(source);
1657 | self.elt.src = newValue;
1658 | },
1659 | });
1660 |
1661 | // private _onended callback, set by the method: onended(callback)
1662 | self._onended = function() {};
1663 | self.elt.onended = function() {
1664 | self._onended(self);
1665 | }
1666 | };
1667 | p5.MediaElement.prototype = Object.create(p5.Element.prototype);
1668 |
1669 |
1670 |
1671 |
1672 | /**
1673 | * Play an HTML5 media element.
1674 | *
1675 | * @method play
1676 | * @return {Object/p5.Element}
1677 | */
1678 | p5.MediaElement.prototype.play = function() {
1679 | if (this.elt.currentTime === this.elt.duration) {
1680 | this.elt.currentTime = 0;
1681 | }
1682 |
1683 | if (this.elt.readyState > 1) {
1684 | this.elt.play();
1685 | } else {
1686 | // in Chrome, playback cannot resume after being stopped and must reload
1687 | this.elt.load();
1688 | this.elt.play();
1689 | }
1690 | return this;
1691 | };
1692 |
1693 | /**
1694 | * Stops an HTML5 media element (sets current time to zero).
1695 | *
1696 | * @method stop
1697 | * @return {Object/p5.Element}
1698 | */
1699 | p5.MediaElement.prototype.stop = function() {
1700 | this.elt.pause();
1701 | this.elt.currentTime = 0;
1702 | return this;
1703 | };
1704 |
1705 | /**
1706 | * Pauses an HTML5 media element.
1707 | *
1708 | * @method pause
1709 | * @return {Object/p5.Element}
1710 | */
1711 | p5.MediaElement.prototype.pause = function() {
1712 | this.elt.pause();
1713 | return this;
1714 | };
1715 |
1716 | /**
1717 | * Set 'loop' to true for an HTML5 media element, and starts playing.
1718 | *
1719 | * @method loop
1720 | * @return {Object/p5.Element}
1721 | */
1722 | p5.MediaElement.prototype.loop = function() {
1723 | this.elt.setAttribute('loop', true);
1724 | this.play();
1725 | return this;
1726 | };
1727 | /**
1728 | * Set 'loop' to false for an HTML5 media element. Element will stop
1729 | * when it reaches the end.
1730 | *
1731 | * @method noLoop
1732 | * @return {Object/p5.Element}
1733 | */
1734 | p5.MediaElement.prototype.noLoop = function() {
1735 | this.elt.setAttribute('loop', false);
1736 | return this;
1737 | };
1738 |
1739 |
1740 | /**
1741 | * Set HTML5 media element to autoplay or not.
1742 | *
1743 | * @method autoplay
1744 | * @param {Boolean} autoplay whether the element should autoplay
1745 | * @return {Object/p5.Element}
1746 | */
1747 | p5.MediaElement.prototype.autoplay = function(val) {
1748 | this.elt.setAttribute('autoplay', val);
1749 | return this;
1750 | };
1751 |
1752 | /**
1753 | * Sets volume for this HTML5 media element. If no argument is given,
1754 | * returns the current volume.
1755 | *
1756 | * @param {Number} [val] volume between 0.0 and 1.0
1757 | * @return {Number|p5.MediaElement} current volume or p5.MediaElement
1758 | * @method volume
1759 | */
1760 | p5.MediaElement.prototype.volume = function(val) {
1761 | if (typeof val === 'undefined') {
1762 | return this.elt.volume;
1763 | } else {
1764 | this.elt.volume = val;
1765 | }
1766 | };
1767 |
1768 | /**
1769 | * If no arguments are given, returns the current playback speed of the
1770 | * element. The speed parameter sets the speed where 2.0 will play the
1771 | * element twice as fast, 0.5 will play at half the speed, and -1 will play
1772 | * the element in normal speed in reverse.(Note that not all browsers support
1773 | * backward playback and even if they do, playback might not be smooth.)
1774 | *
1775 | * @method speed
1776 | * @param {Number} [speed] speed multiplier for element playback
1777 | * @return {Number|Object/p5.MediaElement} current playback speed or p5.MediaElement
1778 | */
1779 | p5.MediaElement.prototype.speed = function(val) {
1780 | if (typeof val === 'undefined') {
1781 | return this.elt.playbackRate;
1782 | } else {
1783 | this.elt.playbackRate = val;
1784 | }
1785 | };
1786 |
1787 | /**
1788 | * If no arguments are given, returns the current time of the element.
1789 | * If an argument is given the current time of the element is set to it.
1790 | *
1791 | * @method time
1792 | * @param {Number} [time] time to jump to (in seconds)
1793 | * @return {Number|Object/p5.MediaElement} current time (in seconds)
1794 | * or p5.MediaElement
1795 | */
1796 | p5.MediaElement.prototype.time = function(val) {
1797 | if (typeof val === 'undefined') {
1798 | return this.elt.currentTime;
1799 | } else {
1800 | this.elt.currentTime = val;
1801 | }
1802 | };
1803 |
1804 | /**
1805 | * Returns the duration of the HTML5 media element.
1806 | *
1807 | * @method duration
1808 | * @return {Number} duration
1809 | */
1810 | p5.MediaElement.prototype.duration = function() {
1811 | return this.elt.duration;
1812 | };
1813 | p5.MediaElement.prototype.pixels = [];
1814 | p5.MediaElement.prototype.loadPixels = function() {
1815 | if (!this.canvas) {
1816 | this.canvas = document.createElement('canvas');
1817 | this.drawingContext = this.canvas.getContext('2d');
1818 | }
1819 | if (this.loadedmetadata) { // wait for metadata for w/h
1820 | if (this.canvas.width !== this.elt.width) {
1821 | this.canvas.width = this.elt.width;
1822 | this.canvas.height = this.elt.height;
1823 | this.width = this.canvas.width;
1824 | this.height = this.canvas.height;
1825 | }
1826 | this.drawingContext.drawImage(this.elt, 0, 0, this.canvas.width, this.canvas.height);
1827 | p5.Renderer2D.prototype.loadPixels.call(this);
1828 | }
1829 | return this;
1830 | }
1831 | p5.MediaElement.prototype.updatePixels = function(x, y, w, h){
1832 | if (this.loadedmetadata) { // wait for metadata
1833 | p5.Renderer2D.prototype.updatePixels.call(this, x, y, w, h);
1834 | }
1835 | return this;
1836 | }
1837 | p5.MediaElement.prototype.get = function(x, y, w, h){
1838 | if (this.loadedmetadata) { // wait for metadata
1839 | return p5.Renderer2D.prototype.get.call(this, x, y, w, h);
1840 | } else if (!x) {
1841 | return new p5.Image(1, 1);
1842 | } else {
1843 | return [0, 0, 0, 255];
1844 | }
1845 | };
1846 | p5.MediaElement.prototype.set = function(x, y, imgOrCol){
1847 | if (this.loadedmetadata) { // wait for metadata
1848 | p5.Renderer2D.prototype.set.call(this, x, y, imgOrCol);
1849 | }
1850 | };
1851 | p5.MediaElement.prototype.copy = function(){
1852 | p5.Renderer2D.prototype.copy.apply(this, arguments);
1853 | };
1854 | p5.MediaElement.prototype.mask = function(){
1855 | this.loadPixels();
1856 | p5.Image.prototype.mask.apply(this, arguments);
1857 | };
1858 | /**
1859 | * Schedule an event to be called when the audio or video
1860 | * element reaches the end. If the element is looping,
1861 | * this will not be called. The element is passed in
1862 | * as the argument to the onended callback.
1863 | *
1864 | * @method onended
1865 | * @param {Function} callback function to call when the
1866 | * soundfile has ended. The
1867 | * media element will be passed
1868 | * in as the argument to the
1869 | * callback.
1870 | * @return {Object/p5.MediaElement}
1871 | * @example
1872 | *
1873 | * function setup() {
1874 | * audioEl = createAudio('assets/beat.mp3');
1875 | * audioEl.showControls(true);
1876 | * audioEl.onended(sayDone);
1877 | * }
1878 | *
1879 | * function sayDone(elt) {
1880 | * alert('done playing ' + elt.src );
1881 | * }
1882 | *
1883 | */
1884 | p5.MediaElement.prototype.onended = function(callback) {
1885 | this._onended = callback;
1886 | return this;
1887 | };
1888 |
1889 |
1890 | /*** CONNECT TO WEB AUDIO API / p5.sound.js ***/
1891 |
1892 | /**
1893 | * Send the audio output of this element to a specified audioNode or
1894 | * p5.sound object. If no element is provided, connects to p5's master
1895 | * output. That connection is established when this method is first called.
1896 | * All connections are removed by the .disconnect() method.
1897 | *
1898 | * This method is meant to be used with the p5.sound.js addon library.
1899 | *
1900 | * @method connect
1901 | * @param {AudioNode|p5.sound object} audioNode AudioNode from the Web Audio API,
1902 | * or an object from the p5.sound library
1903 | */
1904 | p5.MediaElement.prototype.connect = function(obj) {
1905 | var audioContext, masterOutput;
1906 |
1907 | // if p5.sound exists, same audio context
1908 | if (typeof p5.prototype.getAudioContext === 'function') {
1909 | audioContext = p5.prototype.getAudioContext();
1910 | masterOutput = p5.soundOut.input;
1911 | } else {
1912 | try {
1913 | audioContext = obj.context;
1914 | masterOutput = audioContext.destination
1915 | } catch(e) {
1916 | throw 'connect() is meant to be used with Web Audio API or p5.sound.js'
1917 | }
1918 | }
1919 |
1920 | // create a Web Audio MediaElementAudioSourceNode if none already exists
1921 | if (!this.audioSourceNode) {
1922 | this.audioSourceNode = audioContext.createMediaElementSource(this.elt);
1923 |
1924 | // connect to master output when this method is first called
1925 | this.audioSourceNode.connect(masterOutput);
1926 | }
1927 |
1928 | // connect to object if provided
1929 | if (obj) {
1930 | if (obj.input) {
1931 | this.audioSourceNode.connect(obj.input);
1932 | } else {
1933 | this.audioSourceNode.connect(obj);
1934 | }
1935 | }
1936 |
1937 | // otherwise connect to master output of p5.sound / AudioContext
1938 | else {
1939 | this.audioSourceNode.connect(masterOutput);
1940 | }
1941 |
1942 | };
1943 |
1944 | /**
1945 | * Disconnect all Web Audio routing, including to master output.
1946 | * This is useful if you want to re-route the output through
1947 | * audio effects, for example.
1948 | *
1949 | * @method disconnect
1950 | */
1951 | p5.MediaElement.prototype.disconnect = function() {
1952 | if (this.audioSourceNode) {
1953 | this.audioSourceNode.disconnect();
1954 | } else {
1955 | throw 'nothing to disconnect';
1956 | }
1957 | };
1958 |
1959 |
1960 | /*** SHOW / HIDE CONTROLS ***/
1961 |
1962 | /**
1963 | * Show the default MediaElement controls, as determined by the web browser.
1964 | *
1965 | * @method showControls
1966 | */
1967 | p5.MediaElement.prototype.showControls = function() {
1968 | // must set style for the element to show on the page
1969 | this.elt.style['text-align'] = 'inherit';
1970 | this.elt.controls = true;
1971 | };
1972 |
1973 | /**
1974 | * Hide the default mediaElement controls.
1975 | *
1976 | * @method hideControls
1977 | */
1978 | p5.MediaElement.prototype.hideControls = function() {
1979 | this.elt.controls = false;
1980 | };
1981 |
1982 | /*** SCHEDULE EVENTS ***/
1983 |
1984 | /**
1985 | * Schedule events to trigger every time a MediaElement
1986 | * (audio/video) reaches a playback cue point.
1987 | *
1988 | * Accepts a callback function, a time (in seconds) at which to trigger
1989 | * the callback, and an optional parameter for the callback.
1990 | *
1991 | * Time will be passed as the first parameter to the callback function,
1992 | * and param will be the second parameter.
1993 | *
1994 | *
1995 | * @method addCue
1996 | * @param {Number} time Time in seconds, relative to this media
1997 | * element's playback. For example, to trigger
1998 | * an event every time playback reaches two
1999 | * seconds, pass in the number 2. This will be
2000 | * passed as the first parameter to
2001 | * the callback function.
2002 | * @param {Function} callback Name of a function that will be
2003 | * called at the given time. The callback will
2004 | * receive time and (optionally) param as its
2005 | * two parameters.
2006 | * @param {Object} [value] An object to be passed as the
2007 | * second parameter to the
2008 | * callback function.
2009 | * @return {Number} id ID of this cue,
2010 | * useful for removeCue(id)
2011 | * @example
2012 | *
2013 | * function setup() {
2014 | * background(255,255,255);
2015 | *
2016 | * audioEl = createAudio('assets/beat.mp3');
2017 | * audioEl.showControls();
2018 | *
2019 | * // schedule three calls to changeBackground
2020 | * audioEl.addCue(0.5, changeBackground, color(255,0,0) );
2021 | * audioEl.addCue(1.0, changeBackground, color(0,255,0) );
2022 | * audioEl.addCue(2.5, changeBackground, color(0,0,255) );
2023 | * audioEl.addCue(3.0, changeBackground, color(0,255,255) );
2024 | * audioEl.addCue(4.2, changeBackground, color(255,255,0) );
2025 | * audioEl.addCue(5.0, changeBackground, color(255,255,0) );
2026 | * }
2027 | *
2028 | * function changeBackground(val) {
2029 | * background(val);
2030 | * }
2031 | *
2032 | */
2033 | p5.MediaElement.prototype.addCue = function(time, callback, val) {
2034 | var id = this._cueIDCounter++;
2035 |
2036 | var cue = new Cue(callback, time, id, val);
2037 | this._cues.push(cue);
2038 |
2039 | if (!this.elt.ontimeupdate) {
2040 | this.elt.ontimeupdate = this._onTimeUpdate.bind(this);
2041 | }
2042 |
2043 | return id;
2044 | };
2045 |
2046 | /**
2047 | * Remove a callback based on its ID. The ID is returned by the
2048 | * addCue method.
2049 | *
2050 | * @method removeCue
2051 | * @param {Number} id ID of the cue, as returned by addCue
2052 | */
2053 | p5.MediaElement.prototype.removeCue = function(id) {
2054 | for (var i = 0; i < this._cues.length; i++) {
2055 | var cue = this._cues[i];
2056 | if (cue.id === id) {
2057 | this.cues.splice(i, 1);
2058 | }
2059 | }
2060 |
2061 | if (this._cues.length === 0) {
2062 | this.elt.ontimeupdate = null
2063 | }
2064 | };
2065 |
2066 | /**
2067 | * Remove all of the callbacks that had originally been scheduled
2068 | * via the addCue method.
2069 | *
2070 | * @method clearCues
2071 | */
2072 | p5.MediaElement.prototype.clearCues = function() {
2073 | this._cues = [];
2074 | this.elt.ontimeupdate = null;
2075 | };
2076 |
2077 | // private method that checks for cues to be fired if events
2078 | // have been scheduled using addCue(callback, time).
2079 | p5.MediaElement.prototype._onTimeUpdate = function() {
2080 | var playbackTime = this.time();
2081 |
2082 | for (var i = 0 ; i < this._cues.length; i++) {
2083 | var callbackTime = this._cues[i].time;
2084 | var val = this._cues[i].val;
2085 |
2086 |
2087 | if (this._prevTime < callbackTime && callbackTime <= playbackTime) {
2088 |
2089 | // pass the scheduled callbackTime as parameter to the callback
2090 | this._cues[i].callback(val);
2091 | }
2092 |
2093 | }
2094 |
2095 | this._prevTime = playbackTime;
2096 | };
2097 |
2098 |
2099 | // Cue inspired by JavaScript setTimeout, and the
2100 | // Tone.js Transport Timeline Event, MIT License Yotam Mann 2015 tonejs.org
2101 | var Cue = function(callback, time, id, val) {
2102 | this.callback = callback;
2103 | this.time = time;
2104 | this.id = id;
2105 | this.val = val;
2106 | };
2107 |
2108 | // =============================================================================
2109 | // p5.File
2110 | // =============================================================================
2111 |
2112 |
2113 | /**
2114 | * Base class for a file
2115 | * Using this for createFileInput
2116 | *
2117 | * @class p5.File
2118 | * @constructor
2119 | * @param {File} file File that is wrapped
2120 | * @param {Object} [pInst] pointer to p5 instance
2121 | */
2122 | p5.File = function(file, pInst) {
2123 | /**
2124 | * Underlying File object. All normal File methods can be called on this.
2125 | *
2126 | * @property file
2127 | */
2128 | this.file = file;
2129 |
2130 | this._pInst = pInst;
2131 |
2132 | // Splitting out the file type into two components
2133 | // This makes determining if image or text etc simpler
2134 | var typeList = file.type.split('/');
2135 | /**
2136 | * File type (image, text, etc.)
2137 | *
2138 | * @property type
2139 | */
2140 | this.type = typeList[0];
2141 | /**
2142 | * File subtype (usually the file extension jpg, png, xml, etc.)
2143 | *
2144 | * @property subtype
2145 | */
2146 | this.subtype = typeList[1];
2147 | /**
2148 | * File name
2149 | *
2150 | * @property name
2151 | */
2152 | this.name = file.name;
2153 | /**
2154 | * File size
2155 | *
2156 | * @property size
2157 | */
2158 | this.size = file.size;
2159 |
2160 | /**
2161 | * URL string containing image data.
2162 | *
2163 | * @property data
2164 | */
2165 | this.data = undefined;
2166 | };
2167 |
2168 | }));
2169 |
--------------------------------------------------------------------------------
/html/p5/p5.serialport.js:
--------------------------------------------------------------------------------
1 | /*! p5.serialport.js v0.0.1 2015-07-23 */
2 | /**
3 | * @module p5.serialport
4 | * @submodule p5.serialport
5 | * @for p5.serialport
6 | * @main
7 | */
8 | /**
9 | * p5.serialport
10 | * Shawn Van Every (Shawn.Van.Every@nyu.edu)
11 | * ITP/NYU
12 | * LGPL
13 | *
14 | * https://github.com/vanevery/p5.serialport
15 | *
16 | */
17 | (function(root, factory) {
18 | if (typeof define === 'function' && define.amd)
19 | define('p5.serialport', ['p5'], function(p5) {
20 | (factory(p5));
21 | });
22 | else if (typeof exports === 'object')
23 | factory(require('../p5'));
24 | else
25 | factory(root['p5']);
26 | }(this, function(p5) {
27 |
28 | // =============================================================================
29 | // p5.SerialPort
30 | // =============================================================================
31 |
32 |
33 | /**
34 | * Base class for a serial port. Creates an instance of the serial library and prints "hostname":"serverPort" in the console.
35 | *
36 | * @class p5.SerialPort
37 | * @constructor
38 | * @param {String} [hostname] Name of the host. Defaults to 'localhost'.
39 | * @param {Number} [serverPort] Port number. Defaults to 8081.
40 | * @example
41 | * var portName = '/dev/cu.usbmodem1411'; //enter your portName
42 | *
43 | * function setup() {
44 | * createCanvas(400, 300);
45 | * serial = new p5.SerialPort()
46 | * serial.open(portName);
47 | * }
48 | */
49 | p5.SerialPort = function(_hostname, _serverport) {
50 |
51 | var self = this;
52 |
53 | this.bufferSize = 1; // How much to buffer before sending data event
54 | this.serialBuffer = [];
55 | //this.maxBufferSize = 1024;
56 |
57 | this.serialConnected = false; // Is serial connected?
58 |
59 | this.serialport = null;
60 | this.serialoptions = null;
61 |
62 | this.emitQueue = [];
63 |
64 | this.serialportList = [];
65 |
66 | if (typeof _hostname === 'string') {
67 | this.hostname = _hostname;
68 | } else {
69 | //console.log("typeof _hostname " + typeof _hostname + " setting to locahost");
70 | this.hostname = "localhost";
71 | }
72 |
73 | if (typeof _serverport === 'number') {
74 | this.serverport = _serverport;
75 | } else {
76 | //console.log("typeof _serverport " + typeof _serverport + " setting to 8081");
77 | this.serverport = 8081;
78 | }
79 |
80 | try {
81 | this.socket = new WebSocket("ws://" + this.hostname + ":" + this.serverport);
82 | console.log(("ws://" + this.hostname + ":" + this.serverport));
83 | } catch (err) {
84 | //console.log(err + "\n" + "Is the p5.serialserver running?");
85 | if (typeof self.errorCallback !== "undefined") {
86 | self.errorCallback("Couldn't connect to the server, is it running?");
87 | }
88 | }
89 |
90 | this.socket.onopen = function(event) {
91 | console.log('opened socket');
92 | serialConnected = true;
93 |
94 | if (typeof self.connectedCallback !== "undefined") {
95 | self.connectedCallback();
96 | }
97 |
98 | if (self.emitQueue.length > 0) {
99 | for (var i = 0; i < self.emitQueue.length; i ++){
100 | //console.log("queue: " + self.emitQueue[i]);
101 | self.emit(self.emitQueue[i]);
102 | }
103 | self.emitQueue = [];
104 | }
105 |
106 | /* Now handled by the queue
107 | if (self.serialport && self.serialoptions) {
108 | // If they have asked for a connect, these won't be null and we should try the connect now
109 | // Trying to hide the async nature of the server connection and just deal with the async nature of serial for the end user
110 | self.emit({
111 | method: 'openserial',
112 | data: {
113 | serialport: self.serialport,
114 | serialoptions: self.serialoptions
115 | }
116 | });
117 | }
118 | */
119 | };
120 |
121 | this.socket.onmessage = function(event) {
122 | //console.log("socketOnMessage");
123 | //console.log(event);
124 |
125 | var messageObject = JSON.parse(event.data);
126 |
127 | // MESSAGE ROUTING
128 | if (typeof messageObject.method !== "undefined") {
129 | if (messageObject.method == 'echo') {
130 | //console.log("echo: " + messageObject.data);
131 | } else if (messageObject.method === "openserial") {
132 | if (typeof self.openCallback !== "undefined") {
133 | self.openCallback();
134 | }
135 | } else if (messageObject.method === "data") {
136 | // Add to buffer, assuming this comes in byte by byte
137 | //console.log("data: " + JSON.stringify(messageObject.data));
138 | self.serialBuffer.push(messageObject.data);
139 |
140 | //console.log(self.serialBuffer.length);
141 |
142 | if (typeof self.dataCallback !== "undefined") {
143 | // Hand it to sketch
144 | if (self.serialBuffer.length >= self.bufferSize) {
145 | self.dataCallback();
146 | }
147 | //console.log(self.serialBuffer.length);
148 | }
149 |
150 | if (typeof self.rawDataCallback !== "undefined") {
151 | self.rawDataCallback(messageObject.data);
152 | }
153 | } else if (messageObject.method === 'list') {
154 |
155 | self.serialportList = messageObject.data;
156 |
157 | if (typeof self.listCallback !== "undefined") {
158 | self.listCallback(messageObject.data);
159 | }
160 | } else if (messageObject.method === "write") {
161 | // Success Callback?
162 | } else if (messageObject.method === "error") {
163 | //console.log(messageObject.data);
164 |
165 | if (typeof self.errorCallback !== "undefined") {
166 | // Hand it to sketch
167 | self.errorCallback(messageObject.data);
168 | }
169 | } else {
170 | // Got message from server without known method
171 | console.log("Unknown Method: " + messageObject);
172 | }
173 | } else {
174 | console.log("Method Undefined: " + messageObject);
175 | }
176 | };
177 |
178 | this.socket.onclose = function(event) {
179 | //console.log("socketOnClose");
180 | //console.log(event);
181 |
182 | if (typeof self.closeCallback !== "undefined") {
183 | self.closeCallback();
184 | }
185 | };
186 |
187 | this.socket.onerror = function(event) {
188 | //console.log("socketOnError");
189 | //console.log(event);
190 |
191 | if (typeof self.errorCallback !== "undefined") {
192 | self.errorCallback();
193 | }
194 | };
195 |
196 | };
197 |
198 | /**
199 | *
200 | * @method emit
201 | * @private
202 | * @return
203 | * @example
204 | *
205 | */
206 | p5.SerialPort.prototype.emit = function(data) {
207 | if (this.socket.readyState == WebSocket.OPEN) {
208 | this.socket.send(JSON.stringify(data));
209 | } else {
210 | this.emitQueue.push(data);
211 | }
212 | };
213 |
214 | /**
215 | * Tells you whether p5 is connected to the serial port.
216 | *
217 | * @method isConnected
218 | * @return {Boolean} true or false
219 | * @example
220 | * var serial; // variable to hold an instance of the serialport library
221 | * var portName = '/dev/cu.usbmodem1411';
222 | *
223 | * function setup() {
224 | * createCanvas(400, 300);
225 | * serial = new p5.SerialPort();
226 | * serial.open(portName);
227 | * println(serial.isConnected());
228 | * }
229 | */
230 | p5.SerialPort.prototype.isConnected = function() {
231 | if (self.serialConnected) { return true; }
232 | else { return false; }
233 | };
234 |
235 | /**
236 | * Lists serial ports available to the server.
237 | * Synchronously returns cached list, asynchronously returns updated list via callback.
238 | * Must be called within the p5 setup() function.
239 | * Doesn't work with the p5 editor's "Run in Browser" mode.
240 | *
241 | * @method list
242 | * @return {Array} array of available serial ports
243 | * @example
244 | * function setup() {
245 | * createCanvas(windowWidth, windowHeight);
246 | * serial = new p5.SerialPort();
247 | * serial.list();
248 | * serial.open("/dev/cu.usbmodem1411");
249 | * }
250 | *
251 | * For full example: Link
252 | * @example
253 | * function printList(portList) {
254 | * // portList is an array of serial port names
255 | * for (var i = 0; i < portList.length; i++) {
256 | * // Display the list the console:
257 | * println(i + " " + portList[i]);
258 | * }
259 | * }
260 | */
261 | p5.SerialPort.prototype.list = function(cb) {
262 | if (typeof cb === 'function') {
263 | this.listCallback = cb;
264 | }
265 | this.emit({
266 | method: 'list',
267 | data: {}
268 | });
269 |
270 | return this.serialportList;
271 | };
272 |
273 | /**
274 | * Opens the serial port to enable data flow.
275 | * Use the {[serialOptions]} parameter to set the baudrate if it's different from the p5 default, 9600.
276 | *
277 | * @method open
278 | * @param {String} serialPort Name of the serial port, something like '/dev/cu.usbmodem1411'
279 | * @param {Object} [serialOptions] Object with optional options as {key: value} pairs.
280 | * Options include 'baudrate'.
281 | * @param {Function} [serialCallback] Callback function when open completes
282 | * @example
283 | * // Change this to the name of your arduino's serial port
284 | * serial.open("/dev/cu.usbmodem1411");
285 | *
286 | * @example
287 | * // All of the following are valid:
288 | * serial.open(portName);
289 | * serial.open(portName, {}, onOpen);
290 | * serial.open(portName, {baudrate: 9600}, onOpen)
291 | *
292 | * function onOpen() {
293 | * print('opened the serial port!');
294 | * }
295 | */
296 | p5.SerialPort.prototype.open = function(_serialport, _serialoptions, cb) {
297 |
298 | if (typeof cb === 'function') {
299 | this.openCallback = cb;
300 | }
301 |
302 | this.serialport = _serialport;
303 |
304 | if (typeof _serialoptions === 'object') {
305 | this.serialoptions = _serialoptions;
306 | } else {
307 | //console.log("typeof _serialoptions " + typeof _serialoptions + " setting to {}");
308 | this.serialoptions = {};
309 | }
310 | // If our socket is connected, we'll do this now,
311 | // otherwise it will happen in the socket.onopen callback
312 | this.emit({
313 | method: 'openserial',
314 | data: {
315 | serialport: this.serialport,
316 | serialoptions: this.serialoptions
317 | }
318 | });
319 | };
320 |
321 | /**
322 | * Sends a byte to a webSocket server which sends the same byte out through a serial port.
323 | * @method write
324 | * @param {String, Number, Array} Data Writes bytes, chars, ints, bytes[], and strings to the serial port.
325 | * @example
326 | * You can use this with the included Arduino example called PhysicalPixel.
327 | * Works with P5 editor as the socket/serial server, version 0.5.5 or later.
328 | * Written 2 Oct 2015 by Tom Igoe. For full example: Link
329 | *
330 | * function mouseReleased() {
331 | * serial.write(outMessage);
332 | * if (outMessage === 'H') {
333 | * outMessage = 'L';
334 | * } else {
335 | * outMessage = 'H';
336 | * }
337 | * }
338 | *
339 | * For full example: Link
340 | * @example
341 | * function mouseDragged() {
342 | * // map the mouseY to a range from 0 to 255:
343 | * outByte = int(map(mouseY, 0, height, 0, 255));
344 | * // send it out the serial port:
345 | * serial.write(outByte);
346 | * }
347 | */
348 | p5.SerialPort.prototype.write = function(data) {
349 | //Writes bytes, chars, ints, bytes[], Strings to the serial port
350 | var toWrite = null;
351 | if (typeof data == "number") {
352 | // This is the only one I am treating differently, the rest of the clauses are meaningless
353 | toWrite = [data];
354 | } else if (typeof data == "string") {
355 | toWrite = data;
356 | } else if (Array.isArray(data)) {
357 | toWrite = data;
358 | } else {
359 | toWrite = data;
360 | }
361 |
362 | this.emit({
363 | method: 'write',
364 | data: toWrite
365 | });
366 | };
367 |
368 | /**
369 | * Returns a number between 0 and 255 for the next byte that's waiting in the buffer.
370 | * Returns -1 if there is no byte, although this should be avoided by first checking available() to see if data is available.
371 | *
372 | * @method read
373 | * @return {Number} Value of the byte waiting in the buffer. Returns -1 if there is no byte.
374 | * @example
375 | * function serialEvent() {
376 | * inByte = int(serial.read());
377 | * byteCount++;
378 | * }
379 | *
380 | * @example
381 | * function serialEvent() {
382 | * // read a byte from the serial port:
383 | * var inByte = serial.read();
384 | * // store it in a global variable:
385 | * inData = inByte;
386 | * }
387 | */
388 | p5.SerialPort.prototype.read = function() {
389 | if (this.serialBuffer.length > 0) {
390 | return this.serialBuffer.shift();
391 | } else {
392 | return -1;
393 | }
394 | };
395 |
396 | /**
397 | * Returns the next byte in the buffer as a char.
398 | *
399 | * @method readChar
400 | * @return {String} Value of the Unicode-code unit character byte waiting in the buffer, converted from bytes. Returns -1 or 0xffff if there is no byte.
401 | * @example
402 | * var inData;
403 | *
404 | * function setup() {
405 | * // callback for when new data arrives
406 | * serial.on('data', serialEvent);
407 | *
408 | * function serialEvent() {
409 | * // read a char from the serial port:
410 | * inData = serial.readChar();
411 | * }
412 | */
413 | p5.SerialPort.prototype.readChar = function() {
414 | if (this.serialBuffer.length > 0) {
415 | /*var currentByte = this.serialBuffer.shift();
416 | console.log("p5.serialport.js: " + currentByte);
417 | var currentChar = String.fromCharCode(currentByte);
418 | console.log("p5.serialport.js: " + currentChar);
419 | return currentChar;
420 | */
421 | return String.fromCharCode(this.serialBuffer.shift());
422 | } else {
423 | return -1;
424 | }
425 | };
426 |
427 | /**
428 | * Returns a number between 0 and 255 for the next byte that's waiting in the buffer, and then clears the buffer of data. Returns -1 if there is no byte, although this should be avoided by first checking available() to see if data is available.
429 | * @method readBytes
430 | * @return {Number} Value of the byte waiting in the buffer. Returns -1 if there is no byte.
431 | * @example
432 | * var inData;
433 | *
434 | * function setup() {
435 | * // callback for when new data arrives
436 | * serial.on('data', serialEvent);
437 | *
438 | * function serialEvent() {
439 | * // read bytes from the serial port:
440 | * inData = serial.readBytes();
441 | * }
442 | */
443 | p5.SerialPort.prototype.readBytes = function() {
444 | if (this.serialBuffer.length > 0) {
445 | var returnBuffer = this.serialBuffer.slice();
446 |
447 | // Clear the array
448 | this.serialBuffer.length = 0;
449 |
450 | return returnBuffer;
451 | } else {
452 | return -1;
453 | }
454 | };
455 |
456 | /**
457 | * Returns all of the data available, up to and including a particular character.
458 | * If the character isn't in the buffer, 'null' is returned.
459 | * The version without the byteBuffer parameter returns a byte array of all data up to and including the interesting byte.
460 | * This is not efficient, but is easy to use.
461 | *
462 | * The version with the byteBuffer parameter is more efficient in terms of time and memory.
463 | * It grabs the data in the buffer and puts it into the byte array passed in and returns an integer value for the number of bytes read.
464 | * If the byte buffer is not large enough, -1 is returned and an error is printed to the message area.
465 | * If nothing is in the buffer, 0 is returned.
466 | *
467 | * @method readBytesUntil
468 | * @param {[byteBuffer]}
469 | * @return {[Number]} [Number of bytes read]
470 | * @example
471 | * // All of the following are valid:
472 | * charToFind.charCodeAt();
473 | * charToFind.charCodeAt(0);
474 | * charToFind.charCodeAt(0, );
475 | */
476 | p5.SerialPort.prototype.readBytesUntil = function(charToFind) {
477 | console.log("Looking for: " + charToFind.charCodeAt(0));
478 | var index = this.serialBuffer.indexOf(charToFind.charCodeAt(0));
479 | if (index !== -1) {
480 | // What to return
481 | var returnBuffer = this.serialBuffer.slice(0, index + 1);
482 | // Clear out what was returned
483 | this.serialBuffer = this.serialBuffer.slice(index, this.serialBuffer.length + index);
484 | return returnBuffer;
485 | } else {
486 | return -1;
487 | }
488 | };
489 |
490 | /**
491 | * Returns all the data from the buffer as a String.
492 | * This method assumes the incoming characters are ASCII.
493 | * If you want to transfer Unicode data: first, convert the String to a byte stream in the representation of your choice (i.e. UTF8 or two-byte Unicode data).
494 | * Then, send it as a byte array.
495 | *
496 | * @method readString
497 | * @return
498 | * @example
499 | *
500 | *
501 | *
502 | *
503 | */
504 | p5.SerialPort.prototype.readString = function() {
505 | //var returnBuffer = this.serialBuffer;
506 | var stringBuffer = [];
507 | //console.log("serialBuffer Length: " + this.serialBuffer.length);
508 | for (var i = 0; i < this.serialBuffer.length; i++) {
509 | //console.log("push: " + String.fromCharCode(this.serialBuffer[i]));
510 | stringBuffer.push(String.fromCharCode(this.serialBuffer[i]));
511 | }
512 | // Clear the buffer
513 | this.serialBuffer.length = 0;
514 | return stringBuffer.join("");
515 | };
516 |
517 | /**
518 | * Returns all of the data available as an ASCII-encoded string.
519 | *
520 | * @method readStringUntil
521 | * @param {String} stringToFind String to read until.
522 | * @return {String} ASCII-encoded string until and not including the stringToFind.
523 | * @example
524 | *
525 | * For full example: Link
526 | *
527 | * var serial1 = new p5.SerialPort();
528 | * var serial2 = new p5.SerialPort();
529 | * var input1 = '';
530 | * var input2 = '';
531 | *
532 | * function serialEvent(){
533 | * data = serial1.readStringUntil('\r\n');
534 | * if (data.length > 0){
535 | * input1 = data;
536 | * }
537 | * }
538 | *
539 | * function serial2Event() {
540 | * var data = serial2.readStringUntil('\r\n');
541 | * if (data.length > 0){
542 | * input2 = data;
543 | * }
544 | * }
545 | */
546 | p5.SerialPort.prototype.readStringUntil = function(stringToFind) {
547 |
548 | var stringBuffer = [];
549 | //console.log("serialBuffer Length: " + this.serialBuffer.length);
550 | for (var i = 0; i < this.serialBuffer.length; i++) {
551 | //console.log("push: " + String.fromCharCode(this.serialBuffer[i]));
552 | stringBuffer.push(String.fromCharCode(this.serialBuffer[i]));
553 | }
554 | stringBuffer = stringBuffer.join("");
555 | //console.log("stringBuffer: " + stringBuffer);
556 |
557 | var returnString = "";
558 | var foundIndex = stringBuffer.indexOf(stringToFind);
559 | //console.log("found index: " + foundIndex);
560 | if (foundIndex > -1) {
561 | returnString = stringBuffer.substr(0, foundIndex);
562 | this.serialBuffer = this.serialBuffer.slice(foundIndex + stringToFind.length);
563 | }
564 | //console.log("Sending: " + returnString);
565 | return returnString;
566 | };
567 |
568 |
569 | /**
570 | * Returns all of the data available as an ASCII-encoded string until a line break is encountered.
571 | *
572 | * @method readLine
573 | * @return {String} ASCII-encoded string
574 | * @example
575 | *
576 | * You can use this with the included Arduino example called AnalogReadSerial.
577 | * Works with P5 editor as the socket/serial server, version 0.5.5 or later.
578 | * Written 2 Oct 2015 by Tom Igoe. For full example: Link
579 | *
580 | * function gotData() {
581 | * var currentString = serial.readLine(); // read the incoming data
582 | * trim(currentString); // trim off trailing whitespace
583 | *
584 | * if (!currentString) return; { // if the incoming string is empty, do no more
585 | * console.log(currentString);
586 | * }
587 | *
588 | * if (!isNaN(currentString)) { // make sure the string is a number (i.e. NOT Not a Number (NaN))
589 | * textXpos = currentString; // save the currentString to use for the text position in draw()
590 | * }
591 | * }
592 | */
593 | p5.SerialPort.prototype.readLine = function() {
594 | return this.readStringUntil("\r\n");
595 | };
596 |
597 | /**
598 | * Returns the number of bytes available.
599 | *
600 | * @method available
601 | * @return {Number} The length of the serial buffer array, in terms of number of bytes in the buffer.
602 | * @example
603 | * function draw() {
604 | * // black background, white text:
605 | * background(0);
606 | * fill(255);
607 | * // display the incoming serial data as a string:
608 | * var displayString = "inByte: " + inByte + "\t Byte count: " + byteCount;
609 | * displayString += " available: " + serial.available();
610 | * text(displayString, 30, 60);
611 | * }
612 | * */
613 | p5.SerialPort.prototype.available = function() {
614 | return this.serialBuffer.length;
615 | };
616 |
617 | /**
618 | * Returns the last byte of data from the buffer.
619 | *
620 | * @method last
621 | * @return {Number}
622 | * @example
623 | *
624 | * */
625 | p5.SerialPort.prototype.last = function() {
626 | //Returns last byte received
627 | var last = this.serialBuffer.pop();
628 | this.serialBuffer.length = 0;
629 | return last;
630 | };
631 |
632 | /**
633 | * Returns the last byte of data from the buffer as a char.
634 | *
635 | * @method lastChar
636 | * @example
637 | *
638 | * */
639 | p5.SerialPort.prototype.lastChar = function() {
640 | return String.fromCharCode(this.last());
641 | };
642 |
643 | /**
644 | * Clears the underlying serial buffer.
645 | *
646 | * @method clear
647 | * @example
648 | */
649 | p5.SerialPort.prototype.clear = function() {
650 | //Empty the buffer, removes all the data stored there.
651 | this.serialBuffer.length = 0;
652 | };
653 |
654 | /**
655 | * Stops data communication on this port.
656 | * Use to shut the connection when you're finished with the Serial.
657 | *
658 | * @method stop
659 | * @example
660 | *
661 | */
662 | p5.SerialPort.prototype.stop = function() {
663 | };
664 |
665 | /**
666 | * Tell server to close the serial port. This functions the same way as serial.on('close', portClose).
667 | *
668 | * @method close
669 | * @param {String} name of callback
670 | * @example
671 | *
672 | * var inData;
673 | *
674 | * function setup() {
675 | * serial.open(portOpen);
676 | * serial.close(portClose);
677 | * }
678 | *
679 | * function portOpen() {
680 | * println('The serial port is open.');
681 | * }
682 | *
683 | * function portClose() {
684 | * println('The serial port closed.');
685 | * }
686 | */
687 | p5.SerialPort.prototype.close = function(cb) {
688 | //
689 | if (typeof cb === 'function') {
690 | this.closeCallback = cb;
691 | }
692 | this.emit({
693 | method: 'close',
694 | data: {}
695 | });
696 | };
697 |
698 | /**
699 | * // Register callback methods from sketch
700 | *
701 | */
702 | p5.SerialPort.prototype.onData = function(_callback) {
703 | this.on('data',_callback);
704 | };
705 |
706 | p5.SerialPort.prototype.onOpen = function(_callback) {
707 | this.on('open',_callback);
708 | };
709 |
710 | p5.SerialPort.prototype.onClose = function(_callback) {
711 | this.on('close',_callback);
712 | };
713 |
714 | p5.SerialPort.prototype.onError = function(_callback) {
715 | this.on('error',_callback);
716 | };
717 |
718 | p5.SerialPort.prototype.onList = function(_callback) {
719 | this.on('list',_callback);
720 | };
721 |
722 | p5.SerialPort.prototype.onConnected = function(_callback) {
723 | this.on('connected',_callback);
724 | };
725 |
726 | p5.SerialPort.prototype.onRawData = function(_callback) {
727 | this.on('rawdata',_callback);
728 | };
729 |
730 | // Version 2
731 | p5.SerialPort.prototype.on = function(_event, _callback) {
732 | if (_event == 'open') {
733 | this.openCallback = _callback;
734 | } else if (_event == 'data') {
735 | this.dataCallback = _callback;
736 | } else if (_event == 'close') {
737 | this.closeCallback = _callback;
738 | } else if (_event == 'error') {
739 | this.errorCallback = _callback;
740 | } else if (_event == 'list') {
741 | this.listCallback = _callback;
742 | } else if (_event == 'connected') {
743 | this.connectedCallback = _callback;
744 | } else if (_event == 'rawdata') {
745 | this.rawDataCallback = _callback;
746 | }
747 | };
748 | }));
749 |
750 | // EOF
751 |
--------------------------------------------------------------------------------
/html/scope.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ginscope
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/http-server.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var http-server = require('./node_modules/http-server/');
4 |
5 |
6 | //console.log("p5.serialserver is running");
7 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | //https://docs.npmjs.com/getting-started/creating-node-modules
2 | console.log('Hello');
3 | /*
4 | * Todo start serial port and http-server from here
5 | *
6 | *
7 | */
8 | exports.printMsg = function() {
9 | console.log("maybe start p5.serialport and http-server from here");
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ginscope",
3 | "version": "0.0.1",
4 | "description": "Arduino based Html Instruments",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/soholt/ginscope.git"
12 | },
13 | "keywords": [
14 | "arduino",
15 | "serial",
16 | "oscilloscope",
17 | "fft",
18 | "function",
19 | "generator",
20 | "html",
21 | "sci",
22 | "instruments"
23 | ],
24 | "author": "Gintaras Valatka",
25 | "license": "todo",
26 | "bugs": {
27 | "url": "https://github.com/soholt/ginscope/issues"
28 | },
29 | "homepage": "https://github.com/soholt/ginscope#readme",
30 | "dependencies": {
31 | "http-server": "^0.9.0",
32 | "p5.serialserver": "0.0.24"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------