HFollow mf; HRect d; void setup() { size(640,640); H.init(this).background(#202020); smooth(); d = new HRect(100); d .rounding(40) .noStroke() .fill(#ECECEC) .loc(width/2,height/2) .anchorAt(H.CENTER) .rotation(45) ; H.add(d); // HFollow / ease and spring mf = new HFollow() .target(d) .ease(0.1) .spring(0.95) ; } void draw() { H.drawStage(); } 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 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 abstract class HTrigger extends HBehavior { protected HCallback _callback; public HTrigger() { register(); _callback = HConstants.NOP; } public HTrigger callback(HCallback cb) { _callback = (cb==null)? HConstants.NOP : cb; return this; } public HCallback callback() { return _callback; } } 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 implements Iterable { 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 Iterator { 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 interface HColorist { public HColorist fillOnly(); public HColorist strokeOnly(); public HColorist fillAndStroke(); public boolean appliesFill(); public boolean appliesStroke(); public HDrawable applyColor(HDrawable drawable); } public static abstract class HDrawable extends HNode implements HDirectable, HHittable, Iterable { public static final int DEFAULT_FILL = 0xFFFFFFFF; public static final int DEFAULT_STROKE = 0xFF000000; public static final int DEFAULT_WIDTH = 100; public static final int DEFAULT_HEIGHT = 100; public static final byte BITMASK_PROPORTIONAL = 1; public static final byte BITMASK_TRANSFORMS_CHILDREN = 2; public static final byte BITMASK_STYLES_CHILDREN = 4; public static final byte BITMASK_ROTATES_CHILDREN = 8; protected HDrawable _parent; protected HDrawable _firstChild; protected HDrawable _lastChild; protected HBundle _extras; protected float _x; protected float _y; protected float _z; protected float _anchorU; protected float _anchorV; protected float _width; protected float _height; protected float _rotationXRad; protected float _rotationYRad; protected float _rotationZRad; protected float _strokeWeight; protected float _alphaPc; protected int _numChildren; protected int _fill; protected int _stroke; protected int _strokeCap; protected int _strokeJoin; protected byte _flags; public HDrawable() { _alphaPc = 1; _fill = DEFAULT_FILL; _stroke = DEFAULT_STROKE; _strokeCap = PConstants.ROUND; _strokeJoin = PConstants.MITER; _strokeWeight = 1; _width = DEFAULT_WIDTH; _height = DEFAULT_HEIGHT; } public void copyPropertiesFrom(HDrawable other) { _x = other._x; _y = other._y; _anchorU = other._anchorU; _anchorV = other._anchorV; _width = other._width; _height = other._height; _rotationZRad = other._rotationZRad; _alphaPc = other._alphaPc; _strokeWeight = other._strokeWeight; _fill = other._fill; _stroke = other._stroke; _strokeCap = other._strokeCap; _strokeJoin = other._strokeJoin; } public abstract HDrawable createCopy(); public boolean invalidChild(HDrawable destParent) { if(destParent == null) return true; if(destParent.equals(this)) return true; return false; } 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 HCanvas add(HCanvas child) { add((HDrawable) child); return child; } public HEllipse add(HEllipse child) { add((HDrawable) child); return child; } public HGroup add(HGroup child) { add((HDrawable) child); return child; } public HImage add(HImage child) { add((HDrawable) child); return child; } public HPath add(HPath child) { add((HDrawable) child); return child; } public HRect add(HRect child) { add((HDrawable) child); return child; } public HShape add(HShape child) { add((HDrawable) child); return child; } public HText add(HText child) { add((HDrawable) child); return child; } public HDrawable add(HDrawable child) { if(child == null) { HWarnings.warn("An Empty Child", "HDrawable.add()", HWarnings.NULL_ARGUMENT); } else if( child.invalidChild(this) ) { HWarnings.warn("Invalid Child", "HDrawable.add()", HWarnings.INVALID_CHILD); } else if( !parentOf(child) ) { if(_lastChild == null) { _firstChild = _lastChild = child; child.popOut(); child._parent = this; ++_numChildren; } else child.putAfter(_lastChild); } return child; } public HCanvas remove(HCanvas child) { remove((HDrawable) child); return child; } public HEllipse remove(HEllipse child) { remove((HDrawable) child); return child; } public HGroup remove(HGroup child) { remove((HDrawable) child); return child; } public HImage remove(HImage child) { remove((HDrawable) child); return child; } public HPath remove(HPath child) { remove((HDrawable) child); return child; } public HRect remove(HRect child) { remove((HDrawable) child); return child; } public HShape remove(HShape child) { remove((HDrawable) child); return child; } public HText remove(HText child) { remove((HDrawable) child); 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) { _anchorU = pxX / (_width==0? 100 : _width); return this; } public float anchorX() { return _width * _anchorU; } public HDrawable anchorY(float pxY) { _anchorV = pxY / (_height==0? 100 : _height); return this; } public float anchorY() { return _height * _anchorV; } public HDrawable anchorUV(float u, float v) { return anchorU(u).anchorV(v); } public PVector anchorUV() { return new PVector(_anchorU, _anchorV); } public HDrawable anchorU(float u) { _anchorU = u; return this; } public float anchorU() { return _anchorU; } public HDrawable anchorV(float v) { _anchorV = v; return this; } public float anchorV() { return _anchorV; } public HDrawable anchorAt(int where) { if(HMath.hasBits(where, HConstants.CENTER_X)) _anchorU = 0.5f; else if(HMath.hasBits(where, HConstants.LEFT)) _anchorU = 0; else if(HMath.hasBits(where, HConstants.RIGHT)) _anchorU = 1; if(HMath.hasBits(where, HConstants.CENTER_Y)) _anchorV = 0.5f; else if(HMath.hasBits(where, HConstants.TOP)) _anchorV = 0; else if(HMath.hasBits(where, HConstants.BOTTOM)) _anchorV = 1; return this; } public HDrawable size(float w, float h) { onResize( _width, _height, _width=w, _height=h ); return this; } public HDrawable size(float s) { return size(s,s); } public PVector size() { return new PVector(_width,_height); } public HDrawable width(float w) { onResize( _width, _height, _width=w, _height ); return this; } public float width() { return _width; } public HDrawable height(float h) { onResize( _width, _height, _width, _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) { _flags = HMath.setBits(_flags, BITMASK_PROPORTIONAL, b); return this; } public boolean proportional() { return HMath.hasBits(_flags,BITMASK_PROPORTIONAL); } public HDrawable transformsChildren(boolean b) { _flags = HMath.setBits(_flags,BITMASK_TRANSFORMS_CHILDREN,b); return this; } public boolean transformsChildren() { return HMath.hasBits(_flags,BITMASK_TRANSFORMS_CHILDREN); } protected void onResize(float oldW, float oldH, float newW, float newH) { if(proportional()) { if(newH != oldH) { if(oldH != 0) _width = oldW * newH/oldH; } else if(newW != oldW) { if(oldW != 0) _height = oldH * newW/oldW; } } if(transformsChildren()) { float scalew = (oldW==0)? 1 : _width/oldW; float scaleh = (oldH==0)? 1 : _height/oldH; HDrawable child = _firstChild; while(child != null) { child.loc(child._x*scalew, child._y*scaleh); child.scale(scalew,scaleh); child = child._next; } } } public void bounds(PVector boundsLoc, PVector boundsSize) { float[] vals = new float[4]; bounds(vals); boundsLoc.x = vals[0]; boundsLoc.y = vals[1]; boundsSize.x = vals[2]; boundsSize.y = vals[3]; } public void bounds(float[] boundsValues) { float x1 = -anchorX(), y1 = -anchorY(); float x2 = x1+_width, y2 = y1+_height; float minx, miny, maxx, maxy; float[] tl = HMath.absLocArr(this, x1, y1); minx = maxx = tl[0]; miny = maxy = tl[1]; float[] tr = HMath.absLocArr(this, x2, y1); if(tr[0]maxx) maxx=tr[0]; if(tr[1]maxy) maxy=tr[1]; float[] bl = HMath.absLocArr(this, x1, y2); if(bl[0]maxx) maxx=bl[0]; if(bl[1]maxy) maxy=bl[1]; float[] br = HMath.absLocArr(this, x2, y2); if(br[0]maxx) maxx=br[0]; if(br[1]maxy) maxy=br[1]; boundsValues[0] = minx; boundsValues[1] = miny; boundsValues[2] = maxx - minx; boundsValues[3] = maxy - miny; } public PVector boundingSize() { float cosVal = (float)Math.cos(_rotationZRad); float sinVal = (float)Math.sin(_rotationZRad); 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; onStyleChange(); return this; } public HDrawable fill(int clr, int alpha) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16; _fill = HColors.setAlpha(clr,alpha); onStyleChange(); return this; } public HDrawable fill(int r, int g, int b) { _fill = HColors.merge(255,r,g,b); onStyleChange(); return this; } public HDrawable fill(int r, int g, int b, int a) { _fill = HColors.merge(a,r,g,b); onStyleChange(); 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; onStyleChange(); return this; } public HDrawable stroke(int clr, int alpha) { if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16; _stroke = HColors.setAlpha(clr,alpha); onStyleChange(); return this; } public HDrawable stroke(int r, int g, int b) { _stroke = HColors.merge(255,r,g,b); onStyleChange(); return this; } public HDrawable stroke(int r, int g, int b, int a) { _stroke = HColors.merge(a,r,g,b); onStyleChange(); return this; } public int stroke() { return _stroke; } public HDrawable noStroke() { return stroke(HConstants.CLEAR); } public HDrawable strokeCap(int type) { _strokeCap = type; onStyleChange(); return this; } public int strokeCap() { return _strokeCap; } public HDrawable strokeJoin(int type) { _strokeJoin = type; onStyleChange(); return this; } public int strokeJoin() { return _strokeJoin; } public HDrawable strokeWeight(float f) { _strokeWeight = f; onStyleChange(); return this; } public float strokeWeight() { return _strokeWeight; } public HDrawable stylesChildren(boolean b) { _flags = HMath.setBits(_flags, BITMASK_STYLES_CHILDREN, b); return this; } public boolean stylesChildren() { return HMath.hasBits(_flags, BITMASK_STYLES_CHILDREN); } protected void onStyleChange() { if(stylesChildren()) { HDrawable d = _firstChild; while(d!=null) { d._stroke = _stroke; d._strokeWeight = _strokeWeight; d._strokeJoin = _strokeJoin; d._strokeCap = _strokeCap; d._fill = _fill; d = d._next; } } } public HDrawable rotation(float deg) { return rotationZ(deg); } public float rotation() { return rotationZ(); } public HDrawable rotationRad(float rad) { return rotationZRad(rad); } public float rotationRad() { return rotationZRad(); } public HDrawable rotate(float deg) { return rotateZ(deg); } public HDrawable rotateRad(float rad) { return rotateZRad(rad); } public HDrawable rotationX(float deg) { return rotationXRad(deg * HConstants.D2R); } public float rotationX() { return rotationXRad() * HConstants.R2D; } public HDrawable rotationXRad(float rad) { if(rotatesChildren()) { for(HDrawable d=_firstChild;d!=null;) d=d.rotationXRad(rad).next(); } else _rotationXRad = rad; return this; } public float rotationXRad() { return (rotatesChildren() && _firstChild!=null)? _firstChild.rotationXRad() : _rotationXRad; } public HDrawable rotateX(float deg) { return rotationXRad(_rotationXRad + deg*HConstants.D2R); } public HDrawable rotateXRad(float rad) { return rotationXRad(_rotationXRad + rad); } public HDrawable rotationY(float deg) { return rotationYRad(deg * HConstants.D2R); } public float rotationY() { return rotationYRad() * HConstants.R2D; } public HDrawable rotationYRad(float rad) { if(rotatesChildren()) { for(HDrawable d=_firstChild;d!=null;) d=d.rotationYRad(rad).next(); } else _rotationYRad = rad; return this; } public float rotationYRad() { return (rotatesChildren() && _firstChild!=null)? _firstChild.rotationYRad() : _rotationYRad; } public HDrawable rotateY(float deg) { return rotationYRad(_rotationYRad + deg*HConstants.D2R); } public HDrawable rotateYRad(float rad) { return rotationYRad(_rotationYRad + rad); } public HDrawable rotationZ(float deg) { return rotationZRad(deg * HConstants.D2R); } public float rotationZ() { return rotationZRad() * HConstants.R2D; } public HDrawable rotationZRad(float rad) { if(rotatesChildren()) { for(HDrawable d=_firstChild;d!=null;) d=d.rotationZRad(rad).next(); } else _rotationZRad = rad; return this; } public float rotationZRad() { return (rotatesChildren() && _firstChild!=null)? _firstChild.rotationZRad() : _rotationZRad; } public HDrawable rotateZ(float deg) { return rotationZRad(_rotationZRad + deg*HConstants.D2R); } public HDrawable rotateZRad(float rad) { return rotationZRad(_rotationZRad + rad); } public HDrawable rotatesChildren(boolean b) { _flags = HMath.setBits(_flags, BITMASK_ROTATES_CHILDREN, b); return this; } public boolean rotatesChildren() { return HMath.hasBits(_flags, BITMASK_ROTATES_CHILDREN); } public HDrawable alpha(int a) { return alphaPc(a/255f); } public int alpha() { return Math.round( alphaPc()*255 ); } public HDrawable alphaPc(float f) { _alphaPc = (f<0)? 0 : (f>1)? 1 : f; return this; } public float alphaPc() { return (_alphaPc<0)? 0 : _alphaPc; } public HDrawable visibility(boolean v) { if( v && (_alphaPc==0) ) { _alphaPc = 1; } else if( v == (_alphaPc<0) ) { _alphaPc = -_alphaPc; } return this; } public boolean visibility() { return _alphaPc > 0; } public HDrawable show() { return visibility(true); } public HDrawable hide() { return visibility(false); } public HDrawable alphaShift(int da) { return alphaShiftPc( da/255f ); } public HDrawable alphaShiftPc(float f) { return alphaPc(_alphaPc + f); } public float x2u(float px) { return px / (_width==0? 100 : _width); } public float y2v(float px) { return px / (_height==0? 100 : _height); } public float u2x(float pc) { return pc * _width; } public float v2y(float pc) { return pc * _height; } 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) { return (_extras==null)? false : _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 currAlphaPc) { float faPc = currAlphaPc * (_fill >>> 24); g.fill(_fill | 0xFF000000, Math.round(faPc)); if(_strokeWeight > 0) { float saPc = currAlphaPc * (_stroke >>> 24); g.stroke(_stroke | 0xFF000000, Math.round(saPc)); g.strokeWeight(_strokeWeight); g.strokeCap(_strokeCap); g.strokeJoin(_strokeJoin); } else g.noStroke(); } public void paintAll(PGraphics g, boolean usesZ, float currAlphaPc) { if(_alphaPc<=0) return; g.pushMatrix(); if(usesZ) { g.translate(_x,_y,_z); g.rotateX(_rotationXRad); g.rotateY(_rotationYRad); g.rotateZ(_rotationZRad); } else { g.translate(_x,_y); g.rotate(_rotationZRad); } currAlphaPc *= _alphaPc; draw(g, usesZ,-anchorX(),-anchorY(),currAlphaPc); HDrawable child = _firstChild; while(child != null) { child.paintAll(g, usesZ, currAlphaPc); child = child._next; } g.popMatrix(); } public abstract void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPc); public static class HDrawableIterator implements Iterator { 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 abstract class HDrawable3D extends HDrawable { public static final float DEFAULT_DEPTH = 100; protected float _depth; protected float _anchorW; public HDrawable3D() { _depth = DEFAULT_DEPTH; } public HDrawable3D size(float s) { return size(s,s,s); } public HDrawable3D size(float w, float h, float d) { _width = w; _height = h; _depth = d; return this; } public PVector size() { return new PVector(_width,_height,_depth); } public HDrawable3D depth(float f) { _depth = f; return this; } public float depth() { return _depth; } public HDrawable3D scale(float s) { return scale(s,s,s); } public HDrawable3D scale(float sw, float sh, float sd) { return (HDrawable3D) depth(sd).scale(sw,sh); } public HDrawable3D anchor(float ancx, float ancy, float ancz) { return (HDrawable3D) anchorZ(ancz).anchorX(ancx).anchorY(ancy); } public HDrawable3D anchorAt(int where) { if( (where & HConstants.CENTER_X) != 0 ) { anchorU(0.5f); } else if( (where & HConstants.LEFT) != 0 ) { anchorU(0); } else if( (where & HConstants.RIGHT) != 0 ) { anchorU(1); } if( (where & HConstants.CENTER_Y) != 0 ) { anchorV(0.5f); } else if( (where & HConstants.TOP) != 0 ) { anchorV(0); } else if( (where & HConstants.BOTTOM) != 0 ) { anchorV(1); } if( (where & HConstants.CENTER_Z) != 0 ) { anchorW(0.5f); } else if( (where & HConstants.BACK) != 0 ) { anchorW(0); } else if( (where & HConstants.FRONT) != 0 ) { anchorW(1); } return this; } protected void onResize(float oldW, float oldH, float newW, float newH) { super.onResize(oldW, oldH, newW, newH); } public PVector anchor() { return new PVector(anchorX(), anchorY(), anchorZ()); } public PVector anchorUV() { return new PVector(_anchorU, _anchorV, _anchorW); } public HDrawable3D anchorZ(float f) { return anchorZ(z2w(f)); } public float anchorZ() { return w2z(_anchorW); } public HDrawable3D anchorW(float f) { _anchorW = f; return this; } public float anchorW() { return _anchorW; } public float z2w(float px) { return px / (_depth==0? DEFAULT_DEPTH : _depth); } public float w2z(float pc) { return pc * _depth; } } public static class HStage extends HDrawable implements HImageHolder { private PApplet _app; private PImage _bgImg; private boolean _autoClears; private boolean _showsFPS; public HStage(PApplet papplet) { _app = papplet; _autoClears = true; background(HConstants.DEFAULT_BACKGROUND_COLOR); } public boolean invalidChild(HDrawable destParent) { return true; } public HStage background(int clr) { _fill = clr; return clear(); } public HStage backgroundImg(Object arg) { return image(arg); } public HStage image(Object imgArg) { _bgImg = H.getImage(imgArg); return clear(); } public PImage image() { return _bgImg; } public HStage autoClear(boolean b) { _autoClears = b; return this; } public HStage autoClears(boolean b) { _autoClears = b; return this; } public boolean autoClears() { return _autoClears; } public HStage clear() { if(_bgImg == null) _app.background(_fill); else _app.background(_bgImg); return this; } public HDrawable fill(int clr) { background(clr); return this; } public HDrawable fill(int clr, int alpha) { return fill(clr); } public HDrawable fill(int r, int g, int b) { return fill(HColors.merge(255,r,g,b)); } public HDrawable fill(int r, int g, int b, int a) { return fill(r,g,b); } public PVector size() { return new PVector(_app.width,_app.height); } public float width() { return _app.width; } public float height() { return _app.height; } public HStage showsFPS(boolean b) { _showsFPS = b; return this; } public boolean showsFPS() { return _showsFPS; } public void paintAll(PGraphics g, boolean usesZ, float currAlphaPc) { g.pushStyle(); if(_autoClears) clear(); HDrawable child = _firstChild; while(child != null) { child.paintAll(g, usesZ, currAlphaPc); child = child.next(); } g.popStyle(); if(_showsFPS) { g.pushStyle(); g.fill(H.BLACK); g.text(_app.frameRate,1,17); g.fill(H.WHITE); g.text(_app.frameRate,0,16); g.popStyle(); } } public void draw(PGraphics g,boolean b,float x,float y,float p) {} public HDrawable createCopy() { return null; } } public static interface HCallback { public void run(Object obj); } public static interface HDirectable extends HLocatable, HRotatable {} 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 HImageHolder { public HImageHolder image(Object imgArg); public PImage image(); } public static interface HLocatable { public float x(); public HLocatable x(float f); public float y(); public HLocatable y(float f); public float z(); public HLocatable z(float f); } public static interface HRotatable { public float rotationRad(); public HRotatable rotationRad(float rad); } 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 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 H autoClears(boolean b) { _stage.autoClears(b); return _self; } public static boolean autoClears() { return _stage.autoClears(); } public static H clearStage() { _stage.clear(); return _self; } public static HCanvas add(HCanvas stageChild) { return (HCanvas) _stage.add(stageChild); } public static HEllipse add(HEllipse stageChild) { return (HEllipse) _stage.add(stageChild); } public static HGroup add(HGroup stageChild) { return (HGroup) _stage.add(stageChild); } public static HImage add(HImage stageChild) { return (HImage) _stage.add(stageChild); } public static HPath add(HPath stageChild) { return (HPath) _stage.add(stageChild); } public static HRect add(HRect stageChild) { return (HRect) _stage.add(stageChild); } public static HShape add(HShape stageChild) { return (HShape) _stage.add(stageChild); } public static HText add(HText stageChild) { return (HText) _stage.add(stageChild); } public static HDrawable add(HDrawable stageChild) { return _stage.add(stageChild); } public static HCanvas remove(HCanvas stageChild) { return (HCanvas) _stage.remove(stageChild); } public static HEllipse remove(HEllipse stageChild) { return (HEllipse) _stage.remove(stageChild); } public static HGroup remove(HGroup stageChild) { return (HGroup) _stage.remove(stageChild); } public static HImage remove(HImage stageChild) { return (HImage) _stage.remove(stageChild); } public static HPath remove(HPath stageChild) { return (HPath) _stage.remove(stageChild); } public static HRect remove(HRect stageChild) { return (HRect) _stage.remove(stageChild); } public static HShape remove(HShape stageChild) { return (HShape) _stage.remove(stageChild); } public static HText remove(HText stageChild) { return (HText) _stage.remove(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(); } public static PImage getImage(Object imgArg) { if(imgArg instanceof PImage) return (PImage) imgArg; if(imgArg instanceof HImageHolder) return ((HImageHolder)imgArg).image(); if(imgArg instanceof String) return _app.loadImage((String) imgArg); return null; } 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 HCapture { private PGraphics _capturer; private String _renderer; private String _filename; private boolean _isRecording; private int _start, _end; public HCapture() { _start = _end = -1; } public HCapture capture() { _start = _end = H.app().frameCount; return this; } public HCapture capture(int frameNum) { _start = _end = H.app().frameCount; return this; } public HCapture start(int frameNum) { _start = frameNum; return this; } public int start() { return _start; } public HCapture end(int frameNum) { _end = frameNum; return this; } public int end() { return _end; } public boolean isRecording() { return _isRecording; } public HCapture filename(String s) { _filename = s; return this; } public String filename() { return _filename; } public HCapture renderer(String s) { _renderer = s; return this; } public String renderer() { return _renderer; } public void run() { if(_isRecording) { if(_end < 0) { if(H.app().frameCount >= _end) _isRecording = false; } else { PApplet app = H.app(); } if(!_isRecording) { } } else { if(_start < 0) { } else { } if(_isRecording) { H.app().endRecord(); if(_capturer != null) { _capturer.save(_filename); _capturer = null; } } } } } public static class HColors implements HConstants { 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 boolean isTransparent(int clr) { return (clr & 0xFF000000) == 0; } } public static interface HConstants { public static final int NONE = 0, LEFT = 1, RIGHT = 2, CENTER_X = 3, TOP = 4, BOTTOM = 8, CENTER_Y = 12, BACK = 16, FRONT = 32, CENTER_Z = 48, CENTER = 63, 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, 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, ROTATIONX = 15, ROTATIONY = 16, ROTATIONZ = 8, DROTATIONX = 17, DROTATIONY = 18, DROTATIONZ = 9, ISOCELES = 0, EQUILATERAL = 1, ONES = 0xFFFFFFFF, ZEROES = 0; public static final float D2R = PConstants.PI / 180f, R2D = 180f / PConstants.PI, SQRT2 = 1.4142135623730951f, PHI = 1.618033988749895f, PHI_1 = 0.618033988749895f, TOLERANCE = (float)10e-6, EPSILON = (float)10e-12; public static final HCallback NOP = new HCallback() {public void run(Object obj) {}}; } 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 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 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; return solveCubic(a,b,c,d,params); } 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; return solveQuadratic(a,b,c,params); } public static float bezierPoint( float p0, float p1, float p2, float p3, float t ) { float tt = t*t; float a = 3*(p1-p2) - p0 + p3; float b = 3*(p0 - 2*p1 + p2); float c = 3*(p1-p0); return a*tt*t + b*tt + c*t + p0; } public static float bezierPoint(float p0, float p1, float p2, float t) { float a = p2 - 2*p1 + p0; float b = 2 * (p1-p0); return a*t*t + b*t + p0; } public static float bezierTangent( float p0, float p1, float p2, float p3, float t ) { float a = 3 * (3*(p1-p2) - p0 + p3); float b = 6 * (p0 - 2*p1 + p2); float c = 3 * (p1-p0); return a*t*t + b*t + c; } public static float bezierTangent(float p0, float p1, float p2, float t) { float a = 2 * (p2 - 2*p1 + p0); float b = 2 * (p1-p0); return a*t + b; } public static int randomInt(float high) { return (int) Math.floor( H.app().random(high) ); } public static int randomInt(float low, float high) { return (int) Math.floor( H.app().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(byte target, byte mask) { return (target&mask) == mask; } public static boolean hasBits(int target, int mask) { return (target&mask) == mask; } public static byte setBits(byte target, byte mask, boolean val) { return (byte) (val? target|mask : target&(~mask)); } public static int setBits(int target, int mask, boolean val) { return (val)? target|mask : target&(~mask); } public static boolean lessThan(float a, float b, float tolerance) { return a < b + tolerance; } public static boolean lessThan(float a, float b) { return a < b + TOLERANCE; } public static boolean greaterThan(float a, float b, float tolerance) { return b < a + tolerance; } public static boolean greaterThan(float a, float b) { return b < a + TOLERANCE; } public static boolean isEqual(float a, float b, float tolerance) { return Math.abs(a-b) < tolerance; } public static boolean isEqual(float a, float b) { return Math.abs(a-b) < TOLERANCE; } public static boolean isZero(float a, float tolerance) { return Math.abs(a) < tolerance; } public static boolean isZero(float a) { return Math.abs(a) < TOLERANCE; } 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 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 class HVector implements HLocatable { private 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 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.", INVALID_CHILD = "The child you're trying to add is cannot be added to this drawable." ; 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() {} } 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 _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(HDirectable t) { if(_targets.size() <= 0) register(); _targets.add(t); return this; } public HSwarm removeTarget(HDirectable 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(HDirectable target, PApplet app) { HLocatable goal = null; float nearestDist = -1; for(HLocatable h : _goals) { float dist = HMath.dist(target.x(),target.y(), h.x(),h.y()); if(nearestDist<0 || dist it = _targets.iterator(); for(int i=0; i= _interval) { _lastInterval = curr; _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 class HTween extends HBehavior { private HDrawable _target; private HCallback _callback; private float _s1, _s2, _s3; private float _e1, _e2, _e3; private float _curr1, _curr2, _curr3; private float _origw, _origh; private float _raw, _dRaw, _spring, _ease; private int _property; public HTween() { _ease = 1; _callback = HConstants.NOP; register(); } public HTween target(HDrawable d) { _target = d; if(d != null) { _origw = d.width(); _origh = d.height(); } return this; } public HDrawable target() { return _target; } public HTween callback(HCallback c) { _callback = (c==null)? HConstants.NOP : c; return this; } public HCallback callback() { return _callback; } public HTween start(float a) { return start(a,a); } public HTween start(float a, float b) { return start(a,b,0); } public HTween start(float a, float b, float c) { _s1 = a; _s2 = b; _s3 = c; return this; } public float start() { return _s1; } public float start1() { return _s1; } public float start2() { return _s2; } public float start3() { return _s3; } public HTween end(float a) { return end(a,a); } public HTween end(float a, float b) { return end(a,b,0); } public HTween end(float a, float b, float c) { _e1 = a; _e2 = b; _e3 = c; return this; } public float end() { return _e1; } public float end1() { return _e1; } public float end2() { return _e2; } public float end3() { return _e3; } public HTween spring(float f) { _spring = f; return this; } public float spring() { return _spring; } public HTween ease(float f) { _ease = f; return this; } public float ease() { return _ease; } public HTween property(int id) { _property = id; return this; } public int property() { return _property; } public float nextRaw() { _raw += (_dRaw) = (_dRaw*_spring + (1-_raw)*_ease); float c = HMath.round512(_raw); _curr1 = HMath.map(c,0,1,_s1,_e1); _curr2 = HMath.map(c,0,1,_s2,_e2); _curr3 = HMath.map(c,0,1,_s3,_e3); return c; } public float curr() { return _curr1; } public float curr1() { return _curr1; } public float curr2() { return _curr2; } public float curr3() { return _curr3; } public void runBehavior(PApplet app) { if(_target==null) return; float c = nextRaw(); float v1 = _curr1; float v2 = _curr2; float v3 = _curr3; switch(_property) { case HConstants.WIDTH: _target.width(v1); break; case HConstants.HEIGHT: _target.height(v1); break; case HConstants.SCALE: v1 *= _origw; v2 *= _origh; case HConstants.SIZE: _target.size(v1,v2); break; case HConstants.ALPHA: _target.alpha(Math.round(v1)); break; case HConstants.X: _target.x(v1); break; case HConstants.Y: _target.y(v1); break; case HConstants.Z: _target.z(v1); break; case HConstants.LOCATION: _target.loc(v1,v2,v3); break; case HConstants.ROTATIONX: _target.rotationX(v1); break; case HConstants.ROTATIONY: _target.rotationY(v1); break; case HConstants.ROTATIONZ: _target.rotationZ(v1); break; case HConstants.DROTATIONX: _target.rotateX(v1); break; case HConstants.DROTATIONY: _target.rotateY(v1); break; case HConstants.DROTATIONZ: _target.rotateZ(v1); break; case HConstants.DX: _target.move(v1,0); break; case HConstants.DY: _target.move(0,v1); break; case HConstants.DLOC: _target.move(v1,v1); break; default: break; } if(c==1 && HMath.round512(_dRaw)==0) { unregister(); _callback.run(this); } } public HTween register() { return (HTween) super.register(); } public HTween unregister() { _raw = _dRaw = 0; return (HTween) super.unregister(); } } public static class HVelocity extends HBehavior { private float _velocityX, _velocityY, _accelX, _accelY; private HLocatable _target; public HVelocity target(HLocatable t) { if(t == null) unregister(); else register(); _target = t; return this; } public HLocatable target() { return _target; } public HVelocity velocity(float velocity, float deg) { return velocityRad(velocity, deg*HConstants.D2R); } public HVelocity velocityRad(float velocity, float rad) { _velocityX = velocity * (float)Math.cos(rad); _velocityY = velocity * (float)Math.sin(rad); return this; } public HVelocity velocityX(float dx) { _velocityX = dx; return this; } public float velocityX() { return _velocityX; } public HVelocity velocityY(float dy) { _velocityY = dy; return this; } public float velocityY() { return _velocityY; } public HVelocity launchTo(float goalX, float goalY, int numFrames) { if(_target == null) { HWarnings.warn("Null Target", "HVelocity.launchTo()", HWarnings.NULL_TARGET); } else { float nfsq = numFrames*numFrames; _velocityX = (goalX - _target.x() - _accelX*nfsq/2) / numFrames; _velocityY = (goalY - _target.y() - _accelY*nfsq/2) / numFrames; } return this; } public HVelocity accel(float acceleration, float deg) { return accelRad(acceleration, deg*HConstants.D2R); } public HVelocity accelRad(float acceleration, float rad) { _accelX = acceleration * (float)Math.cos(rad); _accelY = acceleration * (float)Math.sin(rad); return this; } public HVelocity accelX(float ddx) { _accelX = ddx; return this; } public float accelX() { return _accelX; } public HVelocity accelY(float ddy) { _accelY = ddy; return this; } public float accelY() { return _accelY; } public void runBehavior(PApplet app) { _target.x(_target.x() + _velocityX); _target.y(_target.y() + _velocityY); _velocityX += _accelX; _velocityY += _accelY; } public HVelocity register() { return (HVelocity) super.register(); } public HVelocity unregister() { return (HVelocity) super.unregister(); } } 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 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 { private float _percA, _percR, _percG, _percB; private 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, HImageHolder { private PImage img; private boolean fillFlag, strokeFlag; public HPixelColorist() { fillAndStroke(); } public HPixelColorist(Object imgArg) { this(); image(imgArg); } public HPixelColorist image(Object imgArg) { img = H.getImage(imgArg); return this; } public PImage image() { return img; } 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 HBox extends HDrawable3D { public HDrawable createCopy() { HBox copy = new HBox(); copy.copyPropertiesFrom(this); copy._depth = _depth; copy._anchorW = _anchorW; return copy; } public void draw( PGraphics g, boolean usesZ, float drawX, float drawY, float currAlphaPc ) { applyStyle(g, currAlphaPc); g.pushMatrix(); g.translate(drawX, drawY, -anchorZ()); g.box(_width,_height,_depth); g.popMatrix(); } } 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 alphaPc) { if(_alphaPc<=0 || _width==0 || _height==0) return; g.pushMatrix(); if(zFlag) g.translate(_x,_y,_z); else g.translate(_x,_y); g.rotate(_rotationZRad); alphaPc *= _alphaPc; _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(), alphaPc); 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 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 alphaPc ) { applyStyle(g,alphaPc); 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() { transformsChildren(true).stylesChildren(true); } public HGroup createCopy() { HGroup copy = new HGroup(); copy.copyPropertiesFrom(this); return copy; } public void paintAll(PGraphics g, boolean usesZ, float alphaPc) { if(_alphaPc<=0) return; g.pushMatrix(); if(usesZ) g.translate(_x,_y,_z); else g.translate(_x,_y); g.rotate(_rotationZRad); alphaPc *= _alphaPc; HDrawable child = _firstChild; while(child != null) { child.paintAll(g, usesZ, alphaPc); child = child.next(); } g.popMatrix(); } public void draw(PGraphics g,boolean b,float x,float y,float f) {} } public static class HImage extends HDrawable implements HImageHolder { 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) { _image = H.getImage(imgArg); 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 alphaPc ) { if(_image==null) return; alphaPc *= (_fill>>>24); g.tint( _fill | 0xFF000000, Math.round(alphaPc) ); 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 { public static final int HANDLE_FILL = 0xFFFF0000; public static final int HANDLE_STROKE = 0xFF202020; public static final float HANDLE_STROKE_WEIGHT = 1; public static final float HANDLE_SIZE = 6; private ArrayList _vertices; private int _mode; private boolean _drawsHandles; public HPath() { this(PConstants.PATH); } public HPath(int modeId) { _mode = modeId; _vertices = new ArrayList(); } public HPath createCopy() { HPath copy = new HPath(_mode); copy.copyPropertiesFrom(this); copy._drawsHandles = _drawsHandles; for(int i=0; i1? numv-2 : 0); for(int i=0; i2); boolean isSimple = true; if(drawsLines) g.beginShape(); else g.beginShape(PConstants.POINTS); int itrs = (isPolygon)? numv+1 : numv; for(int i=0; i 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 alphaPc ) { if(_text == null) return; applyStyle(g,alphaPc); 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 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 class HShapeLayout implements HLayout { private HDrawable _target; private float[] _bounds; private int _iterationLimit; public HShapeLayout() { _iterationLimit = 1024; _bounds = new float[4]; } public HShapeLayout iterationLimit(int i) { _iterationLimit = i; return this; } public int iterationLimit() { return _iterationLimit; } public HShapeLayout target(HDrawable d) { _target = d; if(_target != null) _target.bounds(_bounds); 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 x1 = _bounds[0]; float y1 = _bounds[1]; float x2 = _bounds[0] + _bounds[2]; float y2 = _bounds[1] + _bounds[3]; for(int i=0; i<_iterationLimit; ++i) { float x = H.app().random(x1,x2); float y = H.app().random(y1,y2); if(_target.contains(x,y)) return new PVector(x,y); } return null; } } public static class HDrawablePool implements Iterable { private HLinkedHashSet _activeSet, _inactiveSet; private ArrayList _prototypes; private HCallback _onCreate, _onRequest, _onRelease; 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(); _onCreate = _onRequest = _onRelease = HConstants.NOP; } 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 onCreate(HCallback callback) { _onCreate = (callback==null)? HConstants.NOP : callback; return this; } public HCallback onCreate() { return _onCreate; } public HDrawablePool onRequest(HCallback callback) { _onRequest = (callback==null)? HConstants.NOP : callback; return this; } public HCallback onRequest() { return _onRequest; } public HDrawablePool onRelease(HCallback callback) { _onRelease = (callback==null)? HConstants.NOP : 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 = HConstants.NOP; _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(onCreateFlag) _onCreate.run(drawable); _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); _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 Iterator iterator() { return _activeSet.iterator(); } } public static class HVertex implements HLocatable { public static final float LINE_TOLERANCE = 1.5f; private HPath _path; private byte _numControlPts; private float _u, _v, _cu1, _cv1, _cu2, _cv2; public HVertex(HPath parentPath) { _path = parentPath; } public HVertex createCopy(HPath newParentPath) { HVertex copy = new HVertex(newParentPath); copy._numControlPts = _numControlPts; copy._u = _u; copy._v = _v; copy._cu1 = _cu1; copy._cv1 = _cv1; copy._cu2 = _cu2; copy._cv2 = _cv2; return copy; } public HPath path() { return _path; } public HVertex numControlPts(byte b) { _numControlPts = b; return this; } public byte numControlPts() { return _numControlPts; } public boolean isLine() { return (_numControlPts <= 0); } public boolean isCurved() { return (_numControlPts > 0); } public boolean isQuadratic() { return (_numControlPts == 1); } public boolean isCubic() { return (_numControlPts >= 2); } public HVertex set(float x, float y) { return setUV( _path.x2u(x), _path.y2v(y)); } public HVertex set(float cx, float cy, float x, float y) { return setUV( _path.x2u(cx), _path.y2v(cy), _path.x2u(x), _path.y2v(y)); } public HVertex set( float cx1, float cy1, float cx2, float cy2, float x, float y ) { return setUV( _path.x2u(cx1), _path.y2v(cy1), _path.x2u(cx2), _path.y2v(cy2), _path.x2u(x), _path.y2v(y)); } public HVertex setUV(float u, float v) { _numControlPts = 0; _u = u; _v = v; return this; } public HVertex setUV(float cu, float cv, float u, float v) { _numControlPts = 1; _u = u; _v = v; _cu1 = cu; _cv1 = cv; return this; } public HVertex setUV( float cu1, float cv1, float cu2, float cv2, float u, float v ) { _numControlPts = 2; _u = u; _v = v; _cu1 = cu1; _cv1 = cv1; _cu2 = cu2; _cv2 = cv2; return this; } public HVertex x(float f) { return u(_path.x2u(f)); } public float x() { return _path.u2x(_u); } public HVertex y(float f) { return v(_path.y2v(f)); } public float y() { return _path.v2y(_v); } public HVertex z(float f) { return this; } public float z() { return 0; } public HVertex u(float f) { _u = f; return this; } public float u() { return _u; } public HVertex v(float f) { _v = f; return this; } public float v() { return _v; } public HVertex cx(float f) { return cx1(f); } public float cx() { return cx1(); } public HVertex cy(float f) { return cy1(f); } public float cy() { return cy1(); } public HVertex cu(float f) { return cu1(f); } public float cu() { return cu1(); } public HVertex cv(float f) { return cv1(f); } public float cv() { return cv1(); } public HVertex cx1(float f) { return cu1(_path.x2u(f)); } public float cx1() { return _path.u2x(_cu1); } public HVertex cy1(float f) { return cv1(_path.y2v(f)); } public float cy1() { return _path.v2y(_cv1); } public HVertex cu1(float f) { _cu1 = f; return this; } public float cu1() { return _cu1; } public HVertex cv1(float f) { _cv1 = f; return this; } public float cv1() { return _cv1; } public HVertex cx2(float f) { return cu2(_path.x2u(f)); } public float cx2() { return _path.u2x(_cu2); } public HVertex cy2(float f) { return cv2(_path.y2v(f)); } public float cy2() { return _path.v2y(_cv2); } public HVertex cu2(float f) { _cu2 = f; return this; } public float cu2() { return _cu2; } public HVertex cv2(float f) { _cv2 = f; return this; } public float cv2() { return _cv2; } public void computeMinMax(float[] minmax) { if(_u < minmax[0]) minmax[0] = _u; else if(_u > minmax[2]) minmax[2] = _u; if(_v < minmax[1]) minmax[1] = _v; else if(_v > minmax[3]) minmax[3] = _v; switch(_numControlPts) { case 2: if(_cu2 < minmax[0]) minmax[0] = _cu2; else if(_cu2 > minmax[2]) minmax[2] = _cu2; if(_cv2 < minmax[1]) minmax[1] = _cv2; else if(_cv2 > minmax[3]) minmax[3] = _cv2; case 1: if(_cu1 < minmax[0]) minmax[0] = _cu1; else if(_cu1 > minmax[2]) minmax[2] = _cu1; if(_cv1 < minmax[1]) minmax[1] = _cv1; else if(_cv1 > minmax[3]) minmax[3] = _cv1; break; default: break; } } public void adjust(float offsetU, float offsetV, float oldW, float oldH) { x( oldW*(_u += offsetU) ).y( oldH*(_v += offsetV) ); switch(_numControlPts) { case 2: cx2( oldW*(_cu2 += offsetU) ).cy2( oldH*(_cv2 += offsetV) ); case 1: cx1( oldW*(_cu1 += offsetU) ).cy1( oldH*(_cv1 += offsetV) ); break; default: break; } } private float dv(float pv, float t) { switch(_numControlPts) { case 1: return HMath.bezierTangent(pv,_cv1,_v, t); case 2: return HMath.bezierTangent(pv,_cv2,_cv2,_v, t); default: return _v - pv; } } public boolean intersectTest( HVertex pprev, HVertex prev, float tu, float tv, boolean openPath ) { float u1 = prev._u; float v1 = prev._v; float u2 = _u; float v2 = _v; if(isLine() || openPath) { return ((v1<=tv && tv