diff --git a/charts_common/lib/src/chart/common/chart_canvas.dart b/charts_common/lib/src/chart/common/chart_canvas.dart index 0b65fb772..7e5936d87 100644 --- a/charts_common/lib/src/chart/common/chart_canvas.dart +++ b/charts_common/lib/src/chart/common/chart_canvas.dart @@ -93,7 +93,9 @@ abstract class ChartCanvas { Rectangle clipBounds, Color fill, Color stroke, - double strokeWidthPx}); + double strokeWidthPx, + bool fillGradient, + bool smoothLine}); /// Renders a simple rectangle. void drawRect(Rectangle bounds, @@ -142,4 +144,5 @@ Color getAnimatedColor(Color previous, Color target, double animationPercent) { /// on top of a bar filled with the fill color. /// * [solid] defines a simple bar filled with the fill color. This is the /// default pattern for bars. -enum FillPatternType { forwardHatch, solid } +/// * [gradient] defines a simple bar filled with the gradient color +enum FillPatternType { forwardHatch, solid, gradient } diff --git a/charts_common/lib/src/chart/line/line_renderer.dart b/charts_common/lib/src/chart/line/line_renderer.dart index c164e59e6..4750f87b0 100644 --- a/charts_common/lib/src/chart/line/line_renderer.dart +++ b/charts_common/lib/src/chart/line/line_renderer.dart @@ -940,7 +940,10 @@ class LineRenderer extends BaseCartesianRenderer { canvas.drawPolygon( clipBounds: _getClipBoundsForExtent(area.positionExtent), fill: area.color, - points: area.points); + points: area.points, + fillGradient: config.fillGradient, + smoothLine: config.smoothLine + ); } }); } diff --git a/charts_common/lib/src/chart/line/line_renderer_config.dart b/charts_common/lib/src/chart/line/line_renderer_config.dart index fe67ae5da..9f8911672 100644 --- a/charts_common/lib/src/chart/line/line_renderer_config.dart +++ b/charts_common/lib/src/chart/line/line_renderer_config.dart @@ -73,6 +73,8 @@ class LineRendererConfig extends LayoutViewConfig /// Whether lines should have round end caps, or square if false. final bool roundEndCaps; + final bool fillGradient; + LineRendererConfig( {this.customRendererId, this.radiusPx = 3.5, @@ -86,6 +88,7 @@ class LineRendererConfig extends LayoutViewConfig this.areaOpacity = 0.1, this.smoothLine = false, this.roundEndCaps = false, + this.fillGradient = false, SymbolRenderer symbolRenderer}) : this.strokeWidthPx = strokeWidthPx, this.symbolRenderer = symbolRenderer ?? new LineSymbolRenderer(); diff --git a/charts_flutter/lib/src/canvas/line_painter.dart b/charts_flutter/lib/src/canvas/line_painter.dart index a5dff6567..bbeb9f386 100644 --- a/charts_flutter/lib/src/canvas/line_painter.dart +++ b/charts_flutter/lib/src/canvas/line_painter.dart @@ -90,7 +90,9 @@ class LinePainter { /// Draws smooth lines between each point. void _drawSmoothLine(Canvas canvas, Paint paint, List points) { - var path = MonotoneX.initPath(points); + final path = new Path() + ..moveTo(points.first.x.toDouble(), points.first.y.toDouble()); + MonotoneX.addCurve(path, points); canvas.drawPath(path, paint); } diff --git a/charts_flutter/lib/src/canvas/polygon_painter.dart b/charts_flutter/lib/src/canvas/polygon_painter.dart index 7b0ed34ba..4a5051b66 100644 --- a/charts_flutter/lib/src/canvas/polygon_painter.dart +++ b/charts_flutter/lib/src/canvas/polygon_painter.dart @@ -16,6 +16,7 @@ import 'dart:math' show Point, Rectangle; import 'package:flutter/material.dart'; import 'package:charts_common/common.dart' as common show Color; +import 'package:charts_flutter/src/util/monotonex.dart'; /// Draws a simple line. /// @@ -37,7 +38,9 @@ class PolygonPainter { Rectangle clipBounds, common.Color fill, common.Color stroke, - double strokeWidthPx}) { + double strokeWidthPx, + bool fillGradient, + bool smoothLine}) { if (points.isEmpty) { return; } @@ -75,18 +78,67 @@ class PolygonPainter { } if (fillColor != null) { - paint.color = fillColor; paint.style = PaintingStyle.fill; + + if (fillGradient == true) { + Rect rect = Rect.fromLTWH( + clipBounds.left.toDouble(), + clipBounds.top.toDouble(), + clipBounds.width.toDouble(), + clipBounds.height.toDouble()); + paint.shader = LinearGradient( + colors: [ + Color.fromARGB( + 60, + fillColor.red, + fillColor.green, + fillColor.blue, + ), + Color.fromARGB( + 10, + fillColor.red, + fillColor.green, + fillColor.blue, + ) + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ).createShader(rect); + } else { + paint.color = fillColor; + } } - final path = new Path() - ..moveTo(points.first.x.toDouble(), points.first.y.toDouble()); + final path = new Path(); + if (smoothLine) { + if (points[0].y == points[1].y && points[1].x == points[2].x) { + path.moveTo(points.last.x.toDouble(), points.last.y.toDouble()); + path.lineTo(points[0].x.toDouble(), points[0].y.toDouble()); + path.lineTo(points[1].x.toDouble(), points[1].y.toDouble()); + path.lineTo(points[2].x.toDouble(), points[2].y.toDouble()); + MonotoneX.addCurve(path,points.sublist(2)); + } else { + path.moveTo(points.last.x.toDouble(), points.last.y.toDouble()); + path.lineTo(points[0].x.toDouble(), points[0].y.toDouble()); + + MonotoneX.addCurve(path, + points.sublist(0, points.length ~/ 2).reversed.toList(), true); + path.lineTo(points[points.length ~/ 2].x, + points[points.length ~/ 2].y.toDouble()); + + MonotoneX.addCurve(path, points.sublist(points.length ~/ 2)); - for (var point in points) { - path.lineTo(point.x.toDouble(), point.y.toDouble()); + path.lineTo(points.last.x.toDouble(), points.last.y.toDouble()); + } + } else { + path.moveTo(points.first.x.toDouble(), points.first.y.toDouble()); + for (var point in points) { + path.lineTo(point.x.toDouble(), point.y.toDouble()); + } } canvas.drawPath(path, paint); + paint.shader = null; } if (clipBounds != null) { diff --git a/charts_flutter/lib/src/chart_canvas.dart b/charts_flutter/lib/src/chart_canvas.dart index 44e5de7c9..a5aeede10 100644 --- a/charts_flutter/lib/src/chart_canvas.dart +++ b/charts_flutter/lib/src/chart_canvas.dart @@ -116,7 +116,9 @@ class ChartCanvas implements common.ChartCanvas { Rectangle clipBounds, common.Color fill, common.Color stroke, - double strokeWidthPx}) { + double strokeWidthPx, + bool fillGradient, + bool smoothLine = false }) { _polygonPainter ??= new PolygonPainter(); _polygonPainter.draw( canvas: canvas, @@ -125,7 +127,9 @@ class ChartCanvas implements common.ChartCanvas { clipBounds: clipBounds, fill: fill, stroke: stroke, - strokeWidthPx: strokeWidthPx); + strokeWidthPx: strokeWidthPx, + fillGradient: fillGradient, + smoothLine: smoothLine); } @override @@ -150,6 +154,30 @@ class ChartCanvas implements common.ChartCanvas { case common.FillPatternType.forwardHatch: _drawForwardHatchPattern(myBounds, canvas, fill: fill); break; + case common.FillPatternType.gradient: + _paint.style = PaintingStyle.fill; + _paint.shader = LinearGradient( + colors: [ + Color.fromARGB( + 255, + fill.r, + fill.g, + fill.b, + ), + Color.fromARGB( + 127, + fill.r, + fill.g, + fill.b, + ) + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ).createShader(_getRect(myBounds)); + + canvas.drawRect(_getRect(myBounds), _paint); + _paint.shader = null; + break; case common.FillPatternType.solid: default: diff --git a/charts_flutter/lib/src/util/monotonex.dart b/charts_flutter/lib/src/util/monotonex.dart index 88603177f..5b8181e2c 100644 --- a/charts_flutter/lib/src/util/monotonex.dart +++ b/charts_flutter/lib/src/util/monotonex.dart @@ -36,13 +36,16 @@ class MonotoneX { return path; } - static Path initPath(List points) { + static Path addCurve(Path path, List points, [bool reversed = false]) { var targetPoints = List(); targetPoints.addAll(points); targetPoints.add(Point( points[points.length - 1].x * 2, points[points.length - 1].y * 2)); double x0, y0, x1, y1, t0; - var path = Path(); + if (path == null) { + path = Path(); + } + List> arr = []; for (int i = 0; i < targetPoints.length; i++) { double t1; double x = targetPoints[i].x; @@ -50,17 +53,16 @@ class MonotoneX { if (x == x1 && y == y1) continue; switch (i) { case 0: - path.moveTo(x, y); break; case 1: break; case 2: t1 = _slope3(x0, y0, x1, y1, x, y); - _point(path, x0, y0, x1, y1, _slope2(x0, y0, x1, y1, t1), t1); + arr.add([x0, y0, x1, y1, _slope2(x0, y0, x1, y1, t1), t1]); break; default: t1 = _slope3(x0, y0, x1, y1, x, y); - _point(path, x0, y0, x1, y1, t0, t1); + arr.add([x0, y0, x1, y1, t0, t1]); } x0 = x1; y0 = y1; @@ -68,6 +70,15 @@ class MonotoneX { y1 = y; t0 = t1; } + if (reversed) { + arr.reversed.forEach((f){ + _point(path, f[2], f[3], f[0], f[1], f[5], f[4]); + }); + } else { + arr.forEach((f){ + _point(path, f[0], f[1], f[2], f[3], f[4], f[5]); + }); + } return path; } }