Skip to content

Commit

Permalink
improv: add support for custom modifiers
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardobelchior committed Apr 21, 2022
1 parent 521b064 commit 991d9e6
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 107 deletions.
137 changes: 137 additions & 0 deletions example/lib/pages/polygon_custom.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:graphic/graphic.dart';

Expand Down Expand Up @@ -637,6 +639,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',
Expand Down Expand Up @@ -733,3 +783,90 @@ class PolygonCustomPage extends StatelessWidget {
);
}
}

/// 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
bool operator ==(Object other) => other is DodgeSizeModifier;

@override
DodgeSizeGeomModifierOp toGeomModifierOp(ToGeomModifierOpParams params) {
return DodgeSizeGeomModifierOp(<String, dynamic>{
'form': params.form,
'scales': params.scales,
'groups': params.groups,
'coord': params.coord,
});
}
}

const _kBaseGroupPaddingHorizontal = 32.0;
const _kXAxis = 1;
const _kMinBarSize = 4.0;

/// The dodge geometry modifier.
class DodgeSizeGeomModifier extends GeomModifier {
DodgeSizeGeomModifier(this.band, this.coord);

final CoordConv coord;

/// The band ratio of each value. It represents the width of the interval the
/// [Aes]es have to position themselves within.
/// Its range is ]0, 1].
final double band;

@override
void modify(AesGroups value) {
final ratio = 1 / value.length;
final numGroups = value.length;
final groupHorizontalPadding = _kBaseGroupPaddingHorizontal / numGroups;
final invertedGroupPaddingHorizontal =
coord.invertDistance(groupHorizontalPadding, _kXAxis);

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 value) {
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;
}
}
}

class DodgeSizeGeomModifierOp extends GeomModifierOp<DodgeSizeGeomModifier> {
DodgeSizeGeomModifierOp(Map<String, dynamic> params) : super(params);

@override
DodgeSizeGeomModifier evaluate() {
final form = params['form'] as AlgForm;
final scales = params['scales'] as Map<String, ScaleConv>;
final coord = params['coord'] as CoordConv;

final xField = form.first[0];
final band = (scales[xField]! as DiscreteScaleConv).band;

return DodgeSizeGeomModifier(band, coord);
}
}
128 changes: 58 additions & 70 deletions lib/graphic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,39 +113,7 @@
/// [Crosshair] specified by [Chart.crosshair], and [Annotation]s specified by [Chart.annotations],
library graphic;

export 'src/chart/chart.dart' show Chart;
export 'src/chart/size.dart' show ResizeSignal;

export 'src/data/data_set.dart' show ChangeDataSignal;

export 'src/variable/variable.dart' show Variable;
export 'src/variable/transform/transform.dart' show VariableTransform;
export 'src/variable/transform/filter.dart' show Filter;
export 'src/variable/transform/map.dart' show MapTrans;
export 'src/variable/transform/proportion.dart' show Proportion;
export 'src/variable/transform/sort.dart' show Sort;

export 'src/scale/scale.dart' show Scale;
export 'src/scale/discrete.dart' show DiscreteScale;
export 'src/scale/continuous.dart' show ContinuousScale;
export 'src/scale/linear.dart' show LinearScale;
export 'src/scale/ordinal.dart' show OrdinalScale;
export 'src/scale/time.dart' show TimeScale;

export 'src/geom/element.dart' show GeomElement;
export 'src/geom/function.dart' show FunctionElement;
export 'src/geom/partition.dart' show PartitionElement;
export 'src/geom/area.dart' show AreaElement;
export 'src/geom/custom.dart' show CustomElement;
export 'src/geom/interval.dart' show IntervalElement;
export 'src/geom/line.dart' show LineElement;
export 'src/geom/point.dart' show PointElement;
export 'src/geom/polygon.dart' show PolygonElement;
export 'src/geom/modifier/modifier.dart' show Modifier;
export 'src/geom/modifier/dodge.dart' show DodgeModifier;
export 'src/geom/modifier/stack.dart' show StackModifier;
export 'src/geom/modifier/jitter.dart' show JitterModifier;
export 'src/geom/modifier/symmetric.dart' show SymmetricModifier;
export 'package:path_drawing/path_drawing.dart' show DashOffset;

export 'src/aes/aes.dart' show Attr;
export 'src/aes/channel.dart' show ChannelAttr;
Expand All @@ -155,53 +123,73 @@ export 'src/aes/gradient.dart' show GradientAttr;
export 'src/aes/label.dart' show LabelAttr;
export 'src/aes/shape.dart' show ShapeAttr;
export 'src/aes/size.dart' show SizeAttr;

