1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
/// Where to look in the complex plane — pure Dart, shared by every backend.
///
/// Lives apart from the bridge so the web build never drags `dart:ffi` in
/// through a geometry type.
library;
/// Where to look in the complex plane, and how hard to look.
class FractalView {
const FractalView({
required this.width,
required this.height,
this.centerX = -0.5,
this.centerY = 0.0,
this.scale = 3.0,
this.maxIter = 500,
});
final int width;
final int height;
/// Centre of the viewport in the complex plane.
final double centerX;
final double centerY;
/// Width of the viewport in the complex plane. Smaller = deeper zoom.
final double scale;
/// Escape-time iteration cap. The cost of a frame scales with this.
final int maxIter;
/// Returns this view zoomed by [factor] about a point given in *fractional*
/// viewport coordinates — (0,0) top-left, (1,1) bottom-right.
///
/// The point under the cursor stays under the cursor: that is the whole
/// contract, and it is what makes click-to-zoom feel anchored rather than
/// drifting. `factor < 1` zooms in.
FractalView zoomedAt(double fx, double fy, double factor) {
final aspect = width / height;
final ox = fx - 0.5;
final oy = fy - 0.5;
// The complex-plane point currently under (fx, fy).
final targetX = centerX + ox * scale * aspect;
final targetY = centerY + oy * scale;
final newScale = scale * factor;
return copyWith(
scale: newScale,
centerX: targetX - ox * newScale * aspect,
centerY: targetY - oy * newScale,
);
}
/// Returns this view dragged by a delta given as a *fraction* of the
/// viewport — dragging right by half the width is `fdx = 0.5`.
///
/// The image follows the finger, so the centre moves the opposite way.
FractalView pannedBy(double fdx, double fdy) {
final aspect = width / height;
return copyWith(
centerX: centerX - fdx * scale * aspect,
centerY: centerY - fdy * scale,
);
}
FractalView copyWith({
int? width,
int? height,
double? centerX,
double? centerY,
double? scale,
int? maxIter,
}) =>
FractalView(
width: width ?? this.width,
height: height ?? this.height,
centerX: centerX ?? this.centerX,
centerY: centerY ?? this.centerY,
scale: scale ?? this.scale,
maxIter: maxIter ?? this.maxIter,
);
}
|