import processing.net.*;
import javax.imageio.ImageIO.*;
import javax.swing.*;
import javax.swing.event.*;
 
Controller controls;
JPanel controlPanel;
JLabel status = new JLabel();
int lx,ly;
int bristles = 20;
Vector brush = new Vector();
int splatter = 5;
int splotchPercent = 5;
 
float maxSize = 3;
float minSize = 1;
 
float overShootMax = .5;
float overShootMin = -.5;
float dampingMin = 2;
float dampingMax = 3;
 
int overShootPercent = 40;
 
String outName = "c:
Client myClient;
 
boolean serverMode = false;
 
int alphaRed = 200;
 
Brush standardBristle = new BristleBrush();
Brush marker = new Marker();
Brush pencil = new Pencil();
Brush variWidth = new VariWidth();
Brush bucket = new Bucket();
 
Brush dropper = new Dropper();
 
Brush currentBrush = standardBristle;
 
 
Vector undoBuffer = new Vector();
 
 
void setup(){
if (args != null){
for (int i =0; i < args.length; i++){
if (args[i].equals("--Blender")){
status.setText("Connected to Blender");
serverMode = true;
}
}
}
controls = new Controller(this);
controls.setSize(300,580);
controls.show();
smooth();
size(550,800);
background(alphaRed);
mouseReleased();
int wi = 0;
int wj =0;
int bx = 24;
for (int i=0; i < bristles; i++ ){
brush.add(new hair());
}
if(serverMode){
myClient = new Client(this,"127.0.0.1",50007);
} else{
smooth();
}
}
 
void keyPressed(){
if (key == 'c'){
background(200);
}
else if (key =='q'){
if(serverMode)myClient.write("exit");
exit();
}
else if (key == 'b'){
export();
}
else if(int(key) == 26){
undo();
}
}
 
 
 
void undo(){
if (undoBuffer.size()>1){
loadPixels();
PImage un = (PImage)undoBuffer.elementAt(undoBuffer.size()-2);
arraycopy(un.pixels,0,pixels,0,pixels.length);
updatePixels();
undoBuffer.removeElementAt(undoBuffer.size()-1);
}
}
 
 
void export(){
loadPixels();
boolean ex = false;
int j = 0;
int bt =0,bb=0,bl=0,br=0;
while(j < height && ex == false){
for (int i=0; i < width; i++){
if (red(pixels[j*width + i]) != 200){
ex = true;
bt = j -1;
break;
}
}
j++;
}
strokeWeight(1);
j = height-1;
ex = false;
while(j >0 && ex == false){
for (int i=0; i < width; i++){
if (red(pixels[j*width + i]) != 200){
ex = true;
bb = j +1;
break;
}
}
j--;
}
j = 0;
ex = false;
while(j < width && ex == false){
for (int i=0; i < height; i++){
if (red(pixels[i*width + j]) != 200){
ex = true;
bl = j -1;
break;
}
}
j++;
}
j = width-1;
ex = false;
while(j > 0 && ex == false){
for (int i=0; i < height; i++){
if (red(pixels[i*width + j]) != 200){
ex = true;
br = j +1;
break;
}
}
j--;
}
int iWidth = br-bl;
int iHeight = bb-bt;
PImage sc = get();
PImage crop = sc.get(bl,bt,iWidth,iHeight);
color opaque = color(255);
color trans = color(0);
PImage alphaMask = new PImage(iWidth,iHeight);
for (int i=0; i < iHeight; i++){
for (int jj=0;jj < iWidth; jj++){
if (red(crop.pixels[i*iWidth + jj]) == 200){
alphaMask.set(jj,i,trans);
} else{
alphaMask.set(jj,i,opaque);
}
}
}
crop.mask(alphaMask);
BufferedImage img = new BufferedImage(iWidth,iHeight,BufferedImage.TYPE_INT_ARGB);
BufferedImage imgAlpha = new BufferedImage(iWidth,iHeight,BufferedImage.TYPE_INT_ARGB);
img.setRGB(0,0,iWidth,iHeight,crop.pixels,0,iWidth);
imgAlpha.setRGB(0,0,iWidth,iHeight,alphaMask.pixels,0,iWidth);
try{
javax.imageio.ImageIO.write(img,"png",new File(outName+".png"));
javax.imageio.ImageIO.write(imgAlpha,"png",new File(outName+"Mask.png"));
}catch(Exception e){}
if(serverMode){
myClient.write("plane," + iWidth/50 + "," + iHeight/50 + "," + outName+".png" + "," + outName+"Mask.png");
myClient.stop();
}
}
 
 
void mouseReleased(){
loadPixels();
PImage edit = new PImage(width,height);
arraycopy(pixels,0,edit.pixels,0,edit.pixels.length);
if (undoBuffer.size() > 10){
undoBuffer.removeElementAt(0);
}
undoBuffer.add(edit);
}
 
 
 
 
void draw(){
 
currentBrush.draw();
}
 
 
class hair{
float x,y,lx,ly;
float thickness;
int hairColor;
float damping;
public hair(){
thickness = random(minSize,maxSize);
hairColor = (int)random(0,32);
damping = random(dampingMin,dampingMax);
}
}
 
 
 
