public static abstract class HBehavior extends HNode { protected HBehaviorRegistry _registry; public HBehavior register() { H.behaviors().register(this); return this; } public HBehavior unregister() { H.behaviors().unregister(this); return this; } public HBehavior registered(boolean b) { if(b) H.behaviors().register(this); else H.behaviors().unregister(this); return this; } public boolean poppedOut() { return _registry == null; } public void popOut() { super.popOut(); _registry = null; } public void swapLeft() { if(_prev._prev == null) return; super.swapLeft(); } public void putAfter(HBehavior dest) { if(dest._registry == null) return; super.putAfter(dest); _registry = dest._registry; } public void putBefore(HBehavior dest) { if(dest._registry == null) return; super.putBefore(dest); _registry = dest._registry; } public void replaceNode(HBehavior target) { super.replaceNode(target); _registry = target._registry; target._registry = null; } public abstract void runBehavior(PApplet app); } public static class HBehaviorRegistry { private HBehaviorSentinel _firstSentinel; public HBehaviorRegistry() { _firstSentinel = new HBehaviorSentinel(this); } public boolean isRegistered(HBehavior b) { return (b._registry != null && b._registry.equals(this)); } public void register(HBehavior b) { if(b.poppedOut()) b.putAfter(_firstSentinel); } public void unregister(HBehavior b) { if(isRegistered(b)) b.popOut(); } public void runAll(PApplet app) { HBehavior n = _firstSentinel.next(); while(n != null) { n.runBehavior(app); n = n.next(); } } public static class HBehaviorSentinel extends HBehavior { public HBehaviorSentinel(HBehaviorRegistry r) { _registry = r; } public void runBehavior(PApplet app) {} } } public static class HFollow extends HBehavior { private float _ease, _spring, _dx, _dy; private HLocatable _goal; private HLocatable _follower; public HFollow() { this(1); } public HFollow(float ease) { this(ease,0); } public HFollow(float ease, float spring) { this(ease, spring, H.mouse()); } public HFollow(float ease, float spring, HLocatable goal) { _ease = ease; _spring = spring; _goal = goal; } 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(HLocatable g) { _goal = g; return this; } public HLocatable goal() { return _goal; } public HFollow followMouse() { _goal = H.mouse(); return this; } public HFollow target(HLocatable f) { if(f == null) unregister(); else register(); _follower = f; return this; } public HLocatable target() { return _follower; } public void runBehavior(PApplet app) { if(_follower==null || ! H.mouse().started()) return; _dx = _dx*_spring + (_goal.x()-_follower.x()) * _ease; _dy = _dy*_spring + (_goal.y()-_follower.y()) * _ease; _follower.x(_follower.x() + _dx); _follower.y(_follower.y() + _dy); } public HFollow register() { return (HFollow) super.register(); } public HFollow unregister() { return (HFollow) super.unregister(); } } public static class HMagneticField extends HBehavior { private ArrayList _magnets; private 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) { if(_targets.size() <= 0) register(); _targets.add(d); return this; } public HMagneticField removeTarget(HDrawable d) { _targets.remove(d); if(_targets.size() <= 0) unregister(); 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 { private HDrawable _target; private float _stepDeg, _speed, _min, _max, _freq, _relValue, _origW, _origH; private int _propertyId, _waveform; public HOscillator() { _speed = 1; _min = -1; _max = 1; _freq = 1; _propertyId = HConstants.Y; _waveform = HConstants.SINE; } public HOscillator(HDrawable newTarget) { this(); target(newTarget); } 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) { if(newTarget == null) unregister(); else register(); _target = newTarget; if(_target != null) { _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 nextVal() { float currentDeg = _stepDeg * _freq; float outVal = 0; switch(_waveform) { case HConstants.SINE: outVal = HMath.sineWave(currentDeg); break; case HConstants.TRIANGLE: outVal = HMath.triangleWave(currentDeg);break; case HConstants.SAW: outVal = HMath.sawWave(currentDeg); break; case HConstants.SQUARE: outVal = HMath.squareWave(currentDeg); break; } outVal = HMath.map(outVal, -1,1, _min,_max) + _relValue; _stepDeg += speed(); return outVal; } public void runBehavior(PApplet app) { if(_target == null) return; float val = nextVal(); switch(_propertyId) { case HConstants.WIDTH: _target.width(val); break; case HConstants.HEIGHT: _target.height(val); break; case HConstants.SIZE: _target.size(val); break; case HConstants.ALPHA: _target.alpha(Math.round(val)); break; case HConstants.X: _target.x(val); break; case HConstants.Y: _target.y(val); break; case HConstants.LOCATION: _target.loc(val,val); break; case HConstants.ROTATION: _target.rotation(val); break; case HConstants.DROTATION: _target.rotate(val); break; case HConstants.DX: _target.move(val,0); break; case HConstants.DY: _target.move(0,val); break; case HConstants.DLOC: _target.move(val,val); break; case HConstants.SCALE: _target.size(_origW*val,_origH*val); break; default: break; } } public HOscillator register() { return (HOscillator) super.register(); } public HOscillator unregister() { return (HOscillator) super.unregister(); } } public static class HRotate extends HBehavior { private HRotatable _target; private float _speedRad; public HRotate() {} public HRotate(HRotatable newTarget, float dDeg) { target(newTarget); _speedRad = dDeg * HConstants.D2R; } public HRotate target(HRotatable r) { if(r == null) unregister(); else register(); _target = r; return this; } public HRotatable target() { return _target; } public HRotate speed(float dDeg) { _speedRad = dDeg * HConstants.D2R; return this; } public float speed() { return _speedRad * HConstants.R2D; } public HRotate speedRad(float dRad) { _speedRad = dRad; return this; } public float speedRad() { return _speedRad; } public void runBehavior(PApplet app) { float rot = _target.rotationRad() + _speedRad; _target.rotationRad(rot); } public HRotate register() { return (HRotate) super.register(); } public HRotate unregister() { return (HRotate) super.unregister(); } } public static class HSwarm extends HBehavior { private HLinkedHashSet _goals; private HLinkedHashSet _targets; private float _speed, _turnEase, _twitchRad, _idleGoalX, _idleGoalY; public HSwarm() { _speed = 1; _turnEase = 1; _twitchRad = 0; _goals = new HLinkedHashSet(); _targets = new HLinkedHashSet(); } public HSwarm addTarget(HSwarmer t) { if(_targets.size() <= 0) register(); _targets.add(t); return this; } public HSwarm removeTarget(HSwarmer t) { _targets.remove(t); if(_targets.size() <= 0) unregister(); return this; } public HLinkedHashSet targets() { return _targets; } public HSwarm addGoal(HLocatable g) { _goals.add(g); return this; } public HSwarm addGoal(float x, float y) { return addGoal(new HVector(x,y)); } public HSwarm addGoal(float x, float y, float z) { return addGoal(new HVector(x,y,z)); } public HSwarm removeGoal(HLocatable g) { _goals.remove(g); return this; } public HLinkedHashSet goals() { return _goals; } public HSwarm idleGoal(float x, float y) { _idleGoalX = x; _idleGoalY = y; return this; } public float idleGoalX() { return _idleGoalX; } public float idleGoalY() { return _idleGoalY; } 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) { _twitchRad = deg * HConstants.D2R; return this; } public HSwarm twitchRad(float rad) { _twitchRad = rad; return this; } public float twitch() { return _twitchRad * HConstants.R2D; } public float twitchRad() { return _twitchRad; } private HLocatable getGoal(HSwarmer target, PApplet app) { HLocatable goal = null; float nearestDist = -1; for(HIterator it=_goals.iterator(); it.hasNext();) { HLocatable h = it.next(); float dist = HMath.dist(target.x(),target.y(), h.x(),h.y()); if(nearestDist<0 || dist it = _targets.iterator(); for(int i=0; i { public boolean hasNext(); public U next(); public void remove(); } public static class HLinkedHashSet extends HLinkedList { private HashMap> nodeMap; public HLinkedHashSet() { nodeMap = new HashMap>(); } public boolean remove(T content) { HLinkedListNode node = nodeMap.get(content); if(node==null) return false; unregister(content); node.popOut(); --_size; return true; } public boolean add(T content) { return contains(content)? false : super.add(content); } public boolean push(T content) { return contains(content)? false : super.push(content); } public boolean insert(T content, int index) { return contains(content)? false : super.insert(content, index); } public T pull() { return unregister(super.pull()); } public T pop() { return unregister(super.pop()); } public T removeAt(int index) { return unregister(super.removeAt(index)); } public void removeAll() { while(_size > 0) pop(); } public boolean contains(T obj) { return nodeMap.get(obj) != null; } protected HLinkedListNode register(T obj) { HLinkedListNode node = new HLinkedListNode(obj); nodeMap.put(obj,node); return node; } protected T unregister(T obj) { nodeMap.remove(obj); return obj; } } public static class HLinkedList { protected HLinkedListNode _firstSentinel, _lastSentinel; protected int _size; public HLinkedList() { _firstSentinel = new HLinkedListNode(null); _lastSentinel = new HLinkedListNode(null); _lastSentinel.putAfter(_firstSentinel); } public T first() { return _firstSentinel._next._content; } public T last() { return _lastSentinel._prev._content; } public T get(int index) { HLinkedListNode n = nodeAt(index); return (n==null)? null : n._content; } public boolean push(T content) { if(content==null) return false; register(content).putAfter(_firstSentinel); ++_size; return true; } public boolean add(T content) { if(content==null) return false; register(content).putBefore(_lastSentinel); ++_size; return true; } public boolean insert(T content, int index) { if(content==null) return false; HLinkedListNode n = (index==_size)? _lastSentinel : nodeAt(index); if(n==null) return false; register(content).putBefore(n); ++_size; return true; } public T pop() { HLinkedListNode firstNode = _firstSentinel._next; if(firstNode._content != null) { firstNode.popOut(); --_size; } return firstNode._content; } public T pull() { HLinkedListNode lastNode = _lastSentinel._prev; if(lastNode._content != null) { lastNode.popOut(); --_size; } return lastNode._content; } public T removeAt(int index) { HLinkedListNode n = nodeAt(index); if(n==null) return null; n.popOut(); --_size; return n._content; } public void removeAll() { _lastSentinel.putAfter(_firstSentinel); _size = 0; } public int size() { return _size; } public boolean inRange(int index) { return (0 <= index) && (index < _size); } public HLinkedListIterator iterator() { return new HLinkedListIterator(this); } protected HLinkedListNode nodeAt(int i) { int ri; if(i<0) { ri = -i; i += _size; } else { ri = _size-i; } if(!inRange(i)) { HWarnings.warn("Out of Range: "+i, "HLinkedList.nodeAt()", null); return null; } HLinkedListNode node; if(ri < i) { node = _lastSentinel._prev; while(--ri > 0) node = node._prev; } else { node = _firstSentinel._next; while(i-- > 0) node = node._next; } return node; } protected HLinkedListNode register(T obj) { return new HLinkedListNode(obj); } public static class HLinkedListNode extends HNode> { private U _content; public HLinkedListNode(U nodeContent) { _content = nodeContent; } public U content() { return _content; } } public static class HLinkedListIterator implements HIterator { private HLinkedList list; private HLinkedListNode n1, n2; public HLinkedListIterator(HLinkedList parent) { list = parent; n1 = list._firstSentinel._next; if(n1 != null) n2 = n1._next; } public boolean hasNext() { return (n1._content != null); } public U next() { U content = n1._content; n1 = n2; if(n2 != null) n2 = n2._next; return content; } public void remove() { if(n1._content != null) { n1.popOut(); --list._size; } } } } public static abstract class HNode> { protected T _prev, _next; public T prev() { return _prev; } public T next() { return _next; } public boolean poppedOut() { return (_prev==null) && (_next==null); } public void popOut() { if(_prev!=null) _prev._next = _next; if(_next!=null) _next._prev = _prev; _prev = _next = null; } public void putBefore(T dest) { if(dest==null || dest.equals(this)) return; if(!poppedOut()) popOut(); T p = dest._prev; if(p!=null) p._next = (T) this; _prev = p; _next = dest; dest._prev = (T) this; } public void putAfter(T dest) { if(dest==null || dest.equals(this)) return; if(!poppedOut()) popOut(); T n = dest.next(); dest._next = (T) this; _prev = dest; _next = n; if(n!=null) n._prev = (T) this; } public void replaceNode(T dest) { if(dest==null || dest.equals(this)) return; if(!poppedOut()) popOut(); T p = dest._prev; T n = dest._next; dest._prev = dest._next = null; _prev = p; _next = n; } public void swapLeft() { if(_prev==null) return; T pairPrev = _prev._prev; T pairNext = _next; _next = _prev; _prev._prev = (T) this; _prev._next = pairNext; if(pairNext != null) pairNext._prev = _prev; _prev = pairPrev; if(pairPrev != null) pairPrev._next = (T) this; } public void swapRight() { if(_next==null) return; T pairPrev = _prev; T pairNext = _next._next; _next._next = (T) this; _prev = _next; _next._prev = pairPrev; if(pairPrev != null) pairPrev._next = _next; _next = pairNext; if(pairNext != null) pairNext._prev = (T) this; } } public static class HColorField implements HColorist { private ArrayList _colorPoints; private float _maxDist; private boolean _appliesFill, _appliesStroke, _appliesAlpha; public HColorField() { this(H.app().width, H.app().height); } public HColorField(float xBound, float yBound) { this( (float) Math.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) { int[] baseClrs = HColors.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 = HColors.explode(pt.clr); float distLimit = _maxDist * pt.radius; float dist = HMath.dist(x,y, pt.x,pt.y); if(dist > distLimit) dist = distLimit; for(int j=initJ; j<4; ++j) { int newClrVal = Math.round( HMath.map(dist, 0,distLimit, ptClrs[j], baseClrs[j])); if(newClrVal > maxClrs[j]) maxClrs[j] = newClrVal; } } return HColors.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 { private ArrayList _colorList; private 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; int index = (int) Math.floor(H.app().random(_colorList.size())); 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; private 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) { int[] clrs = HColors.explode(origColor); clrs[0] = Math.round(clrs[0] * _percA) + _offsetA; clrs[1] = Math.round(clrs[1] * _percR) + _offsetR; clrs[2] = Math.round(clrs[2] * _percG) + _offsetG; clrs[3] = Math.round(clrs[3] * _percB) + _offsetB; return HColors.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 { private PImage img; private 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) { return (img==null)? 0 : img.get(Math.round(x), Math.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 class HCanvas extends HDrawable { private PGraphics _graphics; private String _renderer; private float _filterParam; private int _filterKind, _blendMode, _fadeAmt; private boolean _autoClear,_hasFade,_hasFilter,_hasFilterParam,_hasBlend; public HCanvas() { this(H.app().width, H.app().height); } public HCanvas(String bufferRenderer) { this(H.app().width, H.app().height, bufferRenderer); } public HCanvas(float w, float h) { this(w, h, PConstants.JAVA2D); } public HCanvas(float w, float h, String bufferRenderer) { _renderer = bufferRenderer; size(w,h); } public HCanvas createCopy() { HCanvas copy = new HCanvas(_width,_height,_renderer); copy.autoClear(_autoClear).hasFade(_hasFade); if(_hasFilter) copy.filter(_filterKind, _filterParam); if(_hasBlend) copy.blend(_blendMode); copy.copyPropertiesFrom(this); return copy; } protected void updateBuffer() { int w = Math.round(_width); int h = Math.round(_height); _graphics = H.app().createGraphics(w, h, _renderer); _graphics.loadPixels(); _graphics.beginDraw(); _graphics.background(H.CLEAR); _graphics.endDraw(); _width = w; _height = h; } public HCanvas renderer(String s) { _renderer = s; updateBuffer(); return this; } public String renderer() { return _renderer; } public boolean usesZ() { return _renderer.equals(PConstants.P3D) || _renderer.equals(PConstants.OPENGL); } public PGraphics graphics() { return _graphics; } public HCanvas filter(int kind) { _hasFilter = true; _hasFilterParam = false; _filterKind = kind; return this; } public HCanvas filter(int kind, float param) { _hasFilter = true; _hasFilterParam = true; _filterKind = kind; _filterParam = param; return this; } public HCanvas noFilter() { _hasFilter = false; return this; } public boolean hasFilter() { return _hasFilter; } public HCanvas filterKind(int i) { _filterKind = i; return this; } public int filterKind() { return _filterKind; } public HCanvas filterParam(float f) { _filterParam = f; return this; } public float filterParam() { return _filterParam; } public HCanvas blend() { return blend(PConstants.BLEND); } public HCanvas blend(int mode) { _hasBlend = true; _blendMode = mode; return this; } public HCanvas noBlend() { _hasBlend = false; return this; } public HCanvas hasBlend(boolean b) { return (b)? blend() : noBlend(); } public boolean hasBlend() { return _hasBlend; } public HCanvas blendMode(int i) { _blendMode = i; return this; } public int blendMode() { return _blendMode; } public HCanvas fade(int fadeAmt) { _hasFade = true; _fadeAmt = fadeAmt; return this; } public HCanvas noFade() { _hasFade = false; return this; } public HCanvas hasFade(boolean b) { _hasFade = b; return this; } public boolean hasFade() { return _hasFade; } public HCanvas autoClear(boolean b) { _autoClear = b; return this; } public boolean autoClear() { return _autoClear; } public HCanvas background(int clr) { return (HCanvas) fill(clr); } public HCanvas background(int clr, int alpha) { return (HCanvas) fill(clr, alpha); } public HCanvas background(int r, int g, int b) { return (HCanvas) fill(r, g, b); } public HCanvas background(int r, int g, int b, int a) { return (HCanvas) fill(r, g, b, a); } public int background() { return _fill; } public HCanvas noBackground() { return (HCanvas) noFill(); } public HCanvas size(float w, float h) { super.width(w); super.height(h); updateBuffer(); return this; } public HCanvas width(float w) { super.width(w); updateBuffer(); return this; } public HCanvas height(float h) { super.height(h); updateBuffer(); return this; } public void paintAll(PGraphics g, boolean zFlag, float currAlphaPerc) { if(_alphaPerc<=0 || _width==0 || _height==0) return; g.pushMatrix(); if(zFlag) g.translate(_x,_y,_z); else g.translate(_x,_y); g.rotate(_rotationRad); currAlphaPerc *= _alphaPerc; _graphics.beginDraw(); if(_autoClear) { _graphics.clear(); } else { if(_hasFilter) { if(_hasFilterParam) _graphics.filter(_filterKind,_filterParam); else _graphics.filter(_filterKind); } if(_hasFade) { if(!_renderer.equals(PConstants.JAVA2D)) _graphics.loadPixels(); int[] pix = _graphics.pixels; for(int i=0; i>> 24; if(a == 0) continue; a -= _fadeAmt; if(a < 0) a = 0; pix[i] = clr & 0xFFFFFF | (a << 24); } _graphics.updatePixels(); } if(_hasBlend) { _graphics.blend( 0,0, _graphics.width,_graphics.height, 0,0, _graphics.width,_graphics.height, _blendMode); } } HDrawable child = _firstChild; while(child != null) { child.paintAll(_graphics, usesZ(), currAlphaPerc); child = child.next(); } _graphics.endDraw(); g.image(_graphics,0,0); g.popMatrix(); } public void draw(PGraphics g,boolean b,float x,float y,float f) {} } public static abstract class HDrawable extends HNode implements HSwarmer, HHittable { protected HDrawable _parent; protected HDrawable _firstChild; protected HDrawable _lastChild; protected HBundle _extras; protected float _x; protected float _y; protected float _z; protected float _anchorPercX; protected float _anchorPercY; protected float _width; protected float _height; protected float _rotationRad; protected float _strokeWeight; protected float _alphaPerc; protected float _sizeProportion; protected int _numChildren; protected int _fill; protected int _stroke; protected int _strokeCap; protected int _strokeJoin; protected boolean _proportional; public HDrawable() { _alphaPerc = 1; _fill = HConstants.DEFAULT_FILL; _stroke = HConstants.DEFAULT_STROKE; _strokeCap = PConstants.ROUND; _strokeJoin = PConstants.MITER; _strokeWeight = 1; _width = HConstants.DEFAULT_WIDTH; _height = HConstants.DEFAULT_HEIGHT; } 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; _alphaPerc = other._alphaPerc; _strokeWeight = other._strokeWeight; _fill = other._fill; _stroke = other._stroke; _strokeCap = other._strokeCap; _strokeJoin = other._strokeJoin; } public abstract HDrawable createCopy(); private boolean invalidDest(HDrawable dest, String warnLoc) { String warnType; String warnMsg; if( dest == null ) { warnType = "Null Destination"; warnMsg = HWarnings.NULL_ARGUMENT; } else if( dest._parent == null ) { warnType = "Invalid Destination"; warnMsg = HWarnings.INVALID_DEST; } else if( dest._parent.equals(this) ) { warnType = "Recursive Child"; warnMsg = HWarnings.CHILDCEPTION; } else if( dest.equals(this) ) { warnType = "Invalid Destination"; warnMsg = HWarnings.DESTCEPTION; } else return false; HWarnings.warn(warnType, warnLoc, warnMsg); return true; } public boolean poppedOut() { return (_parent == null); } public void popOut() { if(_parent == null) return; if(_prev == null) _parent._firstChild = _next; if(_next == null) _parent._lastChild = _prev; --_parent._numChildren; _parent = null; super.popOut(); } public void putBefore(HDrawable dest) { if(invalidDest(dest,"HDrawable.putBefore()")) return; popOut(); super.putBefore(dest); _parent = dest._parent; if(_prev == null) _parent._firstChild = this; ++_parent._numChildren; } public void putAfter(HDrawable dest) { if(invalidDest(dest,"HDrawable.putAfter()")) return; popOut(); super.putAfter(dest); _parent = dest._parent; if(_next == null) _parent._lastChild = this; ++_parent._numChildren; } public void swapLeft() { boolean isLast = (_next == null); super.swapLeft(); if(_prev == null) _parent._firstChild = this; if(_next != null && isLast) _parent._lastChild = _next; } public void swapRight() { boolean isFirst = (_prev == null); super.swapRight(); if(_next == null) _parent._lastChild = this; if(_prev != null && isFirst) _parent._firstChild = _prev; } public void replaceNode(HDrawable dest) { if(invalidDest(dest,"HDrawable.replaceNode()")) return; super.replaceNode(dest); _parent = dest._parent; dest._parent = null; if(_prev == null) _parent._firstChild = this; if(_next == null) _parent._lastChild = this; } public HDrawable parent() { return _parent; } public HDrawable firstChild() { return _firstChild; } public HDrawable lastChild() { return _lastChild; } public boolean parentOf(HDrawable d) { return (d != null) && (d._parent != null) && (d._parent.equals(this)); } public int numChildren() { return _numChildren; } public HDrawable add(HDrawable child) { if(child == null) { HWarnings.warn("An Empty Child", "HDrawable.add()", HWarnings.NULL_ARGUMENT); } else if( !parentOf(child) ) { if(_lastChild == null) { _firstChild = _lastChild = child; child.popOut(); child._parent = this; ++_numChildren; } else child.putAfter(_lastChild); } return child; } public HDrawable remove(HDrawable child) { if( parentOf(child) ) child.popOut(); else HWarnings.warn("Not a Child", "HDrawable.remove()", null); return child; } public HDrawableIterator iterator() { return new HDrawableIterator(this); } public HDrawable loc(float newX, float newY) { _x = newX; _y = newY; return this; } public HDrawable loc(float newX, float newY, float newZ) { _x = newX; _y = newY; _z = newZ; return this; } public HDrawable loc(PVector pt) { _x = pt.x; _y = pt.y; _z = pt.z; return this; } public PVector loc() { return new PVector(_x,_y,_z); } 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 z(float newZ) { _z = newZ; return this; } public float z() { return _z; } public HDrawable move(float dx, float dy) { _x += dx; _y += dy; return this; } public HDrawable move(float dx, float dy, float dz) { _x += dx; _y += dy; _z += dz; return this; } public HDrawable locAt(int where) { if(_parent!=null) { if(HMath.hasBits(where, HConstants.CENTER_X)) { _x = _parent.width()/2 - _parent.anchorX(); } else if(HMath.hasBits(where, HConstants.LEFT)) { _x = -_parent.anchorX(); } else if(HMath.hasBits(where, HConstants.RIGHT)) { _x = _parent.width() - _parent.anchorX(); } if(HMath.hasBits(where, HConstants.CENTER_Y)) { _y = _parent.height()/2 - _parent.anchorY(); } else if(HMath.hasBits(where, HConstants.TOP)) { _y = -_parent.anchorY(); } else if(HMath.hasBits(where, HConstants.BOTTOM)) { _y = _parent.height() - _parent.anchorY(); } } return this; } public HDrawable anchor(float pxX, float pxY) { return anchorX(pxX).anchorY(pxY); } public HDrawable anchor(PVector pt) { return anchor(pt.x, pt.y); } public PVector anchor() { return new PVector( anchorX(), anchorY() ); } public HDrawable anchorX(float pxX) { _anchorPercX = pxX / (_width==0? 100 : _width); return this; } public float anchorX() { return _width * _anchorPercX; } public HDrawable anchorY(float pxY) { _anchorPercY = pxY / (_height==0? 100 : _height); return this; } public float anchorY() { return _height * _anchorPercY; } public HDrawable anchorPerc(float percX, float percY) { return anchorPercX(percX).anchorPercY(percY); } 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.hasBits(where, HConstants.CENTER_X)) _anchorPercX = 0.5f; else if(HMath.hasBits(where, HConstants.LEFT)) _anchorPercX = 0; else if(HMath.hasBits(where, HConstants.RIGHT)) _anchorPercX = 1; if(HMath.hasBits(where, HConstants.CENTER_Y)) _anchorPercY = 0.5f; else if(HMath.hasBits(where, HConstants.TOP)) _anchorPercY = 0; else if(HMath.hasBits(where, HConstants.BOTTOM)) _anchorPercY = 1; return this; } public HDrawable size(float w, float h) { return width(w).height(h); } public HDrawable size(float s) { size(s,s); return this; } public PVector size() { return new PVector(_width,_height); } public HDrawable width(float w) { if(_proportional) _height = w/_sizeProportion; _width = w; return this; } public float width() { return _width; } public HDrawable height(float h) { if(_proportional) _width = h*_sizeProportion; _height = h; return this; } public float height() { return _height; } public HDrawable scale(float s) { return size(_width*s, _height*s); } public HDrawable scale(float sw, float sh) { return size(_width*sw, _height*sh); } public HDrawable proportional(boolean b) { _proportional = b; if(_proportional) { _sizeProportion = _width/_height; } return this; } public boolean proportional() { return _proportional; } public PVector boundingSize() { float cosVal = (float)Math.cos(_rotationRad); float sinVal = (float)Math.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) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16 | 0xFF000000; _fill = clr; return this; } public HDrawable fill(int clr, int alpha) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16; _fill = HColors.setAlpha(clr,alpha); return this; } public HDrawable fill(int r, int g, int b) { _fill = HColors.merge(255,r,g,b); return this; } public HDrawable fill(int r, int g, int b, int a) { _fill = HColors.merge(a,r,g,b); return this; } public int fill() { return _fill; } public HDrawable noFill() { return fill(HConstants.CLEAR); } public HDrawable stroke(int clr) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16 | 0xFF000000; _stroke = clr; return this; } public HDrawable stroke(int clr, int alpha) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16; _stroke = HColors.setAlpha(clr,alpha); return this; } public HDrawable stroke(int r, int g, int b) { _stroke = HColors.merge(255,r,g,b); return this; } public HDrawable stroke(int r, int g, int b, int a) { _stroke = HColors.merge(a,r,g,b); return this; } public int stroke() { return _stroke; } public HDrawable noStroke() { return stroke(HConstants.CLEAR); } 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 * HConstants.D2R; return this; } public float rotation() { return _rotationRad * HConstants.R2D; } public HDrawable rotationRad(float rad) { _rotationRad = rad; return this; } public float rotationRad() { return _rotationRad; } public HDrawable rotate(float deg) { _rotationRad += deg * HConstants.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 Math.round( alphaPerc()*255 ); } public HDrawable alphaPerc(float aPerc) { _alphaPerc = (aPerc<0)? 0 : (aPerc>1)? 1 : aPerc; return this; } public float alphaPerc() { return (_alphaPerc<0)? 0 : _alphaPerc; } public HDrawable visibility(boolean v) { if( v && (_alphaPerc==0) ) { _alphaPerc = 1; } else if( v == (_alphaPerc<0) ) { _alphaPerc = -_alphaPerc; } return this; } public boolean visibility() { return _alphaPerc > 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(_alphaPerc + daPerc); } public HDrawable extras(HBundle b) { _extras = b; return this; } public HBundle extras() { return _extras; } public HDrawable obj(String key, Object value) { if(_extras == null) _extras = new HBundle(); _extras.obj(key,value); return this; } public HDrawable num(String key, float value) { if(_extras == null) _extras = new HBundle(); _extras.num(key,value); return this; } public HDrawable bool(String key, boolean value) { if(_extras == null) _extras = new HBundle(); _extras.bool(key,value); return this; } public Object obj(String key) { return (_extras==null)? null : _extras.obj(key); } public String str(String key) { return (_extras==null)? null : _extras.str(key); } public float num(String key) { return (_extras==null)? 0 : _extras.num(key); } public int numI(String key) { return (_extras==null)? 0 : _extras.numI(key); } public boolean bool(String key) { if(_extras == null) return false; return _extras.bool(key); } public boolean contains(float absX, float absY, float absZ) { PApplet app = H.app(); absZ -= _z; return contains( app.screenX(absX,absY,absZ), app.screenY(absX,absY,absZ)); } public boolean contains(float absX, float absY) { float[] rel = HMath.relLocArr(this, absX, absY); rel[0] += anchorX(); rel[1] += anchorY(); return containsRel(rel[0], rel[1]); } public boolean containsRel(float relX, float relY, float relZ) { PApplet app = H.app(); relZ -= _z; return containsRel( app.screenX(relX,relY,relZ), app.screenY(relX,relY,relZ)); } public boolean containsRel(float relX, float relY) { return (0 <= relX) && (relX <= width()) && (0 <= relY) && (relY <= height()); } protected void applyStyle(PGraphics g, float currAlphaPerc) { float faPerc = currAlphaPerc * (_fill >>> 24); g.fill(_fill | 0xFF000000, Math.round(faPerc)); if(_strokeWeight > 0) { float saPerc = currAlphaPerc * (_stroke >>> 24); g.stroke(_stroke | 0xFF000000, Math.round(saPerc)); g.strokeWeight(_strokeWeight); g.strokeCap(_strokeCap); g.strokeJoin(_strokeJoin); } else g.noStroke(); } public void paintAll(PGraphics g, boolean usesZ, float currAlphaPerc) { if(_alphaPerc<=0) return; g.pushMatrix(); if(usesZ) g.translate(_x,_y,_z); else g.translate(_x,_y); g.rotate(_rotationRad); currAlphaPerc *= _alphaPerc; draw(g, usesZ,-anchorX(),-anchorY(),currAlphaPerc); HDrawable child = _firstChild; while(child != null) { child.paintAll(g, usesZ, currAlphaPerc); child = child._next; } g.popMatrix(); } public abstract void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPerc); public static class HDrawableIterator implements HIterator { private HDrawable parent, d1, d2; public HDrawableIterator(HDrawable parentDrawable) { parent = parentDrawable; d1 = parent._firstChild; if(d1 != null) d2 = d1._next; } public boolean hasNext() { return (d1 != null); } public HDrawable next() { HDrawable nxt = d1; d1 = d2; if(d2 != null) d2 = d2._next; return nxt; } public void remove() { if(d1 != null) d1.popOut(); } } } public static class HEllipse extends HDrawable { private int _mode; private float _startRad, _endRad; public HEllipse() { _mode = PConstants.PIE; } public HEllipse(float ellipseRadius) { this(); radius(ellipseRadius); } public HEllipse(float radiusX, float radiusY) { this(); 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 HEllipse mode(int t) { _mode = t; return this; } public float mode() { return _mode; } public HEllipse start(float deg) { return startRad(deg * H.D2R); } public float start() { return _startRad * H.R2D; } public HEllipse startRad(float rad) { _startRad = HMath.normalizeAngleRad(rad); if(_startRad > _endRad) _endRad += PConstants.TWO_PI; return this; } public float startRad() { return _startRad; } public HEllipse end(float deg) { return endRad(deg * H.D2R); } public float end() { return _endRad * H.R2D; } public HEllipse endRad(float rad) { _endRad = HMath.normalizeAngleRad(rad); if(_startRad > _endRad) _endRad += PConstants.TWO_PI; return this; } public float endRad() { return _endRad; } public boolean containsRel(float relX, float relY) { float cx = _width/2; float cy = _height/2; float dcx = relX - cx; float dcy = relY - cy; boolean inEllipse = ((dcx*dcx)/(cx*cx) + (dcy*dcy)/(cy*cy) <= 1); if(_startRad == _endRad) return inEllipse; else if(!inEllipse) return false; if(_mode == PConstants.PIE) { float ptAngle = (float) Math.atan2(dcy*cx, dcx*cy); if(_startRad > ptAngle) ptAngle += PConstants.TWO_PI; return (_startRad<=ptAngle && ptAngle<=_endRad); } else { float end = HMath.squishAngleRad(cx, cy, _endRad); float start = HMath.squishAngleRad(cx, cy, _startRad); float[] pt1 = HMath.ellipsePointRadArr(cx,cy, cx,cy, end); float[] pt2 = HMath.ellipsePointRadArr(cx,cy, cx,cy, start); return HMath.rightOfLine(pt1[0],pt1[1], pt2[0],pt2[1], relX,relY); } } public void draw( PGraphics g, boolean usesZ, float drawX,float drawY,float currAlphaPerc ) { applyStyle(g,currAlphaPerc); drawX += _width/2; drawY += _height/2; if(_startRad == _endRad) { g.ellipse(drawX, drawY, _width, _height); } else { g.arc(drawX,drawY,_width,_height,_startRad,_endRad,_mode); } } } public static class HGroup extends HDrawable { public HGroup createCopy() { HGroup copy = new HGroup(); copy.copyPropertiesFrom(this); return copy; } public void paintAll(PGraphics g, boolean usesZ, float currAlphaPerc) { if(_alphaPerc<=0) return; g.pushMatrix(); if(usesZ) g.translate(_x,_y,_z); else g.translate(_x,_y); g.rotate(_rotationRad); currAlphaPerc *= _alphaPerc; HDrawable child = _firstChild; while(child != null) { child.paintAll(g, usesZ, currAlphaPerc); child = child.next(); } g.popMatrix(); } public void draw(PGraphics g,boolean b,float x,float y,float f) {} } public static class HImage extends HDrawable { private PImage _image; public HImage() { this(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 HImage tint(int clr) { fill(clr); return this; } public HImage tint(int clr, int alpha) { fill(clr, alpha); return this; } public HImage tint(int r, int g, int b) { fill(r,g,b); return this; } public HImage tint(int r, int g, int b, int a) { fill(r,g,b,a); return this; } public int tint() { return fill(); } public boolean containsRel(float relX, float relY) { if(_image == null || _image.width <= 0 || _image.height <= 0 || _width <= 0 || _height <= 0) return false; int ix = Math.round(relX * _image.width/_width); int iy = Math.round(relY * _image.height/_height); return (0 < _image.get(ix,iy)>>>24); } public void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPerc ) { if(_image==null) return; currAlphaPerc *= (_fill>>>24); g.tint( _fill | 0xFF000000, Math.round(currAlphaPerc) ); int wscale = 1; int hscale = 1; float w = _width; float h = _height; if(_width < 0) { w = -_width; wscale = -1; drawX = -drawX; } if(_height < 0) { h = -_height; hscale = -1; drawY = -drawY; } g.pushMatrix(); g.scale(wscale, hscale); g.image(_image, drawX,drawY, w,h); g.popMatrix(); } } public static class HPath extends HDrawable { private ArrayList _vertices; private int _mode; public HPath() { this(PConstants.PATH); } public HPath(int pathMode) { _vertices = new ArrayList(); _mode = pathMode; } public HPath createCopy() { HPath copy = new HPath(); copy.copyPropertiesFrom(this); for(int i=0; i<_vertices.size(); ++i) { HVertex v = _vertices.get(i); copy.vertexPerc(v.x, v.y, v.hx1, v.hy1, v.hx2, v.hy2); } return copy; } public HPath mode(int m) { _mode = m; return this; } public int mode() { return _mode; } public HVertex vertex(int index) { return _vertices.get(index); } public HPath removeVertex(int index) { _vertices.remove(index); return this; } public int numVertices() { return _vertices.size(); } public HPath endPath() { int numVertices = _vertices.size(); float minX, maxX, minY, maxY; minX = maxX = minY = maxY = 0; for(int i=0; i maxX) maxX = v.x; if(v.y < minY) minY = v.y; else if(v.y > maxY) maxY = v.y; } float ratioX = maxX - minX; float ratioY = maxY - minY; scale(ratioX, ratioY); anchorPercX((ratioX==0)? 0 : -minX/ratioX); anchorPercY((ratioY==0)? 0 : -minY/ratioY); for(int j=0; j=numVertices()-1)? 0 : i+1); if(v2.isBezier) { float[] params = new float[3]; int numParams = HMath.bezierParam( v1.y, v2.hy1, v2.hy2, v2.y, yPerc, params); for(int j=0; j=numVertices-1) i = -1; } if(_mode == PConstants.POLYGON) g.endShape(PConstants.CLOSE); else g.endShape(); } public static class HVertex { public float x, y, hx1, hy1, hx2, hy2; public boolean isBezier; } } 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 HRect roundingTL(float radius) { _tl = radius; return this; } public float roundingTL() { return _tl; } public HRect roundingTR(float radius) { _tr = radius; return this; } public float roundingTR() { return _tr; } public HRect roundingBR(float radius) { _br = radius; return this; } public float roundingBR() { return _br; } public HRect roundingBL(float radius) { _bl = radius; return this; } public float roundingBL() { return _bl; } public void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPerc ) { applyStyle(g,currAlphaPerc); g.rect(drawX,drawY, _width,_height, _tl,_tr,_br,_bl); } } public static class HShape extends HDrawable { private PShape _shape; private int[] _randomFills, _randomStrokes; 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 HShape randomColors(HColorPool colors) { int numChildren = _shape.getChildCount(); boolean isFill = colors.appliesFill(); boolean isStroke = colors.appliesStroke(); if(isFill) { if(_randomFills==null || _randomFills.length 0)? app.loadFont(str) : app.createFont(str,64); } else if(arg instanceof HText) { _font = ((HText) arg)._font; } else if(arg == null) { _font = app.createFont("SansSerif",64); } adjustMetrics(); return this; } public PFont font() { return _font; } public HText fontSize(float f) { return height(f); } public float fontSize() { return _height; } private void adjustMetrics() { PApplet app = H.app(); app.pushStyle(); app.textFont(_font,(_height < 0)? -_height : _height); _descent = app.textDescent(); _width = (_text==null)? 0 : (_width<0)? -app.textWidth(_text) : app.textWidth(_text); app.popStyle(); } public HText width(float w) { if(w<0 == _width>0) _width = -_width; return this; } public HText height(float h) { _height = h; adjustMetrics(); return this; } public boolean containsRel(float relX, float relY) { if(_text == null || _height == 0) return false; int numChars = _text.length(); float ratio = 64 / _height; float xoff = 0; float yoff = (_height - _descent) * ratio; relX *= ratio; relY *= ratio; for(int i=0; i>>24 > 0) return true; xoff += g.setWidth; } return false; } public void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPerc ) { if(_text == null) return; applyStyle(g,currAlphaPerc); int wscale = 1; int hscale = 1; float h = _height; if(_width < 0) { wscale = -1; drawX = -drawX; } if(_height < 0) { h = -_height; hscale = -1; drawY = -drawY; } g.pushMatrix(); g.scale(wscale, hscale); g.textFont(_font,h); g.text(_text,drawX,drawY+h-_descent); g.popMatrix(); } } public static class HMouse implements HLocatable { private PApplet _app; private int _button; private boolean _started, _moved; public HMouse(PApplet app) { _app = app; } public boolean started() { return _started; } public boolean moved() { return _moved; } public int button() { return _button; } public void handleEvents() { _button = _app.mouseButton; if(!_moved) _moved = (_app.pmouseX != 0) || (_app.pmouseY != 0); else if(!_started) _started = true; } public float x() { return _app.mouseX; } public HMouse x(float newX) { return this; } public float y() { return _app.mouseY; } public HMouse y(float newY) { return this; } public float z() { return 0; } public HMouse z(float newZ) { return this; } } public static interface HCallback { public void run(Object obj); } public static interface HHittable { public boolean contains(float absX, float absY, float absZ); public boolean contains(float absX, float absY); public boolean containsRel(float relX, float relY, float relZ); public boolean containsRel(float relX, float relY); } public static interface HLocatable { public float x(); public HLocatable x(float newX); public float y(); public HLocatable y(float newY); public float z(); public HLocatable z(float newZ); } 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 interface HRotatable { public float rotationRad(); public HRotatable rotationRad(float rad); } public static interface HSwarmer extends HLocatable, HRotatable {} public static class HGridLayout implements HLayout { private int _currentIndex, _numCols; private 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 row = (int) Math.floor(_currentIndex / _numCols); int col = _currentIndex % _numCols; ++_currentIndex; return new PVector(col*_xSpace + _startX, row*_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 HShapeLayout implements HLayout { private HDrawable _target; private int _iterationLimit; public HShapeLayout() { _iterationLimit = 1024; } public HShapeLayout iterationLimit(int i) { _iterationLimit = i; return this; } public int iterationLimit() { return _iterationLimit; } public HShapeLayout target(HDrawable h) { _target = h; return this; } public HDrawable target() { return _target; } public void applyTo(HDrawable target) { PVector pt = getNextPoint(); if(pt != null) target.loc(pt); } public PVector getNextPoint() { if(_target == null) return null; float[] loc = HMath.absLocArr(_target,0,0); float x1 = loc[0] - _target.anchorX(); float y1 = loc[1] - _target.anchorY(); float x2 = x1 + _target.width(); float y2 = y1 + _target.height(); for(int i=0; i<_iterationLimit; ++i) { float x = HMath.random(x1,x2); float y = HMath.random(y1,y2); if(_target.contains(x,y)) return new PVector(x,y); } return null; } } public static class HRandomTrigger extends HTrigger { public float _chance; public HRandomTrigger() {} public HRandomTrigger(float percChance) { _chance = percChance; } public HRandomTrigger chance(float perc) { _chance = perc; return this; } public float chance() { return _chance; } public void runBehavior(PApplet app) { if(HMath.random() <= _chance) { if(_callback != null) _callback.run(null); } } public HRandomTrigger callback(HCallback cb) { return (HRandomTrigger) super.callback(cb); } } public static class HTimer extends HTrigger { private int _lastInterval, _interval, _cycleCounter, _numCycles; private boolean _usesFrames; public HTimer() { _interval = 1000; _lastInterval = -1; } public HTimer(int timerInterval) { _interval = timerInterval; } public HTimer(int timerInterval, int numberOfCycles) { _interval = timerInterval; _numCycles = numberOfCycles; } public HTimer interval(int i) { _interval = i; return this; } public int interval() { return _interval; } public HTimer cycleCounter(int cycleIndex) { _cycleCounter = cycleIndex; return this; } public int cycleCounter() { return _cycleCounter; } public HTimer numCycles(int cycles) { _numCycles = cycles; return this; } public int numCycles() { return _numCycles; } public HTimer cycleIndefinitely() { _numCycles = 0; return this; } public HTimer useMillis() { _usesFrames = false; return this; } public boolean usesMillis() { return !_usesFrames; } public HTimer useFrames() { _usesFrames = true; return this; } public boolean usesFrames() { return _usesFrames; } public void runBehavior(PApplet app) { int curr = (_usesFrames)? app.frameCount : app.millis(); if(_lastInterval < 0) _lastInterval = curr; if(curr-_lastInterval >= _interval) { _lastInterval = curr; if(_callback != null) _callback.run(_cycleCounter); if(_numCycles > 0 && ++_cycleCounter >= _numCycles) unregister(); } } public HTimer callback(HCallback cb) { return (HTimer) super.callback(cb); } public HTimer register() { return (HTimer) super.register(); } public HTimer unregister() { _numCycles = 0; _lastInterval = -1; return (HTimer) super.unregister(); } } public static abstract class HTrigger extends HBehavior { public HCallback _callback; public HTrigger() { register(); } public HTrigger callback(HCallback cb) { _callback = cb; return this; } public HCallback callback() { return _callback; } } public static class H implements HConstants { private static H _self; private static PApplet _app; private static PGraphics _graphicsContext; private static HStage _stage; private static HBehaviorRegistry _behaviors; private static HMouse _mouse; private static boolean _uses3D; public static H init(PApplet applet) { _app = applet; if(_self == null) _self = new H(); if(_stage == null) _stage = new HStage(_app); if(_behaviors == null) _behaviors = new HBehaviorRegistry(); if(_mouse == null) _mouse = new HMouse(_app); try { int dummyVar = _app.g.A; _graphicsContext = _app.g; } catch(Exception e) { Object o = _app; _graphicsContext = (PGraphics) o; } return _self; } public static HStage stage() { return _stage; } public static PApplet app() { return _app; } public static HBehaviorRegistry behaviors() { return _behaviors; } public static HMouse mouse() { return _mouse; } public static H use3D(boolean b) { _uses3D = b; return _self; } public static boolean uses3D() { return _uses3D; } 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 autoClears() { return _stage.autoClears(); } public static H clearStage() { _stage.clear(); 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 drawStage() { _behaviors.runAll(_app); _mouse.handleEvents(); _stage.paintAll(_graphicsContext, _uses3D, 1); return _self; } public static boolean mouseStarted() { return _mouse.started(); } 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 HBundle bool(String key, boolean value) { numberContents.put(key, (value? 1f : 0f) ); return this; } public Object obj(String key) { return objectContents.get(key); } public String str(String key) { Object o = objectContents.get(key); if(o instanceof String) return (String) o; return null; } public float num(String key) { return numberContents.get(key); } public int numI(String key) { return Math.round(numberContents.get(key)); } public boolean bool(String key) { return (numberContents.get(key) != 0); } } public static class HColors { 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) { if(a < 0) a = 0; else if(a > 255) a = 255; if(r < 0) r = 0; else if(r > 255) r = 255; if(g < 0) g = 0; else if(g > 255) g = 255; if(b < 0) b = 0; else if(b > 255) b = 255; return (a<<24) | (r<<16) | (g<<8) | b; } public static int setAlpha(int clr, int newClr) { if(newClr < 0) newClr = 0; else if(newClr > 255) newClr = 255; return clr & 0x00FFFFFF | (newClr << 24); } public static int setRed(int clr, int newClr) { if(newClr < 0) newClr = 0; else if(newClr > 255) newClr = 255; return clr & 0xFF00FFFF | (newClr << 16); } public static int setGreen(int clr, int newClr) { if(newClr < 0) newClr = 0; else if(newClr > 255) newClr = 255; return clr & 0xFFFF00FF | (newClr << 8); } public static int setBlue(int clr, int newClr) { if(newClr < 0) newClr = 0; else if(newClr > 255) newClr = 255; return clr & 0xFFFFFF00 | newClr; } 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 interface HConstants { public static final int NONE = 0, LEFT = 1, RIGHT = 2, CENTER_X = 3, TOP = 4, BOTTOM = 8, CENTER_Y = 12, CENTER = 15, TOP_LEFT = 5, TOP_RIGHT = 6, BOTTOM_LEFT = 9, BOTTOM_RIGHT = 10, CENTER_LEFT = 13, CENTER_RIGHT = 14, CENTER_TOP = 7, CENTER_BOTTOM = 11, DEFAULT_BACKGROUND_COLOR = 0xFFECF2F5, DEFAULT_FILL = 0xFFFFFFFF, DEFAULT_STROKE = 0xFF000000, DEFAULT_WIDTH = 100, DEFAULT_HEIGHT = 100, CLEAR = 0x00FFFFFF, WHITE = 0xFFFFFFFF, LGREY = 0xFFC0C0C0, GREY = 0xFF808080, DGREY = 0xFF404040, BLACK = 0xFF000000, RED = 0xFFFF0000, GREEN = 0xFF00FF00, BLUE = 0xFF0000FF, CYAN = 0xFF00FFFF, MAGENTA = 0xFFFF00FF, YELLOW = 0xFFFFFF00, SAW = 0, SINE = 1, TRIANGLE = 2, SQUARE = 3, WIDTH = 0, HEIGHT = 1, SIZE = 2, ALPHA = 3, X = 4, Y = 5, Z = 6, LOCATION = 7, ROTATION = 8, DROTATION = 9, DX = 10, DY = 11, DZ = 12, DLOC = 13, SCALE = 14, ISOCELES = 0, EQUILATERAL = 1; public static final float D2R = PConstants.PI / 180f, R2D = 180f / PConstants.PI, SQRT2 = 1.4142135623730951f, PHI = 1.618033988749895f, PHI_1 = 0.618033988749895f, EPSILON = (float)10e-12; } public static class HDrawablePool { private HLinkedHashSet _activeSet, _inactiveSet; private ArrayList _prototypes; public HCallback _onCreate, _onRequest, _onRelease; public HPoolListener _listener; private HLayout _layout; private HColorist _colorist; private HDrawable _autoParent; private 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 listener(HPoolListener newListener) { _listener = newListener; return this; } public HDrawablePool onCreate(HCallback callback) { _onCreate = callback; return this; } public HCallback onCreate() { return _onCreate; } public HPoolListener listener() { return _listener; } public HDrawablePool onRequest(HCallback callback) { _onRequest = callback; return this; } public HCallback onRequest() { return _onRequest; } public HDrawablePool onRelease(HCallback callback) { _onRelease = callback; return this; } public HCallback onRelease() { return _onRelease; } 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) { HWarnings.warn("Null Prototype", "HDrawablePool.add()", HWarnings.NULL_ARGUMENT); } 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) { HWarnings.warn("No Prototype", "HDrawablePool.request()", HWarnings.NO_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() { if(_prototypes.size() <= 0) { HWarnings.warn("No Prototype", "HDrawablePool.requestAll()", HWarnings.NO_PROTOTYPE); } else { 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; } private HDrawable createRandomDrawable() { int index = HMath.randomInt(_prototypes.size()); return _prototypes.get(index).createCopy(); } public HIterator iterator() { return _activeSet.iterator(); } } public static class HMath implements HConstants { private static boolean _usingTempSeed; private static int _resetSeedValue; public static float dist(float x1, float y1, float x2, float y2) { float w = x2 - x1; float h = y2 - y1; return (float) Math.sqrt(w*w + h*h); } public static float[] rotatePointArr(float x, float y, float rad) { float[] pt = new float[2]; float c = (float) Math.cos(rad); float s = (float) Math.sin(rad); pt[0] = x*c - y*s; pt[1] = x*s + y*c; return pt; } public static PVector rotatePoint(float x, float y, float rad) { float[] f = rotatePointArr(x,y,rad); return new PVector(f[0], f[1]); } public static float yAxisAngle(float x1, float y1, float x2, float y2) { return (float) Math.atan2(x2-x1, y2-y1); } public static float xAxisAngle(float x1, float y1, float x2, float y2) { return (float) Math.atan2(y2-y1, x2-x1); } public static float[] absLocArr(HDrawable ref, float relX, float relY) { float[] f = {relX, relY, 0}; while(ref != null) { float rot = ref.rotationRad(); float[] g = rotatePointArr(f[0], f[1], rot); f[0] = g[0] + ref.x(); f[1] = g[1] + ref.y(); f[2] += rot; ref = ref.parent(); } return f; } public static PVector absLoc(HDrawable ref, float relX, float relY) { float[] f = absLocArr(ref,relX,relY); return new PVector(f[0], f[1]); } public static PVector absLoc(HDrawable d) { return absLoc(d,0,0); } public static float[] relLocArr(HDrawable ref, float absX, float absY) { float[] f = absLocArr(ref,0,0); return rotatePointArr(absX-f[0], absY-f[1], -f[2]); } public static PVector relLoc(HDrawable ref, float absX, float absY) { float[] f = relLocArr(ref,absX,absY); return new PVector(f[0], f[1]); } public static int quadrant(float cx, float cy, float x, float y) { return (y>=cy)? (x>=cx? 1 : 2) : (x>=cx? 4 : 3); } public static int quadrant(float dcx, float dcy) { return (dcy>=0)? (dcx>=0? 1 : 2) : (dcx>=0? 4 : 3); } public static float ellipseRadius(float a, float b, float deg) { return ellipseRadiusRad(a,b, deg * D2R); } public static float ellipseRadiusRad(float a, float b, float rad) { float cosb = b * (float)Math.cos(rad); float sina = a * (float)Math.sin(rad); return a*b / (float)Math.sqrt(cosb*cosb + sina*sina); } public static PVector ellipsePoint( float cx, float cy, float a, float b, float deg ) { return ellipsePointRad(cx, cy, a, b, deg*D2R); } public static PVector ellipsePointRad( float cx, float cy, float a, float b, float rad ) { float[] f = ellipsePointRadArr(cx,cy, a,b, rad); return new PVector(f[0], f[1]); } public static float[] ellipsePointRadArr( float cx, float cy, float a, float b, float rad ) { float[] f = new float[3]; f[2] = ellipseRadiusRad(a, b, rad); f[0] = f[2] * (float)Math.cos(rad) + cx; f[1] = f[2] * (float)Math.sin(rad) + cy; return f; } public static float normalizeAngle(float deg) { return normalizeAngleRad(deg * D2R) * R2D; } public static float normalizeAngleRad(float rad) { rad %= PConstants.TWO_PI; if(rad < -PConstants.PI) rad += PConstants.TWO_PI; else if(rad > PConstants.PI) rad -= PConstants.TWO_PI; return rad; } public static float normalizeAngle2(float deg) { return normalizeAngleRad2(deg * D2R) * R2D; } public static float normalizeAngleRad2(float rad) { float norm = rad % PConstants.TWO_PI; if(norm < 0) norm += PConstants.TWO_PI; return norm; } public static float squishAngle(float w, float h, float deg) { return squishAngle(w, h, deg * D2R) * R2D; } public static float squishAngleRad(float w, float h, float rad) { float dx = (float)Math.cos(rad) * w/h; float dy = (float)Math.sin(rad); return (float) Math.atan2(dy,dx); } public static float lineSide( float x1, float y1, float x2, float y2, float ptx, float pty ) { return (x2-x1)*(pty-y1) - (y2-y1)*(ptx-x1); } public static boolean collinear( float x1, float y1, float x2, float y2, float ptx, float pty ) { return (lineSide(x1,y1, x2,y2, ptx,pty) == 0); } public static boolean leftOfLine( float x1, float y1, float x2, float y2, float ptx, float pty ) { return (lineSide(x1,y1, x2,y2, ptx,pty) < 0); } public static boolean rightOfLine( float x1, float y1, float x2, float y2, float ptx, float pty ) { return (lineSide(x1,y1, x2,y2, ptx,pty) > 0); } public static int bezierParam( float p0, float p1, float p2, float p3, float val, float[] params ) { float max = p0; if(max < p1) max = p1; if(max < p2) max = p2; if(max < p3) max = p3; float min = p0; if(min > p1) min = p1; if(min > p2) min = p2; if(min > p3) min = p3; if(valmax) return 0; float a = 3*(p1-p2) - p0 + p3; float b = 3*(p0 - 2*p1 + p2); float c = 3*(p1-p0); float d = p0 - val; int numRoots = solveCubic(a,b,c,d,params); int numParams = 0; for(int i=0; i1) continue; params[numParams++] = params[i]; } return numParams; } public static int bezierParam( float p0, float p1, float p2, float val, float[] params ) { float max = p0; if(max < p1) max = p1; if(max < p2) max = p2; float min = p0; if(min > p1) min = p1; if(min > p2) min = p2; if(valmax) return 0; float a = p2 - 2*p1 + p0; float b = 2 * (p1-p0); float c = p0 - val; int numRoots = solveQuadratic(a,b,c,params); int numParams = 0; for(int i=0; i1) continue; params[numParams++] = params[i]; } return numParams; } public static int solveCubic( float a, float b, float c, float d, float[] roots ) { if(Math.abs(a) < EPSILON) return solveQuadratic(b,c,d,roots); b /= a; c /= a; d /= a; float bb = b*b; float p = (bb - 3*c) / 9f; float ppp = p*p*p; float q = (2*bb*b - 9*b*c + 27*d) / 54; float D = q*q - ppp; b /= 3f; if(Math.abs(D) < EPSILON) { if(Math.abs(q) < EPSILON) { roots[0] = -b; return 1; } float sqrtp = (float)Math.sqrt(p); float signq = (q>0)? 1 : -1; roots[0] = -signq*2*sqrtp - b; roots[1] = signq*sqrtp - b; return 2; } if(D < 0) { float sqrtp = (float)Math.sqrt(p); float phi = (float)Math.acos(q / (sqrtp*sqrtp*sqrtp)) / 3; float t = -2*sqrtp; float o = PConstants.TWO_PI/3f; roots[0] = t*(float)Math.cos(phi) - b; roots[1] = t*(float)Math.cos(phi + o) - b; roots[2] = t*(float)Math.cos(phi - o) - b; return 3; } float A = (q>0?-1:1) * (float)Math.pow(Math.abs(q) + Math.sqrt(D), 1.0/3.0); roots[0] = A + p/A - b; return 1; } public static int solveQuadratic(float a, float b, float c, float[] roots) { if(Math.abs(a) < EPSILON) { if(Math.abs(b) >= EPSILON) { roots[0] = -c/b; return 1; } return (Math.abs(c) 0) roots[numRoots++] = (-b+q) / a; return numRoots; } public static float random() { return random(1); } public static float random(float high) { float val; do { val = (float)Math.random() * high; } while(val == high); return val; } public static float random(float low, float high) { if(low >= high) return low; return random(high-low) + low; } public static int randomInt(float high) { return (int) Math.floor( random(high) ); } public static int randomInt(float low, float high) { return (int) Math.floor( random(low,high) ); } public static int randomInt32() { return randomInt(-2147483648,2147483647); } public static void tempSeed(long seed) { if(!_usingTempSeed) { _resetSeedValue = randomInt32(); _usingTempSeed = true; } H.app().randomSeed(seed); } public static void removeTempSeed() { H.app().randomSeed(_resetSeedValue); } public static float sineWave(float stepDegrees) { return (float) Math.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 boolean hasBits(int target, int val) { return ( (target & val) == val ); } public static float map(float val, float start1, float stop1, float start2, float stop2 ) { return start2 + (stop2-start2) * (val-start1)/(stop1-start1); } public static float round512(float val) { return Math.round(val*512)/512f; } } public static class HVector implements HLocatable { public float _x, _y, _z; public HVector() {} public HVector(float xCoord, float yCoord) { _x = xCoord; _y = yCoord; } public HVector(float xCoord, float yCoord, float zCoord) { _x = xCoord; _y = yCoord; _z = zCoord; } public float x() { return _x; } public HVector x(float newX) { _x = newX; return this; } public float y() { return _y; } public HVector y(float newY) { _y = newY; return this; } public float z() { return _z; } public HVector z(float newZ) { _z = newZ; return this; } } public static class HVertexNEW implements HLocatable { private HPath _parent; private float[] _cpts; private float _xPerc, _yPerc; public HVertexNEW(HPath parent) { _parent = parent; } public HPath parent() { return _parent; } public boolean isCurved() { return (_cpts != null); } public boolean isQuadratic() { return (_cpts != null) && (_cpts.length == 2); } public boolean isCubic() { return (_cpts != null) && (_cpts.length >= 4); } private float x2perc(float f) { float w = _parent.width(); if(w==0) w = 100; return _parent.anchorPercX() + f/w; } private float y2perc(float f) { float h = _parent.height(); if(h==0) h = 100; return _parent.anchorPercY() + f/h; } private float x2px(float f) { return _parent.width() * (f-_parent.anchorPercX()); } private float y2px(float f) { return _parent.height() * (f-_parent.anchorPercY()); } public HVertexNEW set(float pxX, float pxY) { return setPerc( x2perc(pxX), y2perc(pxY) ); } public HVertexNEW set(float cx, float cy, float pxX, float pxY) { return setPerc( x2perc(cx), y2perc(cy), x2perc(pxX), x2perc(pxY) ); } public HVertexNEW set( float cx1, float cy1, float cx2, float cy2, float pxX, float pxY ) { return setPerc( x2perc(cx1), y2perc(cy1), x2perc(cx2), y2perc(cy2), x2perc(pxX), x2perc(pxY)); } public HVertexNEW setPerc(float percX, float percY) { _xPerc = percX; _yPerc = percY; return this; } public HVertexNEW setPerc(float cx1, float cy1, float percX, float percY) { if(_cpts==null || _cpts.length!=2) _cpts = new float[2]; _cpts[0] = cx1; _cpts[1] = cy1; return setPerc(percX,percY); } public HVertexNEW setPerc( float cx2, float cy2, float cx1, float cy1, float percX, float percY ) { if(_cpts==null || _cpts.length<4) _cpts = new float[4]; _cpts[0] = cx1; _cpts[1] = cy1; _cpts[3] = cx2; _cpts[4] = cy2; return setPerc(percX,percY); } public float cx1() { return (_cpts==null||_cpts.length<4)? 0 : x2px(_cpts[0]); } public float cy1() { return (_cpts==null||_cpts.length<4)? 0 : y2px(_cpts[1]); } public float cx2() { return (_cpts==null||_cpts.length<4)? 0 : x2px(_cpts[2]); } public float cy2() { return (_cpts==null||_cpts.length<4)? 0 : y2px(_cpts[3]); } public float x() { return x2px(_xPerc); } public HVertexNEW x(float pxX) { _xPerc = x2perc(pxX); return this; } public float y() { return y2px(_yPerc); } public HLocatable y(float pxY) { _yPerc = y2perc(pxY); return this; } public float z() { return 0; } public HVertexNEW z(float pxZ) { return this; } public void computeMinMax(float[] minmax) { if(_xPerc < minmax[0]) minmax[0] = _xPerc; else if(_xPerc > minmax[2]) minmax[2] = _xPerc; if(_yPerc < minmax[1]) minmax[1] = _xPerc; else if(_yPerc > minmax[3]) minmax[3] = _xPerc; if(_cpts == null) return; for(int i=0; i<4; ++i) { int min = (i&1)==0? 0 : 1; int max = min + 2; if(_cpts[i] < minmax[min]) minmax[min] = _cpts[i]; else if(_cpts[i] > minmax[max]) minmax[max] = _cpts[i]; } } public int numCrossings(HVertexNEW prev, float ptx, float pty) { float x1 = prev._xPerc; float y1 = prev._yPerc; float x2 = _xPerc; float y2 = _yPerc; if(_cpts==null) { float side = HMath.round512(HMath.lineSide(x1,y1, x2,y2, ptx,pty)); return (side==0)? -1 : (side<0 || _yPerc==pty)? 0 : 1; } else { int crossings = 0; if(_cpts.length==2) { float[] params = new float[2]; } else { float[] params = new float[3]; } if(crossings>0 && _yPerc==pty) --crossings; return crossings; } } public void draw(PGraphics g, HVertexNEW prev) { if(_cpts==null || prev==null) { g.vertex(x2px(_xPerc), y2px(_yPerc)); } else if(_cpts.length==2) { g.quadraticVertex( x2px(_cpts[0]), y2px(_cpts[1]), x2px(_xPerc), y2px(_yPerc)); } else { g.bezierVertex( x2px(_cpts[0]), y2px(_cpts[1]), x2px(_cpts[2]), y2px(_cpts[3]), x2px(_xPerc), y2px(_yPerc)); } } } public static class HWarnings { public static final String NULL_TARGET = "A target should be assigned before using this method.", NO_PROTOTYPE = "This pool needs at least one prototype before requesting.", NULL_ARGUMENT = "This method does not take null arguments.", INVALID_DEST = "The destination doesn't not belong to any parent.", DESTCEPTION = "The destination cannot be itself", CHILDCEPTION = "Can't add this parent as its own child.", VERTEXPX_ERR = "Set a non-zero size first for this path before setting the\n\t" + "vertex by pixels, or use the vertexPerc() methods instead."; public static void warn(String type, String loc, String msg) { PApplet app = H.app(); app.println("[Warning: "+type+" @ "+loc+"]"); if( msg!=null && msg.length()>0 ) app.println("\t"+msg); } private HWarnings() {} }