export 'src/algebra/varset.dart' show Varset, AlgForm, AlgTerm;

export 'src/shape/shape.dart' show Shape;
export 'src/shape/function.dart' show FunctionShape;
export 'src/shape/partition.dart' show PartitionShape;
export 'src/shape/area.dart' show AreaShape, BasicAreaShape;
export 'src/shape/custom.dart' show CustomShape, CandlestickShape;
export 'src/shape/interval.dart' show IntervalShape, RectShape, FunnelShape;
export 'src/shape/line.dart' show LineShape, BasicLineShape;
export 'src/shape/point.dart' show PointShape, CircleShape, SquareShape;
export 'src/shape/polygon.dart' show PolygonShape, HeatmapShape;
export 'src/shape/util/render_basic_item.dart' show renderBasicItem;

export 'src/graffiti/figure.dart'
show Figure, PathFigure, ShadowFigure, TextFigure, RotatedTextFigure;

export 'src/chart/chart.dart' show Chart;
export 'src/chart/size.dart' show ResizeSignal;
export 'src/common/defaults.dart' show Defaults;
export 'src/common/dim.dart' show Dim;
export 'src/common/label.dart'
show Label, LabelStyle, renderLabel, getPaintPoint;
export 'src/common/styles.dart' show StrokeStyle;
export 'src/coord/coord.dart' show Coord, CoordConv;
export 'src/coord/polar.dart' show PolarCoord, PolarCoordConv;
export 'src/coord/rect.dart' show RectCoord, RectCoordConv;

export 'src/guide/axis/axis.dart'
show TickLine, TickLineMapper, LabelMapper, GridMapper, AxisGuide;
export 'src/guide/interaction/tooltip.dart' show TooltipGuide, TooltipRenderer;
export 'src/guide/interaction/crosshair.dart' show CrosshairGuide;
export 'src/data/data_set.dart' show ChangeDataSignal;
export 'src/dataflow/tuple.dart' show Tuple, Aes, AesGroups;
export 'src/geom/area.dart' show AreaElement;
export 'src/geom/custom.dart' show CustomElement;
export 'src/geom/element.dart' show GeomElement;
export 'src/geom/function.dart' show FunctionElement;
export 'src/geom/interval.dart' show IntervalElement;
export 'src/geom/line.dart' show LineElement;
export 'src/geom/modifier/dodge.dart' show DodgeModifier;
export 'src/geom/modifier/jitter.dart' show JitterModifier;
export 'src/geom/modifier/modifier.dart'
show Modifier, GeomModifier, GeomModifierOp, ToGeomModifierOpParams;
export 'src/geom/modifier/stack.dart' show StackModifier;
export 'src/geom/modifier/symmetric.dart' show SymmetricModifier;
export 'src/geom/partition.dart' show PartitionElement;
export 'src/geom/point.dart' show PointElement;
export 'src/geom/polygon.dart' show PolygonElement;
export 'src/graffiti/figure.dart'
show Figure, PathFigure, ShadowFigure, TextFigure, RotatedTextFigure;
export 'src/guide/annotation/annotation.dart' show Annotation;
export 'src/guide/annotation/custom.dart' show CustomAnnotation;
export 'src/guide/annotation/figure.dart' show FigureAnnotation;
export 'src/guide/annotation/line.dart' show LineAnnotation;
export 'src/guide/annotation/region.dart' show RegionAnnotation;
export 'src/guide/annotation/mark.dart' show MarkAnnotation;
export 'src/guide/annotation/region.dart' show RegionAnnotation;
export 'src/guide/annotation/tag.dart' show TagAnnotation;
export 'src/guide/annotation/custom.dart' show CustomAnnotation;

export 'src/interaction/signal.dart' show Signal, SignalType, SignalUpdater;
export 'src/guide/axis/axis.dart'
show TickLine, TickLineMapper, LabelMapper, GridMapper, AxisGuide;
export 'src/guide/interaction/crosshair.dart' show CrosshairGuide;
export 'src/guide/interaction/tooltip.dart' show TooltipGuide, TooltipRenderer;
export 'src/interaction/gesture.dart' show GestureType, Gesture, GestureSignal;
export 'src/interaction/selection/selection.dart'
show Selection, Selected, SelectionUpdater;
export 'src/interaction/selection/interval.dart' show IntervalSelection;
export 'src/interaction/selection/point.dart' show PointSelection;

