blob: 7e0f6ac6268cfdfff95b13021cde244c9dd97a77 [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;
/// Function to convert a list of points to path.
typedef String LineInterpolator(Iterable<math.Point> points, int tension);
///
/// [SvgLine] provides a data-driven way to create path descriptions
/// that can be used to draw lines.
///
class SvgLine implements SvgShape {
static const LINE_INTERPOLATOR_LINEAR = 'linear';
static final LINE_INTERPOLATORS = {LINE_INTERPOLATOR_LINEAR: _linear};
/// Callback to access/convert datum to x coordinate value.
final SelectionValueAccessor<num> xValueAccessor;
/// Callback to access/convert datum to y coordinate value.
final SelectionValueAccessor<num> yValueAccessor;
/// Callback that is used to determine if a value is considered valid.
/// If [isDefined] returns false at any time, the value isn't part
/// of the line - the line would be split.
final SelectionCallback<bool> isDefined;
/// Interpolator that is used for creating the path.
final LineInterpolator interpolator;
/// Tension of the line, as used by a few interpolators.
final int tension;
SvgLine(
{this.xValueAccessor: defaultDataToX,
this.yValueAccessor: defaultDataToY,
this.isDefined: defaultIsDefined,
this.tension: 0,
String interpolate: LINE_INTERPOLATOR_LINEAR})
: interpolator = LINE_INTERPOLATORS[interpolate] {
assert(interpolator != null);
}
/// Generates path for drawing a line based in the selected [interpolator]
@override
String path(data, int index, Element e) {
assert(data is Iterable);
var segments = new StringBuffer(), points = [];
for (int i = 0, len = data.length; i < len; ++i) {
final d = data.elementAt(i);
if (isDefined(d, i, e)) {
points.add(new math.Point(xValueAccessor(d, i), yValueAccessor(d, i)));
} else if (points.isNotEmpty) {
segments.write('M${interpolator(points, tension)}');
points.clear();
}
}
if (points.isNotEmpty) {
segments.write('M${interpolator(points, tension)}');
}
return segments.toString();
}
/// Default implementation of [xValueAccessor].
/// Returns the first element if [d] is an iterable, otherwise returns [d].
static num defaultDataToX(d, i) => d is Iterable ? d.first : d;
/// Default implementation of [yValueAccessor].
/// Returns the second element if [d] is an iterable, otherwise returns [d].
static num defaultDataToY(d, i) => d is Iterable ? d.elementAt(1) : d;
/// Default implementation of [isDefined].
/// Returns true for all non-null values of [d].
static bool defaultIsDefined(d, i, e) => d != null;
/// Linear interpolator.
static String _linear(Iterable points, _) =>
points.map((pt) => '${pt.x},${pt.y}').join('L');
}