Skip to content

Commit b2b3e76

Browse files
committed
v2.0.3
1 parent f9a9490 commit b2b3e76

17 files changed

+251
-138
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 2.0.3
2+
3+
**2023-04-12**
4+
5+
- Fix the Modifier error when updating: https://github.com/entronad/graphic/issues/206
6+
- Rename enum property `MarkEntrance.alpha` to `MarkEntrance.opacity`.
7+
- Fix polygon shape when there is only one datum or one value in a dim: https://github.com/entronad/graphic/issues/166
8+
19
## 2.0.2
210

311
**2023-04-06**

DEVLOG.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -502,4 +502,12 @@ tagEncode 先不要搞默认值了,变量情况复杂不要弄巧成拙,只
502502

503503
内部工具函数,尽量保留位置参数或必选参数,哪怕填null,减少错误
504504

505-
dart似乎有这样一个规定:当import一般lib(import “library: ... 或文件)时,类名不能冲突。但一个是基础库(例如ui)一个是一般lib时没关系,以一般lib优先。ui.Gradient 和 painting.Gradient 是这种情况,ui.Scene 和 graphic.Scene 是这种情况。原则:凡是和官方库(ui,flutter,painting)冲突的命名都要避免,因此这里也要避免。主要采用增加前缀的方法。因此 View 改为 ChartView,Scene改为MarkScene。至于引擎里的Mark一词是否准确,现在先不想了,这里如果要变体系要变,后面3.0再说吧,这里就这样
505+
dart似乎有这样一个规定:当import一般lib(import “library: ... 或文件)时,类名不能冲突。但一个是基础库(例如ui)一个是一般lib时没关系,以一般lib优先。ui.Gradient 和 painting.Gradient 是这种情况,ui.Scene 和 graphic.Scene 是这种情况。原则:凡是和官方库(ui,flutter,painting)冲突的命名都要避免,因此这里也要避免。主要采用增加前缀的方法。因此 View 改为 ChartView,Scene改为MarkScene。至于引擎里的Mark一词是否准确,现在先不想了,这里如果要变体系要变,后面3.0再说吧,这里就这样
506+
507+
Tuple value 应该还是不接受null:1.这是dart null-safety 倡导的原则,2.已经有了accessor这个处理环节
508+
509+
现在这种修改式的 Modifier 实现是不行的,非幂等,https://github.com/entronad/graphic/issues/206 就是非幂等引起的。整个dataflow op的原则必须是函数式的,幂等的。其它倒不是原则性问题,要求幂等正是为了避免其它问题。
510+
511+
类方法的参数,与类成员名字冲突时,将类方法的参数用缩写,特别是含义明确的 withXX,参考Color.withAlpha
512+
513+
至少目前,参数定义的原则涉及多个维度的还是以list定义为主,而不是分开 aaX, aaY这样,比如 CrosshairCuide, list 成员可以为null,以达到一个设置一个不设置的效果,不要搞单独元素指代两个相同这种,取数用 [0] [1]

example/lib/main.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final routes = {
2020
'/examples/Animation': (context) => const AnimationPage(),
2121
'/examples/Bigdata': (context) => BigdataPage(),
2222
'/examples/Echarts': (context) => EchartsPage(),
23-
'/examples/Debug': (context) => const DebugPage(),
23+
'/examples/Debug': (context) => DebugPage(),
2424
};
2525

