From accff2250565deae29dd38da7b53627b2051ef8b Mon Sep 17 00:00:00 2001 From: Bernardo Belchior Date: Mon, 18 Apr 2022 15:33:32 +0100 Subject: [PATCH] improv: add custom modifier example --- example/lib/pages/polygon_custom.dart | 104 ++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/example/lib/pages/polygon_custom.dart b/example/lib/pages/polygon_custom.dart index 56458e8f..77c8a5db 100644 --- a/example/lib/pages/polygon_custom.dart +++ b/example/lib/pages/polygon_custom.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:graphic/graphic.dart'; @@ -638,6 +640,54 @@ class PolygonCustomPage extends StatelessWidget { ], ), ), + Container( + child: const Text( + 'Custom Modifier', + style: TextStyle(fontSize: 20), + ), + padding: const EdgeInsets.fromLTRB(20, 40, 20, 5), + ), + Container( + child: const Text( + '- With dodge and size modifier that scales the interval element width to fit within its band', + ), + padding: const EdgeInsets.fromLTRB(10, 5, 10, 0), + alignment: Alignment.centerLeft, + ), + Container( + margin: const EdgeInsets.only(top: 10), + width: 350, + height: 300, + child: Chart( + padding: (_) => const EdgeInsets.fromLTRB(40, 5, 10, 40), + data: adjustData, + variables: { + 'index': Variable( + accessor: (Map map) => map['index'].toString(), + ), + 'type': Variable( + accessor: (Map map) => map['type'] as String, + ), + 'value': Variable( + accessor: (Map map) => map['value'] as num, + ), + }, + elements: [ + IntervalElement( + position: + Varset('index') * Varset('value') / Varset('type'), + color: ColorAttr( + variable: 'type', values: Defaults.colors10), + size: SizeAttr(value: 2), + modifiers: [DodgeSizeModifier()], + ) + ], + axes: [ + Defaults.horizontalAxis..tickLine = TickLine(), + Defaults.verticalAxis, + ], + ), + ), Container( child: const Text( 'Candlestick Chart', @@ -734,3 +784,57 @@ class PolygonCustomPage extends StatelessWidget { ); } } + +const _kBaseGroupPaddingHorizontal = 32.0; +const _kMinBarSize = 4.0; + +/// Changes the position of elements while also updating their width to match +/// the number of elements in a single band. Useful for bar charts when the +/// width of the bars can be dynamic. +@immutable +class DodgeSizeModifier extends Modifier { + @override + void modify(AesGroups groups, Map> scales, + AlgForm form, CoordConv coord, Offset origin) { + final xField = form.first[0]; + final band = (scales[xField]! as DiscreteScaleConv).band; + + final ratio = 1 / groups.length; + final numGroups = groups.length; + final groupHorizontalPadding = _kBaseGroupPaddingHorizontal / numGroups; + final invertedGroupPaddingHorizontal = + coord.invertDistance(groupHorizontalPadding, Dim.x); + + final effectiveBand = band - 2 * invertedGroupPaddingHorizontal; + + final maxWidth = coord.convert(const Offset(1, 0)).dx; + final maxWidthInBand = effectiveBand * maxWidth; + final maxWidthPerAes = maxWidthInBand / numGroups; + final barHorizontalPadding = groupHorizontalPadding / 2; + final size = max(maxWidthPerAes - barHorizontalPadding, _kMinBarSize); + + final bias = ratio * effectiveBand; + + // Negatively shift half of the total bias. + var accumulated = -bias * (numGroups + 1) / 2; + + for (final group in groups) { + for (final aes in group) { + final oldPosition = aes.position; + aes.position = oldPosition + .map( + (point) => Offset(point.dx + accumulated + bias, point.dy), + ) + .toList(); + + aes.size = size; + } + accumulated += bias; + } + } + + @override + bool equalTo(Object other) { + return super == other; + } +}