export 'src/common/styles.dart' show StrokeStyle;
export 'src/common/label.dart'
show Label, LabelStyle, renderLabel, getPaintPoint;
export 'src/common/defaults.dart' show Defaults;
export 'src/common/dim.dart' show Dim;

export 'src/dataflow/tuple.dart' show Tuple, Aes;

export 'src/interaction/selection/selection.dart'
show Selection, Selected, SelectionUpdater;
export 'src/interaction/signal.dart' show Signal, SignalType, SignalUpdater;
export 'src/scale/continuous.dart' show ContinuousScale, ContinuousScaleConv;
export 'src/scale/discrete.dart' show DiscreteScale, DiscreteScaleConv;
export 'src/scale/linear.dart' show LinearScale, LinearScaleConv;
export 'src/scale/ordinal.dart' show OrdinalScale, OrdinalScaleConv;
export 'src/scale/scale.dart' show Scale, ScaleConv;
export 'src/scale/time.dart' show TimeScale, TimeScaleConv;
export 'src/shape/area.dart' show AreaShape, BasicAreaShape;
export 'src/shape/custom.dart' show CustomShape, CandlestickShape;
export 'src/shape/function.dart' show FunctionShape;
export 'src/shape/interval.dart' show IntervalShape, RectShape, FunnelShape;
export 'src/shape/line.dart' show LineShape, BasicLineShape;
export 'src/shape/partition.dart' show PartitionShape;
export 'src/shape/point.dart' show PointShape, CircleShape, SquareShape;
export 'src/shape/polygon.dart' show PolygonShape, HeatmapShape;
export 'src/shape/shape.dart' show Shape;
export 'src/shape/util/render_basic_item.dart' show renderBasicItem;
export 'src/util/path.dart' show Paths;
export 'package:path_drawing/path_drawing.dart' show DashOffset;
export 'src/variable/transform/filter.dart' show Filter;
export 'src/variable/transform/map.dart' show MapTrans;
export 'src/variable/transform/proportion.dart' show Proportion;
export 'src/variable/transform/sort.dart' show Sort;
export 'src/variable/transform/transform.dart' show VariableTransform;
export 'src/variable/variable.dart' show Variable;
2 changes: 1 addition & 1 deletion lib/src/dataflow/tuple.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Aes {
final Label? label;

/// The size of the tuple.
final double? size;
double? size;

/// The represent point of [position] points.
Offset get representPoint => shape.representPoint(position);
Expand Down
13 changes: 12 additions & 1 deletion lib/src/geom/modifier/dodge.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:ui';

import 'package:graphic/src/algebra/varset.dart';
import 'package:graphic/src/dataflow/tuple.dart';
import 'package:graphic/src/scale/discrete.dart';
import 'package:graphic/src/scale/scale.dart';
import 'package:graphic/src/algebra/varset.dart';

import 'modifier.dart';

Expand Down Expand Up @@ -34,6 +34,17 @@ class DodgeModifier extends Modifier {
super == other &&
ratio == other.ratio &&
symmetric == other.symmetric;

@override
DodgeGeomModifierOp toGeomModifierOp(ToGeomModifierOpParams params) {
return DodgeGeomModifierOp({
'ratio': ratio,
'symmetric': symmetric ?? true,
'form': params.form,
'scales': params.scales,
'groups': params.groups,
});
}
}

/// The dodge geometry modifier.
Expand Down
13 changes: 11 additions & 2 deletions lib/src/geom/modifier/jitter.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'dart:ui';
import 'dart:math';
import 'dart:ui';

import 'package:graphic/src/algebra/varset.dart';
import 'package:graphic/src/dataflow/tuple.dart';
import 'package:graphic/src/scale/discrete.dart';
import 'package:graphic/src/scale/scale.dart';
import 'package:graphic/src/algebra/varset.dart';

import 'modifier.dart';

Expand All @@ -26,6 +26,15 @@ class JitterModifier extends Modifier {
@override
bool operator ==(Object other) =>
other is JitterModifier && super == other && ratio == other.ratio;

@override
JitterGeomModifierOp toGeomModifierOp(ToGeomModifierOpParams params) {
return JitterGeomModifierOp({
'ratio': ratio ?? 0.5,
'form': params.form,
'scales': params.scales,
});
}
}

/// The jitter geometry modifier.
Expand Down
Loading

0 comments on commit 991d9e6

Please sign in to comment.