public class Controller extends JFrame implements ActionListener{
PApplet owner;
String [] brushTypes = {"Bristle", "Marker", "Pencil", "Bucket Fill","Dropper"};
JComboBox brushList = new JComboBox(brushTypes);
JButton export = new JButton("Export to Blender");
JButton fileExport = new JButton("Export to File");
JButton fileImport = new JButton("Import from File");
JButton clear = new JButton("Clear");
JCheckBox antialias = new JCheckBox("Antialias");
JCheckBox wonkConnect = new JCheckBox("Connected Noise");
JToggleButton eraseMode = new JToggleButton("Erase");
ColorSwatch brushColor = new ColorSwatch(Color.black);
ColorSwatch bucketColor = new ColorSwatch(Color.white);
public Controller(PApplet owner){
this.owner = owner;
setTitle("Controls");
 
brushList.addActionListener(this);
antialias.addActionListener(this);
eraseMode.addActionListener(this);
clear.addActionListener(this);
export.addActionListener(this);
fileExport.addActionListener(this);
fileImport.addActionListener(this);
buildControls();
 
}
private void buildControls(){
JPanel controlPanel = new JPanel();
controlPanel.setLayout(new BoxLayout(controlPanel,BoxLayout.Y_AXIS));
brushList.setMaximumSize(new Dimension(550,150));
controlPanel.add(brushList);
controlPanel.add(currentBrush.getPanel());
controlPanel.add(antialias);
controlPanel.add(wonkConnect);
controlPanel.add(brushColor);
controlPanel.add(eraseMode);
JPanel tm = new JPanel(new FlowLayout());
tm.add(export);
if (!serverMode){
export.setEnabled(false);
antialias.setSelected(true);
}
tm.add(fileExport);
tm.add(fileImport);
tm.add(clear);
 
controlPanel.add(tm);
controlPanel.add(status);
getContentPane().add(controlPanel);
validate();
}
public void actionPerformed(ActionEvent e){
if(e.getActionCommand().equals("Clear")){
owner.background(200);
}
else if (e.getActionCommand().equals("Erase")){
if (eraseMode.isSelected()){
brushColor.setColor(new Color(alphaRed,alphaRed,alphaRed));
}
else{
brushColor.setColor(brushColor.lastColor);
}
}
else if(e.getActionCommand().equals("comboBoxChanged")){
getContentPane().removeAll();
if (brushList.getSelectedItem().equals("Bristle")){
currentBrush = standardBristle;
}
else if (brushList.getSelectedItem().equals("Marker")){
currentBrush = marker;
}
else if (brushList.getSelectedItem().equals("Pencil")){
currentBrush = pencil;
}
else if (brushList.getSelectedItem().equals("Bucket Fill")){
currentBrush = bucket;
}
else if (brushList.getSelectedItem().equals("Dropper")){
currentBrush = dropper;
}
currentBrush.updateBrush();
buildControls();
}
else if(e.getActionCommand().equals("Import from File")){
JFileChooser fc = new JFileChooser("c:\\");
fc.showOpenDialog(owner);
File selFile = fc.getSelectedFile();
if (selFile != null){
outName = selFile.getAbsolutePath();
PImage a = loadImage(outName);
background(alphaRed);
image(a,width/2 - a.width/2,height/2-a.height/2);
}
}
else if(e.getActionCommand().equals("Export to File") || e.getActionCommand().equals("Export to Blender") ){
JFileChooser fc = new JFileChooser("c:\\");
fc.showSaveDialog(owner);
File selFile = fc.getSelectedFile();
if (selFile != null){
outName = selFile.getAbsolutePath();
export();
exit();
}
}
if (antialias.isSelected()){
smooth();
} else{
noSmooth();
}
}
 
}
 
