📄 scalebinding.as
字号:
package flare.vis.data
{
import flare.scale.LinearScale;
import flare.scale.LogScale;
import flare.scale.OrdinalScale;
import flare.scale.QuantileScale;
import flare.scale.QuantitativeScale;
import flare.scale.RootScale;
import flare.scale.Scale;
import flare.scale.ScaleType;
import flare.scale.TimeScale;
import flare.util.Stats;
import flare.vis.events.DataEvent;
/**
* Utility class that binds a data property to a descriptive scale.
* A ScaleBinding provides a layer of indirection between a data field and
* a data scale describing that field. The created scale can be used for
* layout and encoding of data values. When scale parameters such as the
* scale type or value range are updated, an underlying scale instance will
* be updated accordingly or a new instance will be created as needed.
*/
public class ScaleBinding extends Scale
{
/** @private */
protected var _scale:Scale = null;
/** @private */
protected var _scaleType:String = null;
/** @private */
protected var _pmin:Object = null;
/** @private */
protected var _pmax:Object = null;
/** @private */
protected var _base:Number = 10;
/** @private */
protected var _bins:int = 5;
/** @private */
protected var _power:Number = NaN;
/** @private */
protected var _zeroBased:Boolean = false;
/** @private */
protected var _ordinals:Array = null;
/** @private */
protected var _property:String;
/** @private */
protected var _group:String;
/** @private */
protected var _data:Data;
/** @private */
protected var _stats:Stats;
/** If true, updates to the underlying data will be ignored, as will
* any calls to <code>updateBinding</code>. Set this flag if you want
* to prevent the scale values from changing automatically. */
public var ignoreUpdates:Boolean = false;
/** The type of scale to create. */
public override function get scaleType():String {
return _scaleType ? _scaleType : scale.scaleType;
}
public function set scaleType(type:String):void {
_scaleType = type;
_scale = null;
}
/** The preferred minimum data value for the scale. If null, the scale
* minimum will be determined from the data directly. */
public function get preferredMin():Object { return _pmin; }
public function set preferredMin(val:Object):void {
_pmin = val;
if (_scale && _pmin) {
_scale.min = _pmin;
if (_zeroBased) zeroAlignScale(_scale);
}
}
/** The preferred maximum data value for the scale. If null, the scale
* maximum will be determined from the data directly. */
public function get preferredMax():Object { return _pmax; }
public function set preferredMax(val:Object):void {
_pmax = val;
if (_scale && _pmax) {
_scale.max = _pmax;
if (_zeroBased) zeroAlignScale(_scale);
}
}
/** @inheritDoc */
public override function get max():Object { return scale.max; }
public override function set max(v:Object):void { scale.max = v; }
/** @inheritDoc */
public override function get min():Object { return scale.min; }
public override function set min(v:Object):void { scale.min = v; }
/** The number base to use for a quantitative scale (10 by default). */
public function get base():Number { return _base; }
public function set base(val:Number):void {
_base = val;
if (_scale is QuantitativeScale) {
QuantitativeScale(_scale).base = _base;
}
}
/** A free parameter that indicates the exponent for a RootScale. */
public function get power():Number { return _power; }
public function set power(val:Number):void {
_power = val;
if (_scale is RootScale) {
RootScale(_scale).power = _power;
}
}
/** The number of bins for quantile scales. */
public function get bins():int { return _bins; }
public function set bins(count:int):void {
_bins = count;
if (_scale is QuantileScale) {
_scale = null;
}
}
/** Flag indicating if the scale bounds should be flush with the data.
* @see flare.scale.Scale#flush */
public override function get flush():Boolean { return _flush; }
public override function set flush(val:Boolean):void {
_flush = val;
if (_scale) _scale.flush = _flush;
}
/** Formatting pattern for formatting labels for scale values.
* @see flare.vis.scale.Scale#labelFormat. */
public override function get labelFormat():String { return _format; }
public override function set labelFormat(fmt:String):void {
_format = fmt;
if (_scale) _scale.labelFormat = fmt;
}
/** Flag indicating if a zero-based scale should be used. If set to
* true, and the scale type is numerical, the minimum or maximum
* scale value will automatically be adjusted to include the zero
* point as necessary. */
public function get zeroBased():Boolean { return _zeroBased; }
public function set zeroBased(val:Boolean):void {
_zeroBased = val;
if (_scale) zeroAlignScale(_scale);
}
/** An ordered array of values for defining an ordinal scale. */
public function get ordinals():Array { return _ordinals; }
public function set ordinals(ord:Array):void {
_ordinals = ord;
if (ScaleType.isOrdinal(_scaleType)) {
_stats = null;
_scale = null;
}
}
// -----------------------------------------------------
/** The data instance to bind to. */
public function get data():Data { return _data; }
public function set data(data:Data):void {
if (_data != null) { // remove existing listeners
_data.removeEventListener(DataEvent.ADD, onDataEvent);
_data.removeEventListener(DataEvent.REMOVE, onDataEvent);
_data.removeEventListener(DataEvent.UPDATE, onDataEvent);
}
_data = data;
if (_data != null) { // add new listeners
_data.addEventListener(DataEvent.ADD,
onDataEvent, false, 0, true);
_data.addEventListener(DataEvent.REMOVE,
onDataEvent, false, 0, true);
_data.addEventListener(DataEvent.UPDATE,
onDataEvent, false, 0, true);
}
}
/** The data group to bind to. */
public function get group():String { return _group; }
public function set group(name:String):void {
if (name != _group) {
_group = name;
_stats = null;
_scale = null;
}
}
/** The data property to bind to. */
public function get property():String { return _property; }
public function set property(name:String):void {
if (name != _property) {
_property = name;
_stats = null;
_scale = null;
}
}
/** The underlying scale created by this binding. */
protected function get scale():Scale {
if (!_data || !_group || !_property) {
throw new Error("Can't create scale with data to bind to.");
}
if (!_scale) {
_stats = _data.group(_group).stats(_property);
_scale = buildScale(_stats);
}
return _scale;
}
// --------------------------------------------------------------------
/**
* Creates a new ScaleBinding.
*/
public function ScaleBinding()
{
}
/**
* Checks to see if the binding is current. If not, the internal stats
* and scale for this binding will be cleared and lazily recomputed.
* @return true if the binding was updated, false otherwise
*/
public function updateBinding():Boolean
{
if (ignoreUpdates) return false;
var stats:Stats = _data.group(_group).stats(_property);
if (stats !== _stats) { // object identity test
_stats = null;
_scale = null;
return true;
}
return false;
}
/**
* Internal listener for data events that clears the current scale
* instance as needed.
* @param evt a DataEvent
*/
private function onDataEvent(evt:DataEvent):void
{
if (ignoreUpdates) return;
if (evt.list.name == _group) {
if (evt.type == DataEvent.UPDATE) {
updateBinding();
} else {
_stats = null;
_scale = null;
}
}
}
/** @inheritDoc */
public override function clone() : Scale
{
return scale.clone();
}
/**
* Returns the index of the input value in the ordinal array if the
* scale is ordinal or categorical, otherwise returns -1.
* @param value the value to lookup
* @return the index of the input value. If the value is not contained
* in the ordinal array, this method returns -1.
*/
public function index(value:Object):int
{
var s:OrdinalScale = scale as OrdinalScale;
return (s ? s.index(value) : -1);
}
/** The number of distinct values in this scale, if ordinal. */
public function get length():int
{
var s:OrdinalScale = scale as OrdinalScale;
return (s ? s.length : -1);
}
/** @inheritDoc */
public override function interpolate(value:Object) : Number
{
return scale.interpolate(value);
}
/** @inheritDoc */
public override function lookup(f:Number) : Object
{
return scale.lookup(f);
}
/** @inheritDoc */
public override function values(num:int=-1) : Array
{
return scale.values(num);
}
/** @inheritDoc */
public override function label(value:Object) : String
{
return scale.label(value);
}
/** @private */
protected function buildScale(stats:Stats):Scale
{
var type:String = _scaleType ? _scaleType : ScaleType.UNKNOWN;
var vals:Array = _ordinals ? _ordinals : stats.distinctValues;
var scale:Scale;
switch (stats.dataType) {
case Stats.NUMBER:
switch (type) {
case ScaleType.LINEAR:
case ScaleType.UNKNOWN:
scale = new LinearScale(stats.minimum, stats.maximum, _base, _flush, _format);
break;
case ScaleType.ROOT:
var pow:Number = isNaN(_power) ? 2 : _power;
scale = new RootScale(stats.minimum, stats.maximum, _base, _flush, pow, _format);
break;
case ScaleType.LOG:
scale = new LogScale(stats.minimum, stats.maximum, _base, _flush, _format);
break;
case ScaleType.QUANTILE:
scale = new QuantileScale(_bins, stats.values, true, _format);
break;
default:
scale = new OrdinalScale(vals, _flush, false, _format);
break;
}
break;
case Stats.DATE:
switch (type) {
case ScaleType.UNKNOWN:
case ScaleType.LINEAR:
case ScaleType.TIME:
scale = new TimeScale(stats.minDate, stats.maxDate, _flush, _format);
break;
default:
scale = new OrdinalScale(vals, _flush, false, _format);
break;
}
break;
default:
scale = new OrdinalScale(vals, _flush, false, _format);
break;
}
if (_pmin) scale.min = _pmin;
if (_pmax) scale.max = _pmax;
if (_zeroBased) zeroAlignScale(scale);
return scale;
}
private static function zeroAlignScale(scale:Scale):void
{
if (scale is QuantitativeScale) {
var qs:QuantitativeScale = QuantitativeScale(scale);
if (qs.scaleMin > 0) qs.dataMin = 0;
if (qs.scaleMax < 0) qs.dataMax = 0;
}
}
} // end of class ScaleBinding
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -