> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.Viu$HvAPw50/rev.863
 * 
 * authors: 
 *   Masakazu Matsumoto

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



// This sketch builds on a prior work, "Tartan Designer", created by Masakazu Matsumoto
// http://studio.sketchpad.cc/sp/pad/view/ro.9jJoYPveV7jZv/rev.2210



// This sketch builds on a prior work, "Tartan Designer", created by Masakazu Matsumoto
// http://studio.sketchpad.cc/sp/pad/view/ro.9o5t14k3KfkKT/rev.3837



// This sketch builds on a prior work, "Tartan Designer", created by Masakazu Matsumoto
// http://studio.sketchpad.cc/sp/pad/view/ro.9Pyx57Qj7ymC9/rev.147



// This sketch builds on a prior work, "Tartan Designer", created by Masakazu Matsumoto
// http://studio.sketchpad.cc/sp/pad/view/ro.90jg52--oZsvf/rev.302



// -*- javascript -*-
// coding: utf-8
//        Processing.js example sketch

int BINW = 8;


class CheckBox
{
    int x,y,w,h;
    int checked;
    var label;
    CheckBox(int x_, int y_, int siz, int checked_, var label_)
    {
        x = x_;
        y = y_;
        w = siz;
        h = siz;
        checked = checked_;
        label = label_;
    }
    void draw(var colors, var patterns)
    {
        stroke(0);
        strokeWeight(1);
        fill(0,0,100);
        rect(x,y,w,h);
        if ( checked ){
            line(x,y,x+w,y+h);
            line(x+w,y,x,y+h);
        }
        fill(0);
        textAlign(LEFT);
        textSize(h*0.8);
        text(label,x+w*2, y+w*0.8);
    }
    int mousePressed()
    {
        if ( ( x < mouseX ) && ( mouseX < x+w ) &&
            (y < mouseY ) && (mouseY < y+h ) ){
            checked = ! checked;
            return 1;
        }
    }
}

class Bar
{
    int x,y,w,h,delta;
    int x0;
    Bar(int x_, int y_, int width, int height, int delta_)
    {
        x = x_;
        y = y_;
        w = width;
        h = height;
        delta = delta_;
    }
    void draw(var colors, var patterns)
    {
        fill(0,100,100);
        rect(x,y,w,h);
    }
    int mousePressed()
    {
        if ( ( x < mouseX ) && ( mouseX < x+w ) &&
            (y < mouseY ) && (mouseY < y+h ) ){
            x0 = mouseX;
        }
        else{
            x0 = -1;
        }
    }
    int mouseDragged()
    {
        if ( x0 >= 0 ){
            int d = mouseX - x0;
            if ( d > delta ){
                int dx = (int)(d/delta);
                x += dx*delta;
                x0 = mouseX;
                return dx;
            }
            else if ( d < -delta ){
                int dx = (int)(-d/delta);
                if ( x - dx*delta > 0 ){
                    x -= dx*delta;
                    x0 = mouseX;
                    return -dx;
                }
            }
        }
        return 0;
    }
};



class PhotoPanel
{
    int x,y,w,h,yshift,ymargin,lastx;
    PImage pattern;
    PhotoPanel(int x_, int y_, int width, int height)
    {
        x = x_;
        y = y_;
        w = width;
        h = height;
        yshift = 0;
    }
    void draw(var colors, var patterns, var mode, var symm)
    {
        if ( mode == 1 ){
            pattern = PhotorealPattern(colors,patterns,symm,BINW);
        }
        else if ( mode == 2 ){
            pattern = QuickPattern(colors,patterns,symm,BINW);
        }
        PGraphics cc = createGraphics(w,h);
        float zoom = cc.width / pattern.width;
        cc.translate(0,-yshift);
        cc.scale(zoom,zoom);
        int rep = (int)(h/w+1);
        for(int i=0;i<rep;i++){
            cc.image(pattern,0,pattern.height*i);
        }
        ymargin = pattern.height*rep - h / w * pattern.width;
        image(cc,x,y);
    }
    int mousePressed()
    {
        lastx = mouseX;
    }
    int mouseDragged(var patterns, int binw)
    {
        if ( ( x < mouseX ) && ( mouseX < x+w ) &&
            (y < mouseY ) && (mouseY < y+h ) ){
            int delta = (mouseY - pmouseY);
            yshift -= delta;
            if ( yshift <= 0 ) yshift = 0;
            if ( yshift >= ymargin) yshift = ymargin;
            if ( binw ){
                delta = (int)((mouseX - lastx) / binw);
                if ( delta ){
                    lastx = mouseX;
                }
                while ( delta > 0 ){
                    var p = patterns.pop();
                    patterns.unshift(p);
                    delta -= 1;
                }
                while ( delta < 0 ){
                    var p = patterns.shift();
                    patterns.push(p);
                    delta += 1;
                }
            }
            return 1;
        }
        return 0;
    }
};