class sgSlider extends JPanel{
JSlider slider;
JLabel label;
public sgSlider(String sText, int minVal, int maxVal, int currValue, ChangeListener listener){
slider = new JSlider(minVal,maxVal,currValue);
label = new JLabel(sText);
setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
add(label);
add(slider);
slider.addChangeListener(listener);
}
public int getVal(){
return slider.getValue();
}
}
 
 
public class ColorSwatch extends JPanel implements MouseListener{
Color setColor;
Color lastColor;
public ColorSwatch(Color setColor){
this.setColor = setColor;
setBackground(setColor);
addMouseListener(this);
setMaximumSize(new Dimension(400,400));
}
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
setColor = JColorChooser.showDialog(frame, "Dialog Title", setColor);
if (setColor != null){
setBackground(setColor);
currentBrush.updateBrush();
}
}
public void mouseExited(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void setColor(Color newColor){
lastColor = setColor;
setColor = newColor;
setBackground(newColor);
currentBrush.updateBrush();
}
}
 
static public void main(String args[]) {
String [] argv = new String [2];
argv [0] = "sketchBoard";
if (args.length >0){
argv [1] = args[0];
} else{
argv[1] = "--NoBlender";
}
PApplet.main(argv);
}
 
public interface Brush{
public void draw();
public JPanel getPanel();
public void updateBrush();
}
 
public class BristleBrush implements Brush, ChangeListener{
private JPanel controlPanel;
sgSlider oShootMin = new sgSlider("overShootMin",0,50,5,this);
sgSlider oShootPercent = new sgSlider("overShootPercent",0,100,10,this);
sgSlider numBristles = new sgSlider("numBristles",1,40,20,this);
sgSlider minBristle = new sgSlider("BristleMin",0,100,10,this);
sgSlider maxBristle = new sgSlider("BristleMax",0,100,30,this);
sgSlider dampMax = new sgSlider("damping max",1,10,2,this);
sgSlider dampMin = new sgSlider("damping min",1,10,1,this);
public BristleBrush(){
controlPanel = new JPanel();
controlPanel.add(oShootMin);
controlPanel.add(oShootPercent);
controlPanel.add(numBristles);
controlPanel.add(minBristle);
controlPanel.add(maxBristle);
controlPanel.add(dampMin);
controlPanel.add(dampMax);
}
public void draw(){
if (mousePressed){
 
if (mouseButton == LEFT) {
hair current;
float dx,dy, overShoot;
for (int i=0; i < bristles; i++){
current = (hair)brush.elementAt(i);
stroke(current.hairColor);
strokeWeight(current.thickness);
current.lx = current.x;
current.ly = current.y;
current.x -= (current.lx - mouseX)/current.damping ;
current.y -= (current.ly - mouseY)/current.damping;
line(current.x,current.y,current.lx,current.ly);
if (random(100) < overShootPercent){
overShoot = random(overShootMin,overShootMax);
dx = overShoot * (current.x - current.lx);
dy = overShoot * (current.y - current.ly);
line(current.x,current.y,current.x + dx,current.y + dy);
if(controls.wonkConnect.isSelected()){
current.x += dx;
current.y += dy;
}
}
}
}
}
else{
hair current;
for (int i=0; i < brush.size(); i++){
current = (hair)brush.elementAt(i);
current.x = mouseX;
current.y = mouseY;
}
}
}
public JPanel getPanel(){
return controlPanel;
}
public void stateChanged(ChangeEvent e){
bristles = numBristles.getVal();
overShootMax = oShootMin.getVal()/10.f;
overShootMin = -overShootMax;
overShootPercent = oShootPercent.getVal();
minSize = minBristle.getVal()/10.f;
maxSize = maxBristle.getVal()/10.f;
dampingMax = dampMax.getVal();
dampingMin = dampMin.getVal();
updateBrush();
}
public void updateBrush(){
brush.clear();
hair newHair;
while(brush.size() < bristles){
newHair = new hair();
newHair.hairColor = controls.brushColor.setColor.getRGB();
brush.add(newHair);
}
}
}
 
