public void dropEvent(DropEvent theDropEvent) {
// si le drop est une image jpg
// on charge l'adresse dans mUrl.
// if dropped file is an image, its url is stored in mUrl
if (theDropEvent.isFile()) {
mUrl = theDropEvent.filePath();
}
if (theDropEvent.isURL()==true) {
if (theDropEvent.isImage()) {
mUrl = theDropEvent.url().trim();
}
}
}
void rangeController(ControlEvent theControlEvent) {
if (theControlEvent.isFrom("rangeController")) {
// min and max values are stored in an array.
// access this array with controller().arrayValue().
// min is at index 0, max is at index 1.
seuilBrillanceMini = int(theControlEvent.getController().getArrayValue(0));
seuilBrillanceMaxi = int(theControlEvent.getController().getArrayValue(1));
}
}
void Restart() {
initialiser();
}
// color information from ColorPicker 'picker' are forwarded to the picker(int) function
void picker(int col) {
macouleur=col;
}
void controlEvent(ControlEvent theEvent) {
if (theEvent.isFrom(radio)) {
modeCouleur = int(theEvent.group().value());
}
if (theEvent.isFrom(shapeRadio)) {
forme = int(theEvent.group().value());
if(forme==2){
strokeButton.setOn();
fillButton.setOff();
cp.setColorValue(color(100,1,1, 100));
}
}
if(theEvent.isFrom(saveButton)){
dessine=false;
if(savePath==""){savePath=selectFolder("Choose a folder to save this applet captures:");}
if(savePath!=null){saveFrame(savePath+"/worms-####.jpg");}
dessine=true;
}
}
/////////////////////////////////////////////////////////////////////
// GUI //
/////////////////////////////////////////////////////////////////////
import controlP5.*;
//GUI controls
public ControlP5 controlP5;
public ControlWindow controlWindow;
public Textlabel modeCouleurLabel;
public Textlabel seuilMaxLabel;
public Textlabel seuilMinLabel;
public Textlabel pInfo;
public Textlabel behaviourLabel;
public RadioButton radio ;
public RadioButton shapeRadio ;
public Button strokeButton;
public Button fillButton;
public Button drawButton;
public Button restartButton;
public Button limitButton;
public Button saveButton;
public Slider s1;
public Slider s2;
public Slider s3;
public Slider s4;
public Slider s5;
public Slider s6;
public ColorPicker cp;
public Range range;
public Tooltip tt;
String monInfo;
void setupControls(){
//***************************initialize controlP5*************************************
controlP5 = new ControlP5(this);
controlP5.setControlFont(new ControlFont(createFont("ArialUnicodeMS", 10, true), 12));
//initialize controlWindow
controlWindow =controlP5.addControlWindow("controlP5CW",310,hautI).setBackground(128).hideCoordinates();
//***************************add colorpicker cp***************************
cp = controlP5.addColorPicker("picker",10,10,280,12)
.setColorValue(color(255,255,255, 255))
.moveTo(controlWindow);
// ***************************add horizontal sliders and labels for stroke***************************
controlP5.addSlider("myweight", 1, 50, 3, 10, 100, 100, 15).setWindow(controlWindow);
s2 = (Slider)controlP5.controller("myweight")
.setLabel("Stroke width:");
s2.setDecimalPrecision(0);
//***************************add slider for maximum stroke length***************************
controlP5.addSlider("maxstrokelength", 1, 600, 400, 10, 150, 150, 15).moveTo(controlWindow);
s1 = (Slider)controlP5.controller("maxstrokelength")
.setLabel("Max stroke length");
//***************************add strokeButton and fillButton Buttons to control if the seeker draws strokes, fills or both***************************
strokeButton=controlP5.addButton("Stroke", 0, 10, 200, 60, 15)
.setSwitch(true)
.moveTo(controlWindow);
fillButton=controlP5.addButton("Fill", 0, 150,200, 50, 15)
.setSwitch(true)
.setOn()
.moveTo(controlWindow);
// ***************************add radioButton for drawing shape***************************
shapeRadio = controlP5.addRadioButton("shapeRadio", 10, 250)
.moveTo(controlWindow)
.setColorForeground(color(120))
.setColorLabel(color(0))
.setItemsPerRow(4)
.setSpacingColumn(100)
.addItem("Spheres", 1)
.addItem("Lines", 2)
.addItem("Boxes", 3)
//shapeRadio.addItem("Triangle", 3);
.activate("Spheres");
// ***************************add radioButton for drawing modes***************************
radio = controlP5.addRadioButton("radioButton", 10, 600)
.moveTo(controlWindow)
.setColorForeground(color(120))
.setColorLabel(color(0))
.setItemsPerRow(4)
.setSpacingColumn(100)
.addItem("Normal", 1)
.addItem("Pic Colors", 2)
.addItem("Growing", 3)
.activate("Normal");
// ***************************add horizontal sliders and labels for stroke***************************
controlP5.addSlider("amplitudeTest", 1, 5, 2, 10, 650, 100, 15).setWindow(controlWindow);
s6 = (Slider)controlP5.controller("amplitudeTest")
.setLabel("Test amplitude:");
s6.setDecimalPrecision(0);
amplitudeTest=2;
//***************************add switch button for limit to threshold***************************
limitButton= controlP5.addButton("short_strokes",0, 10,350, 110, 15)
.moveTo(controlWindow)
.setLabel("limit to value")
.setSwitch(true).setOn();
//***************************add drawButton***************************
drawButton=controlP5.addButton("Draw", 0, 10, 800, 50, 15)
.setSwitch(true)
.moveTo(controlWindow);
//***************************add saveButton***************************
saveButton=controlP5.addButton("Save", 0, 100, 800, 50, 15)
.moveTo(controlWindow);
//***************************add restartButton***************************
restartButton=controlP5.addButton("Restart", 0, 190, 800, 70, 15)
.moveTo(controlWindow);
//***************************add slider for inertia***************************
controlP5.addSlider("inertia", 1, 20, 8, 10, 480, 200, 15).moveTo(controlWindow);
s3 = (Slider)controlP5.controller("inertia");
s3.setLabel("Inertia:");
//***************************add slider for devMax (acceleration limiter)***************************
controlP5.addSlider("devMax", 0, 5, 2.2, 10, 500, 200, 15).moveTo(controlWindow);
s4 = (Slider)controlP5.controller("devMax");
s4.setLabel("Acceleration:");
//***************************add slider for maxSpeed***************************
controlP5.addSlider("maxSpeed", 0, 100, 40, 10, 520, 200, 15).moveTo(controlWindow);
s5 = (Slider)controlP5.controller("maxSpeed");
s5.setLabel("Max speed:");
s5.setSliderMode(Slider.FIX);
behaviourLabel = controlP5.addTextlabel( "behaviourLabel", "Seekers' behaviour parameters", 30, 450).moveTo(controlWindow)
.setColorValue(0);
//***************************add Range control for value thresholds***************************
range=controlP5.addRange("rangeController", 0, 255, 0, 128, 10, 400, 200, 15).moveTo(controlWindow)
.setDecimalPrecision(0)
.setCaptionLabel("Value range")
.setSliderMode(Slider.FIX);
seuilMaxLabel = controlP5.addTextlabel( "light", "light", 10, 380).moveTo(controlWindow)
.setColorValue(0);
seuilMinLabel = controlP5.addTextlabel( "dark", "dark", 170, 380).moveTo(controlWindow)
.setColorValue(0);
modeCouleurLabel =controlP5.addTextlabel("Drawing modes:", "Drawing modes:", 100, 560).moveTo(controlWindow)
.setColorValue(0);
pInfo=controlP5.addTextlabel( "pInfo", "You can drag and drop a picture (not too big!)"+"\n from your web or file browser...", 10, 700).moveTo(controlWindow);
pInfo.setColorValue(0);
//***************************add some tooltips***************************
tt= controlP5.getTooltip();
tt.setDelay(200);
tt.register("s5","Maximum speed of the seeker.");
tt.register("s2","Width of the strokes");
tt.register("s1","Maximum length of a worm.");
tt.register("s3","Inertia of the seeker");
tt.register("s4","Acceleration of the seeker");
}
class Seeker {
//index
int index;
//position
PVector p=new PVector();
//speed
PVector v=new PVector();
//worm's length
float longueur;
//worm's type
boolean typeVer=true;
//worm's limite
int limiteDown;
//stroke weight
float weight;
boolean deplace;
color couleur;
//variable speed
float variationSpeed= 1+int(random(200));
boolean on = true; // Turns the display on and off
// Constructor
Seeker(int Index,PVector P, PVector V) {
index=Index;
p=P;
v=V;
limiteDown=0;
weight=myweight;
longueur=0;
couleur = macouleur;
deplace=false;
}
void start(float xpos, float ypos) {
on = true;
}
void display(float actuX,float actuY) {
if (on == true) {
// option "pic colors" selected
if (modeCouleur==2) {
macouleur=source.pixels[floor(mySeeker.p.x)+floor(mySeeker.p.y)*larg];
}
//option "growing" selected
if (modeCouleur==3) {
float vert=200;//50+(100*(1-sin(mySeeker.longueur/200)));
float rouge=50*(1-cos(mySeeker.longueur/300));
float bleu=100*(1-sin(mySeeker.longueur/100));//100-vert-rouge;
macouleur=color(rouge, vert, bleu);
}
//control the type of drawing, with or without strokes and/or fill
//strokeWeight(1);
if (strokeButton.isOn()) {
if (fillButton.isOn()) {
stroke(macouleur, -mySeeker.limiteDown+100/mySeeker.weight);
}
else {
stroke(macouleur, -mySeeker.limiteDown+500/mySeeker.weight);
}
}
else noStroke();
if (fillButton.isOn()) {
fill(macouleur);
}
else {
noFill();
}
//shapes size = dia
float dia=mySeeker.weight*(1-sin(mySeeker.longueur/mySeeker.variationSpeed));
switch(forme) {
// spheres
case 1:
strokeWeight(1);
pushMatrix();
translate(this.p.x, this.p.y);
sphereDetail(int(dia/2));
sphere(dia);
popMatrix();
break;
//lines
case 2:
if (!this.deplace) {
strokeWeight(this.weight);
line(actuX, actuY,this.p.x, this.p.y);
}
break;
// boxes
default:
strokeWeight(1);
pushMatrix();
translate(this.p.x, this.p.y);
//rotate(float(mySeeker.longueur)/100);
rotateX(this.longueur/100);
rotateY(this.longueur/100);
box(dia);
popMatrix();
break;
}
}
}
}
// Pierre MARZIN
// Trying to learn Processing... First project, much inspired by "Mycelium" by Scloopy?
// http://marzinp.free.fr/applets
//GUI and Drag 'n drop: great libraries ! Thanks to Andreas Schlegel (http://www.sojamo.de/libraries/index.html)
import sojamo.drop.*;
//import processing.opengl.*;
public SDrop drop;
public String mUrl;
public String savePath="";
// Source image
PImage source;
//worms parameters,
//position
PVector p;
//speed
PVector v;
//aimed direction
PVector vise=new PVector();
//"seeking" vector
PVector deviation=new PVector();
//worm's length
int longueurVer;
//like pixels[], to keep track of how many times a point is reached by a worm
int [] buffer;
boolean deplace;
int limiteDown;
float seekerWeight;
int variationspeed;
//number of worms at the beginning
int nbePoints;
//seekers array
Seeker [] seekersArray;
//width and length of the drawing area
int larg;
int haut;
int largI;
int hautI;
//brightness of the destination pixel
float briMax;
//minimum brightness threshold
public int seuilBrillanceMini;
//maximum brightness threshold
public int seuilBrillanceMaxi;
//location of the tested point in pixels[]
int locTest;
//around a point (worms position), how many pixels will we look at...
int amplitudeTest;
//constrain the aimed direction vector into an r radius circle
int inertia;
//constrain the acceleration vector into a devMax radius circle
float devMax;
//constrain the speed vector into a maxSpeed radius circle
int maxSpeed;
//stroke's weight (slider) or radius of ellipse used for drawing
public float myweight;
//stroke's maximum length
public int maxstrokelength;
//draw or not (button drawButton)
public boolean dessine;
//different drawing options
public int modeCouleur;
public int forme;
float L1color=random(255);
float L2color=random(255);
float L3color=random(255);
float L4color=random(255);
float L5color=random(255);
//fill/stroke color
int macouleur;
boolean limite;
Seeker mySeeker;
//************************************************************************
//setup only sets up what won't change:GUI and window params
//I use initialiser() to set up what has to be initialised
//when you hit "RestartButton" and dessin() to set the drawing parameters
void setup() {
frameRate(100);
larg=largI=1200;
haut=hautI=850;
//size(1200, 1000,OPENGL);
size(1200, 850, P3D);
limite=false;
drop = new SDrop(this);
source = loadImage("tiger.jpg");
//maximize the source image without changing its aspect ratio
if (hautI*source.width>largI*source.height) {
larg=largI;
haut=larg*source.height/source.width;
}
else {
haut=hautI;
larg=haut*source.width/source.height;
}
source.resize(larg, haut);
source.loadPixels();
setupControls();
initialiser();
}
//************************************************************************
//launched after setup and any time you hit the restartButton button
void initialiser() {
background(255);
L1color=random(255);
L2color=random(255);
L3color=random(255);
L4color=random(255);
L5color=random(255);
nbePoints=6;
buffer=new int[(1+haut)*(1+larg)];
smooth();
drawButton.setOn();
dessine=true;
p=new PVector();
v=new PVector();
seekersArray=new Seeker[nbePoints];
maxstrokelength=int(s1.value());
inertia=int(s3.value());
myweight=s2.value();
devMax=s4.value();
maxSpeed=int(s5.value());
seuilBrillanceMini = floor(range.getArrayValue(0));
seuilBrillanceMaxi = floor(range.getArrayValue(1));
forme=int(shapeRadio.value());
for (int i=0;i<nbePoints;i++) {
v=new PVector(3-random(6), 3-random(6));
p=new PVector(random(larg), random(haut));
while ( (brightness (source.pixels[floor (p.x)+floor(p.y)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(p.x)+floor(p.y)*larg])<seuilBrillanceMini))
{
p.x=int(random(larg));
p.y=int(random(haut));
}
seekersArray[i]=new Seeker(i, p, v);
}
}
//************************************************************************
void draw() {
defineLights();
dessine=drawButton.isOn();
limite=limitButton.isOn();
//If the source image changes, it has to be maximized and the sketch to be reinitialized
if (mUrl!=null) {
source=null;
source=loadImage(mUrl);
if (hautI*source.width>largI*source.height) {
larg=largI;
haut=larg*source.height/source.width;
}
else {
haut=hautI;
larg=haut*source.width/source.height;
}
source.resize(larg, haut);
source.loadPixels();
initialiser();
mUrl=null;
}
//if drawButton is on, go drawing
if (dessine) {
dessin();
}
//needed to render controlP5 controls
controlP5.draw();
}
//************************************************************************
void dessin() {
locTest=0;
//for each "seeker" (worm's head)
for (int t=0;t<seekersArray.length;t++) {//X.length
//on va tester les pixels autour du mobile p[t] en direction de la vitesse du mobile
//calcul du barycentre des points testes ponderes de la brillance (vise.x, vise.y)
//for each seeker, we gonna test pixels around the seeker's position and calculate their barycenter, loaded by pixels values (0/255 dark/light)
mySeeker=seekersArray[t];
//barycenter's coordinates
vise.x=0;
vise.y=0;
//avoid looking for mySeeker.p.x for every pixels
float seekX=mySeeker.p.x;
float seekY=mySeeker.p.y;
int actuX=floor(mySeeker.p.x);
int actuY=floor(mySeeker.p.y);
//barycenter calculation
for (int i=-amplitudeTest;i<amplitudeTest+1;i++) {///rdessin
for (int j=-amplitudeTest;j<amplitudeTest+1;j++) {
locTest=floor(seekX)+i+(floor(seekY)+j)*larg;
//does the point belongs to the source image?
if ((seekX+i>0)&& (seekY+j>0)&&(seekX+i<larg-1)&&(seekY+j<haut-1)) {
vise.x+=i*(255-brightness(source.pixels[locTest]));
vise.y+=j*(255-brightness(source.pixels[locTest]));
}
}
}
//coeur du comportement de seeker:
// core of the behaviour of the seeker (http://www.shiffman.net/ see wanderer's code)
vise.normalize();
vise.mult(100/inertia);
//vise.limit(100/inertia);
mySeeker.v.add(new PVector(vise.x, vise.y)); //+hasard-random(2*hasard)
deviation=mySeeker.v.get();
deviation.normalize();
deviation.mult(devMax);
//deviation.limit(devMax);
mySeeker.v.normalize();
mySeeker.v.mult(maxSpeed);
//mySeeker.v.limit(maxSpeed);
mySeeker.p.x+=deviation.x;
mySeeker.p.y+=deviation.y;
//******************different cases that lead to move the seeker to another random place**************
//outside window
if ((mySeeker.p.x<1)|| (mySeeker.p.y<0)||(mySeeker.p.x>larg-1)||(mySeeker.p.y>haut-1))//||
{
deplacePoint(mySeeker);
}
seekX=mySeeker.p.x;
seekY=mySeeker.p.y;
//buffer est une copie vide de l'image. on l'augmente pour chaque point parcouru
//buffer is an empty copy of the source image. It's increased every time a point is reached.
buffer[floor(seekX)+floor(seekY)*larg]++;
//si on est passe plus de 10 fois on demenage le point
// If a point is reached 10 times, seeker is moved
if (buffer[floor(seekX)+floor(seekY)*larg]>10) {
deplacePoint(mySeeker);
}
//on compte les segments de chaque ver
//worm's length is increased
mySeeker.longueur++;
//si c'est trop long on demenage
//seeker's moved if worm's too long
if (mySeeker.longueur>maxstrokelength) {
deplacePoint(mySeeker);
}
//inside window, limite on and inside value range
if ((limite)&&((brightness(source.pixels[floor(seekX)+floor(seekY)*larg])<=seuilBrillanceMaxi)&&(brightness(source.pixels[floor(seekX)+floor(seekY)*larg])>=seuilBrillanceMini)))
{
if (mySeeker.limiteDown!=0) {
mySeeker.limiteDown-=2;
}
}
//limite on and outside value range
if ((limite)&&((brightness(source.pixels[floor(seekX)+floor(seekY)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(seekX)+floor(seekY)*larg])<seuilBrillanceMini)))
{
if (mySeeker.limiteDown==0) {
mySeeker.limiteDown=2;
}
mySeeker.limiteDown+=4;//print(mySeeker.limiteDown+" ");
if (mySeeker.limiteDown>=152/myweight) {
mySeeker.limiteDown=0;
deplacePoint(mySeeker);
}
}
//null deviation
if ((deviation.x==0)&&(deviation.y==0))
{
mySeeker.limiteDown=0;
deplacePoint(mySeeker);
}
else {
briMax=brightness(source.pixels[floor(seekX)+floor(seekY)*larg]);
}
// go draw the seeker's shape
mySeeker.display(actuX,actuY);
//on cree un nouveau vers de temps en temps (on pourrait tester selon la brilance de la zone...)
// from times to times a new worm is created
if (random(1)>1-(255-briMax)/80000)
{seekersArray=(Seeker[])append(seekersArray, new Seeker(seekersArray.length,new PVector(seekX, seekY), new PVector(mySeeker.v.x*(5+random(6)), mySeeker.v.x*(5+random(6)))));
}
}
}
//*****************move the seeker function***************************
void deplacePoint(Seeker seeker) {
mySeeker.longueur=0;
seeker.deplace=true;
seeker.p.x=1+int(random(larg-1));
seeker.p.y=1+int(random(haut-1));
while ( (brightness (source.pixels[floor (seeker.p.x)+floor(seeker.p.y)*larg])>seuilBrillanceMaxi)||(brightness(source.pixels[floor(seeker.p.x)+floor(seeker.p.y)*larg])<seuilBrillanceMini))
{
seeker.p.x=1+int(random(larg-1));
seeker.p.y=1+int(random(haut-1));
}
}
//*******************set the lights function***************************
public void defineLights() {
// Orange point light on the right
pointLight(L4color, 0, L5color, // Color
200, -100, 800); // Position
// Blue directional light from the left
directionalLight(0, L1color,L2color, // Color
1, 0, -2); // The x-, y-, z-axis direction
// Yellow spotlight from the front
spotLight(255, L3color, 50, // Color
400, 440, 800, // Position
0, 0, -1, // Direction
PI/2, 1); // Angle, concentration
}
Latest development of my animated "filter" worms... Drag and drop seems to work! Save shouldn't work here. Play with the settings while the image is drawn.