/** * A general purpose SVG outline layer. */ class SVGLayer extends SVGItem { /** * The layer ID, plus setter */ int layerid=-1; void setLayerId(int l) { layerid = l; } /** * The hooking indicator. If true, this lets the sketch * interact with the page by running plain javascript code */ boolean hooked = false; void setHooked(boolean h) { hooked = h; } /** * general administration */ String pathstring; // the SVG outline path String svgstring; // the SVG representation of this layer XMLElement xmldoc; // the XMLElement representation of that SVG code PShape layershape; // the PShape that wraps that XMLElement /** * The SVG Control Box for this SVG layer */ SVGControlBox controlbox; void updateControlBox() { controlbox = new SVGControlBox(this, getWidth(), getHeight()); } /** * The state indicator that tells us whether or not this * layer is currently selected by the user */ boolean selected = false; void setSelected(boolean s) { selected = s; } boolean isSelected() { return selected; } /** * The state indicator that tells use whether or not this * layer is "lit", the result of the layer being considered * highlighted. */ boolean lit=false; boolean isLit() { return lit; } /** * What to do when we highlight this layer: color the path */ void highlight() { if(!lit) { lit=true; setPathColor(highlight_path_color); }} /** * What to do when we lose highlight for this layer: make the path the default color again */ void unhighlight() { if(lit) { lit=false; setPathColor(default_path_color); }} /** * The various path colors we use in this layer */ String default_path_color = "black"; String selected_path_color = "#220099"; String highlight_path_color = "#990022"; String current_path_color = default_path_color; /** * set the current_path_color to some value. If this value is different * from what it was, update the layer by coloring the path this new color */ void setPathColor(String color) { if(!current_path_color.equals(color)) { current_path_color = color; colorPath(); }} /** * The child position of the

element in its containing * in the SVG code for this layer */ int g_index_for_path; /** * colors the path by setting the SVG

"fill" attribute to whatever "current_path_color" is */ void colorPath() { XMLElement g = xmldoc.getChild(0); XMLElement path = g.getChild(g_index_for_path); path.setAttribute("fill",current_path_color); rebindXML(); } /** * Layer position information. * xpos and ypos are in screen coordinates. */ int xpos = 0; int ypos = 0; int getXPos() { return ((int)xpos); } int getYPos() { return ((int)ypos); } /** * setters, [v] in screen coordinates */ void setXPos(int v) { xpos=v; if(hooked) { document.getElementById("layer"+layerid+"xpos").setAttribute("value", getXPos()); } updateControlBox(); } void setYPos(int v) { ypos=v; if(hooked) { document.getElementById("layer"+layerid+"ypos").setAttribute("value", getYPos()); } updateControlBox(); } /** * Repositioning, [v] in screen coordinates */ void moveRight(int v) { setXPos(getXPos()+v); } void moveLeft(int v) { setXPos(getXPos()-v); } void moveUp(int v) { setYPos(getYPos()-v); } void moveDown(int v) { setYPos(getYPos()+v); } // reposition based on another layer void setPosition(SVGLayer other) { setXPos(other.getXPos()); setYPos(other.getYPos()); } /** * Layer scaling, based on the fact that SVG units do not * correspond to screen units in the slightest. The scaling * is achieved by setting the appropriate "scale" transgorm * in the SVG element. */ float xscale=1.0; float yscale=1.0; float getXScale() { return xscale; } float getYScale() { return yscale; } /** * setters */ void setXScale(float v) { xscale = v; updateControlBox(); } void setYScale(float v) { yscale = v; updateControlBox(); } void setScale(float v) { setXScale(v); setYScale(v); } /** * Converts full SVG units to scaled units, * according to this layer's scaling factors */ float getXScaled(float v) { return getXScale() * v; } float getYScaled(float v) { return getYScale() * v; } /** * Flip administration */ boolean xflip = false; boolean yflip = false; boolean getXFlip() { return xflip; } boolean getYFlip() { return yflip; } /** * trigger methods. */ void flipX() { xflip = !xflip; } void flipY() { yflip = !yflip; } /** * Repositioning variables, used for computing layer movement * (such as through mouse or keyboard manipulation) */ int xmark = 0; // marks the location from which we're moving int ymark = 0; int xdiff = 0; // marks the difference with respect to the mark int ydiff = 0; /** * forms the SVG header for this layer */ String makeHeader() { String header = makeSVGHeader(); // handle flipping if(xflip&&yflip) { header += ""; } else if(xflip) { header += ""; } else if(yflip) { header += ""; } else { header += ""; } // show layer dimensions header += ""; g_index_for_path++; // add svg outline header += "" + makeSVGFooter(); } /** * And then finally, the layer's constructor * * path: the outline path string. * width: the glyph's advance width. * height: the glyph's quad height (not outline shape height!). * xflip: flag to indicate whether or not to render this layer flipped horizontally. * yflip: flag to indicate whether or not to render this layer flipped vertically. */ SVGLayer(String path, int width, int height, boolean xflip, boolean yflip) { super(width,height); this.pathstring = path; this.xflip=xflip; this.yflip=yflip; bindXML(); } /** * This method rebuilds the SVG string, and then bind the layer's PShape * so that it reflects the current content. */ void bindXML() { controlbox = new SVGControlBox(this, getWidth(), getHeight()); svgstring = makeHeader() + pathstring + makeFooter(); xmldoc = new XMLElement(svgstring); layershape = new PShapeSVG(xmldoc); } /** * This method rebinds the layer's PShape to reflect any changes made to the local XMLElement */ void rebindXML() { layershape = new PShapeSVG(xmldoc); } /** * Draws this layer onto the canvas, using the shape(PShape, x, y, width, height) call, * then tells the control box to draw itself. */ void draw() { int x = getXPos()+xdiff; int y = getYPos()+ydiff; int w = (int)(getWidth()*getXScale()); int h = (int)(getHeight()*getYScale()); shape(layershape, x, y, w, h); controlbox.draw(); } /** * This method checks whether a (mouse) coordinate falls inside this layer */ boolean contains(int mx, int my) { int x = getXPos()+xdiff; int y = getYPos()+ydiff; int w = getXScaled(getWidth()); int h = getYScaled(getHeight()); // evaluate boolean xin = (x<=mx && mx<=(x+w)); boolean yin = (y<=my && my<=(y+h)); return xin && yin; } // --------------- // Mouse Handlers // --------------- // processing has no mouseOver/mouseOut event handling boolean mouseover = false; int this_cursor = MOVE; int getCursor() { return this_cursor; } void mouseMoved(int mx, int my) { // handle only if controlbox didn't handle if(!controlbox.mouseMoved(mx,my)) { if(contains(mx,my)) { if(!mouseover) { highlight(); } controlbox.setVisible(true); cursor(this_cursor); mouseover = true; } else if(mouseover) { mouseover=false; mouseOut(mx,my); }}} void mouseOut(int mx, int my) { unhighlight(); controlbox.setVisible(false); } void mousePressed(int mx, int my) { // handle only if controlbox didn't handle if(!controlbox.mousePressed(mx,my) && mouseover) { selected = true; xmark = mx-xdiff; ymark = my-ydiff; }} void mouseReleased(int mx, int my) { controlbox.mouseReleased(mx,my); // always handle if(mouseover) { selected = false; setXPos(xpos + xdiff); setYPos(ypos + ydiff); xdiff=0; ydiff=0; }} void mouseDragged(int mx, int my) { // handle only if controlbox didn't handle if(!controlbox.mouseDragged(mx,my) && mouseover && selected) { xdiff = mx-xmark; controlbox.setXDiff(xdiff); ydiff = my-ymark; controlbox.setYDiff(ydiff); }} }