/*
* HYPE_Processing
* http://www.hypeframework.org/ & https://github.com/hype/HYPE_Processing
*
* Copyright (c) 2013 Joshua Davis & James Cruz
*
* Distributed under the BSD License. See LICENSE.txt for details.
*
* All rights reserved.
*/
package hype.core.drawable;
import hype.core.collection.HNode;
import hype.core.interfaces.HDirectable;
import hype.core.interfaces.HHittable;
import hype.core.util.H;
import hype.core.util.HBundle;
import hype.core.util.HColors;
import hype.core.util.HConstants;
import hype.core.util.HMath;
import hype.core.util.HWarnings;
import hype.extended.drawable.HCanvas;
import hype.extended.drawable.HEllipse;
import hype.extended.drawable.HGroup;
import hype.extended.drawable.HImage;
import hype.extended.drawable.HPath;
import hype.extended.drawable.HRect;
import hype.extended.drawable.HShape;
import hype.extended.drawable.HText;
import java.util.Iterator;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PGraphics;
import processing.core.PVector;
/**
* The superclass of all drawables.
*
* Drawables represent objects that are displayable to the stage. TODO
*
* (The stage itself is also technically a drawable, but its usage is different
* from the other classes.)
*
* @author james
*/
public abstract class HDrawable extends HNode<HDrawable> implements
HDirectable,
HHittable,
Iterable<HDrawable>
{
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; // 0b0001
public static final byte BITMASK_TRANSFORMS_CHILDREN = 2;// 0b0010
public static final byte BITMASK_STYLES_CHILDREN = 4; // 0b0100
public static final byte BITMASK_ROTATES_CHILDREN = 8; // 0b1000
/** The parent of this drawable */
protected HDrawable _parent;
/** The first child of this drawable */
protected HDrawable _firstChild;
/** The last child of this drawable */
protected HDrawable _lastChild;
/** The extras bundle of this drawable */
protected HBundle _extras;
/** The x location of this drawable */
protected float _x;
/** The y location of this drawable */
protected float _y;
/** The z location of this drawable */
protected float _z;
/** The x anchor percentage of this drawable */
protected float _anchorU;
/** The y anchor percentage of this drawable */
protected float _anchorV;
/** The width of this drawable */
protected float _width;
/** The height of this drawable */
protected float _height;
/** The rotation along the x axis of this drawable, in radians */
protected float _rotationXRad;
/** The rotation along the y axis of this drawable, in radians */
protected float _rotationYRad;
/** The rotation along the z axis of this drawable, in radians */
protected float _rotationZRad;
/** The stroke width of this drawable, in pixels */
protected float _strokeWeight;
/** The alpha of this drawable, in percentage */
protected float _alphaPc;
/** The width-to-height ratio used for proportional resizing */
/** The number of this drawable's children */
protected int _numChildren;
/** The fill color of this drawable */
protected int _fill;
/** The stroke color of this drawable */
protected int _stroke;
/** The stroke cap of this drawable */
protected int _strokeCap;
/** The stroke join of this drawable */
protected int _strokeJoin;
/** The bitset that determines proportional resizing, and child transforms */
protected byte _flags;
// COPY & CREATE //
/**
* The default constructor for HDrawable.
*
* It sets several fields into their proper default values:
* - alpha percentage = 1 (100%)
* - fill = white
* - stroke = black
* - stroke cap = round
* - stroke join = miter
* - stroke weight = 1
* - width = 100
* - height = 100
*/
public HDrawable() {
_alphaPc = 1;
_fill = DEFAULT_FILL;
_stroke = DEFAULT_STROKE;
_strokeCap = PConstants.ROUND;
_strokeJoin = PConstants.MITER;
_strokeWeight = 1;
_width = DEFAULT_WIDTH;
_height = DEFAULT_HEIGHT;
}
/**
* Copies the values of `other`'s basic HDrawable fields.
*
* This method is primarily used for implementing createCopy().
* It copies the following fields from `other`:
* - x & y coordinates
* - x & y anchors
* - width & height
* - rotation
* - alpha
* - stroke & fill properties
*
* @see createCopy()
* @param other The drawable to copy its properties from.
*/
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;
}
/**
* Creates a copy of this drawable.
*
* This method is abstract and is meant to be implemented by the children
* of this class.
*
* @return A copy of this drawable.
*/
public abstract HDrawable createCopy();
// PARENT & CHILD //
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;
}
@Override
public boolean poppedOut() {
return (_parent == null);
}
@Override
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();
}
@Override
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;
}
@Override
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;
}
@Override
public void swapLeft() {
boolean isLast = (_next == null);
super.swapLeft();
if(_prev == null) _parent._firstChild = this;
if(_next != null && isLast) _parent._lastChild = _next;
}
@Override
public void swapRight() {
boolean isFirst = (_prev == null);
super.swapRight();
if(_next == null) _parent._lastChild = this;
if(_prev != null && isFirst) _parent._firstChild = _prev;
}
@Override
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;
}
/**
* Returns the parent of this drawable.
*
* @return The parent of this drawable, or null if there's none.
*/
public HDrawable parent() {
return _parent;
}
/**
* Returns the first child of this drawable.
*
* If this drawable has only one child, then the first child is also
* considered as the last child.
*
* @return The first child of this drawable, or null if there's none.
*/
public HDrawable firstChild() {
return _firstChild;
}
/**
* Returns the last child of this drawable.
*
* If this drawable has only one child, then the last child is also
* considered as the first child.
*
* @return The last child of this drawable, or null if there's none.
*/
public HDrawable lastChild() {
return _lastChild;
}
/**
* Checks if the given drawable is the parent of this drawable.
*
* @param d The drawable to be checked
* @return True if this drawable is the parent of `d`
*/
public boolean parentOf(HDrawable d) {
return (d != null) && (d._parent != null) && (d._parent.equals(this));
}
/**
* Returns the number of children of this drawable.
*
* @return The number of children of this drawable.
*/
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;
}
/**
* Adds the passed drawable as this drawable's child.
*
* If `child` is already a child of another drawable, it removes itself from
* its current parent and gets added to this drawable.
*
* If `child` is already a child of this drawable, then it does nothing.
*
* @param child The child to be added to this drawable.
* @return The drawable passed through this method.
*/
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;
}
/**
* Removes a child from this drawable.
*
* If `child` isn't a child of this drawable, this method will do nothing.
* Regardless, it will still return `child`.
*
* @param child The child to be removed from this drawable
* @return The drawable passed through this method.
*/
public HDrawable remove(HDrawable child) {
if( parentOf(child) ) child.popOut();
else HWarnings.warn("Not a Child", "HDrawable.remove()", null);
return child;
}
/**
* Creates a new iterator for this drawable to iterate through its children.
*
* Note that while HIterator has similar functionalities with
* java.util.Iterator, the former does _not_ extend the latter. This is due
* to js mode compatibility issues.
*
* @see HDrawableIterator
* @see HIterator
* @return A new HIterator for this drawable
*/
@Override
public HDrawableIterator iterator() {
return new HDrawableIterator(this);
}
// POSITION //
/**
* Sets the x and y position of this drawable.
*
* @chainable
* @param newX The new x coordinate for this drawable.
* @param newY The new y coordinate for this drawable.
* @return This drawable.
*/
public HDrawable loc(float newX, float newY) {
_x = newX;
_y = newY;
return this;
}
/**
* Sets the x, y and z position of this drawable.
*
* @chainable
* @param newX The new x coordinate for this drawable.
* @param newY The new y coordinate for this drawable.
* @param newZ The new z coordinate for this drawable.
* @return This drawable.
*/
public HDrawable loc(float newX, float newY, float newZ) {
_x = newX;
_y = newY;
_z = newZ;
return this;
}
/**
* Sets the position of this drawable via PVector.
*
* @chainable
* @param pt A PVector containing the new coordinates for this drawable.
* @return This drawable.
*/
public HDrawable loc(PVector pt) {
_x = pt.x;
_y = pt.y;
_z = pt.z;
return this;
}
/**
* Returns the position of this drawable as a PVector.
*
* @return A new PVector containing the coordinates of this drawable.
*/
public PVector loc() {
return new PVector(_x,_y,_z);
}
@Override
public HDrawable x(float newX) {
_x = newX;
return this;
}
@Override
public float x() {
return _x;
}
@Override
public HDrawable y(float newY) {
_y = newY;
return this;
}
@Override
public float y() {
return _y;
}
@Override
public HDrawable z(float newZ) {
_z = newZ;
return this;
}
@Override
public float z() {
return _z;
}
/**
* Moves this drawable from its original x & y position.
*
* @chainable
* @param dx The amount this drawable will be moved on the x-axis.
* @param dy The amount this drawable will be moved on the y-axis.
* @return This drawable.
*/
public HDrawable move(float dx, float dy) {
_x += dx;
_y += dy;
return this;
}
/**
* Moves this drawable from its original x, y & z position.
*
* @chainable
* @param dx The amount this drawable will be moved on the x-axis.
* @param dy The amount this drawable will be moved on the y-axis.
* @param dz The amount this drawable will be moved on the z-axis.
* @return This drawable.
*/
public HDrawable move(float dx, float dy, float dz) {
_x += dx;
_y += dy;
_z += dz;
return this;
}
/**
* Positions this drawable at the defined location in relation to its
* parent, or (0,0) if the parent is null.
*
* The `where` parameter can be any of the following HConstants values:
* - `HConstants.NONE` (does nothing)
* - `HConstants.LEFT`
* - `HConstants.RIGHT`
* - `HConstants.CENTER_X`
* - `HConstants.TOP`
* - `HConstants.BOTTOM`
* - `HConstants.CENTER_Y`
* - `HConstants.CENTER`
* - `HConstants.TOP_LEFT`
* - `HConstants.TOP_RIGHT`
* - `HConstants.BOTTOM_LEFT`
* - `HConstants.BOTTOM_RIGHT`
* - `HConstants.CENTER_LEFT`
* - `HConstants.CENTER_RIGHT`
* - `HConstants.CENTER_TOP`
* - `HConstants.CENTER_BOTTOM`
*
* These values can be combined via bitwise OR, so `H.TOP | H.LEFT` would be
* equal to `H.TOP_LEFT`.
*
* @chainable
* @see anchorAt(int)
* @param where The value that represents the location for this drawable.
* @return This drawable.
*/
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;
}
// ANCHOR //
/**
* Sets the anchor of this drawable by pixels.
*
* Note that HDrawable stores its anchor coordinates as a percentage of its
* width and height. So if the current size of this drawable is `(100,100)`,
* setting the anchor to `(75,75)` will be stored as `(0.75,0.75)`.
*
* In case that the current width or height is 0, then the width or height
* is assumed to be 100 when computing the anchor in this method.
*
* @chainable
* @see anchor(PVector)
* @see anchorX(float)
* @see anchorY(float)
* @param pxX The desired x anchor for this drawable, in pixels.
* @param pxY The desired y anchor for this drawable, in pixels.
* @return This drawable.
*/
public HDrawable anchor(float pxX, float pxY) {
return anchorX(pxX).anchorY(pxY);
}
/**
* Sets the anchor of this drawable by pixels via a PVector.
*
* This method calls anchor(float,float) with `pt`'s x and y fields as the
* arguments.
*
* @chainable
* @see anchor(float,float)
* @param pt The PVector containing the desired x and y anchor for this drawable, in pixels
* @return This drawable.
*/
public HDrawable anchor(PVector pt) {
return anchor(pt.x, pt.y);
}
/**
* Returns the anchor of this drawable in pixels.
*
* The result of this method is the product of its width & height and its
* x & y anchor percentages respectively. So if this drawable is anchored at
* the center, this method will return `(50,50)` when the size is
* `(100,100)`, `(30,30)` when the size is `(60,60)` and `(0,0)` when size
* is `(0,0)`.
*
* @see anchorX()
* @see anchorY()
* @return A new PVector containing the anchor of this drawable, in pixels.
*/
public PVector anchor() {
return new PVector( anchorX(), anchorY() );
}
/**
* Sets the x anchor of this drawable by pixels.
*
* Note that HDrawable stores its x anchor coordinates as a percentage of
* its width. If the width of this drawable is 0, it is assumed as 100 when
* computing the x anchor in this method.
*
* @chainable
* @see anchor(float,float)
* @see anchor(PVector)
* @see anchorY(float)
* @param pxX The desired x anchor for this drawable, in pixels.
* @return This drawable.
*/
public HDrawable anchorX(float pxX) {
_anchorU = pxX / (_width==0? 100 : _width);
return this;
}
/**
* Returns the x anchor of this drawable in pixels.
*
* The result of this method is the product of its width and x anchor
* percentage.
*
* @see anchor()
* @see anchorY()
* @return The x anchor of this drawable, in pixels.
*/
public float anchorX() {
return _width * _anchorU;
}
/**
* Sets the y anchor of this drawable by pixels.
*
* Note that HDrawable stores its y anchor coordinates as a percentage of
* its height. If the height of this drawable is 0, it is assumed as 100
* when computing the y anchor in this method
*
* @chainable
* @see anchor(float,float)
* @see anchor(PVector)
* @see anchorX(float)
* @param pxY The desired y anchor for this drawable, in pixels.
* @return This drawable.
*/
public HDrawable anchorY(float pxY) {
_anchorV = pxY / (_height==0? 100 : _height);
return this;
}
/**
* Returns the y anchor of this drawable in pixels.
*
* The result of this method is the product of its height and y anchor
* percentage.
*
* @see anchor()
* @see anchorX()
* @return The y anchor of this drawable, in pixels.
*/
public float anchorY() {
return _height * _anchorV;
}
/**
* Sets the anchor of this drawable in UV coordinates.
*
* @chainable
* @see anchorU(float)
* @see anchorV(float)
* @param u The desired `u` coordinate for this drawable's anchor.
* @param v The desired `v` coordinate for this drawable's anchor.
* @return This drawable.
*/
public HDrawable anchorUV(float u, float v) {
return anchorU(u).anchorV(v);
}
/**
* Returns the UV coordinates of this drawable's anchor.
*
* @see anchorU()
* @see anchorV()
* @return A new PVector containing this drawable's anchor as percentage
*/
public PVector anchorUV() {
return new PVector(_anchorU, _anchorV);
}
/**
* Sets the `u` coordinate for this drawable's anchor.
*
* @chainable
* @see anchorUV(float,float)
* @see anchorV(float)
* @param u The desired `u` coordinate for this drawable's anchor.
* @return This drawable.
*/
public HDrawable anchorU(float u) {
_anchorU = u;
return this;
}
/**
* Returns the `u` coordinate of this drawable's anchor.
*
* @see anchorUV(), anchorV()
* @return The `u` coordinate of this drawable's anchor.
*/
public float anchorU() {
return _anchorU;
}
/**
* Sets the `u` coordinate for this drawable's anchor.
*
* @chainable
* @see anchorUV()
* @see anchorU()
* @param v The desired `v` coordinate for this drawable's anchor.
* @return This drawable.
*/
public HDrawable anchorV(float v) {
_anchorV = v;
return this;
}
/**
* Returns the `v` coordinate of this draable's anchor.
*
* @see anchorUV()
* @see anchorU()
* @return The y anchor of this drawable.
*/
public float anchorV() {
return _anchorV;
}
/**
* Sets the anchor of this drawable at the defined location in relation to
* itself.
*
* The `where` parameter can be any of the following HConstants values:
* - `HConstants.NONE` (does nothing)
* - `HConstants.LEFT`
* - `HConstants.RIGHT`
* - `HConstants.CENTER_X`
* - `HConstants.TOP`
* - `HConstants.BOTTOM`
* - `HConstants.CENTER_Y`
* - `HConstants.CENTER`
* - `HConstants.TOP_LEFT`
* - `HConstants.TOP_RIGHT`
* - `HConstants.BOTTOM_LEFT`
* - `HConstants.BOTTOM_RIGHT`
* - `HConstants.CENTER_LEFT`
* - `HConstants.CENTER_RIGHT`
* - `HConstants.CENTER_TOP`
* - `HConstants.CENTER_BOTTOM`
*
* These values can be combined via bitwise OR, so `H.TOP | H.LEFT` would be
* equal to `H.TOP_LEFT`.
*
* @chainable
* @see locAt(int)
* @param where The value that represents the anchor for this drawable.
* @return This drawable.
*/
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;
}
// SIZE //
/**
* Sets the size of this drawable.
*
* If this drawable is set to resize proportionally, the new width will be
* adjusted according to the new height by default. (The exception to this
* is if the new height is the same as the old height, then the new height
* will be adjusted according to the new width instead.)
*
* @chainable
* @see size(float)
* @see width(float)
* @see height(float)
* @param w The new width for this drawable.
* @param h The new height for this drawable.
* @return This drawable.
*/
public HDrawable size(float w, float h) {
onResize( _width, _height, _width=w, _height=h );
return this;
}
/**
* Sets the size of this drawable.
*
* This method delegates size(float,float).
*
* @chainable
* @see size(float,float)
* @see width(float)
* @see height(float)
* @param s The new width and height for this drawable.
* @return This drawable.
*/
public HDrawable size(float s) {
return size(s,s);
}
public HDrawable size(PVector s) {
return size(s.x,s.y);
}
/**
* Returns the size of this drawable.
*
* @see width()
* @see height()
* @return A new PVector containing the width and height of this drawable.
*/
public PVector size() {
return new PVector(_width,_height);
}
/**
* Sets the width of this drawable.
*
* If this drawable is set to resize proportionally, the height will also
* change accordingly via onResize(float,float,float,float).
*
* @chainable
* @see size(float,float)
* @see size(float)
* @see height(float)
* @param w The new width for this drawable
* @return This drawable.
*/
public HDrawable width(float w) {
onResize( _width, _height, _width=w, _height );
return this;
}
/**
* Returns the width of this drawable.
*
* @see size()
* @see height()
* @return This drawable's width.
*/
public float width() {
return _width;
}
/**
* Sets the height of this drawable.
*
* If this drawable is set to resize proportionally, the width will also
* change accordingly.
*
* @chainable
* @see size(float,float)
* @see size(float)
* @see width(float)
* @param h The new height for this drawable
* @return This drawable.
*/
public HDrawable height(float h) {
onResize( _width, _height, _width, _height=h );
return this;
}
/**
* Returns the height of this drawable.
*
* @see size()
* @see width()
* @return This drawable's height
*/
public float height() {
return _height;
}
/**
* Multiplies the width and height of this drawable by the given scale
* factor.
*
* This method delegates size(float,float).
*
* @chainable
* @see scale(float,float)
* @param s The scale factor for the width and height.
* @return This drawable.
*/
public HDrawable scale(float s) {
return size(_width*s, _height*s);
}
/**
* Multiplies the width and height of this drawable by the given scale
* factors.
*
* This method delegates size(float,float).
*
* @chainable
* @see scale(float)
* @param sw The scale factor for the width.
* @param sh The scale factor for the height.
* @return This drawable.
*/
public HDrawable scale(float sw, float sh) {
return size(_width*sw, _height*sh);
}
/**
* Sets whether or not this drawable will be resized proportionally.
*
* If set to true, then this drawable will try to keep the width and height
* at the same ratio when you try to change either the width or height.
*
* @chainable
* @param b True, if this drawable will keep its size proportionally.
* @return This drawable.
*/
public HDrawable proportional(boolean b) {
_flags = HMath.setBits(_flags, BITMASK_PROPORTIONAL, b);
return this;
}
/**
* Returns whether or not this drawable is in proportional mode.
*
* @return True if this drawable is in proportional mode, else false.
*/
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);
}
/**
* Called after the size of this drawable is changed.
*
* By default, this method handles proportional resizing, child
* transformations.
*
* If you subclass HDrawable and want to execute code whenever your custom
* class changes size, override this method. But remember to call
* `super.onResize()` at the start of your override when you want to keep
* proportional resizing and child transforms.
*
* @param oldW The old width before being resize.
* @param oldH The old height before being resize.
* @param newW The new width after being resized.
* @param newH The new height after being resized.
*/
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]<minx) minx=tr[0]; else if(tr[0]>maxx) maxx=tr[0];
if(tr[1]<miny) miny=tr[1]; else if(tr[1]>maxy) maxy=tr[1];
float[] bl = HMath.absLocArr(this, x1, y2);
if(bl[0]<minx) minx=bl[0]; else if(bl[0]>maxx) maxx=bl[0];
if(bl[1]<miny) miny=bl[1]; else if(bl[1]>maxy) maxy=bl[1];
float[] br = HMath.absLocArr(this, x2, y2);
if(br[0]<minx) minx=br[0]; else if(br[0]>maxx) maxx=br[0];
if(br[1]<miny) miny=br[1]; else if(br[1]>maxy) maxy=br[1];
boundsValues[0] = minx;
boundsValues[1] = miny;
boundsValues[2] = maxx - minx;
boundsValues[3] = maxy - miny;
}
/**
* Returns the bounding size of this drawable based on this drawble's
* rotation.
*
* @deprecated This will be replaced with boundingBox() in the future.
* @return The bounding size of this drawable.
*/
public PVector boundingSize() {
// !!CAUTION!! Maths ahead!
float cosVal = (float)Math.cos(_rotationZRad);
float sinVal = (float)Math.sin(_rotationZRad);
float drawX = -anchorX();
float drawY = -anchorY();
float x1 = drawX; // left x
float x2 = _width + drawX; // right x
float y1 = drawY; // top y
float y2 = _height + drawY; // bottom y
float[] xCoords = new float[4];
float[] yCoords = new float[4];
// top-left
xCoords[0] = x1*cosVal + y1*sinVal;
yCoords[0] = x1*sinVal + y1*cosVal;
// top-right
xCoords[1] = x2*cosVal + y1*sinVal;
yCoords[1] = x2*sinVal + y1*cosVal;
// bottom-left
xCoords[2] = x1*cosVal + y2*sinVal;
yCoords[2] = x1*sinVal + y2*cosVal;
// bottom-right
xCoords[3] = x2*cosVal + y2*sinVal;
yCoords[3] = x2*sinVal + y2*cosVal;
// get the min/max x & y with one loop
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);
}
// STYLE //
/**
* Sets the fill for this drawable.
*
* This method takes in a standard Processing color integer, which are
* commonly represented as `0xAARRGGBB` or `#RRGGBB`.
*
* @chainable
* @see fill(int,int)
* @see fill(int,int,int)
* @see fill(int,int,int,int)
* @param clr The new fill for this drawable.
* @return This drawable.
*/
public HDrawable fill(int clr) {
if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16 | 0xFF000000;
_fill = clr;
onStyleChange();
return this;
}
/**
* Sets the fill for this drawable.
*
* The `clr` parameter is a standard Processing color integer and alpha is
* any number from 0 to 255.
*
* Note that `alpha` would override the existing alpha value of `clr`. So if
* `clr` is `0x80FFFFFF` and `alpha` is `0x40`, the fill will become
* `0x40FFFFFF`.
*
* @chainable
* @see fill(int)
* @see fill(int,int,int)
* @see fill(int,int,int,int)
* @see noFill()
* @param clr The new fill for this drawable, excluding its alpha value.
* @param alpha The alpha for the new fill for this drawable.
* @return This drawable.
*/
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;
}
/**
* Sets the fill for this drawable.
*
* The parameters `r`, `g` and `b` can be any number from 0 to 255.
*
* @chainable
* @see fill(int)
* @see fill(int,int)
* @see fill(int,int,int,int)
* @see noFill()
* @param r The red component for the new fill for this drawable
* @param g The green component for the new fill for this drawable
* @param b The blue component for the new fill for this drawable
* @return This drawable.
*/
public HDrawable fill(int r, int g, int b) {
_fill = HColors.merge(255,r,g,b);
onStyleChange();
return this;
}
/**
* Sets the fill for this drawable.
*
* The parameters `r`, `g`, `b` and `a` can be any number from 0 to 255.
*
* @chainable
* @see fill(int)
* @see fill(int,int)
* @see fill(int,int,int)
* @see noFill()
* @param r The red component for the new fill for this drawable
* @param g The green component for the new fill for this drawable
* @param b The blue component for the new fill for this drawable
* @param a The alpha component for the new fill for this drawable
* @return This drawable.
*/
public HDrawable fill(int r, int g, int b, int a) {
_fill = HColors.merge(a,r,g,b);
onStyleChange();
return this;
}
/**
* Returns the fill for this drawable.
*
* The color returned by this method is a standard Processing color integer.
*
* @return The fill of this drawable.
*/
public int fill() {
return _fill;
}
/**
* Removes the fill of this drawable.
*
* Technically, this method sets the fill to HConstants.CLEAR, which is
* `0x00FFFFFF` or fully transparent white.
*
* @chainable
* @see fill(int)
* @see fill(int,int)
* @see fill(int,int,int)
* @see fill(int,int,int,int)
* @return This drawable.
*/
public HDrawable noFill() {
return fill(HConstants.CLEAR);
}
/**
* Sets the stroke color for this drawable.
*
* This method takes in a standard Processing color integer, which are
* commonly represented as `0xAARRGGBB` or `#RRGGBB`.
*
* @chainable
* @see stroke(int,int)
* @see stroke(int,int,int)
* @see stroke(int,int,int,int)
* @see noStroke()
* @param clr The new stroke color for this drawable.
* @return This drawable
*/
public HDrawable stroke(int clr) {
if(0 <= clr && clr <= 255) clr |= clr<<8 | clr<<16 | 0xFF000000;
_stroke = clr;
onStyleChange();
return this;
}
/**
* Sets the stroke color for this drawable.
*
* The `clr` parameter is a standard Processing color integer and alpha is
* any number from 0 to 255.
*
* Note that `alpha` would override the existing alpha value of `clr`. So if
* `clr` is `0x80FFFFFF` and `alpha` is `0x40`, the fill will become
* `0x40FFFFFF`.
*
* @chainable
* @see stroke(int)
* @see stroke(int,int,int)
* @see stroke(int,int,int,int)
* @see noStroke()
* @param clr The new stroke color for this drawable, excluding its alpha value.
* @param alpha The alpha for the new stroke color for this drawable.
* @return This drawable.
*/
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;
}
/**
* Sets the stroke color for this drawable.
*
* The parameters `r`, `g` and `b` can be any number from 0 to 255.
*
* @chainable
* @see stroke(int)
* @see stroke(int,int)
* @see stroke(int,int,int,int)
* @see noStroke()
* @param r The red component for the new stroke color for this drawable
* @param g The green component for the new stroke color for this drawable
* @param b The blue component for the new stroke color for this drawable
* @return This drawable.
*/
public HDrawable stroke(int r, int g, int b) {
_stroke = HColors.merge(255,r,g,b);
onStyleChange();
return this;
}
/**
* Sets the stroke color for this drawable.
*
* The parameters `r`, `g` `b` and `a` can be any number from 0 to 255.
*
* @chainable
* @see stroke(int)
* @see stroke(int,int)
* @see stroke(int,int,int)
* @see noStroke()
* @param r The red component for the new stroke color for this drawable
* @param g The green component for the new stroke color for this drawable
* @param b The blue component for the new stroke color for this drawable
* @param a The alpha component for the new stroke color for this drawable
* @return This drawable.
*/
public HDrawable stroke(int r, int g, int b, int a) {
_stroke = HColors.merge(a,r,g,b);
onStyleChange();
return this;
}
/**
* Returns the stroke color for this drawable.
*
* The color returned by this method is a standard Processing color integer.
*
* @return The stroke color of this drawable.
*/
public int stroke() {
return _stroke;
}
/**
* Removes the stroke color of this drawable.
*
* Technically, this method sets the stroke color to HConstants.CLEAR, which
* is equalt to `0x00FFFFFF` or fully transparent white.
*
* @chainable
* @see stroke(int)
* @see stroke(int,int)
* @see stroke(int,int,int)
* @see stroke(int,int,int,int)
* @return This drawable.
*/
public HDrawable noStroke() {
return stroke(HConstants.CLEAR);
}
/**
* Sets the stroke cap for this drawable.
*
* The stroke cap can be any of the following values:
* - `PConstants.SQUARE`
* - `PConstants.PROJECT`
* - `PConstants.ROUND`
*
* @chainable
* @see Processing::strokeCap()
* @param type The stroke cap type for this drawable.
* @return This drawable.
*/
public HDrawable strokeCap(int type) {
_strokeCap = type;
onStyleChange();
return this;
}
/**
* Returns the stroke cap for this drawable.
*
* @return The integer representing the stroke cap of this drawable.
*/
public int strokeCap() {
return _strokeCap;
}
/**
* Sets the stroke join for this drawable.
*
* The stroke join can be any of the following values:
* - `PConstants.MITER`
* - `PConstants.BEVEL`
* - `PConstants.ROUND`
*
* @chainable
* @see Processing::strokeJoin()
* @param type The stroke join type for this drawable.
* @return This drawable.
*/
public HDrawable strokeJoin(int type) {
_strokeJoin = type;
onStyleChange();
return this;
}
/**
* Returns the stroke join for this drawable.
*
* @return The integer representing the stroke join of this drawable.
*/
public int strokeJoin() {
return _strokeJoin;
}
/**
* Sets the stroke weight, or thickness of this drawable.
*
* @chainable
* @param f The thickness for this drawable's stroke in pixels.
* @return This drawable
*/
public HDrawable strokeWeight(float f) {
_strokeWeight = f;
onStyleChange();
return this;
}
/**
* Returns the stroke weight, or thickness of this drawable.
*
* @return The stroke weight for this drawable, in pixels.
*/
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;
}
}
}
// ROTATION //
/**
* Sets the rotation for this drawable, in degrees.
*
* @chainable
* @param deg The new rotation for this drawable, in degrees.
* @return This drawable.
*/
public HDrawable rotation(float deg) {
return rotationZ(deg);
}
/**
* Returns the rotation for this drawable, in degrees.
*
* @return This drawable's rotation in degrees.
*/
public float rotation() {
return rotationZ();
}
@Override
public HDrawable rotationRad(float rad) {
return rotationZRad(rad);
}
@Override
public float rotationRad() {
return rotationZRad();
}
/**
* Shifts this drawable's current rotation by the passed value, in degrees.
*
* @chainable
* @param deg The amount of degrees for the current rotation to be shifted.
* @return This drawable.
*/
public HDrawable rotate(float deg) {
return rotateZ(deg);
}
/**
* Shifts this drawable's current rotation by the passed value, in radians.
*
* @chainable
* @param rad The amount of radians for the current rotation to be shifted.
* @return This drawable.
*/
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);
}
// VISIBILITY //
/**
* Sets the transparency for this drawable.
*
* The argument for this method can be any number from 0 to 255. Otherwise,
* if the argument is less than 0, the alpha will be 0; and if the argument
* is greater than 255, the alpha will be 255.
*
* Note that transparency _is independent from fill and stroke color_, even
* if the fill and stroke can also have alpha values. And unlike fill and
* stroke, this transparency can be passed down to this drawable's children.
*
* Also note that HDrawable actually stores `alpha` as a percentage ranging
* from 0 to 1 to keep the draw() and paintAll() methods a bit faster.
*
* @chainable
* @see alphaPc(float)
* @param a The new alpha for this drawable.
* @return This drawable.
*/
public HDrawable alpha(int a) {
return alphaPc(a/255f);
}
/**
* Returns the transparency for this drawable, ranging at `[0,255]`.
*
* You can be assured that the return value will only be in the
* aforementioned range.
*
* @see alphaPc()
* @return The alpha for this drawable with range `(0,255)`.
*/
public int alpha() {
return Math.round( alphaPc()*255 );
}
/**
* Sets the transparency for this drawable, as percentage.
*
* The argument for this method can be any number from 0 to 1. Otherwise, if
* the argument is less than 0f, the alpha will be 0f; and if the argument
* is greater than 1f, the alpha will be 1f.
*
* @chainable
* @see alphaPc(float)
* @param f The new alpha for this drawable, as percentage.
* @return This drawable.
*/
public HDrawable alphaPc(float f) {
_alphaPc = (f<0)? 0 : (f>1)? 1 : f;
return this;
}
/**
* Returns the transparency for this drawable, ranging from 0 to 255.
*
* You can be assured that the return value will only be in the
* aforementioned range.
*
* @see alpha()
* @return The alpha for this drawable with range `(0f,1f)`.
*/
public float alphaPc() {
return (_alphaPc<0)? 0 : _alphaPc;
}
/**
* Sets the visibility of this drawable.
*
* Technically this method manipulates the alpha of this drawable. If `v` is
* false, the alpha will become the negative of the current alpha, making
* this drawable invisible. Otherwise, if `v` is true, the alpha will become
* the positive of the current alpha (or 1f if the current alpha is 0),
* making this drawable visible.
*
* @chainable
* @see show(), hide()
* @param v True if this drawable will be visible, false if invisible.
* @return This drawable.
*/
public HDrawable visibility(boolean v) {
if( v && (_alphaPc==0) ) {
_alphaPc = 1;
} else if( v == (_alphaPc<0) ) {
_alphaPc = -_alphaPc;
}
return this;
}
/**
* Returns the visibility of this drawable.
*
* @return True if this drawable is visible, otherwise false.
*/
public boolean visibility() {
return _alphaPc > 0;
}
/**
* Makes this drawable visible by calling `visibility(true)`.
*
* @chainable
* @see visibility(boolean), hide()
* @return This drawable.
*/
public HDrawable show() {
return visibility(true);
}
/**
* Makes this drawable invisible by calling `visibility(false)`.
*
* @chainable
* @see visibility(boolean), show()
* @return This drawable.
*/
public HDrawable hide() {
return visibility(false);
}
/**
* Shifts the current alpha of this drawable by the given value.
*
* @chainable
* @see alpha(int)
* @param da The amount that the alpha will be shifted.
* @return This drawable.
*/
public HDrawable alphaShift(int da) {
return alphaShiftPc( da/255f );
}
/**
* Shifts the current alpha of this drawable, by percentage.
*
* @chainable.
* @see alphaPc(float)
* @param f The amount that alpha will be shifted, by percentage.
* @return This drawable.
*/
public HDrawable alphaShiftPc(float f) {
return alphaPc(_alphaPc + f);
}
// UTILITY //
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;
}
/**
* Assigns an _extras bundle_ for this drawable to hold arbitrary data.
*
* This follows the same idea as with Android's Bundles.
*
* @chainable
* @see Android::Bundle
* @param b The new extras bundle for this drawable.
* @return This drawable.
*/
public HDrawable extras(HBundle b) {
_extras = b;
return this;
}
/**
* Returns this drawable's _extras bundle_.
*
* @return The extras bundle for this drawable.
*/
public HBundle extras() {
return _extras;
}
/**
* Puts any arbitrary object into this drawable's _extras bundle_.
*
* If the extras bundle is null, this method will create a new one.
*
* @chainable
* @see HBundle.obj(String,Object)
* @param key The string that will be the key for `value`.
* @param value The object to be stored.
* @return This drawable.
*/
public HDrawable obj(String key, Object value) {
if(_extras == null) _extras = new HBundle();
_extras.obj(key,value);
return this;
}
/**
* Puts any arbitrary native value into this drawable's _extras bundle_.
*
* If the extras bundle is null, this method will create a new one.
*
* @chainable
* @see HBundle.num(String,float)
* @param key The string that will be the key for `value`.
* @param value The native value to be stored.
* @return This drawable.
*/
public HDrawable num(String key, float value) {
if(_extras == null) _extras = new HBundle();
_extras.num(key,value);
return this;
}
/**
* Puts any arbitrary boolean value into this drawable's _extras bundle_.
*
* @chainable
* @see HBundle.bool(String,boolean)
* @param key The string the will be the key for `value`.
* @param value The boolean to be stored
* @return This drawable.
*/
public HDrawable bool(String key, boolean value) {
if(_extras == null) _extras = new HBundle();
_extras.bool(key,value);
return this;
}
/**
* Returns the object from the _extras bundle_ with the corresponding key.
*
* If the extras bundle is null, this method will return null.
*
* @see HBundle.num(String)
* @param key The key that will be used to fetch the object.
* @return The object with the corresponding key.
*/
public Object obj(String key) {
return (_extras==null)? null : _extras.obj(key);
}
/**
* Returns the string from the _extras bundle_ with the corresponding key.
*
* This is a shorthand for `(String) obj(key)`. If the extras bundle is
* null, this method will return null.
*
* @see HBundle.str(String)
* @param key The key that will be used to fetch the string.
* @return The string with the corresponding key.
*/
public String str(String key) {
return (_extras==null)? null : _extras.str(key);
}
/**
* Returns the number from the _extras bundle_ with the corresponding key.
*
* If the extras bundle is null, this method will return 0.
*
* @see HBundle.num(String)
* @param key The key that will be used to fetch the float value.
* @return The float value with the corresponding key.
*/
public float num(String key) {
return (_extras==null)? 0 : _extras.num(key);
}
/**
* Returns the rounded number from the _extras bundle_ with the corresponding key.
*
* If the extras bundle is null, this method will return 0.
*
* @param key The key that will be used to fetch the integer value.
* @return The integer value with the corresponding key.
*/
public int numI(String key) {
return (_extras==null)? 0 : _extras.numI(key);
}
/**
* Returns the equivalent boolean value from the _extras bundle_ with the corresponding key.
*
* @see HBundle.bool(String)
* @param key
* @return
*/
public boolean bool(String key) {
return (_extras==null)? false : _extras.bool(key);
}
@Override
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));
}
@Override
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]);
}
@Override
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));
}
@Override
public boolean containsRel(float relX, float relY) {
return (0 <= relX) && (relX <= _width) &&
(0 <= relY) && (relY <= _height);
}
// DRAWING //
/**
* Applies this drawable's styling to the given graphics context.
*
* This method is primarily used by some subclasses' `draw()` method.
*
* @param g The graphics context for this drawable.
* @param currAlphaPc The current alpha value in the draw cycle.
*/
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();
}
/**
* Prepares the environment for drawing this drawable and its children.
*
* This method is primarily called internally during the draw cycle and
* probably wouldn't need to be called directly.
*
* @param g The graphics context for this drawable.
* @param usesZ Indicates if z-coordinates are used.
* @param currAlphaPc The current alpha value in the draw cycle.
*/
public void paintAll(PGraphics g, boolean usesZ, float currAlphaPc) {
if(_alphaPc<=0) return;
g.pushMatrix();
// Rotate and translate
if(usesZ) {
g.translate(_x,_y,_z);
g.rotateX(_rotationXRad);
g.rotateY(_rotationYRad);
g.rotateZ(_rotationZRad);
} else {
g.translate(_x,_y);
g.rotate(_rotationZRad);
}
// Compute current alpha
currAlphaPc *= _alphaPc;
// Draw self
draw(g, usesZ,-anchorX(),-anchorY(),currAlphaPc);
// Draw children
HDrawable child = _firstChild;
while(child != null) {
child.paintAll(g, usesZ, currAlphaPc);
child = child._next;
}
g.popMatrix();
}
/**
* Draws this drawable.
*
* This method is abstract and is meant to be implemented by the children
* of this class. Also, this method is primarily called internally during
* the draw cycle and probably wouldn't need to be called directly.
*
* @param g The graphics context for this drawable.
* @param usesZ Indicates if z-coordinates are used.
* @param drawX The x coordinate where this drawable would consider as 0
* @param drawY The y coordinate where this drawable would consider as 0
* @param currAlphaPc The current alpha value in the draw cycle.
*/
public abstract void draw( PGraphics g, boolean usesZ,
float drawX, float drawY, float currAlphaPc);
/**
* An HIterator used for iterating through HDrawable's children.
*
* @author james
*/
public static class HDrawableIterator implements Iterator<HDrawable> {
private HDrawable parent, d1, d2;
public HDrawableIterator(HDrawable parentDrawable) {
parent = parentDrawable;
d1 = parent._firstChild;
if(d1 != null) d2 = d1._next;
}
@Override
public boolean hasNext() {
return (d1 != null);
}
@Override
public HDrawable next() {
HDrawable nxt = d1;
d1 = d2;
if(d2 != null) d2 = d2._next;
return nxt;
}
@Override
public void remove() {
if(d1 != null) d1.popOut();
}
}
}