2626
class MyApp extends StatelessWidget {

example/lib/pages/animation.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,13 @@ class AnimationPageState extends State<AnimationPage> {
373373
Defaults.colors10.first.withAlpha(10),
374374
])),
375375
transition: Transition(duration: Duration(seconds: 2)),
376-
entrance: {MarkEntrance.x, MarkEntrance.y, MarkEntrance.alpha},
376+
entrance: {MarkEntrance.x, MarkEntrance.y, MarkEntrance.opacity},
377377
),
378378
LineMark(
379379
shape: ShapeEncode(value: BasicLineShape(smooth: true)),
380380
size: SizeEncode(value: 0.5),
381381
transition: Transition(duration: Duration(seconds: 2)),
382-
entrance: {MarkEntrance.x, MarkEntrance.y, MarkEntrance.alpha},
382+
entrance: {MarkEntrance.x, MarkEntrance.y, MarkEntrance.opacity},
383383
),
384384
],
385385
axes: [

example/lib/pages/debug.dart

+85-76
Original file line numberDiff line numberDiff line change
@@ -3,95 +3,104 @@ import 'package:graphic/graphic.dart';
33

44
import '../data.dart';
55

6-
const adjustData = [
7-
{"type": "Email", "index": 0, "value": 120},
8-
{"type": "Email", "index": 1, "value": 132},
9-
{"type": "Email", "index": 2, "value": 101},
10-
{"type": "Email", "index": 3, "value": 134},
11-
{"type": "Email", "index": 4, "value": 90},
12-
{"type": "Email", "index": 5, "value": 230},
13-
{"type": "Email", "index": 6, "value": 210},
14-
{"type": "Affiliate", "index": 0, "value": 220},
15-
{"type": "Affiliate", "index": 1, "value": 182},
16-
{"type": "Affiliate", "index": 2, "value": 191},
17-
{"type": "Affiliate", "index": 3, "value": 234},
18-
{"type": "Affiliate", "index": 4, "value": 290},
19-
{"type": "Affiliate", "index": 5, "value": 330},
20-
{"type": "Affiliate", "index": 6, "value": 310},
21-
{"type": "Video", "index": 0, "value": 150},
22-
{"type": "Video", "index": 1, "value": 232},
23-
{"type": "Video", "index": 2, "value": 201},
24-
{"type": "Video", "index": 3, "value": 154},
25-
{"type": "Video", "index": 4, "value": 190},
26-
{"type": "Video", "index": 5, "value": 330},
27-
{"type": "Video", "index": 6, "value": 410},
28-
{"type": "Direct", "index": 0, "value": 320},
29-
{"type": "Direct", "index": 1, "value": 332},
30-
{"type": "Direct", "index": 2, "value": 301},
31-
{"type": "Direct", "index": 3, "value": 334},
32-
{"type": "Direct", "index": 4, "value": 390},
33-
{"type": "Direct", "index": 5, "value": 330},
34-
{"type": "Direct", "index": 6, "value": 320},
35-
{"type": "Search", "index": 0, "value1": 320},
36-
{"type": "Search", "index": 1, "value1": 432},
37-
{"type": "Search", "index": 2, "value1": 401},
38-
{"type": "Search", "index": 3, "value1": 434},
39-
{"type": "Search", "index": 4, "value1": 390},
40-
{"type": "Search", "index": 5, "value1": 430},
41-
{"type": "Search", "index": 6, "value1": 420},
42-
];
43-
446
class DebugPage extends StatelessWidget {
45-
const DebugPage({Key? key}) : super(key: key);
7+
DebugPage({Key? key}) : super(key: key);
8+
9+
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
4610

4711
@override
4812
Widget build(BuildContext context) {
4913
return Scaffold(
50-
body: Center(
51-
child: Container(
52-
margin: const EdgeInsets.only(top: 10),
53-
width: 350,
54-
height: 300,
55-
child: Chart(
56-
rebuild: true,
57-
data: basicData,
58-
variables: {
59-
'genre': Variable(
60-
accessor: (Map map) => map['genre'] as String,
61-
),
62-
'sold': Variable(
63-
accessor: (Map map) => map['sold'] as num,
64-
),
65-
},
66-
marks: [
67-
IntervalMark(
68-
label: LabelEncode(
69-
encoder: (tuple) => Label(tuple['sold'].toString())),
70-
elevation: ElevationEncode(value: 0, updaters: {
71-
'tap': {true: (_) => 5}
72-
}),
73-
color: ColorEncode(value: Defaults.primaryColor, updaters: {
74-
'tap': {false: (color) => color.withAlpha(100)}
75-
}),
76-
selected: {
77-
'tap': {0}
78-
},
79-
)
80-
],
81-
axes: [
82-
Defaults.horizontalAxis,
83-
Defaults.verticalAxis,
14+
key: _scaffoldKey,
15+
appBar: AppBar(
16+
title: const Text('Rectangle Interval Mark'),
17+
),
18+
backgroundColor: Colors.white,
19+
body: SingleChildScrollView(
20+
child: Center(
21+
child: Column(
22+
children: <Widget>[
23+
24+
buildChart('single point', [Data(0, 5, 1)]),
25+
buildChart('2 points in the same sector at different radiuses',
26+
[Data(0, 5, 1), Data(0, 6, 2)]),
27+
buildChart('2 points with different sector and radius',
28+
[Data(0, 5, 1), Data(4, 6, 2)]),
29+
buildChart('3 points with different everything',
30+
[Data(0, 5, 1), Data(1, 6, 2), Data(2, 7, 3)]),
31+
buildChart('3 points with a duplicate',
32+
[Data(0, 5, 1), Data(1, 6, 2), Data(1, 6, 2)]),
33+
34+
8435
],
85-
selections: {'tap': PointSelection(dim: Dim.x)},
86-
tooltip: TooltipGuide(),
87-
crosshair: CrosshairGuide(),
8836
),
8937
),
9038
),
9139
);
9240
}
9341
}
42+
Widget buildChart(String name, List<Data> data) => Row(
43+
mainAxisSize: MainAxisSize.min,
44+
children: [
45+
Container(width: 300, child: Text(name)),
46+
SizedBox(width: 100),
47+
Container(
48+
width: 150,
49+
height: 150,
50+
child: Chart(
51+
data: data,
52+
variables: {
53+
'sector': Variable(
54+
accessor: (Data d) => d.sector.toString(),
55+
scale: OrdinalScale(
56+
values: List<int>.generate(10, (i) => i++)
57+
.map((s) => s.toString())
58+
.toList(),
59+
),
60+
),
61+
'radius': Variable(
62+
accessor: (Data d) => d.radius,
63+
scale: LinearScale(
64+
min: 0,
65+
max: 10,
66+
),
67+
),
68+
'value': Variable(
69+
accessor: (Data d) => d.value,
70+
scale: LinearScale(
71+
min: 0,
72+
max: 10,
73+
),
74+
),
75+
},
76+
marks: [
77+
PolygonMark(
78+
shape: ShapeEncode(value: HeatmapShape(sector: true, tileCounts: [10, 10])),
79+
color: ColorEncode(
80+
variable: 'value',
81+
values: [Colors.blue, Colors.red],
82+
),
83+
)
84+
],
85+
coord: PolarCoord(),
86+
axes: [
87+
Defaults.circularAxis,
88+
Defaults.radialAxis,
89+
],
90+
),
91+
),
92+
],
93+
94+
);
95+
9496

97+
class Data {
98+
final int sector;
99+
final double radius;
100+
final double value;
101+
102+
Data(this.sector, this.radius, this.value);
103+
}
95104
// class DebugPage extends StatefulWidget {
96105
// const DebugPage({Key? key}) : super(key: key);
97106

example/lib/pages/interval.dart

+3
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ class IntervalPage extends StatelessWidget {
276276
modifiers: [StackModifier()],
277277
)
278278
],
279+
coord: RectCoord(
280+
horizontalRangeUpdater: Defaults.horizontalRangeEvent,
281+
),
279282
axes: [
280283
Defaults.horizontalAxis,
281284
Defaults.verticalAxis,

example/lib/pages/polygon_custom.dart

+25-5
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,9 @@ class PolygonCustomPage extends StatelessWidget {
548548
modifiers: [DodgeModifier(ratio: 0.1)],
549549
)
550550
],
551+
coord: RectCoord(
552+
horizontalRangeUpdater: Defaults.horizontalRangeEvent,
553+
),
551554
axes: [
552555
Defaults.horizontalAxis..tickLine = TickLine(),
553556
Defaults.verticalAxis,
@@ -695,6 +698,9 @@ class PolygonCustomPage extends StatelessWidget {
695698
modifiers: [DodgeSizeModifier()],
696699
)
697700
],
701+
coord: RectCoord(
702+
horizontalRangeUpdater: Defaults.horizontalRangeEvent,
703+
),
698704
axes: [
699705
Defaults.horizontalAxis..tickLine = TickLine(),
700706
Defaults.verticalAxis,
@@ -807,7 +813,7 @@ const _kMinBarSize = 4.0;
807813
@immutable
808814
class DodgeSizeModifier extends Modifier {
809815
@override
810-
void modify(
816+
AttributesGroups modify(
811817
AttributesGroups groups,
812818
Map<String, ScaleConv<dynamic, num>> scales,
813819
AlgForm form,
@@ -836,19 +842,33 @@ class DodgeSizeModifier extends Modifier {
836842
// Negatively shift half of the total bias.
837843
var accumulated = -bias * (numGroups + 1) / 2;
838844

845+
final AttributesGroups rst = [];
839846
for (final group in groups) {
847+
final groupRst = <Attributes>[];
840848
for (final attributes in group) {
841849
final oldPosition = attributes.position;
842-
attributes.position = oldPosition
850+
851+
groupRst.add(Attributes(
852+
index: attributes.index,
853+
tag: attributes.tag,
854+
position: oldPosition
843855
.map(
844856
(point) => Offset(point.dx + accumulated + bias, point.dy),
845857
)
846-
.toList();
847-
848-
attributes.size = size;
858+
.toList(),
859+
shape: attributes.shape,
860+
color: attributes.color,
861+
gradient: attributes.gradient,
862+
elevation: attributes.elevation,
863+
label: attributes.label,
864+
size: size,
865+
));
849866
}
867+
rst.add(groupRst);
850868
accumulated += bias;
851869
}
870+
871+
return rst;
852872
}
853873

854874
@override

lib/src/dataflow/tuple.dart

+27-8
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:graphic/src/common/label.dart';
33
import 'package:graphic/src/coord/coord.dart';
44
import 'package:graphic/src/graffiti/element/label.dart';
55
import 'package:graphic/src/mark/mark.dart';
6+
import 'package:graphic/src/mark/modifier/modifier.dart';
67
import 'package:graphic/src/scale/scale.dart';
78
import 'package:graphic/src/shape/shape.dart';
89
import 'package:graphic/src/util/assert.dart';
@@ -53,29 +54,47 @@ class Attributes {
5354
/// The count of points is determined by the geometry mark type. The values
5455
/// of each point dimension is scaled and normalized value of `[0, 1]`. the position
5556
/// points can be converted to canvas points by [CoordConv].
56-
List<Offset> position;
57+
final List<Offset> position;
5758

5859
/// The shape of the tuple.
59-
Shape shape;
60+
final Shape shape;
6061

6162
/// The color of the tuple.
62-
Color? color;
63+
final Color? color;
6364

6465
/// The gradient of the tuple.
65-
Gradient? gradient;
66+
final Gradient? gradient;
6667

6768
/// The shadow elevation of the tuple.
68-
double? elevation;
69+
final double? elevation;
6970

7071
/// The label of the tuple.
71-
Label? label;
72+
final Label? label;
7273

7374
/// The size of the tuple.
74-
double? size;
75+
final double? size;
7576

7677
/// The represent point of [position] points.
7778
Offset get representPoint => shape.representPoint(position);
7879

80+
/// Returns a new attributes that matches this attributes with the position replaced
81+
/// with [p].
82+
///
83+
/// This method is mainly used for [Modifier]s.
84+
Attributes withPosition(List<Offset> p) => Attributes(
85+
index: index,
86+
tag: tag,
87+
position: p,
88+
shape: shape,
89+
color: color,
90+
gradient: gradient,
91+
elevation: elevation,
92+
label: label,
93+
size: size,
94+
);
95+
96+
/// Returns the original state of an item attributes in animation according to
97+
/// [entrance] type.
7998
Attributes deflate(Set<MarkEntrance> entrance) {
8099
var rst = this;
81100

@@ -121,7 +140,7 @@ class Attributes {
121140
);
122141
}
123142

124-
if (entrance.contains(MarkEntrance.alpha)) {
143+
if (entrance.contains(MarkEntrance.opacity)) {
125144
final labelColor = rst.label?.style.textStyle?.color;
126145
final labelRst = labelColor == null
127146
? rst.label

0 commit comments

Comments
 (0)