grid.dart 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import 'dart:async';
  2. import 'dart:math';
  3. import 'package:flutter/material.dart';
  4. import 'package:grpc/grpc_web.dart';
  5. import 'package:cyclop/cyclop.dart';
  6. import 'src/color_ref.dart';
  7. import 'src/connection/ws.dart';
  8. import 'src/widget/square.dart';
  9. class TransformationsDemo extends StatefulWidget {
  10. const TransformationsDemo({Key? key}) : super(key: key);
  11. @override
  12. State<TransformationsDemo> createState() => _TransformationsDemoState();
  13. }
  14. class _TransformationsDemoState extends State<TransformationsDemo>
  15. with TickerProviderStateMixin {
  16. final GlobalKey _targetKey = GlobalKey();
  17. final Map<Point<int>, Widget> elements = <Point<int>, Widget>{};
  18. Set<Color> swatches = Colors.primaries.map((e) => Color(e.value)).toSet();
  19. final double minScale = 2;
  20. final ColorRef cc = ColorRef(Colors.blueGrey);
  21. final TransformationController _transformationController =
  22. TransformationController();
  23. Animation<Matrix4>? _animationReset;
  24. late AnimationController _controllerReset;
  25. Matrix4? _homeMatrix;
  26. final channel = GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080'));
  27. @override
  28. void initState() {
  29. super.initState();
  30. // WebRTCChannels().connect((x) {});
  31. }
  32. @override
  33. Widget build(BuildContext context) {
  34. // The scene is drawn by a CustomPaint, but user interaction is handled by
  35. // the InteractiveViewer parent widget.
  36. return Scaffold(
  37. backgroundColor: Theme.of(context).colorScheme.primary,
  38. // appBar: AppBar(
  39. // automaticallyImplyLeading: false,
  40. // title: const Text('title'),
  41. // ),
  42. body: Container(
  43. color: Colors.white,
  44. child: LayoutBuilder(
  45. builder: (context, constraints) {
  46. // Start the first render, start the scene centered in the viewport.
  47. if (_homeMatrix == null) {
  48. _homeMatrix = Matrix4.identity() * minScale
  49. ..translate(0, 0);
  50. _transformationController.value = _homeMatrix!;
  51. createComponentMap();
  52. }
  53. return ClipRect(
  54. child: MouseRegion(
  55. cursor: SystemMouseCursors.click,
  56. child: GestureDetector(
  57. behavior: HitTestBehavior.opaque,
  58. // onTapUp: _onTapUp,
  59. child: InteractiveViewer(
  60. constrained: false,
  61. key: _targetKey,
  62. transformationController: _transformationController,
  63. boundaryMargin: const EdgeInsets.all(0),
  64. minScale: minScale,
  65. maxScale: 30,
  66. // onInteractionStart: _onScaleStart,
  67. onInteractionUpdate: _onInteractionUpdate,
  68. onInteractionEnd: _onInteractionEnd,
  69. child: SizedBox(
  70. width: 256 * 90,
  71. height: 256 * 53,
  72. child: Stack(
  73. children: [
  74. getGrid(),
  75. ...elements.values.toList(),
  76. ],
  77. ),
  78. ),
  79. ),
  80. ),
  81. ),
  82. );
  83. },
  84. ),
  85. ),
  86. // persistentFooterButtons: [],
  87. floatingActionButton: ColorButton(
  88. darkMode: true,
  89. key: const Key('c2'),
  90. color: cc.getColor(),
  91. boxShape: BoxShape.circle,
  92. swatches: swatches,
  93. size: 64,
  94. config: const ColorPickerConfig(
  95. enableOpacity: false,
  96. enableLibrary: true,
  97. ),
  98. onColorChanged: (value) => setState(() {
  99. cc.setColor(value);
  100. }),
  101. onSwatchesChanged: (newSwatches) =>
  102. setState(() => swatches = newSwatches),
  103. ),
  104. );
  105. }
  106. Widget getGrid() {
  107. Widget grid = Container();
  108. double scale = _transformationController.value.clone().getMaxScaleOnAxis();
  109. if (scale > 10) {
  110. double opacity = 1.0;
  111. if (scale <= 15) {
  112. opacity = 1 - (15 - scale) / 5;
  113. }
  114. const oneGrid = GridPaper(
  115. color: Colors.black12,
  116. interval: 256,
  117. divisions: 16,
  118. subdivisions: 16,
  119. child: SizedBox(
  120. width: 256,
  121. height: 256,
  122. ));
  123. grid = Opacity(
  124. opacity: opacity,
  125. child: Stack(
  126. children: elements.keys
  127. .toList()
  128. .map((point) => Container(
  129. margin: EdgeInsets.only(
  130. left: 256.0 * point.x,
  131. top: 256.0 * point.y,
  132. ),
  133. child: oneGrid,
  134. ))
  135. .toList()));
  136. }
  137. return grid;
  138. }
  139. @override
  140. void dispose() {
  141. // _controllerReset.dispose();
  142. super.dispose();
  143. }
  144. void _onInteractionUpdate(ScaleUpdateDetails details) => _onUpdate();
  145. void _onInteractionEnd(ScaleEndDetails details) {
  146. _onUpdate();
  147. Timer(const Duration(milliseconds: 250), () => _onUpdate());
  148. Timer(const Duration(milliseconds: 500), () => _onUpdate());
  149. }
  150. void _onUpdate() {
  151. setState(() {
  152. createComponentMap();
  153. });
  154. }
  155. void createComponentMap() {
  156. var bounds = MediaQuery.of(context).size;
  157. var topLeft =
  158. _transformationController.toScene(bounds.topLeft(Offset.zero));
  159. var bottomRight =
  160. _transformationController.toScene(bounds.bottomRight(Offset.zero));
  161. var newSet = <Point<int>>{};
  162. var rec = Rectangle<int>.fromPoints(
  163. _offsetToIndex(topLeft), _offsetToIndex(bottomRight));
  164. for (var i = rec.left; i <= rec.right; i++) {
  165. for (var j = rec.top; j <= rec.bottom; j++) {
  166. newSet.add(Point<int>(i, j));
  167. }
  168. }
  169. final oldKeys = Set.from(elements.keys);
  170. final newPoints = newSet.difference(oldKeys);
  171. final disposePoints = oldKeys.difference(newSet);
  172. for (var point in newPoints) {
  173. elements[point] = Container(
  174. key: Key(point.toString()),
  175. margin: EdgeInsets.only(left: 256.0 * point.x, top: 256.0 * point.y),
  176. child: Stack(children: [
  177. // CustomPaint(
  178. // painter: OpenPainter(point),
  179. // ),
  180. Text(point.toString()),
  181. SquareComponent(color: cc, tile: point, key: Key(point.toString())),
  182. ])
  183. // child: SquareComponent(point, cc),
  184. );
  185. }
  186. for (var point in disposePoints) {
  187. elements.remove(point);
  188. }
  189. }
  190. }
  191. Point<int> _offsetToIndex(Offset offset) {
  192. offset = offset / 256.0;
  193. var x = offset.dx.floor();
  194. var y = offset.dy.floor();
  195. if (x < 0) x = 0;
  196. if (y < 0) y = 0;
  197. return Point<int>(x, y);
  198. }
  199. class OpenPainter extends CustomPainter {
  200. Point<int> point;
  201. OpenPainter(Point<int> this.point);
  202. @override
  203. void paint(Canvas canvas, Size size) {
  204. var paint1 = Paint()
  205. ..color = Color.fromARGB(
  206. 100,
  207. ((point.x * point.y * 7) % 64) * 4,
  208. ((point.x * 10039) % 64) * 4,
  209. ((point.y * 10061) % 64) * 4,
  210. )
  211. ..style = PaintingStyle.fill;
  212. canvas.drawRect(const Offset(0, 0) & const Size(256, 256), paint1);
  213. }
  214. @override
  215. bool shouldRepaint(CustomPainter oldDelegate) => true;
  216. }