class PreviewPanel
{
    int x,y,w,h,shift;
    Array pattern;
    PreviewPanel(int x_, int y_, int width, int height)
    {
        x = x_;
        y = y_;
        w = width;
        h = height;
        shift = 2;
        pattern = new Array(4);
    }
    void draw(var colors, var patterns, var mode)
    {
        if ( mode == 1 ){
            for(int i=0;i<4;i++){
                pattern[i] = BasicPattern(colors,patterns,1<<i);
            }
        }
        else if ( mode == 2 ){
            for(int i=0;i<4;i++){
                pattern[i] = QuickPattern(colors,patterns,patterns,1<<i);
            }
        }
        PGraphics canvas = createGraphics(w,h);
        int iw = (int)(w / pattern[shift].width+1);
        int ih = (int)(h / pattern[shift].width+1);
        for(int i=0;i<iw;i++){
            for(int j=0;j<ih;j++){
                canvas.image(pattern[shift],pattern[shift].width*i,pattern[shift].width*j);
            }
        }
        image(canvas,x,y);
    }
    int mousePressed()
    {
        if ( ( x <= mouseX ) && ( mouseX < x+w ) &&
         ( y <= mouseY ) && ( mouseY < y+h ) ){
            shift ++;
            if ( shift == 4 ){
                shift = 0;
            }
            return 1;
        }
        return 0;
    }

};

class StripeChooser
{
    int x,y,w,h;
    int oy,ox,lastx;
    StripeChooser(int x_,int y_,int width, int height)
    {
        x = x_;
        y = y_;
        w = width;
        h = height;
    }
    void draw(var colors,var patterns)
    {
        var binwidth = w / patterns.length();
        var binheight = h / colors.length();
        stroke(0);
        for(int i=0;i<patterns.length();i++){
            for(int j=0;j<colors.length();j++){
                if ( patterns[i] == j ){
                    var c = colors[patterns[i]];
                    fill(c[0],c[1],c[2]);
                }
                else{
                    fill(0,0,100);
                }
                rect(binwidth*i+x,binheight*j+y,binwidth,binheight);
            }
        }
    }
    int mousePressed(var colors, var patterns)
    {
        var binwidth = w / patterns.length();
        var binheight = h / colors.length();
        int ix = ( mouseX - x ) / binwidth;
        int iy = ( mouseY - y ) / binheight;
        if ( ( 0 <= ix ) && ( ix < patterns.length() ) &&
             (0 <= iy ) && (iy < colors.length() )){
            patterns[(int)ix] = (int)iy;
            ox = (int)ix;
            oy = (int)iy;
            lastx = ox;
            return 1;
        }
        return 0;
    }
    int mouseDragged(var colors, var patterns)
    {
        var binwidth = w / patterns.length();
        var binheight = h / colors.length();
        int ix = (int)(( mouseX - x ) / binwidth);
        int iy = (int)(( mouseY - y ) / binheight);
        if ( ( 0 <= ix ) && ( ix < patterns.length() ) ){
            if ( ox < ix ){
                if ( ix < lastx ){
                    int next = (lastx + 1) % patterns.length;
                    for(int i=ix+1;i <=lastx; i++){
                        patterns[i] = patterns[next];
                    }
                }
                else{
                    for(int i=lastx;i<=ix;i++){
                        patterns[i] = oy;
                    }
                }
            }
            else{
                if ( ix > lastx ){
                    int next = (lastx - 1 + patterns.length) % patterns.length;
                    for(int i=lastx;i <ix; i++){
                        patterns[i] = patterns[next];
                    }
                }
                else{
                    for(int i=ix;i<=lastx;i++){
                        patterns[i] = oy;
                    }
                }
            }
            lastx = ix;
            return 1;
        }
        return 0;
    }
};


