grid.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. // Copyright 2019 The Flutter team. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. import 'dart:async';
  5. import 'dart:collection' show IterableMixin;
  6. import 'dart:convert';
  7. import 'dart:developer' as developer;
  8. import 'dart:io';
  9. import 'dart:math';
  10. import 'dart:typed_data';
  11. import 'dart:ui' show ImmutableBuffer, PixelFormat, Vertices;
  12. import 'package:flutter/material.dart';
  13. import 'package:flutter/rendering.dart';
  14. import 'package:flutter/services.dart';
  15. import 'package:grpc/grpc.dart';
  16. import 'package:the_paint/src/generated/client.pbgrpc.dart';
  17. import 'package:vector_math/vector_math_64.dart' show Vector3;
  18. import 'dart:ui' as ui;
  19. import 'package:grpc/grpc_web.dart';
  20. import 'package:flutter_webrtc/flutter_webrtc.dart';
  21. import 'package:cyclop/cyclop.dart';
  22. class TransformationsDemo extends StatefulWidget {
  23. const TransformationsDemo({Key? key}) : super(key: key);
  24. @override
  25. State<TransformationsDemo> createState() => _TransformationsDemoState();
  26. }
  27. class _TransformationsDemoState extends State<TransformationsDemo>
  28. with TickerProviderStateMixin {
  29. final GlobalKey _targetKey = GlobalKey();
  30. Map<Point<int>, Widget> elements = <Point<int>, Widget>{};
  31. Color appbarColor = Colors.blueGrey;
  32. Set<Color> swatches = Colors.primaries.map((e) => Color(e.value)).toSet();
  33. double minScale = 2;
  34. final TransformationController _transformationController =
  35. TransformationController();
  36. Animation<Matrix4>? _animationReset;
  37. late AnimationController _controllerReset;
  38. Matrix4? _homeMatrix;
  39. final channel = GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080'));
  40. void _onTapUp(TapUpDetails details) {
  41. final renderBox =
  42. _targetKey.currentContext!.findRenderObject() as RenderBox;
  43. final offset =
  44. details.globalPosition - renderBox.localToGlobal(Offset.zero);
  45. // final scenePoint = _transformationController.toScene(offset);
  46. // final boardPoint = _board.pointToBoardPoint(scenePoint);
  47. // setState(() {
  48. // // _board = _board.copyWithSelected(boardPoint);
  49. // });
  50. _onUpdate();
  51. }
  52. @override
  53. void initState() {
  54. super.initState();
  55. // _controllerReset = AnimationController(
  56. // vsync: this,
  57. // );
  58. }
  59. @override
  60. Widget build(BuildContext context) {
  61. // The scene is drawn by a CustomPaint, but user interaction is handled by
  62. // the InteractiveViewer parent widget.
  63. return Scaffold(
  64. backgroundColor: Theme.of(context).colorScheme.primary,
  65. // appBar: AppBar(
  66. // automaticallyImplyLeading: false,
  67. // title: const Text('title'),
  68. // ),
  69. body: Container(
  70. color: Colors.white,
  71. child: LayoutBuilder(
  72. builder: (context, constraints) {
  73. // Start the first render, start the scene centered in the viewport.
  74. if (_homeMatrix == null) {
  75. _homeMatrix = Matrix4.identity() * minScale
  76. ..translate(0, 0);
  77. _transformationController.value = _homeMatrix!;
  78. elements = _start(
  79. context: context,
  80. channel: channel,
  81. transformationController: _transformationController);
  82. // _onUpdate(context: context);
  83. }
  84. return ClipRect(
  85. child: MouseRegion(
  86. cursor: SystemMouseCursors.click,
  87. child: GestureDetector(
  88. behavior: HitTestBehavior.opaque,
  89. onTapUp: _onTapUp,
  90. child: InteractiveViewer(
  91. constrained: false,
  92. key: _targetKey,
  93. transformationController: _transformationController,
  94. boundaryMargin: EdgeInsets.all(0),
  95. minScale: minScale,
  96. maxScale: 30,
  97. // onInteractionStart: _onScaleStart,
  98. onInteractionUpdate: _onInteractionUpdate,
  99. onInteractionEnd: _onInteractionEnd,
  100. child: SizedBox(
  101. width: 256 * 90,
  102. height: 256 * 53,
  103. child: Stack(children: elements.values.toList()),
  104. ),
  105. ),
  106. ),
  107. ),
  108. );
  109. },
  110. ),
  111. ),
  112. // persistentFooterButtons: [],
  113. floatingActionButton: ColorButton(
  114. darkMode: true,
  115. key: const Key('c2'),
  116. color: appbarColor,
  117. boxShape: BoxShape.circle,
  118. swatches: swatches,
  119. size: 64,
  120. config: const ColorPickerConfig(
  121. enableOpacity: false,
  122. enableLibrary: true,
  123. ),
  124. onColorChanged: (value) => setState(() => appbarColor = value),
  125. onSwatchesChanged: (newSwatches) =>
  126. setState(() => swatches = newSwatches),
  127. ),
  128. );
  129. }
  130. @override
  131. void dispose() {
  132. // _controllerReset.dispose();
  133. super.dispose();
  134. }
  135. void _onInteractionUpdate(ScaleUpdateDetails details) {
  136. _onUpdate();
  137. }
  138. void _onUpdate({BuildContext? context}) {
  139. var context2 = context ?? _targetKey.currentContext!;
  140. setState(() {
  141. _start(
  142. context: context2,
  143. channel: channel,
  144. transformationController: _transformationController,
  145. elements: elements);
  146. });
  147. }
  148. static Map<Point<int>, Widget> _start(
  149. {required BuildContext context,
  150. required GrpcWebClientChannel channel,
  151. required TransformationController transformationController,
  152. Map<Point<int>, Widget>? elements}) {
  153. var bounds = MediaQuery.of(context).size;
  154. var topLeft = transformationController.toScene(bounds.topLeft(Offset.zero));
  155. var bottomRight =
  156. transformationController.toScene(bounds.bottomRight(Offset.zero));
  157. var newSet = <Point<int>>{};
  158. var rec = Rectangle<int>.fromPoints(
  159. _offsetToIndex(topLeft), _offsetToIndex(bottomRight));
  160. for (var i = rec.left; i <= rec.right; i++) {
  161. for (var j = rec.top; j <= rec.bottom; j++) {
  162. newSet.add(Point<int>(i, j));
  163. }
  164. }
  165. var elements2 = elements ?? <Point<int>, Widget>{};
  166. newSet
  167. .difference(Set.from(elements2.keys))
  168. .forEach((point) => elements2[point] ??= Container(
  169. margin:
  170. EdgeInsets.only(left: 256.0 * point.x, top: 256.0 * point.y),
  171. child: MyImage(channel, point),
  172. ));
  173. Set.from(elements2.keys).difference(newSet).forEach(
  174. (point) => elements2.remove(point),
  175. );
  176. // print(elements2.length);
  177. WebRTCChannels(channel).connect();
  178. return elements2;
  179. }
  180. void _onInteractionEnd(ScaleEndDetails details) => _onUpdate();
  181. }
  182. Point<int> _offsetToIndex(Offset offset) {
  183. offset = offset / 256.0;
  184. var x = offset.dx.floor();
  185. var y = offset.dy.floor();
  186. if (x < 0) x = 0;
  187. if (y < 0) y = 0;
  188. return Point<int>(x, y);
  189. }
  190. class MyImage extends StatefulWidget {
  191. GrpcWebClientChannel channel;
  192. Point<int> tile;
  193. MyImage(this.channel, this.tile, {Key? key}) : super(key: key);
  194. @override
  195. MyImageState createState() => MyImageState();
  196. }
  197. class MyImageState extends State<MyImage> {
  198. late ResponseStream<MonitorReply> responses;
  199. ui.Image? img;
  200. Uint8List data = Uint8List(256 * 256 * 4);
  201. void makeImage() {
  202. ui.decodeImageFromPixels(data, 256, 256, ui.PixelFormat.rgba8888, setImg,
  203. allowUpscaling: false);
  204. }
  205. void setImg(ui.Image data) {
  206. if (mounted) {
  207. setState(() {
  208. img = data;
  209. });
  210. }
  211. }
  212. @override
  213. void initState() {
  214. final stub = PaintClient(widget.channel,
  215. options: CallOptions(timeout: Duration(seconds: 300)));
  216. MonitorSub outgoingNotes =
  217. MonitorSub(tile: BPoint(x: widget.tile.x, y: widget.tile.y));
  218. responses = stub.monitor(outgoingNotes);
  219. grpc();
  220. super.initState();
  221. }
  222. Future<void> grpc() async {
  223. responses.listen((value) {
  224. BPoint? lastPosition;
  225. for (var point in value.points) {
  226. if (point.point != null) {
  227. lastPosition = point.point;
  228. }
  229. var p = lastPosition ?? point.point;
  230. final startByte = (p.x + p.y * 256) * 4;
  231. List.generate(
  232. 4,
  233. (index) =>
  234. data[startByte + index] = point.color.rgba >> 8 * index & 0xff);
  235. }
  236. makeImage();
  237. });
  238. }
  239. @override
  240. Widget build(BuildContext context) {
  241. return RawImage(
  242. image: img,
  243. filterQuality: FilterQuality.none,
  244. );
  245. }
  246. @override
  247. void dispose() {
  248. super.dispose();
  249. print("ok");
  250. }
  251. }
  252. class WebRTCChannels {
  253. GrpcWebClientChannel channel;
  254. bool _inCalling = false;
  255. RTCPeerConnection? _pc;
  256. RTCDataChannel? _dc;
  257. PaintClient? _grpcClient;
  258. WebRTCChannels(this.channel);
  259. final Map<String, dynamic> _iceServers = {
  260. 'iceServers': [
  261. {
  262. 'url': 'stun:melvans.com:3478',
  263. 'username': 'b84e4232f84505e3c3967184df374f2cdaa954f1',
  264. 'credential': 'afc39ae0591c132292f73a87f6f3725311fbafa0'
  265. },
  266. ]
  267. };
  268. final Map<String, dynamic> _config = {
  269. 'mandatory': {},
  270. 'optional': [
  271. {'DtlsSrtpKeyAgreement': true},
  272. ],
  273. };
  274. final Map<String, dynamic> _constraints = {
  275. 'mandatory': {
  276. // 'OfferToReceiveAudio': true,
  277. // 'OfferToReceiveVideo': true,
  278. },
  279. 'optional': [],
  280. };
  281. Future<void> connect() async {
  282. print("Connecting GRPC...");
  283. _grpcClient = PaintClient(channel);
  284. RTCPeerConnection pc = await createPeerConnection(
  285. _iceServers,
  286. );
  287. pc.onIceCandidate = (candidate) {
  288. print('pc2: onIceCandidate: ${candidate.candidate}');
  289. };
  290. pc.onDataChannel = (channel) {
  291. RTCDataChannel dc = channel;
  292. print('\nnew channel');
  293. dc.onDataChannelState = (state) {
  294. print('\ndc2: Received message: ${state.toString()}');
  295. };
  296. dc.onMessage = (data) {
  297. print('\ndc2: Received message: ${data.text}');
  298. // setState(() {
  299. // dctatus += '\ndc2: Received message: ${data.text}';
  300. // });
  301. // dc.send(
  302. // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
  303. };
  304. };
  305. pc.onIceGatheringState = (state) async {
  306. if (state == RTCIceGatheringState.RTCIceGatheringStateComplete) {
  307. print('RTCIceGatheringStateComplete');
  308. RTCSessionDescription? sdp = await pc.getLocalDescription();
  309. if (sdp == null) {
  310. return;
  311. }
  312. // _send(sdp.sdp!);
  313. }
  314. };
  315. // RequestRTCRequest rtpcrequest = RequestRTCRequest();
  316. var request = StreamController<RTCData>();
  317. final call = _grpcClient!.requestRTC(request.stream);
  318. await for (var data in call) {
  319. print("GRPC value: ${data}");
  320. // pc.setRemoteDescription(RTCSessionDescription(value.data, value.event));
  321. }
  322. print("Closed GRPC");
  323. // createSubscriber();
  324. }
  325. static Future<void> answer(RTCPeerConnection pc) async {
  326. var answer = await pc.createAnswer();
  327. await pc.setLocalDescription(answer);
  328. print("STARTING!!!");
  329. }
  330. // static Future<void> startwebrtc(RequestRTCReply value) async {
  331. // print("STARTING!!!");
  332. // RTCPeerConnection pc = await createPeerConnection(_iceServers,);
  333. // pc.onIceCandidate = (candidate) {
  334. // print('pc2: onIceCandidate: ${candidate.candidate}');
  335. // };
  336. // pc.onDataChannel = (channel) {
  337. // RTCDataChannel dc= channel;
  338. // dc.onDataChannelState = (state) {
  339. // // setState(() {
  340. // // dctatus += '\ndc2: state: ${state.toString()}';
  341. // // });
  342. // };
  343. // dc.onMessage = (data) {
  344. // print('\ndc2: Received message: ${data.text}');
  345. // // setState(() {
  346. // // dctatus += '\ndc2: Received message: ${data.text}';
  347. // // });
  348. // // dc.send(
  349. // // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
  350. // };
  351. // };
  352. // await pc.setRemoteDescription(RTCSessionDescription(value.data, value.event));
  353. // var answer = await pc.createAnswer();
  354. // print('pc2 answer: ${answer.sdp}');
  355. // await pc.setLocalDescription(answer);
  356. // }
  357. // void _onMessage(data) {
  358. // if (_pc == null) return;
  359. // _pc!.setRemoteDescription(new RTCSessionDescription(data, 'answer'));
  360. // }
  361. // void createSubscriber() async {
  362. // if (_inCalling) {
  363. // return;
  364. // }
  365. // _pc = await createPeerConnection(_iceServers, _config);
  366. // _dc = await _pc!.createDataChannel('data', RTCDataChannelInit());
  367. // _pc!.onDataChannel = (channel) {
  368. // RTCDataChannel dc = channel;
  369. // dc.onDataChannelState = (state) {
  370. // print('\ndc2: Received message: ${state.toString()}');
  371. // };
  372. // dc.onMessage = (data) {
  373. // print('\ndc2: Received message: ${data.text}');
  374. // // setState(() {
  375. // // dctatus += '\ndc2: Received message: ${data.text}';
  376. // // });
  377. // // dc.send(
  378. // // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
  379. // };
  380. // };
  381. // // _dc.onMessage
  382. // _pc!.onIceGatheringState = (state) async {
  383. // if (state == RTCIceGatheringState.RTCIceGatheringStateComplete) {
  384. // print('RTCIceGatheringStateComplete');
  385. // RTCSessionDescription? sdp = await _pc!.getLocalDescription();
  386. // if (sdp == null) {
  387. // return;
  388. // }
  389. // _send(sdp.sdp!);
  390. // }
  391. // };
  392. // RTCSessionDescription description = await _pc!.createOffer(_constraints);
  393. // print('Subscriber createOffer');
  394. // _pc!.setLocalDescription(description);
  395. // _inCalling = true;
  396. // }
  397. // void _send(String data) {
  398. // print('send: ' + data);
  399. // if (_grpcClient != null) {
  400. // RequestRTCRequest rtpcrequest = RequestRTCRequest(data: data);
  401. // _grpcClient!.requestRTC(rtpcrequest).listen((value) {
  402. // print(value);
  403. // _pc!.setRemoteDescription(RTCSessionDescription(value.data, 'answer'));
  404. // // startwebrtc(value);
  405. // });
  406. // }
  407. // }
  408. }