Vi lager et enkelt oppsett det vi har to canvas-elementer. I det ene kan vi lage linjer og vi kan
spare linjene våre i text-format(json) på serveren.
I det andre kan vi laste opp linjene og vise dem fram.
De to canvas-elementene ligger her på samme webside, men det spiller ingen rolle siden
skissene lagres på server. Datatransporten ordnes med et eget Javascript som snakker med PDE-koden
i de to canvas-elementene. Scriptet bruker jQuery/AJAX, se modulen jQueryAjax,
og en colorpicker, [1] .
DRAW and SAVE
tegn noe her, save det...
LOAD and SHOW
... og load det her
Dataen flyttes fram og tilbake til tjeneren som json-filer og Ajax-kall.
En skisse av json-formatet er slik:
Dataene er ordnet i "strokes". Hvert stroke sier hvor tykk pen vi skal bruke og hvilken farge som skal
brukes. I tillegg er det gjort klart for å benytte tid ved retegning og det er gjort klart for
alternativer til rene linjer med å legg inn en type, som er 1 for linjetegning.
De to siste parametrne er altså ikke brukt i dette eksempelet.
Begge canvasene, tegning og framvisning, benytter seg av samme pde-filer. Tegning er blokkert for den
canvasen som står for framvisning, se flagget canDraw i koden.
_sketching2.pde
/*
Drawing simple lines with width and color
lines are ordered in strokes
Prepared for more options in a stroke (stype and time)
*/
// initial values, thin and black
int strokeWidth=1;
int strokeColor=unhex("FF000000");
// flag for allowing draw or not
boolean canDraw=true;
// all strokes
ArrayList<pstroke> strokes;
// and the one we are working on
pstroke currentStroke=null;
void setup() {
size(300, 300);
smooth();
frameRate(20);
strokes=new ArrayList<pstroke>();
currentStroke=null;
}
void draw() {
background(255, 255, 180);
for(pstroke s:strokes)
s.drawStroke();
}
//-------------------------------------
// mousing only active when canDraw == true
// pressed mouse and we start a new stroke
void mousePressed() {
if (canDraw) {
addStroke(strokeWidth, strokeColor);
currentStroke.addpt(new pt(mouseX, mouseY));
}
}
void mouseDragged() {
if (canDraw) {
if (currentStroke!=null)
currentStroke.addpt(new pt(mouseX, mouseY));
}
}
void mouseReleased() {
if (canDraw) {
if (currentStroke!=null) {
currentStroke.addpt(new pt(mouseX, mouseY));
}
}
}
//----------------------------------
// Make the complete drawing in json form
String getDrawingAsJson() {
if (strokes.size()==0)
return "none";
String S="{\"strokes\":[";
for (int ix=0;ix < strokes.size();ix++)
S+=((pstroke)strokes.get(ix)).toString()+",";
S=S.substring(0, S.length()-1)+"]}";
return S;
}
// add a new stroke
void addStroke(int strokeWidth, int strokeColor) {
currentStroke=new pstroke(strokeWidth, strokeColor);
strokes.add(currentStroke);
}
// remove latest stroke
// called from javascript
void removeStroke() {
strokes.remove(strokes.size()-1);
currentStroke=null;
}
// allow mousing or not
// set from javascript
void setDrawable(boolean b) {
canDraw=b;
}
// add a new point to current stroke
// from javascript loading a drawing
void addPoint(int x, int y) {
currentStroke.addpt(new pt(x, y));
}
// set weight
// set from javascript
void setStrokeWidth(int w) {
strokeWidth=w;
}
// set color
// set from javascript
void setStrokeColor(String c) {
strokeColor=unhex(c);
}
_pstroke.pde
// hold a stroke with:
// color, width and a series of points
// type and timeused is not used
class pstroke {
private int pcolor, pwidth;
// all the points in the stroke
private ArrayList<pt> pts;
// constructor
public pstroke(int pwidth, int pcolor) {
this.pwidth=pwidth;
this.pcolor=pcolor;
this.pts=new ArrayList<pt>();
}
// add a point
public void addpt(pt p) {
pts.add(p);
}
// draw this stroke with all lines
public void drawStroke() {
// set color and width
strokeWeight(this.pwidth);
stroke(this.pcolor);
// loop points
int ix=1;
while (ix < pts.size ()) {
line(pts.get(ix-1).x, pts.get(ix-1).y,
pts.get(ix).x, pts.get(ix).y);
ix++;
}
}
// prepare this stroke for json
public String toString() {
String S= "{\"width\":"+pwidth+",\"color\":"+pcolor;
S+=",\"p\":[";
for (int ix=0;ix < pts.size();ix++) {
S+=""+pts.get(ix).toString()+",";
}
return S.substring(0, S.length()-1)+"]}";
}
}
_pt.pde
// hold a single point
class pt {
public int x, y;
// constructor
public pt(int x, int y) {
this.x=x;
this.y=y;
}
public String toString() {
return ""+x+","+y;
}
}
Javascriptet som handterer datatransporten ser slik ut:
_index.js
// load points from file and give it to processing
function getData(targetNodeId,procId,pname){
if(pname==null)
pname='points.json';
$.ajax({
url:'http://www.it.hiof.no/~borres/cgi-bin/processing/linedrawing/deliverPoints.py',
data:'name='+pname+'&rand='+new Date(),
success:function(data)
{
// while testing:$('#'+targetNodeId).html("loading:"+data);
makeDrawingfromJson(procId,data);
},
error:function(data)
{
$('#'+targetNodeId).html('error'+data);
}
});
}
//get data from processing and send them to file
function saveData(targetNodeId,procId){
var pjs = Processing.getInstanceById(procId);
var pdata=pjs.getDrawingAsJson();
$('#'+targetNodeId).html("");
if(pdata=="none"){
$('#'+targetNodeId).html("nothing to save");
return;
}
//while testing:$('#'+targetNodeId).html(pdata);
$.ajax({
url:'http://www.it.hiof.no/~borres/cgi-bin/processing/linedrawing/receivePoints.py',
data:"name="+'points.json'+"&data="+pdata,
type:'POST',
success:function(data, textStatus)
{
// while testing:$('#'+targetNodeId).html(textStatus);
},
error:function(data)
{
$('#'+targetNodeId).html('error'+data);
}
});
}
// parse json and build drawing
function makeDrawingfromJson(procId,data){
var pjs = Processing.getInstanceById(procId);
var drawing=JSON.parse(data);
var strokes=drawing.strokes;
for(var ix=0;ix< strokes.length;ix++){
pjs.addStroke(strokes[ix].width,strokes[ix].color);
coords=strokes[ix].p;
for(var pix=0;pix< coords.length;pix=pix+2)
pjs.addPoint(coords[pix],coords[pix+1]);
}
pjs.reDraw();
}
// clear everything
function clearData(targetNodeId,procId){
var pjs = Processing.getInstanceById(procId);
pjs.setup();
pjs.draw();
}
// make a script drawable or not
function setCanDraw(procId,b){
var pjs = Processing.getInstanceById(procId);
pjs.setDrawable(b);
}
//tell processing about the drawing params
function setPenWidth(){
var w=document.forms['colorform']['penwidth'].value;
setWidth('drawFace',w);
}
function setFormColor(){
var c=document.forms['colorform']['color1'].value;
c='FF'+c.substr(1).toUpperCase();
setColor('drawFace',c);
}
// init by drawing a fixed sketch
function init(){
setCanDraw('showFace',false);
getData('msg','showFace','bs.json');
setFormColor();
setPenWidth();
}
// react to users choice from menu
function setWidth(procId,w){
var pjs = Processing.getInstanceById(procId);
pjs.setStrokeWidth(w);
}
function removeStroke(procId){
var pjs = Processing.getInstanceById(procId);
pjs.removeStroke();
}
function setColor(procId,w){
var pjs = Processing.getInstanceById(procId);
pjs.setStrokeColor(w);
}
Python programmene som lagrer og henter filer er slik:
Jeg har latt meg inspirere av Aron Koblins Sheep Market [2] .
Dette er et pussig prosjekt. 10 000 personer ble invitert til å tegne en sau, og samlingen
er en slags utstilling. Jeg har gjort det litt enklere og har
laget en side med editerbare portretter,
se Faces.