// Global variables HDrawablePool pool; // Setup the Processing Canvas void setup() { size(640,640); H.init(this).background(#000000); smooth(); final HColorPool colors = new HColorPool(#702917,#8D3D22,#29160F,#4E2716,#B76540,#90601E,#C59741,#71541C,#B58920,#DBC059,#957E20,#D1B42C,#F9EE5E,#F9F8DA); HShape svg1 = new HShape("svg/scarab_05.svg"); svg1 .loc(width/2,height/2) .anchorAt(H.CENTER_X | H.CENTER_Y) .strokeWeight(6) .stroke(#ffffff) .scale(.5) .rotation(90f) ; svg1.randomColors(colors).fillOnly(); H.add(svg1); pool = new HDrawablePool(100); pool.autoAddToStage() .add ( new HRect() ) .setOnCreate ( new HCallback() { public void run(Object obj) { HDrawable d = (HDrawable) obj; d .fill(#242424) .strokeWeight(1) .stroke(#999999) .loc( (int)random(width), (int)random(height) ) .size(50) ; } } ) .requestAll(); H.drawStage(); //http://processing.org/reference/noLoop_.html noLoop(); } // Main draw loop void draw() { } public static abstract class HBehavior { public HBehavior() { H.addBehavior(this); } public HBehavior register() { H.addBehavior(this); return this; } public HBehavior unregister() { H.removeBehavior(this); return this; } public abstract void runBehavior(PApplet app); } public static class HFollow extends HBehavior { protected float _ease, _spring, _dx, _dy; protected HFollowable _goal; protected HFollower _follower; public HFollow() { this(1); } public HFollow(float ease) { this(ease,0); } public HFollow(float ease, float spring) { this(ease, spring, H.stage()); } public HFollow(float ease, float spring, HFollowable goal) { _ease = ease; _spring = spring; _goal = goal; H.addBehavior(this); } public HFollow ease(float f) { _ease = f; return this; } public float ease() { return _ease; } public HFollow spring(float f) { _spring = f; return this; } public float spring() { return _spring; } public HFollow goal(HFollowable g) { _goal = g; return this; } public HFollowable goal() { return _goal; } public HFollow followMouse() { _goal = H.stage(); return this; } public HFollow target(HFollower f) { _follower = f; return this; } public HFollower target() { return _follower; } public void runBehavior(PApplet app) { if(_follower==null || ! H.stage().mouseStarted()) return; _dx = _dx*_spring + (_goal.followableX()-_follower.followerX()) * _ease; _dy = _dy*_spring + (_goal.followableY()-_follower.followerY()) * _ease; _follower.follow(_dx,_dy); } public HFollow register() { return (HFollow) super.register(); } public HFollow unregister() { return (HFollow) super.unregister(); } } public static class HMagneticField extends HBehavior { protected ArrayList _magnets; protected HLinkedHashSet _targets; public HMagneticField() { _magnets = new ArrayList(); _targets = new HLinkedHashSet(); } public HMagneticField addMagnet(float sx, float sy, float nx, float ny) { HMagnet m = new HMagnet(); m.southx = sx; m.southy = sy; m.northx = nx; m.northy = ny; _magnets.add(m); return this; } public HMagnet magnet(int index) { return _magnets.get(index); } public HMagneticField removeMagnet(int index) { _magnets.remove(index); return this; } public HMagneticField addTarget(HDrawable d) { _targets.add(d); return this; } public HMagneticField removeTarget(HDrawable d) { _targets.remove(d); return this; } public float getRotation(float x, float y) { float northRot = 0; float southRot = 0; int numMagnets = _magnets.size(); for(int i=0; i it=_targets.iterator(); it.hasNext();) { HDrawable d = it.next(); d.rotationRad( getRotation(d.x(), d.y()) ); } } public HMagneticField register() { return (HMagneticField) super.register(); } public HMagneticField unregister() { return (HMagneticField) super.unregister(); } public static class HMagnet { public float southx, southy, northx, northy; } } public static class HOscillator extends HBehavior { protected HDrawable _target; protected float _stepDeg, _speed, _min, _max, _freq, _relValue, _origW, _origH; protected int _propertyId, _waveform; public HOscillator() { _speed = 1; _min = -1; _max = 1; _freq = 1; _propertyId = H.Y; _waveform = H.SINE; H.addBehavior(this); } public HOscillator(HDrawable target) { this(); _target = target; } public HOscillator createCopy() { HOscillator osc = new HOscillator() .currentStep(_stepDeg) .speed(_speed) .range(_min, _max) .freq(_freq) .relativeVal(_relValue) .property(_propertyId) .waveform(_waveform); return osc; } public HOscillator target(HDrawable newTarget) { _target = newTarget; _origW = _target.width(); _origH = _target.height(); return this; } public HDrawable target() { return _target; } public HOscillator currentStep(float stepDegrees) { _stepDeg = stepDegrees; return this; } public float currentStep() { return _stepDeg; } public HOscillator speed(float spd) { _speed = spd; return this; } public float speed() { return _speed; } public HOscillator range(float minimum, float maximum) { _min = minimum; _max = maximum; return this; } public HOscillator min(float minimum) { _min = minimum; return this; } public float min() { return _min; } public HOscillator max(float maximum) { _max = maximum; return this; } public float max() { return _max; } public HOscillator freq(float frequency) { _freq = frequency; return this; } public float freq() { return _freq; } public HOscillator relativeVal(float relativeValue) { _relValue = relativeValue; return this; } public float relativeVal() { return _relValue; } public HOscillator property(int propertyId) { _propertyId = propertyId; return this; } public int property() { return _propertyId; } public HOscillator waveform(int form) { _waveform = form; return this; } public int waveform() { return _waveform; } public float next() { float currentDeg = _stepDeg * _freq; float outVal = 0; switch(_waveform) { case H.SINE: outVal = sineWave(currentDeg); break; case H.TRIANGLE:outVal = triangleWave(currentDeg); break; case H.SAW: outVal = sawWave(currentDeg); break; case H.SQUARE: outVal = squareWave(currentDeg); break; } outVal = H.app().map(outVal, -1,1, _min,_max) + _relValue; _stepDeg += speed(); return outVal; } public void runBehavior(PApplet app) { if(_target == null) return; if(_propertyId == H.SCALE) { float val = next(); _target.size(_origW*val, _origH*val); } else { _target.set(_propertyId, next()); } } public HOscillator register() { return (HOscillator) super.register(); } public HOscillator unregister() { return (HOscillator) super.unregister(); } public static float sineWave(float stepDegrees) { return H.app().sin(stepDegrees * H.D2R); } public static float triangleWave(float stepDegrees) { float outVal = (stepDegrees % 180) / 90; if(outVal > 1) outVal = 2-outVal; if(stepDegrees % 360 > 180) outVal = -outVal; return outVal; } public static float sawWave(float stepDegrees) { float outVal = (stepDegrees % 180) / 180; if(stepDegrees % 360 >= 180) outVal -= 1; return outVal; } public static float squareWave(float stepDegrees) { return (stepDegrees % 360 > 180)? -1 : 1; } } public static class HSwarm extends HBehavior implements HFollower, HFollowable { protected float _goalX, _goalY, _speed, _turnEase, _twitch; protected HLinkedHashSet _swarmers; public HSwarm() { _speed = 1; _turnEase = 1; _twitch = 16; _swarmers = new HLinkedHashSet(); H.addBehavior(this); } public HSwarm addTarget(HDrawable d) { _swarmers.add(d); return this; } public HSwarm removeTarget(HDrawable d) { _swarmers.remove(d); return this; } public HSwarm goal(float x, float y) { _goalX = x; _goalY = y; return this; } public PVector goal() { return new PVector(_goalX,_goalY); } public HSwarm goalX(float x) { _goalX = x; return this; } public float goalX() { return _goalX; } public HSwarm goalY(float y) { _goalY = y; return this; } public float goalY() { return _goalY; } public HSwarm speed(float s) { _speed = s; return this; } public float speed() { return _speed; } public HSwarm turnEase(float e) { _turnEase = e; return this; } public float turnEase() { return _turnEase; } public HSwarm twitch(float deg) { _twitch = deg * H.D2R; return this; } public HSwarm twitchRad(float rad) { _twitch = rad; return this; } public float twitch() { return _twitch * H.R2D; } public float twitchRad() { return _twitch; } public float followerX() { return _goalX; } public float followerY() { return _goalY; } public float followableX() { return _goalX; } public float followableY() { return _goalY; } public void follow(float dx, float dy) { _goalX += dx; _goalY += dy; } public void runBehavior(PApplet app) { int numSwarmers = _swarmers.size(); HIterator it = _swarmers.iterator(); for(int i=0; i 0 && ++_cycleCounter >= _numCycles) unregister(); } } public HTimer register() { return (HTimer) super.register(); } public HTimer unregister() { _numCycles = 0; _intervalCounter = 0; return (HTimer) super.unregister(); } } public static class HColorField implements HColorist { protected ArrayList _colorPoints; protected float _maxDist; protected boolean _appliesFill, _appliesStroke, _appliesAlpha; public HColorField() { this(H.app().width, H.app().height); } public HColorField(float xBound, float yBound) { this(H.app().sqrt(xBound*xBound + yBound*yBound)); } public HColorField(float maximumDistance) { _colorPoints = new ArrayList(); _maxDist = maximumDistance; fillAndStroke(); } public HColorField addPoint(PVector loc, int clr, float radius) { return addPoint(loc.x,loc.y, clr, radius); } public HColorField addPoint(float x, float y, int clr, float radius) { HColorPoint pt = new HColorPoint(); pt.x = x; pt.y = y; pt.radius = radius; pt.clr = clr; _colorPoints.add(pt); return this; } public int getColor(float x, float y, int baseColor) { PApplet app = H.app(); int[] baseClrs = HColorUtil.explode(baseColor); int[] maxClrs = new int[4]; int initJ; if(_appliesAlpha) { initJ = 0; } else { initJ = 1; maxClrs[0] = baseClrs[0]; } for(int i=0; i<_colorPoints.size(); ++i) { HColorPoint pt = _colorPoints.get(i); int[] ptClrs = HColorUtil.explode(pt.clr); float distLimit = _maxDist * pt.radius; float dist = app.dist(x,y, pt.x,pt.y); if(dist > distLimit) dist = distLimit; for(int j=initJ; j<4; ++j) { int newClrVal = app.round( app.map(dist, 0,distLimit, ptClrs[j], baseClrs[j])); if(newClrVal > maxClrs[j]) maxClrs[j] = newClrVal; } } return HColorUtil.merge(maxClrs[0],maxClrs[1],maxClrs[2],maxClrs[3]); } public HColorField appliesAlpha(boolean b) { _appliesAlpha = b; return this; } public boolean appliesAlpha() { return _appliesAlpha; } public HColorField fillOnly() { _appliesFill = true; _appliesStroke = false; return this; } public HColorField strokeOnly() { _appliesFill = false; _appliesStroke = true; return this; } public HColorField fillAndStroke() { _appliesFill = _appliesStroke = true; return this; } public boolean appliesFill() { return _appliesFill; } public boolean appliesStroke() { return _appliesStroke; } public HDrawable applyColor(HDrawable drawable) { float x = drawable.x(); float y = drawable.y(); if(_appliesFill) { int baseFill = drawable.fill(); drawable.fill( getColor(x,y, baseFill) ); } if(_appliesStroke) { int baseStroke = drawable.stroke(); drawable.stroke( getColor(x,y, baseStroke) ); } return drawable; } public static class HColorPoint { public float x, y, radius; public int clr; } } public static interface HColorist { public HColorist fillOnly(); public HColorist strokeOnly(); public HColorist fillAndStroke(); public boolean appliesFill(); public boolean appliesStroke(); public HDrawable applyColor(HDrawable drawable); } public static class HColorPool implements HColorist { protected ArrayList _colorList; protected boolean _fillFlag, _strokeFlag; public HColorPool(int... colors) { _colorList = new ArrayList(); for(int i=0; i 0) _colorList.add(clr); return this; } public int getColor() { if(_colorList.size() <= 0) return 0; PApplet app = H.app(); int index = app.round(app.random(_colorList.size()-1)); return _colorList.get(index); } public int getColor(int seed) { HMath.tempSeed(seed); int clr = getColor(); HMath.removeTempSeed(); return clr; } public HColorPool fillOnly() { _fillFlag = true; _strokeFlag = false; return this; } public HColorPool strokeOnly() { _fillFlag = false; _strokeFlag = true; return this; } public HColorPool fillAndStroke() { _fillFlag = _strokeFlag = true; return this; } public boolean appliesFill() { return _fillFlag; } public boolean appliesStroke() { return _strokeFlag; } public HDrawable applyColor(HDrawable drawable) { if(_fillFlag) drawable.fill(getColor()); if(_strokeFlag) drawable.stroke(getColor()); return drawable; } } public static class HColorTransform implements HColorist { public float _percA, _percR, _percG, _percB; public int _offsetA, _offsetR, _offsetG, _offsetB; protected boolean fillFlag, strokeFlag; public HColorTransform() { _percA = _percR = _percG = _percB = 1; fillAndStroke(); } public HColorTransform offset(int off) { _offsetA = _offsetR = _offsetG = _offsetB = off; return this; } public HColorTransform offset(int r, int g, int b, int a) { _offsetA = a; _offsetR = r; _offsetG = g; _offsetB = b; return this; } public HColorTransform offsetA(int a) { _offsetA = a; return this; } public int offsetA() { return _offsetA; } public HColorTransform offsetR(int r) { _offsetR = r; return this; } public int offsetR() { return _offsetR; } public HColorTransform offsetG(int g) { _offsetG = g; return this; } public int offsetG() { return _offsetG; } public HColorTransform offsetB(int b) { _offsetB = b; return this; } public int offsetB() { return _offsetB; } public HColorTransform perc(float percentage) { _percA = _percR = _percG = _percB = percentage; return this; } public HColorTransform perc(int r, int g, int b, int a) { _percA = a; _percR = r; _percG = g; _percB = b; return this; } public HColorTransform percA(float a) { _percA = a; return this; } public float percA() { return _percA; } public HColorTransform percR(float r) { _percR = r; return this; } public float percR() { return _percR; } public HColorTransform percG(float g) { _percG = g; return this; } public float percG() { return _percG; } public HColorTransform percB(float b) { _percB = b; return this; } public float percB() { return _percB; } public HColorTransform mergeWith(HColorTransform other) { if(other != null) { _percA *= other._percA; _percR *= other._percR; _percG *= other._percG; _percB *= other._percB; _offsetA += other._offsetA; _offsetR += other._offsetR; _offsetG += other._offsetG; _offsetB += other._offsetB; } return this; } public HColorTransform createCopy() { HColorTransform copy = new HColorTransform(); copy._percA = _percA; copy._percR = _percR; copy._percG = _percG; copy._percB = _percB; copy._offsetA = _offsetA; copy._offsetR = _offsetR; copy._offsetG = _offsetG; copy._offsetB = _offsetB; return copy; } public HColorTransform createNew(HColorTransform other) { return createCopy().mergeWith(other); } public int getColor(int origColor) { PApplet app = H.app(); int[] clrs = HColorUtil.explode(origColor); clrs[0] = app.round(clrs[0] * _percA) + _offsetA; clrs[1] = app.round(clrs[1] * _percR) + _offsetR; clrs[2] = app.round(clrs[2] * _percG) + _offsetG; clrs[3] = app.round(clrs[3] * _percB) + _offsetB; return HColorUtil.merge(clrs[0],clrs[1],clrs[2],clrs[3]); } public HColorTransform fillOnly() { fillFlag = true; strokeFlag = false; return this; } public HColorTransform strokeOnly() { fillFlag = false; strokeFlag = true; return this; } public HColorTransform fillAndStroke() { fillFlag = strokeFlag = true; return this; } public boolean appliesFill() { return fillFlag; } public boolean appliesStroke() { return strokeFlag; } public HDrawable applyColor(HDrawable drawable) { if(fillFlag) { int fill = drawable.fill(); drawable.fill( getColor(fill) ); } if(strokeFlag) { int stroke = drawable.stroke(); drawable.stroke( getColor(stroke) ); } return drawable; } } public static class HPixelColorist implements HColorist { protected PImage img; protected boolean fillFlag, strokeFlag; public HPixelColorist() { fillAndStroke(); } public HPixelColorist(Object imgArg) { this(); setImage(imgArg); } public HPixelColorist setImage(Object imgArg) { if(imgArg instanceof PImage) { img = (PImage) imgArg; } else if(imgArg instanceof HImage) { img = ((HImage) imgArg).image(); } else if(imgArg instanceof String) { img = H.app().loadImage((String) imgArg); } else if(imgArg == null) { img = null; } return this; } public PImage getImage() { return img; } public int getColor(float x, float y) { if(img == null) return 0; PApplet app = H.app(); return img.get(app.round(x), app.round(y)); } public HPixelColorist fillOnly() { fillFlag = true; strokeFlag = false; return this; } public HPixelColorist strokeOnly() { fillFlag = false; strokeFlag = true; return this; } public HPixelColorist fillAndStroke() { fillFlag = strokeFlag = true; return this; } public boolean appliesFill() { return fillFlag; } public boolean appliesStroke() { return strokeFlag; } public HDrawable applyColor(HDrawable drawable) { int clr = getColor(drawable.x(), drawable.y()); if(fillFlag) drawable.fill(clr); if(strokeFlag) drawable.stroke(clr); return drawable; } } public static abstract class HDrawable implements HFollower, HFollowable { protected HDrawable _parent; protected HBundle _extras; protected HChildSet _children; protected float _x, _y, _anchorPercX, _anchorPercY, _width, _height, _rotationRad, _strokeWeight, _alpha; protected int _fill, _stroke, _strokeCap, _strokeJoin; public HDrawable() { _alpha = 1; _fill = H.DEFAULT_FILL; _stroke = H.DEFAULT_STROKE; _strokeCap = PConstants.ROUND; _strokeJoin = PConstants.MITER; _strokeWeight = 1; _width = 64; _height = 64; } public void copyPropertiesFrom(HDrawable other) { _x = other._x; _y = other._y; _anchorPercX = other._anchorPercX; _anchorPercY = other._anchorPercY; _width = other._width; _height = other._height; _rotationRad = other._rotationRad; _alpha = other._alpha; _strokeWeight = other._strokeWeight; _fill = other._fill; _stroke = other._stroke; _strokeCap = other._strokeCap; _strokeJoin = other._strokeJoin; } public abstract HDrawable createCopy(); public void _set_parent_(HDrawable newParent) { _parent = newParent; } public HDrawable parent() { return _parent; } public HChildSet children() { return _children; } public int numChildren() { if(_children==null) return 0; return _children.size(); } public boolean hasChildren() { return _children!=null && _children.size()>0; } public HDrawable add(HDrawable child) { if(_children==null) _children = new HChildSet(this); _children.add(child); return child; } public HDrawable remove(HDrawable child) { if(_children!=null) _children.remove(child); return child; } public HIterator iterator() { return _children.iterator(); } public HDrawable loc(float newX, float newY) { _x = newX; _y = newY; return this; } public HDrawable loc(PVector pt) { _x = pt.x; _y = pt.y; return this; } public PVector loc() { return new PVector(_x,_y); } public HDrawable x(float newX) { _x = newX; return this; } public float x() { return _x; } public HDrawable y(float newY) { _y = newY; return this; } public float y() { return _y; } public HDrawable move(float dx, float dy) { _x += dx; _y += dy; return this; } public HDrawable locAt(int where) { if(_parent!=null) { if(HMath.containsBits(where,H.CENTER_X)) _x = _parent.width()/2 - _parent.anchorX(); else if(HMath.containsBits(where,H.LEFT)) _x = -_parent.anchorX(); else if(HMath.containsBits(where,H.RIGHT)) _x = _parent.width() - _parent.anchorX(); if(HMath.containsBits(where,H.CENTER_Y)) _y = _parent.height()/2 - _parent.anchorY(); else if(HMath.containsBits(where,H.TOP)) _y = -_parent.anchorY(); else if(HMath.containsBits(where,H.BOTTOM)) _y = _parent.height() + _parent.anchorY(); } return this; } public HDrawable anchor(float pxX, float pxY) { if(_height == 0 || _width == 0) { H.warn("Division by 0", "HDrawable.anchor()", "Size must be greater than 0 before setting the Anchor " + "by pixel. Set the size for this drawable first, or set the " + "Anchor by percentage via HDrawable.anchorPerc() instead"); } else { _anchorPercX = pxX / _width; _anchorPercY = pxY / _height; } return this; } public HDrawable anchor(PVector pt) { return anchor(pt.x, pt.y); } public PVector anchor() { return new PVector( anchorX(), anchorY() ); } public HDrawable anchorX(float pxX) { if(_width == 0) { H.warn("Division by 0", "HDrawable.anchorX()", "Width must be greater than 0 before setting the X Anchor " + "by pixel. Set the width for this drawable first, or set the " + "X Anchor by percentage via HDrawable.anchorPercX() instead"); } else { _anchorPercX = pxX / _width; } return this; } public float anchorX() { return _width * _anchorPercX; } public HDrawable anchorY(float pxY) { if(_height == 0) { H.warn("Division by 0", "HDrawable.anchorY()", "Width must be greater than 0 before setting the Y Anchor " + "by pixel. Set the height for this drawable first, or set the " + "Y Anchor by percentage via HDrawable.anchorPercY() instead"); } else { _anchorPercY = pxY / _height; } return this; } public float anchorY() { return _height * _anchorPercY; } public HDrawable anchorPerc(float percX, float percY) { _anchorPercX = percX; _anchorPercY = percY; return this; } public PVector anchorPerc() { return new PVector(_anchorPercX, _anchorPercY); } public HDrawable anchorPercX(float percX) { _anchorPercX = percX; return this; } public float anchorPercX() { return _anchorPercX; } public HDrawable anchorPercY(float percY) { _anchorPercY = percY; return this; } public float anchorPercY() { return _anchorPercY; } public HDrawable anchorAt(int where) { if(HMath.containsBits(where,H.CENTER_X)) _anchorPercX = 0.5f; else if(HMath.containsBits(where,H.LEFT)) _anchorPercX = 0; else if(HMath.containsBits(where,H.RIGHT)) _anchorPercX = 1; if(HMath.containsBits(where,H.CENTER_Y)) _anchorPercY = 0.5f; else if(HMath.containsBits(where,H.TOP)) _anchorPercY = 0; else if(HMath.containsBits(where,H.BOTTOM)) _anchorPercY = 1; return this; } public HDrawable size(float w, float h) { width(w); height(h); return this; } public HDrawable size(float s) { size(s,s); return this; } public PVector size() { return new PVector(_width,_height); } public HDrawable width(float w) { _width = w; return this; } public float width() { return _width; } public HDrawable height(float h) { _height = h; return this; } public float height() { return _height; } public HDrawable scale(float s) { size(_width*s, _height*s); return this; } public HDrawable scale(float sw, float sh) { size(_width*sw, _height*sh); return this; } public PVector boundingSize() { PApplet app = H.app(); float cosVal = app.cos(_rotationRad); float sinVal = app.sin(_rotationRad); float drawX = -anchorX(); float drawY = -anchorY(); float x1 = drawX; float x2 = _width + drawX; float y1 = drawY; float y2 = _height + drawY; float[] xCoords = new float[4]; float[] yCoords = new float[4]; xCoords[0] = x1*cosVal + y1*sinVal; yCoords[0] = x1*sinVal + y1*cosVal; xCoords[1] = x2*cosVal + y1*sinVal; yCoords[1] = x2*sinVal + y1*cosVal; xCoords[2] = x1*cosVal + y2*sinVal; yCoords[2] = x1*sinVal + y2*cosVal; xCoords[3] = x2*cosVal + y2*sinVal; yCoords[3] = x2*sinVal + y2*cosVal; float minX = xCoords[3]; float maxX = minX; float minY = yCoords[3]; float maxY = maxX; for(int i=0; i<3; ++i) { float x = xCoords[i]; float y = yCoords[i]; if(x < minX) minX = x; else if(x > maxX) maxX = x; if(y < minY) minY = y; else if(y > maxY) maxY = y; } return new PVector(maxX-minX, maxY-minY); } public HDrawable fill(int clr) { _fill = clr; return this; } public HDrawable fill(int clr, int alpha) { _fill = HColorUtil.setAlpha(clr,alpha); return this; } public HDrawable fill(int r, int g, int b) { _fill = HColorUtil.merge(255,r,g,b); return this; } public HDrawable fill(int r, int g, int b, int a) { _fill = HColorUtil.merge(a,r,g,b); return this; } public int fill() { return _fill; } public HDrawable stroke(int clr) { _stroke = clr; return this; } public HDrawable stroke(int clr, int alpha) { _stroke = HColorUtil.setAlpha(clr,alpha); return this; } public HDrawable stroke(int r, int g, int b) { _stroke = HColorUtil.merge(255,r,g,b); return this; } public HDrawable stroke(int r, int g, int b, int a) { _stroke = HColorUtil.merge(r,g,b,a); return this; } public int stroke() { return _stroke; } public HDrawable strokeCap(int type) { _strokeCap = type; return this; } public int strokeCap() { return _strokeCap; } public HDrawable strokeJoin(int type) { _strokeJoin = type; return this; } public int strokeJoin() { return _strokeJoin; } public HDrawable strokeWeight(float f) { _strokeWeight = f; return this; } public float strokeWeight() { return _strokeWeight; } public HDrawable rotation(float deg) { _rotationRad = deg * H.D2R; return this; } public float rotation() { return _rotationRad * H.R2D; } public HDrawable rotationRad(float rad) { _rotationRad = rad; return this; } public float rotationRad() { return _rotationRad; } public HDrawable rotate(float deg) { _rotationRad += deg * H.D2R; return this; } public HDrawable rotateRad(float rad) { _rotationRad += rad; return this; } public HDrawable alpha(int a) { return alphaPerc(a/255f); } public int alpha() { return H.app().round( alphaPerc()*255 ); } public HDrawable alphaPerc(float aPerc) { _alpha = (aPerc<0)? 0 : (aPerc>1)? 1 : aPerc; return this; } public float alphaPerc() { return (_alpha<0)? 0 : _alpha; } public HDrawable visibility(boolean v) { if(v && _alpha == 0) { _alpha = 1; } else if(v == _alpha < 0) { _alpha = -_alpha; } return this; } public boolean visibility() { return _alpha > 0; } public HDrawable show() { return visibility(true); } public HDrawable hide() { return visibility(false); } public HDrawable alphaShift(int da) { return alphaShiftPerc( da/255f ); } public HDrawable alphaShiftPerc(float daPerc) { return alphaPerc(_alpha + daPerc); } public float followerX() { return _x; } public float followerY() { return _y; } public float followableX() { return _x; } public float followableY() { return _y; } public void follow(float dx, float dy) { move(dx,dy); } public HDrawable extras(HBundle b) { _extras = b; return this; } public HBundle extras() { return _extras; } public float[] abs2rel(float x, float y) { return null; } public float[] rel2abs(float x, float y) { return null; } public void set(int propId, float val) { switch(propId) { case H.WIDTH: width(val); break; case H.HEIGHT: height(val); break; case H.SIZE: size(val); break; case H.ALPHA: alpha(H.app().round(val)); break; case H.X: x(val); break; case H.Y: y(val); break; case H.LOCATION: loc(val,val); break; case H.ROTATION: rotation(val); break; case H.DROTATION: rotate(val); break; case H.DX: move(val,0); break; case H.DY: move(0,val); break; case H.DLOC: move(val,val); break; case H.SCALE: scale(val); break; default: break; } } protected void applyStyle(PApplet app, float currAlphaPerc) { int falpha = _fill>>>24; falpha = app.round( currAlphaPerc * falpha ) << 24; int currFill = _fill & 0x00FFFFFF | falpha; app.fill(currFill); if(_strokeWeight > 0) { int salpha = _stroke>>>24; salpha = app.round( currAlphaPerc * salpha ) << 24; int currStroke = _stroke & 0x00FFFFFF | salpha; app.stroke(currStroke); app.strokeWeight(_strokeWeight); app.strokeCap(_strokeCap); app.strokeJoin(_strokeJoin); } else app.noStroke(); } public void paintAll(PApplet app, float currAlphaPerc) { if(_alpha<=0 || _width<=0 || _height<=0) return; app.pushMatrix(); app.translate(_x,_y); app.rotate(_rotationRad); currAlphaPerc *= _alpha; draw(app,-anchorX(),-anchorY(),currAlphaPerc); if(hasChildren()) { HIterator it = _children.iterator(); while(it.hasNext()) it.next().paintAll(app,currAlphaPerc); } app.popMatrix(); } public abstract void draw( PApplet app, float drawX, float drawY, float currAlphaPerc); } public static class HEllipse extends HDrawable { public HEllipse() {} public HEllipse(float ellipseRadius) { radius(ellipseRadius); } public HEllipse(float radiusX, float radiusY) { radius(radiusX,radiusY); } public HEllipse createCopy() { HEllipse copy = new HEllipse(); copy.copyPropertiesFrom(this); return copy; } public HEllipse radius(float r) { size(r*2); return this; } public HEllipse radius(float radiusX, float radiusY) { size(radiusX*2,radiusY*2); return this; } public HEllipse radiusX(float radiusX) { width(radiusX * 2); return this; } public float radiusX() { return _width/2; } public HEllipse radiusY(float radiusY) { height(radiusY * 2); return this; } public float radiusY() { return _height/2; } public boolean isCircle() { return _width == _height; } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) { applyStyle(app,currAlphaPerc); app.ellipse(drawX+_width/2, drawY+_height/2, _width, _height); } } public static class HImage extends HDrawable { protected PImage _image; public HImage() { image(null); } public HImage(Object imgArg) { image(imgArg); } public HImage createCopy() { HImage copy = new HImage(_image); copy.copyPropertiesFrom(this); return copy; } public HImage resetSize() { if(_image == null) { size(0f,0f); } else { size(_image.width, _image.height); } return this; } public HImage image(Object imgArg) { if(imgArg instanceof PImage) { _image = (PImage) imgArg; } else if(imgArg instanceof String) { _image = H.app().loadImage((String) imgArg); } else if(imgArg instanceof HImage) { _image = ((HImage) imgArg)._image; } else if(imgArg == null) { _image = null; } return resetSize(); } public PImage image() { return _image; } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) { if(_image==null) return; app.tint( app.round(currAlphaPerc*255) ); app.image(_image,drawX,drawY); } } public static class HRect extends HDrawable { public float _tl, _tr, _bl, _br; public HRect() {} public HRect(float s) { size(s); } public HRect(float w, float h) { size(w,h); } public HRect(float w, float h, float roundingRadius) { size(w,h); rounding(roundingRadius); } public HRect createCopy() { HRect copy = new HRect(); copy._tl = _tl; copy._tr = _tr; copy._bl = _bl; copy._br = _br; copy.copyPropertiesFrom(this); return copy; } public HRect rounding(float radius) { _tl = _tr = _bl = _br = radius; return this; } public HRect rounding( float topleft, float topright, float bottomright, float bottomleft ) { _tl = topleft; _tr = topright; _br = bottomright; _bl = bottomleft; return this; } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) { applyStyle(app,currAlphaPerc); app.rect(drawX,drawY, _width,_height, _tl,_tr,_br,_bl); } } public static class HShape extends HDrawable { protected PShape _shape; protected HColorPool _randomColors; public HShape() { shape(null); } public HShape(Object shapeArg) { shape(shapeArg); } public HShape createCopy() { HShape copy = new HShape(_shape); copy.copyPropertiesFrom(this); return copy; } public HShape resetSize() { if(_shape == null) { size(0,0); } else { size(_shape.width,_shape.height); } return this; } public HShape shape(Object shapeArg) { if(shapeArg instanceof PShape) { _shape = (PShape) shapeArg; } else if(shapeArg instanceof String) { _shape = H.app().loadShape((String) shapeArg); } else if(shapeArg instanceof HShape) { _shape = ((HShape) shapeArg)._shape; } else if(shapeArg == null) { _shape = null; } return resetSize(); } public PShape shape() { return _shape; } public HShape enableStyle(boolean b) { if(b) _shape.enableStyle(); else _shape.disableStyle(); return this; } public HColorPool randomColors(HColorPool colorPool) { return randomColors(colorPool,true); } public HColorPool randomColors(HColorPool colorPool, boolean isCopy) { if(isCopy) colorPool = colorPool.createCopy(); _shape.disableStyle(); _randomColors = colorPool; return _randomColors; } public HColorPool randomColors() { return _randomColors; } public HShape resetRandomColors() { _shape.enableStyle(); _randomColors = null; return this; } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) { if(_shape == null) return; applyStyle(app,currAlphaPerc); if(_randomColors == null) { app.shape(_shape, drawX,drawY, _width,_height); } else for(int i=0; i<_shape.getChildCount(); ++i) { PShape childShape = _shape.getChild(i); childShape.width = _shape.width; childShape.height = _shape.height; if(_randomColors.appliesFill()) app.fill(_randomColors.getColor()); if(_randomColors.appliesStroke()) app.stroke(_randomColors.getColor()); app.shape(childShape, drawX,drawY, _width,_height); } } } public static class HStage extends HDrawable { protected PApplet _app; protected HLinkedHashSet _behaviors; protected PImage _bgImg; protected int _bgColor; protected boolean _autoClearFlag, _mouseStarted; public HStage(PApplet papplet) { _app = papplet; _children = new HChildSet(this); _behaviors = new HLinkedHashSet(); _bgColor = H.DEFAULT_BACKGROUND_COLOR; _autoClearFlag = true; _app.background(_bgColor); } public HLinkedHashSet behaviors() { return _behaviors; } public HDrawable createCopy() { return null; } public void background(int clr) { _bgColor = clr; clear(); } public void backgroundImg(Object arg) { if(arg instanceof String) { _bgImg = _app.loadImage((String) arg); } else if(arg instanceof PImage) { _bgImg = (PImage) arg; } clear(); } public HStage autoClear(boolean b) { _autoClearFlag = b; return this; } public boolean autoClear() { return _autoClearFlag; } public HStage clear() { if(_bgImg == null) _app.background(_bgColor); else _app.background(_bgImg); return this; } public PVector size() { return new PVector(_app.width,_app.height); } public float width() { return _app.width; } public float height() { return _app.height; } public float followableX() { return _app.mouseX; } public float followableY() { return _app.mouseY; } public boolean mouseStarted() { return _mouseStarted; } public void paintAll(PApplet app, float currAlphaPerc) { if(!_mouseStarted && _app.pmouseX+_app.pmouseY > 0) _mouseStarted = true; if(_behaviors.size()>0) { HIterator bIt = _behaviors.iterator(); while(bIt.hasNext()) bIt.next().runBehavior(_app); } app.pushStyle(); if(_autoClearFlag) clear(); if(_children.size()>0) { HIterator cIt = _children.iterator(); while(cIt.hasNext()) cIt.next().paintAll(app,1); } app.popStyle(); } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) {} } public static class HText extends HDrawable { protected PFont _font; protected String _text; protected float _descent; public HText() { this(null,16); } public HText(String textString) { this(textString,16,null); } public HText(String textString, float size) { this(textString,size,null); } public HText(String textString, float size, Object fontArg) { _text = textString; height(size); font(fontArg); } public HText createCopy() { HText copy = new HText(_text,_height,_font); copy.copyPropertiesFrom(this); copy.adjustMetrics(); return copy; } public HText text(String txt) { _text = txt; adjustMetrics(); return this; } public String text() { return _text; } public HText font(Object arg) { PApplet app = H.app(); if(arg instanceof PFont) { _font = (PFont) arg; } else if(arg instanceof String) { String str = (String) arg; _font = H.endsWith(str,".vlw")? app.loadFont(str) : app.createFont(str,_height); } else if(arg instanceof HText) { _font = ((HText) arg)._font; } else if(arg == null) { _font = null; } adjustMetrics(); return this; } public PFont font() { return _font; } public HText fontSize(float f) { return height(f); } public float fontSize() { return _height; } protected void adjustMetrics() { PApplet app = H.app(); app.pushStyle(); if(_font == null) app.textSize(_height); else app.textFont(_font,_height); _descent = app.textDescent(); super.width( (_text==null)? 0 : app.textWidth(_text) ); app.popStyle(); } public HText width(float w) { return this; } public HText height(float h) { super.height(h); adjustMetrics(); return this; } public HText size(float w, float h) { return height(h); } public HText size(float s) { return height(s); } public HText scale(float s) { super.scale(s); adjustMetrics(); return this; } public HText scale(float sw, float sh) { return scale(sh); } public void draw(PApplet app,float drawX,float drawY,float currAlphaPerc) { if(_text == null) return; applyStyle(app,currAlphaPerc); if(_font == null) app.textSize(_height); else app.textFont(_font,_height); app.text(_text,drawX,drawY+_height-_descent); } } public static class HGridLayout implements HLayout { protected int _currentIndex, _numCols; protected float _startX, _startY, _xSpace, _ySpace; public HGridLayout() { _xSpace = _ySpace = _numCols = 16; } public HGridLayout(int numOfColumns) { this(); _numCols = numOfColumns; } public HGridLayout currentIndex(int i) { _currentIndex = i; return this; } public int currentIndex() { return _currentIndex; } public HGridLayout resetIndex() { _currentIndex = 0; return this; } public HGridLayout cols(int numOfColumns) { _numCols = numOfColumns; return this; } public int cols() { return _numCols; } public PVector startLoc() { return new PVector(_startX, _startY); } public HGridLayout startLoc(float x, float y) { _startX = x; _startY = y; return this; } public float startX() { return _startX; } public HGridLayout startX(float x) { _startX = x; return this; } public float startY() { return _startY; } public HGridLayout startY(float y) { _startY = y; return this; } public PVector spacing() { return new PVector(_xSpace, _ySpace); } public HGridLayout spacing(float xSpacing, float ySpacing) { _xSpace = xSpacing; _ySpace = ySpacing; return this; } public float spacingX() { return _xSpace; } public HGridLayout spacingX(float xSpacing) { _xSpace = xSpacing; return this; } public float spacingY() { return _ySpace; } public HGridLayout spacingY(float ySpacing) { _ySpace = ySpacing; return this; } public PVector getNextPoint() { int currentRow = H.app().floor(_currentIndex / _numCols); int currentCol = _currentIndex % _numCols; ++_currentIndex; return new PVector( currentCol*_xSpace + _startX, currentRow*_ySpace + _startY ); } public void applyTo(HDrawable target) { target.loc(getNextPoint()); } } public static interface HLayout { public void applyTo(HDrawable target); public abstract PVector getNextPoint(); } public static class H implements HConstants { private static H _self; private static PApplet _app; private static HStage _stage; public static H init(PApplet applet) { _app = applet; HMath.init(_app); if(_self == null) _self = new H(); if(_stage == null) _stage = new HStage(_app); return _self; } public static HStage stage() { return _stage; } public static PApplet app() { return _app; } public static H background(int clr) { _stage.background(clr); return _self; } public static H backgroundImg(Object arg) { _stage.backgroundImg(arg); return _self; } public static H autoClear(boolean b) { _stage.autoClear(b); return _self; } public static boolean autoClear() { return _stage.autoClear(); } public static H clearStage() { _stage.clear(); return _self; } public static H drawStage() { _stage.paintAll(_app,0); return _self; } public static HDrawable add(HDrawable stageChild) { return _stage.add(stageChild); } public static HDrawable remove(HDrawable stageChild) { return _stage.remove(stageChild); } public static H addBehavior(HBehavior b) { _stage.behaviors().add(b); return _self; } public static H removeBehavior(HBehavior b) { _stage.behaviors().remove(b); return _self; } public static boolean mouseStarted() { return _stage.mouseStarted(); } public static boolean endsWith(String haystack, String needle) { return (haystack.indexOf(needle,haystack.length()-needle.length()) > 0); } public static void warn(String type, String loc, String msg) { _app.println("[Warning: "+type+" @ "+loc+"]"); if( msg!=null && msg.length()>0 ) _app.println(msg); } private H() {} } public static class HBundle { private HashMap objectContents; private HashMap numberContents; public HBundle() { objectContents = new HashMap(); numberContents = new HashMap(); } public HBundle obj(String key, Object value) { objectContents.put(key,value); return this; } public HBundle num(String key, float value) { numberContents.put(key,value); return this; } public Object obj(String key) { return objectContents.get(key); } public float num(String key) { return numberContents.get(key); } public int numI(String key) { return H.app().round(numberContents.get(key)); } public boolean numB(String key) { return (numberContents.get(key) != 0); } } public static interface HCallback { public void run(Object obj); } public static class HColorUtil { public static int[] explode(int clr) { int[] explodedColors = new int[4]; for(int i=0; i<4; ++i) explodedColors[3-i] = (clr >>> (i*8)) & 0xFF; return explodedColors; } public static int merge(int a, int r, int g, int b) { PApplet app = H.app(); a = app.constrain(a, 0, 0xFF); r = app.constrain(r, 0, 0xFF); g = app.constrain(g, 0, 0xFF); b = app.constrain(b, 0, 0xFF); return (a<<24) | (r<<16) | (g<<8) | b; } public static int setAlpha(int clr, int newAlpha) { newAlpha = H.app().constrain(newAlpha, 0, 0xFF); return clr & 0x00FFFFFF | (newAlpha << 24); } public static int setRed(int clr, int newRed) { newRed = H.app().constrain(newRed, 0, 0xFF); return clr & 0xFF00FFFF | (newRed << 16); } public static int setGreen(int clr, int newGreen) { newGreen = H.app().constrain(newGreen, 0, 0xFF); return clr & 0xFFFF00FF | (newGreen << 8); } public static int setBlue(int clr, int newBlue) { newBlue = H.app().constrain(newBlue, 0, 0xFF); return clr & 0xFFFFFF00 | newBlue; } public static int getAlpha(int clr) { return clr >>> 24; } public static int getRed(int clr) { return (clr >>> 16) & 255; } public static int getGreen(int clr) { return (clr >>> 8) & 255; } public static int getBlue(int clr) { return clr & 255; } public static int multiply(int c1, int c2) { return H.app().round(c1 * c2 / 255f); } public static int multiplyAlpha(int clr, int a) { return clr & 0x00FFFFFF | ( multiply(getAlpha(clr),a) << 24 ); } public static int multiplyRed(int clr, int r) { return clr & 0xFF00FFFF | ( multiply(getRed(clr),r) << 16 ); } public static int multiplyGreen(int clr, int g) { return clr & 0xFFFF00FF | ( multiply(getGreen(clr),g) << 8 ); } public static int multiplyBlue(int clr, int b) { return clr & 0xFFFFFF00 | multiply(getBlue(clr),b); } } public static interface HConstants { public static final int NONE = 0, SIN = 1, COS = 2, TAN = 3, LEFT = 1, RIGHT = 2, TOP = 4, BOTTOM = 8, CENTER_X = 3, CENTER_Y = 12, CENTER = 15, DEFAULT_BACKGROUND_COLOR = 0xFFECF2F5, DEFAULT_FILL = 0x00FFFFFF, DEFAULT_STROKE = 0x00FFFFFF, SAW = 0, SINE = 1, TRIANGLE = 2, SQUARE = 3, WIDTH = 0, HEIGHT = 1, SIZE = 2, ALPHA = 3, X = 4, Y = 5, LOCATION = 6, ROTATION = 7, DROTATION = 8, DX = 9, DY = 10, DLOC = 11, SCALE = 12; public static final float D2R = PConstants.PI / 180f, R2D = 180f / PConstants.PI; } public static class HDrawablePool { protected HLinkedHashSet _activeSet, _inactiveSet; protected ArrayList _prototypes; public HCallback _onCreate, _onRequest, _onRelease; public HPoolListener _listener; protected HLayout _layout; protected HColorist _colorist; protected HDrawable _autoParent; protected int _max; public HDrawablePool() { this(64); } public HDrawablePool(int maximumDrawables) { _max = maximumDrawables; _activeSet = new HLinkedHashSet(); _inactiveSet = new HLinkedHashSet(); _prototypes = new ArrayList(); } public int max() { return _max; } public HDrawablePool max(int m) { _max = m; return this; } public int numActive() { return _activeSet.size(); } public int numInactive() { return _inactiveSet.size(); } public int currentIndex() { return _activeSet.size() - 1; } public HLayout layout() { return _layout; } public HDrawablePool layout(HLayout newLayout) { _layout = newLayout; return this; } public HColorist colorist() { return _colorist; } public HDrawablePool colorist(HColorist newColorist) { _colorist = newColorist; return this; } public HDrawablePool setOnCreate(HCallback callback) { _onCreate = callback; return this; } public HDrawablePool listener(HPoolListener newListener) { _listener = newListener; return this; } public HPoolListener listener() { return _listener; } public HDrawablePool setOnRequest(HCallback callback) { _onRequest = callback; return this; } public HDrawablePool setOnRelease(HCallback callback) { _onRelease = callback; return this; } public HDrawablePool autoParent(HDrawable parent) { _autoParent = parent; return this; } public HDrawablePool autoAddToStage() { _autoParent = H.stage(); return this; } public HDrawable autoParent() { return _autoParent; } public boolean isFull() { return count() >= _max; } public int count() { return _activeSet.size() + _inactiveSet.size(); } public HDrawablePool destroy() { _activeSet.removeAll(); _inactiveSet.removeAll(); _prototypes.clear(); _onCreate = _onRequest = _onRelease = null; _layout = null; _autoParent = null; _max = 0; return this; } public HDrawablePool add(HDrawable prototype, int frequency) { if(prototype == null) { H.warn("Invalid Argument", "HDrawablePool.add()", "The new prototype shouldn't be null."); } else { _prototypes.add(prototype); while(frequency-- > 0) _prototypes.add(prototype); } return this; } public HDrawablePool add(HDrawable prototype) { return add(prototype,1); } public HDrawable request() { if(_prototypes.size() <= 0) { H.warn("Invalid Argument", "HDrawablePool.request()", "Request aborted. HDrawablePool can't request a new object " + "without an existing prototype. Try using " + "HDrawablePool.add( HDrawable ) to add a new prototype"); return null; } HDrawable drawable; boolean onCreateFlag = false; if(_inactiveSet.size() > 0) { drawable = _inactiveSet.pull(); } else if(count() < _max) { drawable = createRandomDrawable(); onCreateFlag = true; } else return null; _activeSet.add(drawable); if(_autoParent != null) _autoParent.add(drawable); if(_layout != null) _layout.applyTo(drawable); if(_colorist != null) _colorist.applyColor(drawable); if(_listener != null) { int index = currentIndex(); if(onCreateFlag) _listener.onCreate(drawable, index, this); _listener.onRequest(drawable, index, this); } if(onCreateFlag && _onCreate != null) _onCreate.run(drawable); if(_onRequest != null) _onRequest.run(drawable); return drawable; } public HDrawablePool requestAll() { while(count() < _max) request(); return this; } public boolean release(HDrawable d) { if(_activeSet.remove(d)) { _inactiveSet.add(d); if(_autoParent != null) _autoParent.remove(d); if(_listener != null) _listener.onRelease(d, currentIndex(), this); if(_onRelease != null) _onRelease.run(d); return true; } return false; } public HLinkedHashSet activeSet() { return _activeSet; } public HLinkedHashSet inactiveSet() { return _inactiveSet; } protected HDrawable createRandomDrawable() { PApplet app = H.app(); int numPrototypes = _prototypes.size(); int index = app.round( app.random(numPrototypes-1) ); return _prototypes.get(index).createCopy(); } public HIterator iterator() { return _activeSet.iterator(); } } public static interface HFollowable { public float followableX(); public float followableY(); } public static interface HFollower { public float followerX(); public float followerY(); public void follow(float dx, float dy); } public static class HMath { private static PApplet _app; private static boolean _usingTempSeed; private static int _resetSeedValue; public static void init(PApplet applet) { _app = applet; } public static float[] rotatePoint(float x, float y, float rad) { float[] pt = new float[2]; float c = _app.cos(rad); float s = _app.sin(rad); pt[0] = x*c + y*s; pt[1] = x*s + y*c; return pt; } public static float yAxisAngle(float x1, float y1, float x2, float y2) { return _app.atan2(x2-x1, y2-y1); } public static float xAxisAngle(float x1, float y1, float x2, float y2) { return _app.atan2(y2-y1, x2-x1); } public static int randomInt32() { float f = _app.random(1); f = _app.map(f, 0, 1, -2147483648, 2147483647); return _app.round(f); } public static void tempSeed(long seed) { if(!_usingTempSeed) { _resetSeedValue = randomInt32(); _usingTempSeed = true; } _app.randomSeed(seed); } public static void removeTempSeed() { _app.randomSeed(_resetSeedValue); } public static boolean containsBits(int target, int val) { return ( (target & val) == val ); } } public static class HPoolAdapter implements HPoolListener { public void onCreate(HDrawable d, int index, HDrawablePool pool) {} public void onRequest(HDrawable d, int index, HDrawablePool pool) {} public void onRelease(HDrawable d, int index, HDrawablePool pool) {} } public static interface HPoolListener { public void onCreate(HDrawable d, int index, HDrawablePool pool); public void onRequest(HDrawable d, int index, HDrawablePool pool); public void onRelease(HDrawable d, int index, HDrawablePool pool); } public static class HChildSet extends HLinkedHashSet { private HDrawable _parent; public HChildSet(HDrawable parent) { _parent = parent; } protected HLinkNode register(HDrawable d) { HDrawable dparent = d.parent(); if(dparent != null) dparent.remove(d); d._set_parent_(_parent); return super.register(d); } protected HDrawable unregister(HDrawable d) { if(contains(d)) d._set_parent_(null); return super.unregister(d); } } public static interface HIterator { public boolean hasNext(); public U next(); public void remove(); } public static class HLinkedHashSet extends HLinkedList { protected HashMap> nodeMap; public HLinkedHashSet() { nodeMap = new HashMap>(); } public boolean remove(T obj) { HLinkNode node = nodeMap.get(obj); if(node == null) return false; if(node.equals(firstNode)) { pop(); } else if(node.equals(lastNode)) { pull(); } else { unregister(obj); node.popOut(); --numNodes; } return true; } public boolean add(T obj) { if(contains(obj)) return false; return addNode(register(obj)); } public T pull() { return unregister(super.pull()); } public boolean push(T obj) { if(contains(obj)) return false; return pushNode(register(obj)); } public T pop() { return unregister(super.pop()); } public boolean insert(T obj, int index) { return insertNode(register(obj), index); } public T removeAt(int index) { return unregister(super.removeAt(index)); } public void removeAll() { nodeMap.clear(); super.removeAll(); } public boolean contains(T obj) { return nodeMap.get(obj) != null; } protected HLinkNode register(T obj) { if(obj == null) return null; HLinkNode node = new HLinkNode(obj); nodeMap.put(obj,node); return node; } protected T unregister(T obj) { nodeMap.remove(obj); return obj; } } public static class HLinkedList { protected HLinkNode firstNode, lastNode; protected int numNodes; public int size() { return numNodes; } public T first() { return (firstNode==null)? null : firstNode.content(); } protected boolean pushNode(HLinkNode node) { if(node==null) return false; if(numNodes <= 0) { node.popOut(); firstNode = lastNode = node; numNodes = 1; } else { node.putBefore(firstNode); firstNode = node; ++numNodes; } return true; } public boolean push(T obj) { return pushNode(new HLinkNode(obj)); } public T pop() { T content = (firstNode==null)? null : firstNode.content(); removeNode(firstNode); return content; } public T last() { return (lastNode==null)? null : lastNode.content(); } protected boolean addNode(HLinkNode node) { if(node==null) return false; if(numNodes <= 0) { node.popOut(); lastNode = firstNode = node; numNodes = 1; } else { node.putAfter(lastNode); lastNode = node; ++numNodes; } return true; } public boolean add(T obj) { return addNode(new HLinkNode(obj)); } public T pull() { T content = (lastNode==null)? null : lastNode.content(); removeNode(lastNode); return content; } public T get(int index) { HLinkNode n = getNode(index); return (n==null)? null : n.content(); } protected HLinkNode getNode(int index) { int reverseIndex; if(index < 0) { reverseIndex = index; index = numNodes + index; } else { reverseIndex = index - numNodes; } if( !inRange(index) ) { H.warn("Out of Range","HLinkedList.getNode()",null); return null; } HLinkNode n; if(index <= -reverseIndex) { n = firstNode; while(0 < index--) n = n.next(); } else { n = lastNode; while(0 > ++reverseIndex) n = n.prev(); } return n; } protected boolean insertNode(HLinkNode node, int index) { if( node == null ) return false; if( index < 0 ) index = numNodes + index; if( index == 0 ) return pushNode(node); if( index == numNodes ) return addNode(node); if( inRange(index) ) { node.putBefore( getNode(index) ); ++numNodes; return true; } else { H.warn("Out of Range","HLinkedList.insertNode()",null); return false; } } public boolean insert(T obj, int index) { return insertNode(new HLinkNode(obj), index); } public T removeAt(int index) { HLinkNode n = getNode(index); return removeNode(n)? n.content() : null; } protected boolean removeNode(HLinkNode node) { if( node == null ) return false; HLinkNode pn = node.prev(); HLinkNode nn = node.next(); node.popOut(); --numNodes; if( numNodes > 0 ) { if(pn==null) firstNode = nn; if(nn==null) lastNode = pn; } else { lastNode = firstNode = null; } return true; } public void removeAll() { firstNode = lastNode = null; numNodes = 0; } public boolean inRange(int index) { return (0 <= index) && (index < numNodes); } public HIterator iterator() { return new HLinkedListIterator(this); } public static class HLinkedListIterator implements HIterator { HLinkedList parentList; HLinkNode n1, n2; public HLinkedListIterator(HLinkedList parent) { parentList = parent; n1 = parentList.firstNode; if(n1 != null) n2 = n1.next(); } public boolean hasNext() { return (n1 != null); } public U next() { U content = n1.content(); n1 = n2; if(n2 != null) n2 = n2.next(); return content; } public void remove() { parentList.removeNode(n1); } } } public static class HLinkNode { private HLinkNode _prev, _next; private T _content; public HLinkNode(T o) { _content = o; } public T content() { return _content; } public HLinkNode prev() { return _prev; } public HLinkNode next() { return _next; } public void replace(HLinkNode otherNode) { popOut(); if(otherNode != null) { HLinkNode otherPrev = otherNode._prev; HLinkNode otherNext = otherNode._next; stitchTogether(otherPrev,this); stitchTogether(this,otherNext); } } public void putBefore(HLinkNode otherNode) { popOut(); if(otherNode != null) { HLinkNode otherPrev = otherNode._prev; stitchTogether(otherPrev,this); stitchTogether(this,otherNode); } } public void putAfter(HLinkNode otherNode) { popOut(); if(otherNode != null) { HLinkNode otherNext = otherNode._next; stitchTogether(otherNode,this); stitchTogether(this,otherNext); } } public void popOut() { stitchTogether(_prev,_next); } public static void swap(HLinkNode node1, HLinkNode node2) { if(node1 == null || node2 == null) return; HLinkNode prev1 = node1._prev; HLinkNode next1 = node1._next; HLinkNode prev2 = node2._prev; HLinkNode next2 = node2._next; stitchTogether(prev1,node2); stitchTogether(node2,next1); stitchTogether(prev2,node1); stitchTogether(node1,next2); } public static void stitchTogether(HLinkNode leftNode, HLinkNode rightNode) { if(leftNode != null) { if(leftNode._next != null) leftNode._next._prev = null; leftNode._next = rightNode; } if(rightNode != null) { if(rightNode._prev != null) rightNode._prev._next = null; rightNode._prev = leftNode; } } }