package flare.vis.operator.label { import flare.animate.Transitioner; import flare.display.TextSprite; import flare.util.Filter; import flare.util.IEvaluable; import flare.util.Property; import flare.util.Shapes; import flare.vis.data.Data; import flare.vis.data.DataSprite; import flare.vis.operator.Operator; import flash.display.Sprite; import flash.text.TextFormat; /** * Labeler that adds labels for items in a visualization. By default, this * operator adds labels that are centered on each data sprite; this can be * changed by configuring the offset and anchor settings. * *
Labelers support two different approaches for adding labels:
* CHILD
mode (the default) and LAYER
mode.
*
CHILD
mode, labels are added as children of
* DataSprite
instances and so become part of the data
* sprite itself. In this mode, labels will automatically change
* position as data sprites are re-positioned.LAYER
mode, labels are instead added to a separate
* layer of the visualization above the
* Visualization.marks
layer that contains the data
* sprites. A new layer will be created as needed and can be accessed
* through the Visualization.labels
property. This mode
* is particularly useful for ensuring that no labels can be occluded
* by data marks. In LAYER
mode, labels will not
* automatically move along with the labeled DataSprite
* instances if they are re-positioned. Instead, the labeler must be
* re-run to keep the layout current.To access created labels after a Labeler
has been run,
* use the props.label
property of a DataSprite
.
* To have labels stored under a different property name, set the
* access
property of this class to the desired name.
textFunction
* property is non-null. */
public function get source():String { return _source.name; }
public function set source(s:String):void { _source = Property.$(s); }
/** Boolean function indicating which items to process. Only items
* for which this function return true will be considered by the
* labeler. If the function is null, all items will be considered.
* @see flare.util.Filter */
public function get filter():Function { return _filter; }
public function set filter(f:*):void { _filter = Filter.$(f); }
/** Boolean function indicating whether label text values should be
* cached or not. If set to true then the label text is calculated
* only the first time it's needed and re-used from them on. If set
* to false then the label is recalculated at each update. */
public function get cacheText():Boolean { return _cacheText; }
public function set cacheText(c:Boolean):void { _cacheText = c; }
/** A sprite containing the labels, if a layer policy is used. */
public function get labels():Sprite { return _labels; }
/** The policy for how labels should be applied.
* One of LAYER (for adding a separate label layer) or
* CHILD (for adding labels as children of data objects). */
public function get labelPolicy():String { return _policy; }
/** Optional function for determining label text. */
public var textFunction:Function = null;
/** The text format to apply to labels. */
public var textFormat:TextFormat;
/** The text mode to use for the TextSprite labels.
* @see flare.display.TextSprite */
public var textMode:int = TextSprite.BITMAP;
/** The horizontal alignment for labels.
* @see flare.display.TextSprite */
public var horizontalAnchor:int = TextSprite.CENTER;
/** The vertical alignment for labels.
* @see flare.display.TextSprite */
public var verticalAnchor:int = TextSprite.MIDDLE;
/** The default x
value for labels. */
public var xOffset:Number = 0;
/** The default y
value for labels. */
public var yOffset:Number = 0;
// --------------------------------------------------------------------
/**
* Creates a new Labeler.
* @param source the property from which to retrieve the label text.
* If this value is a string or property instance, the label text will
* be pulled directly from the named property. If this value is a
* Function or Expression instance, the value will be used to set the
* textFunction property and the label text will be
* determined by evaluating that function.
* @param group the data group to process
* @param format optional text formatting information for labels
* @param filter a Boolean-valued filter function determining which
* items will be given labels
* @param policy the label creation policy. One of LAYER (for adding a
* separate label layer) or CHILD (for adding labels as children of
* data objects)
*/
public function Labeler(source:*=null, group:String=Data.NODES,
format:TextFormat=null, filter:*=null, policy:String=CHILD)
{
if (source is String) {
_source = Property.$(source);
} else if (source is Property) {
_source = Property(source);
} else if (source is IEvaluable) {
textFunction = IEvaluable(source).eval;
} else if (source is Function) {
textFunction = source;
}
_group = group;
textFormat = format ? format : new TextFormat("Arial",12);
this.filter = filter;
_policy = policy;
}
/** @inheritDoc */
public override function setup():void
{
if (_policy == LAYER) {
_labels = visualization.labels;
if (_labels == null) {
_labels = new Sprite();
_labels.mouseChildren = false;
_labels.mouseEnabled = false;
visualization.labels = _labels;
}
}
}
/** @inheritDoc */
public override function operate(t:Transitioner=null):void
{
_t = (t ? t : Transitioner.DEFAULT);
visualization.data.visit(process, group, filter);
_t = null;
}
/**
* Performs label creation and layout for the given data sprite.
* Subclasses should override this method to perform custom labeling.
* @param d the data sprite to process
*/
protected function process(d:DataSprite):void
{
var label:TextSprite = getLabel(d, true);
var o:Object, x:Number, y:Number, a:Number, v:Boolean;
if (_policy == LAYER) {
o = _t.$(d);
if (o.shape == Shapes.BLOCK) {
x = o.u + o.w/2;
y = o.v + o.h/2;
} else {
x = o.x;
y = o.y;
}
a = o.alpha;
v = o.visible;
o = _t.$(label);
o.x = x + xOffset;
o.y = y + yOffset;
o.alpha = a;
o.visible = v;
} else {
o = _t.$(label);
o.x = xOffset;
o.y = yOffset;
}
label.render();
}
/**
* Computes the label text for a given sprite.
* @param d the data sprite for which to produce label text
* @return the label text
*/
protected function getLabelText(d:DataSprite):String
{
if (textFunction != null) {
return textFunction(d);
} else {
return _source.getValue(d);
}
}
/**
* Retrives and optionally creates a label TextSprite for the given
* data sprite.
* @param d the data sprite to process
* @param create if true, a new label will be created as needed
* @param visible indicates if new labels should be visible by default
* @return the label
*/
protected function getLabel(d:DataSprite,
create:Boolean=false, visible:Boolean=true):TextSprite
{
var label:TextSprite = _access.getValue(d);
if (!label && !create) {
return null;
} else if (!label) {
label = new TextSprite("", null, textMode);
label.text = getLabelText(d);
label.visible = visible;
label.applyFormat(textFormat);
_access.setValue(d, label);
if (_policy == LAYER) {
_labels.addChild(label);
label.x = d.x + xOffset;
label.y = d.y + yOffset;
} else {
d.addChild(label);
label.mouseEnabled = false;
label.mouseChildren = false;
label.x = xOffset;
label.y = yOffset;
}
} else if (label && !cacheText) {
var o:Object = _t.$(label);
o.text = getLabelText(d);
}
label.textMode = textMode;
label.horizontalAnchor = horizontalAnchor;
label.verticalAnchor = verticalAnchor;
return label;
}
} // end of class Labeler
}