Information | Donate |
---|---|
knowledge that doesn't stick well or takes some time to settle in my brain. i come here often to look at the stuff :] |
This repository is just a dump of things I occasionally need in daily problem solving but I am too lazy to properly memorize or whatever. I mainly work with Processing (Java), openFrameworks (C++) and Arduino (C/C++) but I have written examples in a way that they are universally understandable for beginners.
Sometimes you have to go back and forth between x/y coordinates and a linear assortment of data, for example within an ArrayList or some similar list-like data structure.
x and y coordinates to uni-dimensional coordinate:
int uniDimensional = y*width+x;
uni-dimensional coordinate to x and y coordinate:
int x = (int)i%width;
int y = (int)i/width;
width= eg. if you have a 4×3 grid with data/objects/images etc. then 4 is your width
i= your current index, for example within a for-loop
remap range (-1,1) to (0,1)
example: -1 + 1 = 0 * 0,5 -> 0 -0.5 + 1 = 0.5 * 0.5 -> 0.25 0 + 1 = 1 * 0.5 -> 0.5 0.5 + 1 = 1,5 * 0.5 -> 0.75 1 + 1 = 2 * 0,5 -> 1
float remap(float val) {
return (val + 1) * 0.5;
}
Please check out my Base64 encoding/decoding for Images in Processing repository on this matter. It's easy to use!
There are many fun ways to avoid delay() functions in Processing and Arduino. Beginners listen up!
positive delta timing:
long timestamp = 0;
long interval = 1000; // 1000 milliseconds = 1 second
if(millis() - timestamp > interval) {
timestamp = millis();
// run code within this block every 1 second!
}
negating delta timing:
long timestamp = 0;
long interval = 1000; // 1000 milliseconds = 1 second
if(millis() - timestamp < interval) return;
timestamp = millis();
// run code within this block every 1 second!
// it's just a different way of writing code, maybe a bit faster?
// the delta timing cancels your loop, e.g. draw() in Processing or loop() within Arduino
// also useful when used with Finite State-machines
timestamp class
import java.util.Date;
class Timestamp {
public Timestamp() {
}
long getTimestamp() {
Date d = new Date();
long current = d.getTime()/1000;
return current;
}
String getTime() {
return "["+ hour()+":"+minute()+":"+second()+"] ";
}
String getDatePrefix() {
return (year() + "" + month() + "" + day() + "_");
}
}
usage:
Timestamp ts;
void setup() {
ts = new Timestamp();
println(ts.getTimestamp());
}
void draw() {
}
A finite-state machine, FSM or State machine can help you structure your code way much better.
It will help you entangle code and to abstract your ideas into a code blocks. With the help of a switch-case block we can then structure our program better. If certain criteria match within the blocks, we can move forward to another block or remain within the block if needed.
Processing example of a Finite-state machine:
int state = INIT;
static final int INIT = 0;
static final int NEXT = 1;
static final int RANDOM = 2;
void setState(int _state) {
state = _state;
}
void stateMachine(int state) {
switch(state) {
case INIT:
// do something here
if(certainCriteriaIsMe) {
setState(NEXT);
}
break;
case NEXT:
if(millis() - timestamp < interval) return;
timestamp = millis();
// code in the NEXT state is only executed for example every 1 second, as we have seen in Delta timing
setState(RANDOM);
break;
case RANDOM:
// do some cool stuff.
// let's return to our first state then
setState(INIT);
break;
}
}
Arduino example:
#define INTRO 0
#define RUN 1
int state = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Serial.println("setup");
}
void loop() {
stateMachine();
}
void stateMachine() {
switch(state) {
case INTRO:
Serial.println("intro");
state = RUN;
break;
case RUN:
Serial.println("run");
break;
}
}
( relevant for microcontrollers with embedded C language but no floating point alu ) https://embeddedgurus.com/stack-overflow/2011/02/efficient-c-tip-13-use-the-modulus-operator-with-caution/
tl;dr
C = A % B is equivalent to C = A - B * (A / B)
In Arduino you cannot natively map a float value. Period. Here is a function that fixes this issue. Credit: jonasotto.de / julian-h.de
// original comment on this issue // for arduino :( // arduino is stupid. // so jonasotto.de and me wrote this. // end of problems.
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
set() is faster than image() when drawing untransformed images.
put more "data" into a single color channel with bitwise encoding
let rotation = 1; // 01 (2 bits)
let offset = 5; // 101 (3 bits)
let boolValue = 1; // 1 (1 bit)
let imageSet = 4; // 100 (3 bits)
// Encoding the values
let encodedValue = (rotation << 6) | (offset << 3) | (boolValue << 2) | imageSet;
console.log("Encoded Value:", encodedValue); // This should output the combined value
// Decoding the values
let decodedRotation = (encodedValue >> 6) & 0b11; // 2 bits for rotation
let decodedOffset = (encodedValue >> 3) & 0b111; // 3 bits for offset
let decodedBool = (encodedValue >> 2) & 0b1; // 1 bit for bool
let decodedImageSet = encodedValue & 0b111; // 3 bits for image set
console.log("Decoded Values:", decodedRotation, decodedOffset, decodedBool, decodedImageSet);
Via Jonas from https://github.com/gre/smoothstep/blob/master/index.js
module.exports = function smoothstep (min, max, value) {
var x = Math.max(0, Math.min(1, (value-min)/(max-min)));
return x*x*(3 - 2*x);
};
Original source: https://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
int ledPin = 9;
float speed = PI/2; // anything from PI to PI/2 goes here
void setup() {
}
void loop() {
float val = (exp(sin(millis()/2000.0*speed)) - 0.36787944)*108.0;
analogWrite(ledPin, val);
}
with a Processing example:
float[] calculateAspectRatioFit(float srcWidth, float srcHeight, float maxWidth, float maxHeight) {
//float[] result;
float ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
float result[] = {srcWidth*ratio, srcHeight*ratio};
//return { width: srcWidth*ratio, height: srcHeight*ratio };
return result;
}
usage:
PImage p = loadImage("yourImage.jpg");
float[] aspect = calculateAspectRatioFit(setWidth, setHeight, 1280, 720);
surface.setSize((int)aspect[0], (int)aspect[1]);
Integer-ratio scaling aka "Quickly resize an image without that weird smudgy interpolation" (aka "sans smoothing")
PImage img;
PImage canvas;
PGraphics dd;
void setup()
{
size(200,200);
img = loadImage("base.png");
image(img,0,0);
dd = createGraphics(2*img.width,2*img.height);
dd.noSmooth();
dd.beginDraw();
dd.clear();
dd.endDraw();
noLoop();
}
void draw()
{
canvas = img.get(0,0,width,height);
canvas.loadPixels();
dd.beginDraw();
dd.loadPixels();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
dd.set(x*2, y*2, canvas.get(x, y));
dd.set(x*2+1, y*2+1, canvas.get(x, y));
dd.set(x*2+1, y*2, canvas.get(x, y));
dd.set(x*2, y*2+1, canvas.get(x, y));
}
}
dd.updatePixels();
dd.endDraw();
dd.save("zoom.png");
println("..finished.");
}
boolean big = true;
void setup(){
fullScreen();
surface.setResizable(true);
surface.setSize(600, 400);
}
void draw(){
rect(200, 200, 20, 20);
}
void mousePressed(){
if(big) {
surface.setSize(600,400);
big = false;
} else {
surface.setSize(displayWidth,displayHeight);
big = true;
}
}
color averageColor(PImage source, float x, float y, float w, float h) {
PImage temp = source.get((int)x, (int)y, (int)w, (int)h);
temp.loadPixels();
int r = 0;
int g = 0;
int b = 0;
for (int i=0; i<temp.pixels.length; i++) {
color c = temp.pixels[i];
r += c>>16&0xFF;
g += c>>8&0xFF;
b += c&0xFF;
}
// return the mean of the R, G and B conponents
r /= temp.pixels.length;
g /= temp.pixels.length;
b /= temp.pixels.length;
return color(r, g, b);
}
color averageColorCircular(PImage img, int x, int y, int radius) {
float r = 0;
float g = 0;
float b = 0;
int num = 0;
/* Iterate through a bounding box in which the circle lies */
for (int i = x - radius; i < x + radius; i++) {
for (int j = y - radius; j < y + radius; j++) {
// if pixel is outside of canvas: skip
if (i < 0 || i >= width || j < 0 || j >= height)
continue;
// if pixel is outside of circle: skip
if (dist(x, y, i, j) > r)
continue;
color c = img.get(i, j);
r += c>>16&0xFF;
g += c>>8&0xFF;
b += c&0xFF;
num++;
}
}
// return the mean of the R, G, and B components
return color(r/num, g/num, b/num);
}
Video explainer by minutephysics: Computer Color is Broken instead of using for example:
r += c>>16&0xFF;
r /= temp.pixels.length;
sum the squares of components and return th sqrt of the squared R, G and B sums:
r += (c>>16&0xFF) * (c>>16&0xFF);
r /= sqrt(r/temp.pixels.length);
from https://github.com/sighack/easing-functions/tree/master/code/easing
/* The map2() function supports the following easing types */
final int LINEAR = 0;
final int QUADRATIC = 1;
final int CUBIC = 2;
final int QUARTIC = 3;
final int QUINTIC = 4;
final int SINUSOIDAL = 5;
final int EXPONENTIAL = 6;
final int CIRCULAR = 7;
final int SQRT = 8;
/* When the easing is applied (in, out, or both) */
final int EASE_IN = 0;
final int EASE_OUT = 1;
final int EASE_IN_OUT = 2;
/*
* A map() replacement that allows for specifying easing curves
* with arbitrary exponents.
*
* value : The value to map
* start1: The lower limit of the input range
* stop1 : The upper limit of the input range
* start2: The lower limit of the output range
* stop2 : The upper limit of the output range
* type : The type of easing (see above)
* when : One of EASE_IN, EASE_OUT, or EASE_IN_OUT
*/
float map2(float value, float start1, float stop1, float start2, float stop2, int type, int when) {
float b = start2;
float c = stop2 - start2;
float t = value - start1;
float d = stop1 - start1;
float p = 0.5;
switch (type) {
case LINEAR:
return c*t/d + b;
case SQRT:
if (when == EASE_IN) {
t /= d;
return c*pow(t, p) + b;
} else if (when == EASE_OUT) {
t /= d;
return c * (1 - pow(1 - t, p)) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*pow(t, p) + b;
return c/2 * (2 - pow(2 - t, p)) + b;
}
break;
case QUADRATIC:
if (when == EASE_IN) {
t /= d;
return c*t*t + b;
} else if (when == EASE_OUT) {
t /= d;
return -c * t*(t-2) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
}
break;
case CUBIC:
if (when == EASE_IN) {
t /= d;
return c*t*t*t + b;
} else if (when == EASE_OUT) {
t /= d;
t--;
return c*(t*t*t + 1) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*t*t*t + b;
t -= 2;
return c/2*(t*t*t + 2) + b;
}
break;
case QUARTIC:
if (when == EASE_IN) {
t /= d;
return c*t*t*t*t + b;
} else if (when == EASE_OUT) {
t /= d;
t--;
return -c * (t*t*t*t - 1) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*t*t*t*t + b;
t -= 2;
return -c/2 * (t*t*t*t - 2) + b;
}
break;
case QUINTIC:
if (when == EASE_IN) {
t /= d;
return c*t*t*t*t*t + b;
} else if (when == EASE_OUT) {
t /= d;
t--;
return c*(t*t*t*t*t + 1) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*t*t*t*t*t + b;
t -= 2;
return c/2*(t*t*t*t*t + 2) + b;
}
break;
case SINUSOIDAL:
if (when == EASE_IN) {
return -c * cos(t/d * (PI/2)) + c + b;
} else if (when == EASE_OUT) {
return c * sin(t/d * (PI/2)) + b;
} else if (when == EASE_IN_OUT) {
return -c/2 * (cos(PI*t/d) - 1) + b;
}
break;
case EXPONENTIAL:
if (when == EASE_IN) {
return c * pow( 2, 10 * (t/d - 1) ) + b;
} else if (when == EASE_OUT) {
return c * ( -pow( 2, -10 * t/d ) + 1 ) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2 * pow( 2, 10 * (t - 1) ) + b;
t--;
return c/2 * ( -pow( 2, -10 * t) + 2 ) + b;
}
break;
case CIRCULAR:
if (when == EASE_IN) {
t /= d;
return -c * (sqrt(1 - t*t) - 1) + b;
} else if (when == EASE_OUT) {
t /= d;
t--;
return c * sqrt(1 - t*t) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return -c/2 * (sqrt(1 - t*t) - 1) + b;
t -= 2;
return c/2 * (sqrt(1 - t*t) + 1) + b;
}
break;
};
return 0;
}
/*
* A map() replacement that allows for specifying easing curves
* with arbitrary exponents.
*
* value : The value to map
* start1: The lower limit of the input range
* stop1 : The upper limit of the input range
* start2: The lower limit of the output range
* stop2 : The upper limit of the output range
* v : The exponent value (e.g., 0.5, 0.1, 0.3)
* when : One of EASE_IN, EASE_OUT, or EASE_IN_OUT
*/
float map3(float value, float start1, float stop1, float start2, float stop2, float v, int when) {
float b = start2;
float c = stop2 - start2;
float t = value - start1;
float d = stop1 - start1;
float p = v;
float out = 0;
if (when == EASE_IN) {
t /= d;
out = c*pow(t, p) + b;
} else if (when == EASE_OUT) {
t /= d;
out = c * (1 - pow(1 - t, p)) + b;
} else if (when == EASE_IN_OUT) {
t /= d/2;
if (t < 1) return c/2*pow(t, p) + b;
out = c/2 * (2 - pow(2 - t, p)) + b;
}
return out;
}
From PaulStoffregen: UART + I2C display --> not working over 1200 baud rate for UART
Sending Microcontroller
int pin0 = A0;
int pin1 = A1;
unsigned long my_time;
int reset_switch = 16;
int reset_pin = 0;
void setup() {
// put your setup code here, to run once:
pinMode(reset_switch, INPUT_PULLUP);
Serial.begin(9600);
Serial1.begin(1000000);
Serial.println("Start Send");
if (Serial.available() > 0) {
Serial.clear();
delay(50);
}
if (Serial1.available() > 0) {
Serial1.clear();
delay(50);
}
}
void loop() {
static int messageCount=0;
reset_pin = digitalRead(reset_switch);
if (reset_pin == LOW) {
do_reset();
}
int val0 = map(analogRead(pin0), 0, 1023, 0, 1100);
int val1 = map(analogRead(pin1), 0, 1023, 0, 200);
my_time = millis();
Serial1.print("<");
Serial1.print(val0);
Serial1.print(",");
Serial1.print(val1);
Serial1.print(">");
Serial.print(reset_pin);
Serial.print("\t");
Serial.print(my_time);
Serial.print("\t");
Serial.print(val0);
Serial.print("\t");
Serial.println(val1);
// once every 5000 messages, allow a brief silent time
// for the receiver to detect line idle and then start bit
if (++messageCount >= 5000) {
Serial1.flush(); // wait for buffered data to transmit
delayMicroseconds(10); // then wait approx 10 bit times
messageCount = 0;
}
}
void do_reset() {
// send reboot command -----
SCB_AIRCR = 0x05FA0004;
}
Receiving Microcontroller
//Receiver Code
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing
int integerFromPC = 0;
float floatFromPC = 0.0;
boolean newData = false;
int reset_switch = 16;
int reset_pin = 0;
void setup() {
// put your setup code here, to run once:
pinMode(reset_switch, INPUT_PULLUP);
Serial.begin(9600);
Serial1.begin(1000000);
Serial.println("Start Receive");
if (Serial.available() > 0) {
Serial.clear();
delay(50);
}
if (Serial1.available() > 0) {
Serial1.clear();
delay(50);
}
Serial.println("start");
}
void loop() {
reset_pin = digitalRead(reset_switch);
if (reset_pin == LOW) {
do_reset();
}
recvWithStartEndMarkers();
if (newData == true) {
strcpy(tempChars, receivedChars);
// this temporary copy is necessary to protect the original data
// because strtok() used in parseData() replaces the commas with \0
parseData();
showParsedData();
newData = false;
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
while (Serial1.available() > 0 && newData == false) {
rc = Serial1.read();
//Serial.println((char)rc);
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
else {
Serial.print("Unexpected character: ");
Serial.println(rc);
}
}
}
//============
void parseData() { // split the data into its parts
char * strtokIndx; // this is used by strtok() as an index
strtokIndx = strtok(tempChars, ","); // get the first part - the string
integerFromPC = atoi(strtokIndx); // convert this part to an integer
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx); // convert this part to a float
}
//============
void showParsedData() {
Serial.print("Integer ");
Serial.println(integerFromPC);
Serial.print("Float ");
Serial.println(floatFromPC);
}
void do_reset() {
// send reboot command -----
SCB_AIRCR = 0x05FA0004;
}
var grid = 2;
var gridOffset = grid / 2;
function snap(op) {
var cell = Math.round((op - gridOffset) / grid);
return cell * grid + gridOffset;
}
x = snap(x);
this is an easy one but i didn't think about it yesterday: what if you want to get the aspect ratio of an image? you divide the width by the height, right? width=200, height=100 = width/height = 200/100 = 2 = 2:1 aspect ratio. what if you have something more exotic or even something much more familiar? width=1920, height=1080 = 1920/1080 = 1.777777778 = 1.7778:1 ? it's technically correct, but we would rather see the "16:9" here. what we are missing is the GCD (greatest common divisor) or GGT (größter gemeinsamer teiler) to normalize the values.
we can use either of these two implementations:
// greatest common divisor. standard implementation.
int gcd(int a, int b) {
if (a == 0) return b;
while (b != 0) {
if (a > b) a = a - b;
else b = b - a;
}
return a;
}
// greatest common divisor. modified version
int gcd2(int a, int b) {
int h;
while (b != 0) {
h = a % b;
a = b;
b = h;
}
return a;
}
then use the following function (not fully implemented tho)
void getAspectRatio(int _w, int _h) {
int divisor = gcd(_w, _h);
println((_w/divisor) + ":" + (_h/divisor));
}
getAspectRatio(1920, 1080);
getAspectRatio(200, 100);
getAspectRatio(1690, 2000);
quick start via shiffman:
- https://github.com/sighack/easing-functions/tree/master/code/easing
- [https://www.youtube.com/watch?v=pI2gvl9sdtE](How to Make a Processing (Java) Library Part 1)
- [https://www.youtube.com/watch?v=U0TGZCEWn8g](How to Make a Processing (Java) Library Part 2)
a catalog of core.jar functions: https://processing.github.io/processing-javadocs/core/index-all.html
follow everything in the tutorial, but if you want to compile and have it run in Processing4+ please change the following in resources/build.xml
<target name="copyToSketchbook">
…
<fileset dir="${project.tmp}/${project.name}"/>
</target>
and modify it to:
<target name="copyToSketchbook">
…
<fileset dir="${project.tmp}/${project.name}">
<!--
https://github.com/processing/processing-library-template/pull/31
To avoid the following error in Processing 4.0 and newer:
The library "${project.name}" cannot be used
because it contains the processing.core libraries.
Please contact the library author for an update.
-->
<exclude name="**/core.jar"/>
</fileset>
</target>
this will exclude the core.jar from compiling with the library and ensure that Processing4 can load and run your library.
- add the .jar-file that you need to your eclipse project: drag & drop file to /lib
- in /resources/build.properties add the jar to the local classpath:
classpath.local.include=core.jar,oscp5.jar
- in build.xml add the jar as an exclude to the block:
<target name="copyToSketchbook">
…
<fileset dir="${project.tmp}/${project.name}">
…
// possible other excludes
<exclude name="**/oscP5.jar"/> // or yourFileName.jar
</fileset>
</target>
- in your java-class add the imports to the header:
// the standard stuff
package display.library;
import processing.core.*;
// the lib import
import oscP5.*;
import netP5.*;
- in your processing sketches import your library but also the imports that you did in step 4. this should do the trick for now.
int modWrap(int i, int max) {
return ((i % max) + max) % max;
}
int modWrap(int a, int b){
return (a+(ceil(abs(a)/b))*b) % b;
}
[ ] autostart [ ] teamviewer [ ] no mouse in fullscreen [ ] remove taskbar —> https://forums.raspberrypi.com/viewtopic.php?t=325477 [ ] overlayroot fs (file protection)
method 1 // have to update later with the proper example and a also watchdog example // these are just "notes" for now
- https://discourse.processing.org/t/autostart-a-program/24368/11
- https://forums.raspberrypi.com/viewtopic.php?f=91&t=287334&p=1738121&hilit=humidity#p1738121
- https://forums.raspberrypi.com/viewtopic.php?t=294014 (this is the most important for now)
- https://forums.raspberrypi.com/viewtopic.php?t=230911
i believe this goes into the lxde autostart file (from https://forum.processing.org/two/discussion/22968/#Comment_100027)
/usr/local/bin/processing-java --sketch=/home/pi/sketchbook/sketchname --run
example
/home/curtain/Downloads/processing-4.0b8/processing-java --sketch=/home/curtain/Desktop/sketchname --run
method 2 (https://u-labs.de/portal/3-wege-1-verbotener-um-programme-auf-dem-raspberry-pi-os-automatisch-zu-starten/) via Desktop entry / XDKG Autostart.
mkdir ~/.config/autostart
nano ~/.config/autostart/[filename_or_appname].desktop
Then edit that file
[Desktop Entry]
Type=Application
Name=Run Processing
Exec=[/usr/local/bin/processing-java --sketch=/home/pi/sketchbook/sketchname --run]
Terminal=false
Method 3 ( Research ) https://forums.raspberrypi.com/viewtopic.php?t=294014
https://learn.adafruit.com/read-only-raspberry-pi/overview
https://forums.raspberrypi.com/viewtopic.php?t=358654
>> in file .config/wf-panel-pi.ini ADD the following:
>> autohide=true
>> autohide_duration=500
>>
>> Autohide duration is the time in Milliseconds before hiding the panel...(this is 1/2 second)
/boot/config.txt
dtparam=audio=off
force_eeprom_read=0
dtoverlay=vc4-kms-v3d,noaudio
dtoverlay=hifiberry-dacplus,slave
$pidof java // this will return the $kill SIGKILL
create a two-dimensional empty array quickly:
var arr = Array.from(Array(2), () => new Array(4));
before using this, have your loadPixels on your canvas/buffer already loaded, here eg. randomStateBuffer
function pset(x, y, d, v) {
let off = (int(y) * int(randomStateBuffer.width) + int(x)) * int(d) * 4;
randomStateBuffer.pixels[off] = v;
randomStateBuffer.pixels[off + 1] = v;
randomStateBuffer.pixels[off + 2] = v;
randomStateBuffer.pixels[off + 3] = v;
}
function pget(x, y, d) {
let off = (int(y) * int(randomStateBuffer.width) + int(x)) * int(d) * 4;
let components = [
randomStateBuffer.pixels[off],
randomStateBuffer.pixels[off + 1],
randomStateBuffer.pixels[off + 2],
randomStateBuffer.pixels[off + 3]
];
return components;
}
need a mapping function like in Processing / p5.js for a shader?
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
Uniforms vs. Consts: Huge performance when used consts. I often turn all uniforms to consts to speed things up as part of my step of loading the shader. Uniforms are for things that change every frame, shader code is what stays constant every frame
"Const and defines both can be use by compilers to make significant optimizations. They could end up removing sections of code, unfolding for loops, etc. All those are call “compilation time” optimizations… the resulting binary that gets execute in the GPU is hyper efficient. Uniform impact “run time” efficiency or/and optimizations. For example using if statements that check’s the state of a uniform in theory don’t produce logic branching… but still slower than a const and similar to a #ifdef. (bearbeitet) #defines are useful for meta programming (over const) because allows you to do some templeting. Very useful for reducing code size, sometimes at the expense of making your code a bit harder to read or debug. I personally use them a lot in lygia.xyz because of the flexibility of templating with macros"
Defines:
#define R return
~ph precision highp~uf uniform~T return~S smoothstep~N normalize~L length~cl clamp~Z floor~B abs~U min~G max~W vec3~V vec2~X vec4~F float~E 1e-2~MAXD 240~TAU ${TAU}~H0 uvec4(9,7,5,3)*23205u~D gl_VertexID
where every ~ gets replaced with a \n#define