blob: a8424492e62d12fee7a117f1d41b2280ec58f9d4 [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.shapes;
///
/// [SvgArc] provides a data-driven way to create path descriptions
/// that can be used to draw arcs - like those used in pie-charts.
///
class SvgArc implements SvgShape {
static const _OFFSET = -HALF_PI;
static const _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.
final SelectionCallback<num> innerRadiusCallback;
/// [outerRadiusCallback] is called to get outer radius of the arc.
/// As with other callbacks, [outerRadiusCallback] is passed data, index
/// and element in the context.
final SelectionCallback<num> outerRadiusCallback;
/// [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.
final SelectionCallback<num> startAngleCallback;
/// [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.
final SelectionCallback<num> endAngleCallback;
SvgArc(
{this.innerRadiusCallback: defaultInnerRadiusCallback,
this.outerRadiusCallback: defaultOuterRadiusCallback,
this.startAngleCallback: defaultStartAngleCallback,
this.endAngleCallback: defaultEndAngleCallback});
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
? "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"
: "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
? "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"
: "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.innerRadius == null
? 0
: (d as SvgArcData).innerRadius;
/// Default [outerRadiusCallback] returns data.outerRadius
static num defaultOuterRadiusCallback(d, i, e) =>
d is! SvgArcData || d.outerRadius == null
? 0
: (d as SvgArcData).outerRadius;
/// Default [startAngleCallback] returns data.startAngle
static num defaultStartAngleCallback(d, i, e) =>
d is! SvgArcData || d.startAngle == null
? 0
: (d as SvgArcData).startAngle;
/// Default [endAngleCallback] that returns data.endAngle
static num defaultEndAngleCallback(d, i, e) =>
d is! SvgArcData || d.endAngle == null ? 0 : (d as SvgArcData).endAngle;
}
/// Value type for SvgArc as used by default property accessors in SvgArc
class SvgArcData {
dynamic data;
num value;
num innerRadius;
num outerRadius;
num startAngle;
num endAngle;
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 outerRadius with
/// newer ones, as well as older startAngle and endAngle with newer ones.
Interpolator 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));
}