class ColorChooser
{
    int x,y,w,h;
    int selected;
    ColorChooser(int x_,int y_,int w_,int h_)
    {
        x = x_;
        y = y_;
        w = w_;
        h = h_;
        selected = 0;
    }
    void draw(var colors)
    {
        //all colors
        var h1 = h / colors.length;
        textSize(h1*0.28);
        var x0 = x + h1*1.1;
        for(int j=0;j<colors.length; j++){
            //selected color
            var col = colors[j];
            color c;
            if ( j == selected ){
                stroke(0,100,100);
                strokeWeight(3);
            }
            else{
                stroke(0,0,0);
                strokeWeight(1);
            }
            c = color(col[0],col[1],col[2]);
            fill(c);
            var y0 = y + h1*j;
            ellipse(x+h1/2,y0+h1*0.5,h1*0.9,h1*0.9);
            fill(0);
            textAlign(LEFT);
            text("H",x0,y0+h1*0.3);
            text("S",x0,y0+h1*0.6);
            text("B",x0,y0+h1*0.9);
            textAlign(RIGHT);
            text((int)(col[0]*3.6),x0+h1,y0+h1*0.3);
            text((int)col[1],x0+h1,y0+h1*0.6);
            text((int)col[2],x0+h1,y0+h1*0.9);
        }
        //selected color
        var col = colors[selected];
        PImage hue = createImage(100,1,ARGB);
        PImage sat = createImage(100,1,ARGB);
        PImage bri = createImage(100,1,ARGB);
        //hue bar
        for(int i=0;i<100;i++){
            color c;
            c = color(i,col[1],col[2]);
            hue.pixels[i] = c;
        }
        //saturation bar
        for(int i=0;i<100;i++){
            color c;
            c = color(col[0],i,col[2]);
            sat.pixels[i] = c;
        }
        //brightness bar
        for(int i=0;i<100;i++){
            color c;
            c = color(col[0],col[1],i);
            bri.pixels[i] = c;
        }
        var barheight = h1;
        var margin = h1*1.5 + barheight;
        var x0 = x + margin;
        var barwidth  = w - margin;
        pushMatrix();
        translate(x0,y);
        scale(barwidth/100,barheight);
        fill(255);
        image(hue,0,0);
        image(sat,0,1);
        image(bri,0,2);
        popMatrix();
        fill(100,0,100,100);
        stroke(0);
        strokeWeight(1);
        for(int i=0;i<3;i++){
            beginShape();
            vertex(col[i]*barwidth/100+x0,y+barheight*(i+0.5));
            vertex(col[i]*barwidth/100+x0-barheight/3,y+barheight*i);
            vertex(col[i]*barwidth/100+x0+barheight/3,y+barheight*i);
            endShape(CLOSE);
        }

        textSize(barheight*0.7);
        fill(100,0,0,100);
        textAlign(RIGHT);
        text((int)(col[0]*3.6),x0+barwidth+1,y+barheight*0.8+1);
        text((int)col[1],x0+barwidth+1,y+barheight*1.8+1);
        text((int)col[2],x0+barwidth+1,y+barheight*2.8+1);
        textAlign(LEFT);
        text("H 色",x0+1,y+1+barheight*0.8);
        text("S 彩",x0+1,y+1+barheight*1.8);
        text("B 明",x0+1,y+1+barheight*2.8);
        fill(100,0,100,100);
        textAlign(RIGHT);
        text((int)(col[0]*3.6),x0+barwidth,y+barheight*0.8);
        text((int)col[1],x0+barwidth,y+barheight*1.8);
        text((int)col[2],x0+barwidth,y+barheight*2.8);
        textAlign(LEFT);
        text("H 色",x0,y+barheight*0.8);
        text("S 彩",x0,y+barheight*1.8);
        text("B 明",x0,y+barheight*2.8);
    }
    int mousePressed(var colors, int select)
    {
        var h1 = h / colors.length;
        if (( x < mouseX) && ( mouseX <= x+h1 ) ){
            selected = (int)( (mouseY-y) / h1 );
            return 8;
        }
        var barheight = h1;
        var margin = h1*1.5 + barheight;
        var col = colors[selected];
        var x0 = x + margin;
        var barwidth  = w - margin;
        if (( x0 < mouseX) && ( mouseX <= x0+barwidth ) ){
            if ( (y < mouseY) && (mouseY <= y+barheight)  && ( select & 1 )){
                int hue = (mouseX-x0)*100/barwidth;
                col[0] = hue;
                return 1;
            }
            else if ( (y+barheight < mouseY) && (mouseY <= y+barheight*2) && ( select & 2 )  ){
                int sat = (mouseX-x0)*100/barwidth;
                col[1] = sat;
                return 2;
            }
            else if ( (y+barheight*2 < mouseY) && (mouseY <= y+barheight*3) && ( select & 4 )  ){
                int bri = (mouseX-x0)*100/barwidth;
                col[2] = bri;
                return 4;
            }
        }
        return 0;
    }
        
};


