blob: 50c519891cf62ecd45e4ecdec15dbc07e165967f [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file or at
* https://developers.google.com/open-source/licenses/bsd
*/
part of charted.svg;
/**
* [SvgArc] provides a data-driven way to create Svg path descriptions
* that can be used to draw arcs - like those used in pie-charts.
*/
class SvgArc implements SvgPathGenerator {
static const num _OFFSET = -HALF_PI;
static const num _MAX = TAU - EPSILON;
/**
* [innerRadiusCallback] is called to get inner radius of the arc.
* As with other callbacks, [innerRadiusCallback] is passed data, index
* and element in the context.
*
* If not specified, [defaultInnerRadiusCallback] is used.
*/
SelectionCallback<num> innerRadiusCallback = defaultInnerRadiusCallback;
/**
* [outerRadiusCallback] is called to get outer radius of the arc.
* As with other callbacks, [outerRadiusCallback] is passed data, index
* and element in the context.
*
* If not specified, [defaultOuterRadiusCallback] is used.
*/
SelectionCallback<num> outerRadiusCallback = defaultOuterRadiusCallback;
/**
* [startAngleCallback] is called to get the start angle of the arc.
* As with other callbacks, [startAngleCallback] is passed data, index
* and element in the context.
*
* If not specified, [defaultStartAngleCallback] is used.
*/
SelectionCallback<num> startAngleCallback = defaultStartAngleCallback;
/**
* [endAngleCallback] is called to get the start angle of the arc.
* As with other callbacks, [endAngleCallback] is passed data, index
* and element in the context.
*
* If not specified, [defaultEndAngleCallback] is used.
*/
SelectionCallback<num> endAngleCallback = defaultEndAngleCallback;
/** Sets a constant value as inner radius of the arc. */
set innerRadius(num value) => innerRadiusCallback = toCallback(value);
/** Sets a constant value as outer radius of the arc. */
set outerRadius(num value) => outerRadiusCallback = toCallback(value);
/** Sets a constant value as start angle of the arc. */
set startAngle(num value) => startAngleCallback = toCallback(value);
/** Sets a constant value as end angle of the arc. */
set endAngle(num value) => endAngleCallback = toCallback(value);
String path(d, int i, Element e) {
var ir = innerRadiusCallback(d, i, e),
or = outerRadiusCallback(d, i, e),
start = startAngleCallback(d, i, e) + _OFFSET,
end = endAngleCallback(d, i, e) + _OFFSET,
sa = math.min(start, end),
ea = math.max(start, end),
delta = ea - sa;
if (delta > _MAX) {
return ir > 0 ?
// Concentric circles with area between them
"M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or"
"M0,$ir" "A$ir,$ir 0 1,0 0,-$ir" "A$ir,$ir 0 1,0 0,$ir" "Z" :
// Circle with enclosed area
"M0,$or" "A$or,$or 0 1,1 0,-$or" "A$or,$or 0 1,1 0,$or" "Z";
}
var ss = math.sin(sa),
se = math.sin(ea),
cs = math.cos(sa),
ce = math.cos(ea),
df = delta < PI ? 0 : 1;
return ir > 0 ?
// Arc capped at ends with lines
"M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
"L${ir * ce},${ir * se}" "A$ir,$ir 0 $df,0 ${ir * cs},${ir * ss}"
"Z" :
// A circular sector
"M${or * cs},${or * ss}" "A$or,$or 0 $df,1 ${or * ce},${or * se}"
"L0,0" "Z";
}
List centroid(d, int i, Element e) {
var r = (innerRadiusCallback(d, i, e) + outerRadiusCallback(d, i, e)) / 2,
a = (startAngleCallback(d, i, e) + endAngleCallback(d, i, e)) / 2 -
math.PI / 2;
return [math.cos(a) * r, math.sin(a) * r];
}
/** Default [innerRadiusCallback] returns data.innerRadius */
static num defaultInnerRadiusCallback(d, i, e) => d is !SvgArcData ||
d == null || d.innerRadius == null ? 0 : d.innerRadius;
/** Default [outerRadiusCallback] returns data.outerRadius */
static num defaultOuterRadiusCallback(d, i, e) => d is !SvgArcData ||
d == null || d.outerRadius == null ? 0 : d.outerRadius;
/** Default [startAngleCallback] returns data.startAngle */
static num defaultStartAngleCallback(d, i, e) => d is !SvgArcData ||
d == null || d.startAngle == null ? 0 : d.startAngle;
/** Default [endAngleCallback] that returns data.endAngle */
static num defaultEndAngleCallback(d, i, e) => d is !SvgArcData ||
d == null || d.endAngle == null ? 0 : d.endAngle;
}
/** Value type for SvgArc as used by default property accessors in SvgArc */
class SvgArcData {
var data;
num value;
num innerRadius;
num outerRadius;
num startAngle = 0;
num endAngle = 2 * PI;
SvgArcData(this.data, this.value, this.startAngle,
this.endAngle, [this.innerRadius=0, this.outerRadius=100]);
}
/**
* Returns the interpolator between two [SvgArcData] [a] and [b].
*
* The interpolator will interpolate the older innerRadius and outerDadius with
* newer ones, as well as older startAngle and endAngle with newer ones.
*
*/
InterpolateFn interpolateSvgArcData(SvgArcData a, SvgArcData b) {
var ast = a.startAngle,
aen = a.endAngle,
ai = a.innerRadius,
ao = a.outerRadius,
bst = b.startAngle - ast,
ben = b.endAngle - aen,
bi = b.innerRadius - ai,
bo = b.outerRadius - ao;
return (t) => new SvgArcData(b.data, b.value,
(ast + bst * t), (aen + ben * t), (ai + bi * t), (ao + bo * t));
}