public class Marker implements Brush, ChangeListener{
private JPanel controlPanel;
private sgSlider size = new sgSlider("size",0,50,5,this);
int myColor, brushSize =20;
public JPanel getPanel(){
return controlPanel;
}
public Marker(){
controlPanel = new JPanel();
controlPanel.add(size);
}
public void draw(){
if (mousePressed){
if (mouseButton == LEFT) {
noStroke();
fill(myColor);
ellipse(mouseX,mouseY,brushSize,brushSize);
}
}
}
public void updateBrush(){
myColor = controls.brushColor.setColor.getRGB();
}
public void stateChanged(ChangeEvent e){
brushSize = size.getVal();
}
}
 
 
public class Pencil implements Brush,ChangeListener{
int lx,ly,x,y;
private sgSlider size = new sgSlider("size",0,50,5,this);
int brushSize = 2;
private JPanel controlPanel;
int myColor = 0;
public Pencil(){
controlPanel = new JPanel();
controlPanel.add(size);
}
public JPanel getPanel(){
return controlPanel;
}
public void draw(){
if (mousePressed){
if (mouseButton == LEFT) {
stroke(myColor);
 
lx = x;
ly = y;
x = mouseX;
y = mouseY;
strokeWeight(brushSize);
line(x,y,lx,ly);
 
}
}
else{
x = mouseX;
y = mouseY;
}
}
public void updateBrush(){
myColor = controls.brushColor.setColor.getRGB();
}
public void stateChanged(ChangeEvent e){
brushSize = size.getVal();
}
}
 
 
public class Dropper implements Brush{
 
private JPanel controlPanel = new JPanel();
public JPanel getPanel(){
return controlPanel;
}
public void draw(){
if (mousePressed){
if (mouseButton == LEFT) {
loadPixels();
controls.brushColor.setColor(new Color(pixels[mouseY*width + mouseX]));
 
}
}
}
public void updateBrush(){
}
}
 
 
 
public class Bucket implements Brush{
private JPanel controlPanel = new JPanel();
public JPanel getPanel(){
return controlPanel;
}
public void draw(){
if (mousePressed){
if (mouseButton == LEFT) {
loadPixels();
bucketFill(mouseX,mouseY,controls.brushColor.setColor.getRGB(),pixels[mouseY*width + mouseX]);
updatePixels();
}
}
}
public void bucketFill(int x, int y, color newColor, color oldColor){
if (oldColor == newColor) return;
if (pixels[y*width+x] != oldColor) return;
int y1;
y1 = y;
while(pixels[y1*width+x] == oldColor && y1 < height-1){
pixels[y1*width+x] = newColor;
y1++;
}
y1 = y-1;
while(pixels[y1*width+x] == oldColor && y1 > 0){
pixels[y1*width+x] = newColor;
y1--;
}
y1 = y;
while(pixels[y1*width+x] == newColor && y1 < height-1){
if(x > 0 && pixels[y1*width+x-1] == oldColor){
bucketFill(x-1,y1,newColor,oldColor);
}
y1++;
}
y1 = y-1;
while(pixels[y1*width+x] == newColor && y1 >= 0){
if(x > 0 && pixels[y1*width+x-1] == oldColor){
bucketFill(x-1,y1,newColor,oldColor);
}
y1--;
}
y1 = y;
while(pixels[y1*width+x] == newColor && y1 < height){
if(x < width-1 && pixels[y1*width+x+1] == oldColor){
bucketFill(x+1,y1,newColor,oldColor);
}
y1++;
}
y1 = y-1;
while(pixels[y1*width+x] == newColor && y1 >= 0){
if(x < width-1 && pixels[y1*width+x+1] == oldColor){
bucketFill(x+1,y1,newColor,oldColor);
}
y1--;
}
}
public void updateBrush(){
}
}
 
public class VariWidth implements Brush{
int lx,ly,x,y;
float ax,ay,bx,by,cx,cy,ex,ey;
private JPanel controls;
public JPanel getPanel(){
return controls;
}
public void draw(){
if (mousePressed){
if (mouseButton == LEFT) {
noStroke();
fill(0);
lx = x;
ly = y;
x = mouseX;
y = mouseY;
float dx = (x - lx);
float dy = (y - ly);
if (dx >= 0) dx = 10;
else dx = 10;
if (dy >0) dy = 10;
else dy = -10;
ax = x-dy;
ay = y+dx;
 
bx = x+dy;
by = y-dx;
 
 
quad(ax,ay,bx,by,cx,cy,ex,ey);
 
cx = bx;
cy = by;
ex = ax;
ey = ay;
}
}
else{
x = mouseX;
y = mouseY;
ax = mouseX;
ay = mouseY;
bx = mouseX;
by = mouseY;
cx = mouseX;
cy = mouseY;
ex = mouseX;
ey = mouseY;
}
}
public void updateBrush(){
}
}