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