colors = [];
colors[0] = [0,98,80];
colors[1] = [56/3.6,98,80];
colors[2] = [33,98,20];
colors[3] = [66,98,40];
colors[4] = [0,0,80];
colors[5] = [0,0,5];
 
var patterns = [4,4,4,4,0,0,5,0,0,0,0,2,2,2,2,2,2,4,4,5,1,1,5,5,5,5,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
var winx = 800;
var winy = 600;
var photow = BINW*patterns.length();
var photoh = 300;
var margin = 10;
var previeww = winx - margin - photow;
var previewh = photoh;
var cntlheight = winy - photoh - 1;
var barheight = cntlheight / colors.length();
PhotoPanel photopanel = new PhotoPanel(0,0,photow,photoh);
Bar bar = new Bar(photow,0,margin,winy,BINW);
PreviewPanel previewpanel = new PreviewPanel(photow+margin,0,previeww,previewh);
StripeChooser sc = new StripeChooser(0,photoh,photow,cntlheight);
ColorChooser cc = new ColorChooser(photow+margin,photoh,previeww-margin,cntlheight);
CheckBox cb1 = new CheckBox(photow+previewh/2, photoh+previewh/2+margin, previewh/18., 0, "Asymmetric");
//CheckBox cb2 = new CheckBox(photow+previewh/2, photoh+previewh/2+previewh/9, previewh/18., 0, "Narrow bin");

void setup() {
    noStroke(0);
    fill(0);
    textSize(barheight*0.6);
    textAlign(CENTER);
    frameRate(24);
    colorMode(HSB, 100);
    size(800,600);
    noLoop();
}



//basic
PImage BasicPattern(var colors, var patterns, int sup)
{
    PImage pattern = createImage(patterns.length()*sup, patterns.length()*sup,ARGB);
    int L = 0;
    for(int i=0; i<patterns.length()*sup;i++){
        for(int j=0; j<patterns.length()*sup;j++){
            int k = (i+j) % 4;
            int  p;
            float ratio;
            if ( k < 2 ){
                p = patterns[(int)(i/sup)];
                ratio = 90 - random(10);
            }
            else{
                p = patterns[(int)(j/sup)];
                ratio = 110 + random(10);
            }
            var c = colors[p];
            float bri = c[2] * ratio / 100;
            if ( bri > 100 ) bri = 100;
            pattern.pixels[L] = color(c[0],c[1],bri,100);
            L ++;
        }
    }
    return pattern;
}

//photorealistic
PImage PhotorealPattern(var colors, var patterns, var symm, int sup)
{
    PImage pattern = createImage(patterns.length()*sup, symm.length()*sup,ARGB);
    int L = 0;
    for(int i=0; i<symm.length()*sup;i++){
        for(int j=0; j<patterns.length()*sup;j++){
            i2 = i >> 1;
            j2 = j >> 1;
            int k = (i2+j2) % 4;
            int  p;
            float ratio;
            if ( k < 2 ){
                p = symm[(int)(i/sup)];
                ratio = 100 - random(20);
                if ( i & 1 ){
                    ratio -=20;
                }
            }
            else{
                p = patterns[(int)(j/sup)];
                ratio = 100 + random(20);
                if ( j & 1 ){
                    ratio -=20;
                }
            }
            var c = colors[p];
            float bri = c[2] * ratio / 100;
            if ( bri > 100 ) bri = 100;
            pattern.pixels[L] = color(c[0],c[1],bri,100);
            L ++;
        }
    }
    return pattern;
}


//quick
PGraphics QuickPattern(var colors, var patterns, var symm, int sup)
{
    PGraphics pattern = createGraphics(patterns.length()*sup, symm.length()*sup);
    pattern.noStroke();
    pattern.colorMode(HSB,100);
    for(int j=0; j<patterns.length();j++){
        int p = patterns[j];
        var c = colors[p];
        pattern.fill(c[0],c[1],c[2],100);
        pattern.rect(j*sup,0,sup,pattern.height);
    }
    for(int i=0; i<symm.length();i++){
        int p = symm[i];
        var c = colors[p];
        pattern.fill(c[0],c[1],c[2],60);
        pattern.rect(0,i*sup,pattern.width,sup);
    }
    return pattern;
}



int pressedBar = 0;
int dragMode = 0;
int colorChanged = 1;
int viewChanged = 1;
int drawMode = 1; //2: quick, but need refresh; 1: photorealistic

void mousePressed()
{
    colorChanged = 0;
    viewChanged = 0;
    dragMode = 0;
    if ( sc.mousePressed(colors, patterns) ){
        colorChanged = 1;
        dragMode = 3;
    }
    pressedBar = cc.mousePressed(colors,7);
    if ( pressedBar ){
        if ( pressedBar == 8 ){
            //ellipse selected; no drag, no redraw
            colorChanged = 2;
        }
        else{
            colorChanged = 3;
            dragMode = 4;
        }
    }
    bar.mousePressed();
    photopanel.mousePressed();
    if ( previewpanel.mousePressed() ){
        viewChanged = 1;
    }
    if ( cb1.mousePressed() ){
        colorChanged = 4;
    }
    /*if ( cb2.mousePressed() ){
        colorChanged = 5;
        if (cb2.checked ){
            BINW = 4;
        }
        else{
            BINW = 8;
        }
    }*/
    //println(["mp",viewChanged,colorChanged]);
    if ( colorChanged ){
        drawMode = 2;
    }
    else if ( viewChanged ){
        drawMode = 0;
    }
    loop();
}


void mouseDragged()
{
    if ( dragMode == 4 ){
        if ( cc.mousePressed(colors,pressedBar) ){
            colorChanged = 1;
        }
    }
    else if ( dragMode == 3 ){
        if ( sc.mouseDragged(colors, patterns) ){
            colorChanged = 1;
        }
    }
    else{
        int delta = bar.mouseDragged();
        if ( delta ){
            if ( delta < 0 ){
                patterns.length += delta;
            }
            if ( delta > 0 ){
                var c = patterns[patterns.length-1];
                for(int i=0; i<delta; i++){
                    patterns.push(c);
                }
            }
            if ( delta ){
                //left
                photopanel.w += BINW*delta;
                sc.w         += BINW*delta;
                //right
                previewpanel.w -= BINW*delta;
                previewpanel.x += BINW*delta;
                cc.w -= BINW*delta;
                cc.x += BINW*delta;
                cb1.x += BINW*delta;
                colorChanged = 1;
            }
        }
        int b = cb1.checked;
        if ( b ){
            b = BINW;
        }
        if ( photopanel.mouseDragged(patterns, b) ){
            viewChanged = 1;
            if ( b ){
                colorChanged = 1;
            }
        }
    }
    if ( colorChanged ){
        drawMode = 2;
    }
    else if ( viewChanged ){
        drawMode = 0;
    }
    loop();
}

void mouseReleased()
{
    if ( drawMode == 2 ){
        colorChanged = 1;
        drawMode = 1;
    }
    loop();
}



var symmetrize(var patterns)
{
    var symm = [];
    int left = 0;
    for(int i=0;i<patterns.length; i++){
        if ( patterns[i] != patterns[0] ){
            break;
        }
        left = i;
    }
    int right;
    for(int i=patterns.length-1; i>=0; i--){
        if ( patterns[i] != patterns[patterns.length-1] ){
            break;
        }
        right = i;
    }
    for(int i=0;i<patterns.length; i++){
        symm[i] = patterns[i];
    }
    for(int i=right-1;left<i;i--){
        symm.push(patterns[i]);
    }
    return symm;
}




void draw() {
    if ( colorChanged == 1 ){
        //after photorealistic rendering, loop stops.
        noLoop();
    }
    if ( colorChanged || viewChanged ){
        if(colorChanged)background(100,0,100);
        fill(255);
        int asymm = cb1.checked;
        var symm;
        if ( asymm ){
            symm = patterns;
        }
        else{
            symm = symmetrize(patterns);
        }
        //var now = (new Date()).getTime();
        photopanel.draw(colors,patterns,drawMode,symm);
        //var now2 = (new Date()).getTime();
        //println([1,now2-now]);
        //now = now2;
        previewpanel.draw(colors, symm,drawMode);
        //var now2 = (new Date()).getTime();
        //println([2,now2-now]);
        //now = now2;
        if ( colorChanged ){
            bar.draw();
            sc.draw(colors,patterns);
            cc.draw(colors);
            cb1.draw();
        }
        //var now2 = (new Date()).getTime();
        //println([3,now2-now]);
        //now = now2;
        //cb2.draw();
        colorChanged = 0;
        viewChanged = 0;
    }
}