Files
GenerateMe/ultimateSort/ultimateSort.pde
2015-09-16 15:28:51 +02:00

967 lines
27 KiB
Plaintext
Executable File

// Ultimate Sort Machine
// Tomasz Sulej, generateme.blog@gmail.com, http://generateme.tumblr.com
// Licence: http://unlicense.org/
// Usage:
// * press SPACE to save
// * press 'a' to save animation frames
// * press 'b' for batch process files from set folder
// Read ALL (I repeat ALL) text below until written "STOP READING HERE" :)
// This is quite complicated machine for sort images in many various ways.
// I'll try to explain how to use it and what all this stuff means.
//
// Here is the idea:
// 1. You select pixels which are involved in sorting. Selection is made using few rules and logical operators.
// You can think about this as more sophisticated threshold. Remember only chosen pixels are involved in sorting.
// That means that any gaps between pixels are left untouched.
// 2. You can define "select and sort" process as many times as you want
// 3. You can sort pixels partly. That means you can interrupt sorting in defined moment.
// It's very crutial, every sorting method has its own patterns and you can achieve various results
// 4. You can sort in ascending/descending order
// 5. You can sort negated pixels
// 6. You can "lay down" result in 4 direction: from left to right, from top to bottom and opposite. It is different than sorting order (and can be combined)
// 7. Having above 3 possibilities you can achive 16 possible way to produce result using one single sorting algorithm.
// 8. Finally, you can use many different sorting algorithms (+3 extra bonus nonsorting methods :))
// Ok, that was idea and now let's go through configuration
// First, set up filename you want to operate, remember when you see the result you can save it by pressing SPACE
String filename = "test";
String fileext = ".jpg";
String foldername = "./"; // folder used for batch process also
// Second, set up window size (image size can be huge, this parameter will help you to see it)
int max_display_size = 800;
// Third, you can choose to blend result with original image
boolean do_blend = false;
int blend_mode = OVERLAY; // blend type
// Here are all blend modes available
final static int[] blends = {
ADD, SUBTRACT, DARKEST, LIGHTEST, DIFFERENCE, EXCLUSION, MULTIPLY, SCREEN, OVERLAY, HARD_LIGHT, SOFT_LIGHT, DODGE, BURN
};
// If you want to animate process, set number of frames you want to save
int animation_frames_count = 60;
int animation_easing = LINEAR; // LINEAR or CUBIC
// We are near to the crucial part, configuration of sorting machine
// But first look at block below, you need to remember one thing here: name of the variable ie. HUE_RED, HUE_CYAN_W, etc...
// See, there is also name 'TRUE'
// It will be explained later, skip now
IEval HUE_RED_W = OR(RANGE(HUE, hs(0), hs(20)), RANGE(HUE, hs(346), hs(359)));
IEval HUE_RED = OR(RANGE(HUE, hs(0), hs(10)), RANGE(HUE, hs(355), hs(359)));
IEval HUE_ORANGE_W = RANGE(HUE, hs(11), hs(50));
IEval HUE_ORANGE = RANGE(HUE, hs(21), hs(40));
IEval HUE_YELLOW_W = RANGE(HUE, hs(41), hs(80));
IEval HUE_YELLOW = RANGE(HUE, hs(51), hs(60));
IEval HUE_GREEN_W = RANGE(HUE, hs(61), hs(169));
IEval HUE_GREEN = RANGE(HUE, hs(81), hs(140));
IEval HUE_CYAN_W = RANGE(HUE, hs(141), hs(220));
IEval HUE_CYAN = RANGE(HUE, hs(170), hs(200));
IEval HUE_BLUE_W = RANGE(HUE, hs(201), hs(280));
IEval HUE_BLUE = RANGE(HUE, hs(221), hs(240));
IEval HUE_MAGENTA_W = RANGE(HUE, hs(241), hs(330));
IEval HUE_MAGENTA = RANGE(HUE, hs(281), hs(320));
IEval HUE_PINK_W = RANGE(HUE, hs(321), hs(355));
IEval HUE_PINK = RANGE(HUE, hs(331), hs(345));
IEval TRUE = new C_TRUE();
// HERE IT IS, machine configuration, every single step (select and sort process) begins with name RECIPE.
// rules (recipes) are separated by comma
// Find example configuration below. It means exactly this: make 2 steps where in
// Step 1: Sort only RED pixels using HEAPSORT algorithm and stop sorting when you get 5% better sort than at beginning, lay down pixels from left to right, sort negated pixels in ascending order
// Step 2: Sort only pixels which are not RED using QUICKSORT, interrupt when you get 20% improvement, sort in descent order, lay down pixels bottom to top
// Run script now and see result.
Configurator[] recipes = { // do not touch this line...
RECIPE(QUICK, 0.5, DEFAULT, RIGHT, HUE_RED_W), // comma!
RECIPE(MERGE, 0.95, DEFAULT, BOTTOM, NOT(HUE_RED_W)) // last rule do not have comma
}; // ...neither this
//another example below
//Configurator[] recipes = { // do not touch this line...
// RECIPE(PERMUTE, 0.523635, ABSOLUTE | DESC, RIGHT, OR(HUE_RED,HUE_PINK_W)), // comma!
// RECIPE(SHELL, 0.01523635, DESC, BOTTOM, OR(HUE_RED,HUE_PINK_W)), // comma!
// RECIPE(HEAP, 0.008751599, DEFAULT | DESC, TOP, NOT(OR(HUE_RED,HUE_PINK_W)))
// }; // ...neither this
// If you want to add/replace your rules for your step, you need to construct your own RECIPE line. You have to define 5 things:
// 1. Set sort algorithm (full list below, look for "SORTING ALGORITHMS" line)
// 2. Set amount of sort. This is a number between 0 (no sort) and 1 (full sort). You can have two meanings of this parameter:
// a) in RELATIVE mode it means how much sort improvement you want to achive. Eg. 0.123 value means that you get 12,3% more sorted pixels than at beginning.
// b) in ABSOLUTE mode it means how much sort you want to have regardless of current value. Eg. 0.75 means that sort stops after 75% of pixels will be sorted.
// one note here, pixels taken from image are usually partly sorted (let's say it's 50% moreless), some algorithms can lower this value, some only improves, you need to experiment
// 3. Set some specific flags. Flags can be combined together using "|" delimiter (obligatory)
// a) DEFAULT - ascending sort with relative amount
// b) RELATIVE (default) or ABSOLUTE - described in point 2
// c) ASC (default) or DESC - ascending or descending sort
// d) POSITIVE (default) or NEGATIVE - work on image as it is or negate pixels
// 4. Set sort direction: RIGHT (to right), LEFT (to left), TOP (to top), BOTTOM (to bottom)
// 5. Threshold rule. This is hardest stuff. Rule has a grammar and it's as follows
// a) Remember now block above with variable names starting HUE_... and TRUE at the end. These are predefined rules for you.
// HUE_color means: choose pixels with particular color. There are two modes: normal range and wide range.
// Eg. HUE_MAGENTA is normale range and chooses magenta pixels (hue range between 281 and 320) but
// HUE_MAGENTA_W is wider version of magenta with pink and blue touch (hue range between 241 and 330).
// All ranges are described under following link: http://www.workwithcolor.com/color-names-01.htm
// TRUE means - sort me whole line, no treshold!
// b) you can define your own range on any possible channel, syntax is:
// RANGE( channel, minimum value, maximum value)
// - list of all possible channels is below (look for "CHANNELS" line)
// - minimum and maximum value can have 3 options:
// Option a: integer number between 0 and 255, typical channels value.
// Option b: float number between 0 and 0.99999999 (normalized form)
// Option c: uniform random value between specified range in form RAND(from, to)
// Example 1: RANGE(BRIGHTNESS,0,100) - choose pixels with brighntess value between 0 and 100. Only dark pixel.
// Example 2: RANGE(RED, 0.5, 0.8) - choose pixels with red channel value between 50% and 80%
// Example 3: RANGE(ALPHA, RAND(0,0.1), 0.5) - choose pixel with alpha value between random value (from 0 to 10%) and 50%
// c) you can make nagation of every rule using NOT(your rule here)
// Example 1: NOT(HUE_PINK) - choose not pink pixels
// Example 2: NOT( RANGE(SATURATION, 50,100) ) - choose pixels with saturation between 0-49 and 101-255.
// d) you can use logical operator AND and OR.
// - AND(first rule, second rule) - first and second rule must be valid for choosen pixel
// - OR(first rule, second rule) - first or second rule (or both) must be valid
// Example 1: AND( NOT(HUE_RED), RANGE(BRIGHTNESS,0,50) ) - choose pixels which are not red and are dark (brightness between 0 and 50
// Example 2: OR( HUE_GREEN_W, AND( NOT(HUE_RED), RANGE(BRIGHTNESS,0,50) ) ) - choose pixels which are green (wide range) or are as described above (dark, not red)
// NOTE: you can build threshold as complicated as you want
// now names you will use
// FLAGS (described in point 2 above)
final static int DEFAULT = 0;
final static int ABSOLUTE = 1;
final static int RELATIVE = 0;
final static int ASC = 0;
final static int DESC = 2;
final static int POSITIVE = 0;
final static int NEGATE = 4;
// SORTING ALGORITHMS
final static int BUBBLE = 0;
final static int INSERTION = 1;
final static int SELECTION = 2;
final static int HEAP = 3; // kind of echo, http://generateme.tumblr.com/post/121956583990/splice
final static int SHELL = 4;
final static int MERGE = 5; // nice blocky patterns, http://generateme.tumblr.com/post/121920710155/unmerged-sculpture-based-on-work-by-stefanie
final static int QUICK = 6; // nice mess pattern
final static int SMOOTH = 7; // lines
// bonus, not sort methods
final static int PERMUTE = 8; // pixels are just randomized, amount is not used
final static int ONECOLOR = 9; // set pixels to one color, amount means which color use (0 first pixel, 1 last pixel, 0.5 middle pixel, etc)
final static int ROLL = 10; // roll pixels left, amount means how much to roll (0.5 - halfway)
final static int ONECOLORB = 11; // as one color but made in blocks (based on noise), amount means scale factor for noise function
// more algorithms to come: coctail, gnome, comb, oddeven, maybe more...
// CHANNELS
// ALL Channels, Nxxx stand for negative (255-value)
final static int RED = 0;
final static int GREEN = 1;
final static int BLUE = 2;
final static int HUE = 3;
final static int SATURATION = 4;
final static int BRIGHTNESS = 5;
final static int ALPHA = 6;
final static int NRED = 7;
final static int NGREEN = 8;
final static int NBLUE = 9;
final static int NHUE = 10;
final static int NSATURATION = 11;
final static int NBRIGHTNESS = 12;
final static int NALPHA = 13;
// EASING in animations
final static int LINEAR = 0;
final static int CUBIC = 1;
// if you press 'b' every frame following code will be called, put here anything you want
void batchCallback(float step) {
// recipes[0].amount = map(sin(step*TWO_PI),-1,1,0.2,0.8);
}
// YOU ARE READY TO USE, EXPERIMENT, EXPLORE, DON'T GIVE UP, ENJOY
// STOP READING HERE
int sortType = HEAP; // sort method, list below
float sortAmount = 0.01; // percent of sort to achive
boolean relativeThr = true; // is above relative to current value or absolute
boolean reverseSort = false; // reverse sort order
boolean negateColors = false;
int direction = LEFT;
IEval eval;
color[] bufw;
color[] bufh;
int[] posx;
int[] posy;
// working buffer
PGraphics buffer;
// image
PImage img;
PImage orig;
String sessionid;
void setup() {
sessionid = hex((int)random(0xffff), 4);
img = loadImage(foldername+filename+fileext);
orig = img.get();
buffer = createGraphics(img.width, img.height);
buffer.beginDraw();
buffer.noStroke();
buffer.smooth(8);
buffer.background(0);
buffer.image(img, 0, 0);
buffer.endDraw();
// calculate window size
float ratio = (float)img.width/(float)img.height;
int neww, newh;
if (ratio < 1.0) {
neww = (int)(max_display_size * ratio);
newh = max_display_size;
} else {
neww = max_display_size;
newh = (int)(max_display_size / ratio);
}
size(neww, newh);
bufw = new color[img.width];
bufh = new color[img.height];
posx = new int[img.width];
posy = new int[img.height];
processImage();
}
float anim_step = 0;
boolean animate = false;
String anim_hash = "1234";
int anim_cnt = 1000;
void draw() {
if (animate) {
buffer.image(orig, 0, 0);
img = orig.get();
for (int i=0; i<recipes.length; i++) {
Configurator c = recipes[i];
c.amount = animation_easing == LINEAR ? anim_step : sq(anim_step);
}
anim_step += 1.0/animation_frames_count;
println(anim_step);
processImage();
saveFrame(foldername + filename + "/anim"+sessionid+anim_hash+"/frame" + anim_cnt +fileext);
anim_cnt++;
if(anim_step>1.0) animate = false;
}
if(doBatch) {
batchStep();
}
}
void processImage() {
for (int i=0; i<recipes.length; i++) {
Configurator c = recipes[i];
sortType = c.sort;
sortAmount = c.amount;
eval = c.evaluator;
direction = c.direction;
if ( (c.flags & ABSOLUTE) > 0)
relativeThr = false;
else
relativeThr = true;
if ( (c.flags & DESC) > 0)
reverseSort = true;
else
reverseSort = false;
if ( (c.flags & NEGATE) > 0)
negateColors = true;
else
negateColors = false;
println("Sorting, pass: " + (i+1));
buffer.beginDraw();
if (direction == LEFT || direction == RIGHT)
sortH();
else
sortV();
buffer.endDraw();
img = buffer.get(0, 0, img.width, img.height);
}
buffer.beginDraw();
if (do_blend)
buffer.blend(orig, 0, 0, img.width, img.height, 0, 0, buffer.width, buffer.height, blend_mode);
buffer.endDraw();
image(buffer, 0, 0, width, height);
}
void keyPressed() {
// SPACE to save
if (keyCode == 32) {
String fn = foldername + filename + "/res_" + sessionid + hex((int)random(0xffff), 4)+"_"+filename+fileext;
buffer.save(fn);
println("Image "+ fn + " saved");
}
if(key == 'a') {
anim_hash = hex((int)random(0xffff), 4);
anim_step = 0;
animate = true;
anim_cnt = 1000;
}
if(key == 'b' && !doBatch) {
batchProcess();
}
}
//
void sortH() {
for (int y=0; y<img.height; y++) {
int s = 0;
for (int x=0; x<img.width; x++) {
currentColor = img.get(x, y);
if ( eval.eval() ) {
posx[s] = x;
bufw[s] = currentColor;
s++;
}
}
if (reverseSort | negateColors) reverseTab(bufw, s);
if (relativeThr) {
float thr = sortRatio(bufw, s);
thr+=(1.0-thr)*sortAmount;
sortMe(bufw, s, thr);
} else {
sortMe(bufw, s, sortAmount);
}
if (reverseSort | negateColors) reverseTab(bufw, s);
for (int i=0; i<s; i++) {
int ii = direction == RIGHT ? i : s-1-i;
buffer.set(posx[ii], y, bufw[i]);
}
}
}
void sortV() {
for (int x=0; x<img.width; x++) {
int s = 0;
for (int y=0; y<img.height; y++) {
currentColor = img.get(x, y);
if ( eval.eval() ) {
posy[s] = y;
bufh[s] = currentColor;
s++;
}
}
if (reverseSort | negateColors) reverseTab(bufh, s);
if (relativeThr) {
float thr = sortRatio(bufh, s);
thr+=(1.0-thr)*sortAmount;
sortMe(bufh, s, thr);
} else sortMe(bufh, s, sortAmount);
if (reverseSort | negateColors) reverseTab(bufh, s);
for (int i=0; i<s; i++) {
int ii = direction == BOTTOM ? i : s-1-i;
buffer.set(x, posy[ii], bufh[i]);
}
}
}
void reverseTab(color[] t, int cnt) {
for (int i=0; i<cnt; i++) {
if (reverseSort) t[i] *= -1;
if (negateColors) t[i] = ~t[i];
}
}
void sortMe(color[] t, int cnt, float thr) {
switch(sortType) {
case BUBBLE:
bubbleSort(t, cnt, thr);
break;
case INSERTION:
insertionSort(t, cnt, thr);
break;
case SELECTION:
selectionSort(t, cnt, thr);
break;
case HEAP:
heapSort(t, cnt, thr);
break;
case SHELL:
shellSort(t, cnt, thr);
break;
case MERGE:
mergeSort(t, cnt, thr);
break;
case QUICK:
quickSort(t, cnt, thr);
break;
case SMOOTH:
smoothSort(t, cnt, thr);
break;
case PERMUTE:
permute(t, cnt, thr);
break;
case ONECOLOR:
onecolor(t, cnt, thr);
break;
case ROLL:
roll(t, cnt, thr);
break;
case ONECOLORB:
onecolorb(t, cnt, thr);
break;
default:
bubbleSort(t, cnt, thr);
}
}
void roll(color[] t, int cnt, float thr) {
if (cnt>2) {
int i = (int)map(thr, 0, 1, 2, cnt-2);
color[] tmp = new int[cnt-i];
arrayCopy(t, i, tmp, 0, cnt-i);
arrayCopy(t, 0, t, cnt-i-1, i);
arrayCopy(tmp, 0, t, 0, cnt-i);
}
}
void permute(color[] t, int cnt, float thr) {
for (int i=1; i<cnt; i++) {
int r = (int)random(i, cnt);
swap(t, r, i-1);
}
}
void onecolorb(color[] t, int cnt, float thr) {
float np = 0;
boolean nv = noise(np, 0.5) > 0.5;
color c = t[0];
for (int i=0; i<cnt-1; i++) {
t[i] = c;
boolean cv = noise(np, 0.5) > 0.5;
if ( nv != cv ) {
nv = cv;
c = t[i+1];
}
np += thr;
}
}
void onecolor(color[] t, int cnt, float thr) {
int i = (int)constrain(map(thr, 0, 1, 0, cnt-1), 0, cnt-1);
color c = t[i];
for (i=0; i<cnt; i++) t[i] = c;
}
// https://kenai.com/projects/java-lab/sources/java-svn/content/Sorting/src/sorting/algorithms/selection/SmoothSort.java
final static int LP[] = {
1, 1, 3, 5, 9, 15, 25, 41, 67, 109,
177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891,
35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457,
1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703,
48315633, 78176337, 126491971, 204668309, 331160281, 535828591,
866988873
};
void smoothSort(color[] t, int lo, int hi, int cnt, float thr) {
int head = lo;
int p = 1;
int pshift = 1;
while (head < hi) {
if ((p & 3) == 3) {
sift(t, pshift, head, cnt, thr);
p >>>= 2;
pshift += 2;
} else {
if (LP[pshift - 1] >= hi - head) {
trinkle(t, p, pshift, head, false, cnt, thr);
} else {
sift(t, pshift, head, cnt, thr);
}
if (pshift==1) {
p <<= 1;
pshift--;
} else {
p <<= (pshift - 1);
pshift = 1;
}
}
p |= 1;
head++;
if (sortRatio(t, cnt)>=thr) return;
}
trinkle(t, p, pshift, head, false, cnt, thr);
while (pshift != 1 || p != 1) {
if (pshift<=1) {
int trail = Integer.numberOfTrailingZeros(p & ~1);
p >>>= trail;
pshift += trail;
} else {
p <<= 2;
p ^= 7;
pshift -= 2;
trinkle(t, p >>> 1, pshift + 1, head - LP[pshift] - 1, true, cnt, thr);
if (sortRatio(t, cnt)>=thr) return;
trinkle(t, p, pshift, head - 1, true, cnt, thr);
if (sortRatio(t, cnt)>=thr) return;
}
head--;
}
}
void sift(int[] m, int pshift, int head, int cnt, float thr) {
int val = m[head];
while (pshift > 1) {
int rt = head - 1;
int lf = head - 1 - LP[pshift - 2];
if (val >= m[lf] && val >= m[rt]) {
break;
}
if (m[lf] >= m[rt]) {
m[head] = m[lf];
head = lf;
pshift -= 1;
} else {
m[head] = m[rt];
head = rt;
pshift -= 2;
}
}
m[head] = val;
}
private void trinkle(int[] m, int p, int pshift, int head, boolean isTrusty, int cnt, float thr) {
int val = m[head];
while (p != 1) {
int stepson = head - LP[pshift];
if (m[stepson] <= val) {
break;
}
if (!isTrusty && pshift > 1) {
int rt = head - 1;
int lf = head - 1 - LP[pshift - 2];
if (m[rt] > m[stepson] || m[lf] > m[stepson]) {
break;
}
}
m[head] = m[stepson];
head = stepson;
int trail = Integer.numberOfTrailingZeros(p & ~1);
p >>>= trail;
pshift += trail;
isTrusty = false;
}
if (!isTrusty) {
m[head] = val;
sift(m, pshift, head, cnt, thr);
}
}
void smoothSort(color[] t, int cnt, float thr) {
smoothSort(t, 0, cnt-1, cnt, thr);
}
void quickSort(color[] t, int a, int b, int cnt, float thr) {
if (a>=b) return;
if (sortRatio(t, cnt)>=thr) return;
int pivotidx = a+(b-a)/2;
color pivot = t[pivotidx];
swap(t, pivotidx, b);
int i=a-1;
int j=b;
do {
do {
i++;
}
while (t[i] < pivot);
do {
j--;
}
while (t[j] > pivot && j>a);
if (i<j) swap(t, i, j);
}
while (i<j);
swap(t, b, i);
if (sortRatio(t, cnt)>=thr) return;
quickSort(t, a, i-1, cnt, thr);
quickSort(t, i+1, b, cnt, thr);
}
void quickSort(color[] t, int cnt, float thr) {
quickSort(t, 0, cnt-1, cnt, thr);
}
void topdownMerge(color[] t, color[] b, int ibegin, int imiddle, int iend) {
int i0=ibegin;
int i1=imiddle;
for (int j=ibegin; j<iend; j++) {
if (i0<imiddle && (i1 >= iend || t[i0] <= t[i1])) {
b[j] = t[i0];
i0++;
} else {
b[j] = t[i1];
i1++;
}
}
}
void topdownSplitMerge(color[] t, color[] b, int ibegin, int iend, float thr, int cnt) {
if (iend-ibegin<2) return;
if (sortRatio(t, cnt)>=thr) return;
int imiddle = (iend+ibegin)/2;
topdownSplitMerge(t, b, ibegin, imiddle, thr, cnt);
topdownSplitMerge(t, b, imiddle, iend, thr, cnt);
if (sortRatio(t, cnt)>=thr) return;
topdownMerge(t, b, ibegin, imiddle, iend);
arrayCopy(b, ibegin, t, ibegin, iend-ibegin);
}
void mergeSort(color[] t, int cnt, float thr) {
color[] buf = new color[cnt];
topdownSplitMerge(t, buf, 0, cnt, thr, cnt);
}
final static int[] shell_gaps = {
701, 301, 132, 57, 23, 10, 4, 1
};
void shellSort(color[] t, int cnt, float thr) {
for (int g=0; g<shell_gaps.length; g++) {
int gap = shell_gaps[g];
for (int i=gap; i<cnt; i++) {
int tmp = t[i];
int j=i;
for (; j>=gap && t[j-gap] > tmp; j-=gap) {
t[j] = t[j-gap];
}
t[j] = tmp;
if (gap>=10 && sortRatio(t, cnt)>=thr) return;
}
if (gap<10 && sortRatio(t, cnt)>=thr) return;
}
}
void heapify(color[] t, int cnt, float thr) {
int start = floor((cnt - 2)/2.0);
while (start>=0) {
siftDown(t, start, cnt-1);
start--;
if (sortRatio(t, cnt)>=thr) return;
}
}
void siftDown(color[] t, int start, int end) {
int root = start;
while ( (root*2+1)<=end) {
int child = root*2+1;
int sw = root;
if (t[sw]<t[child]) sw = child;
if ( (child+1)<=end && t[sw]<t[child+1]) sw = child + 1;
if (sw == root) return;
else {
swap(t, root, sw);
root = sw;
}
}
}
void heapSort(color[] t, int cnt, float thr) {
if (sortRatio(t, cnt)>=thr) return;
heapify(t, cnt, thr);
int end = cnt - 1;
while (end>0) {
swap(t, end, 0);
end--;
siftDown(t, 0, end);
if (sortRatio(t, cnt)>=thr) return;
}
}
void selectionSort(color[] t, int cnt, float thr) {
for (int j=0; j<cnt-1; j++) {
int imin = j;
for (int i=j+1; i<cnt; i++) {
if (t[i]<t[imin]) imin = i;
}
if (imin != j) swap(t, j, imin);
if (sortRatio(t, cnt)>=thr) return;
}
}
void bubbleSort(color[] t, int cnt, float thr) {
int n = cnt;
do {
int newn = 0;
for (int i=1; i<n; i++) {
if (t[i-1]>t[i]) {
swap(t, i-1, i);
newn = i;
}
}
if (sortRatio(t, cnt)>=thr) return;
n = newn;
}
while (n>0);
}
void insertionSort(color[] t, int cnt, float thr) {
for (int i=1; i<cnt; i++) {
color x = t[i];
int j = i;
while (j>0 && t[j-1]>x) {
t[j] = t[j-1];
j--;
}
t[j] = x;
if (sortRatio(t, cnt)>=thr) return;
}
}
// helpers
void swap(color[] t, int a, int b) {
color tmp = t[a];
t[a] = t[b];
t[b] = tmp;
}
float sortRatio(color[] t, int cnt) {
int n = 0;
for (int i=1; i<cnt; i++) {
if (t[i-1]<=t[i]) n++;
}
return n/(float)(cnt-1);
}
float getChannel(color c, int channel) {
int ch = channel>6?channel-7:channel;
float cc;
switch(ch) {
case RED:
cc = red(c);
break;
case GREEN:
cc = green(c);
break;
case BLUE:
cc = blue(c);
break;
case HUE:
cc = hue(c);
break;
case SATURATION:
cc = saturation(c);
break;
case ALPHA:
cc = alpha(c);
break;
default:
cc= brightness(c);
break;
}
return (channel>6?255-cc:cc);
}
// Configurator class
class Configurator {
int sort;
float amount;
int flags;
int direction;
IEval evaluator;
public Configurator(int s, float a, int f, int d, IEval e) {
sort = s;
amount = a;
flags = f;
direction = d;
evaluator = e;
}
}
// HUE ranges
final int hs(float ang) {
return (int)map(ang, 0, 359, 0, 255);
}
// evaluator part
Configurator RECIPE(int s, float a, int f, int d, IEval e) {
return new Configurator(s, a, f, d, e);
}
PVector RAND(float a, float b) {
return new PVector(a, b);
}
IEval RANGE(int ch, float a, float b) {
return new C_RANGE(ch, a, a, b, b);
}
IEval RANGE(int ch, PVector a, float b) {
return new C_RANGE(ch, a.x, a.y, b, b);
}
IEval RANGE(int ch, float a, PVector b) {
return new C_RANGE(ch, a, a, b.x, b.y);
}
IEval RANGE(int ch, PVector a, PVector b) {
return new C_RANGE(ch, a.x, a.y, b.x, b.y);
}
IEval AND(IEval a, IEval b) {
return new C_AND(a, b);
}
IEval OR(IEval a, IEval b) {
return new C_OR(a, b);
}
IEval NOT(IEval a) {
return new C_NOT(a);
}
color currentColor;
interface IEval {
public boolean eval();
}
class C_TRUE implements IEval {
public boolean eval() {
return true;
}
}
class C_AND implements IEval {
IEval a, b;
public C_AND(IEval aa, IEval bb) {
a = aa;
b = bb;
}
public boolean eval() {
return a.eval() && b.eval();
}
}
class C_OR extends C_AND {
public C_OR(IEval aa, IEval bb) {
super(aa, bb);
}
public boolean eval() {
return a.eval() || b.eval();
}
}
class C_NOT implements IEval {
IEval a;
public C_NOT(IEval aa) {
a = aa;
}
public boolean eval() {
return !a.eval();
}
}
class C_RANGE implements IEval {
int channel;
float a, b;
boolean ra = false;
boolean rb = false;
float a2, b2;
public C_RANGE(int ch, float aa, float aa2, float bb, float bb2) {
a = aa < 1.0 ? map(aa, 0, 1, 0, 256) : aa;
b = bb < 1.0 ? map(bb, 0, 1, 0, 256) : bb;
if (aa2 > aa) {
ra = true;
a2 = aa2 < 1.0 ? map(aa2, 0, 1, 0, 256) : aa2;
}
if (bb2 > bb) {
rb = true;
b2 = bb2 < 1.0 ? map(bb2, 0, 1, 0, 256) : bb2;
}
channel = ch;
}
public boolean eval() {
float v = getChannel(currentColor, channel);
float newa = ra ? random(a, a2) : a;
float newb = rb ? random(b, b2) : b;
return (v >= newa) && (v <= newb);
}
}
// code for batch processing
void batchStep() {
File n = batchList[batchIdx];
String name = n.getAbsolutePath();
if(name.endsWith(fileext)) {
print(n.getName()+"... ");
img = loadImage(name);
buffer.image(img, 0, 0);
batchCallback((float)batchIdx / batchFiles);
processImage();
save(foldername+batchUID+"/"+n.getName());
println("saved");
}
batchIdx++;
if(batchIdx >= batchList.length) {
doBatch = false;
println("results saved in "+ foldername+batchUID + " folder");
}
}
File[] batchList;
int batchIdx = 0;
String batchUID;
boolean doBatch = false;
float batchFiles = 0;
void batchProcess() {
batchUID = sessionid + hex((int)random(0xffff),4);
File dir = new File(sketchPath+'/'+foldername);
batchList = dir.listFiles();
batchIdx = 0;
batchFiles = 0;
for(File n : batchList) {
if(n.getName().endsWith(fileext)) batchFiles=batchFiles+1.0;
}
println("Processing "+int(batchFiles)+" images from folder: " + foldername);
doBatch = true;
}