123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- // Copyright 2019 The Flutter team. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- import 'dart:async';
- import 'dart:collection' show IterableMixin;
- import 'dart:convert';
- import 'dart:developer' as developer;
- import 'dart:io';
- import 'dart:math';
- import 'dart:typed_data';
- import 'dart:ui' show ImmutableBuffer, PixelFormat, Vertices;
- import 'package:flutter/material.dart';
- import 'package:flutter/rendering.dart';
- import 'package:flutter/services.dart';
- import 'package:grpc/grpc.dart';
- import 'package:the_paint/src/generated/client.pbgrpc.dart';
- import 'package:vector_math/vector_math_64.dart' show Vector3;
- import 'dart:ui' as ui;
- import 'package:grpc/grpc_web.dart';
- import 'package:flutter_webrtc/flutter_webrtc.dart';
- import 'package:cyclop/cyclop.dart';
- class TransformationsDemo extends StatefulWidget {
- const TransformationsDemo({Key? key}) : super(key: key);
- @override
- State<TransformationsDemo> createState() => _TransformationsDemoState();
- }
- class _TransformationsDemoState extends State<TransformationsDemo>
- with TickerProviderStateMixin {
- final GlobalKey _targetKey = GlobalKey();
- Map<Point<int>, Widget> elements = <Point<int>, Widget>{};
- Color appbarColor = Colors.blueGrey;
- Set<Color> swatches = Colors.primaries.map((e) => Color(e.value)).toSet();
- double minScale = 2;
- final TransformationController _transformationController =
- TransformationController();
- Animation<Matrix4>? _animationReset;
- late AnimationController _controllerReset;
- Matrix4? _homeMatrix;
- final channel = GrpcWebClientChannel.xhr(Uri.parse('http://localhost:8080'));
- void _onTapUp(TapUpDetails details) {
- final renderBox =
- _targetKey.currentContext!.findRenderObject() as RenderBox;
- final offset =
- details.globalPosition - renderBox.localToGlobal(Offset.zero);
- // final scenePoint = _transformationController.toScene(offset);
- // final boardPoint = _board.pointToBoardPoint(scenePoint);
- // setState(() {
- // // _board = _board.copyWithSelected(boardPoint);
- // });
- _onUpdate();
- }
- @override
- void initState() {
- super.initState();
- // _controllerReset = AnimationController(
- // vsync: this,
- // );
- }
- @override
- Widget build(BuildContext context) {
- // The scene is drawn by a CustomPaint, but user interaction is handled by
- // the InteractiveViewer parent widget.
- return Scaffold(
- backgroundColor: Theme.of(context).colorScheme.primary,
- // appBar: AppBar(
- // automaticallyImplyLeading: false,
- // title: const Text('title'),
- // ),
- body: Container(
- color: Colors.white,
- child: LayoutBuilder(
- builder: (context, constraints) {
- // Start the first render, start the scene centered in the viewport.
- if (_homeMatrix == null) {
- _homeMatrix = Matrix4.identity() * minScale
- ..translate(0, 0);
- _transformationController.value = _homeMatrix!;
- elements = _start(
- context: context,
- channel: channel,
- transformationController: _transformationController);
- // _onUpdate(context: context);
- }
- return ClipRect(
- child: MouseRegion(
- cursor: SystemMouseCursors.click,
- child: GestureDetector(
- behavior: HitTestBehavior.opaque,
- onTapUp: _onTapUp,
- child: InteractiveViewer(
- constrained: false,
- key: _targetKey,
- transformationController: _transformationController,
- boundaryMargin: EdgeInsets.all(0),
- minScale: minScale,
- maxScale: 30,
- // onInteractionStart: _onScaleStart,
- onInteractionUpdate: _onInteractionUpdate,
- onInteractionEnd: _onInteractionEnd,
- child: SizedBox(
- width: 256 * 90,
- height: 256 * 53,
- child: Stack(children: elements.values.toList()),
- ),
- ),
- ),
- ),
- );
- },
- ),
- ),
- // persistentFooterButtons: [],
- floatingActionButton: ColorButton(
- darkMode: true,
- key: const Key('c2'),
- color: appbarColor,
- boxShape: BoxShape.circle,
- swatches: swatches,
- size: 64,
- config: const ColorPickerConfig(
- enableOpacity: false,
- enableLibrary: true,
- ),
- onColorChanged: (value) => setState(() => appbarColor = value),
- onSwatchesChanged: (newSwatches) =>
- setState(() => swatches = newSwatches),
- ),
- );
- }
- @override
- void dispose() {
- // _controllerReset.dispose();
- super.dispose();
- }
- void _onInteractionUpdate(ScaleUpdateDetails details) {
- _onUpdate();
- }
- void _onUpdate({BuildContext? context}) {
- var context2 = context ?? _targetKey.currentContext!;
- setState(() {
- _start(
- context: context2,
- channel: channel,
- transformationController: _transformationController,
- elements: elements);
- });
- }
- static Map<Point<int>, Widget> _start(
- {required BuildContext context,
- required GrpcWebClientChannel channel,
- required TransformationController transformationController,
- Map<Point<int>, Widget>? elements}) {
- var bounds = MediaQuery.of(context).size;
- var topLeft = transformationController.toScene(bounds.topLeft(Offset.zero));
- var bottomRight =
- transformationController.toScene(bounds.bottomRight(Offset.zero));
- var newSet = <Point<int>>{};
- var rec = Rectangle<int>.fromPoints(
- _offsetToIndex(topLeft), _offsetToIndex(bottomRight));
- for (var i = rec.left; i <= rec.right; i++) {
- for (var j = rec.top; j <= rec.bottom; j++) {
- newSet.add(Point<int>(i, j));
- }
- }
- var elements2 = elements ?? <Point<int>, Widget>{};
- newSet
- .difference(Set.from(elements2.keys))
- .forEach((point) => elements2[point] ??= Container(
- margin:
- EdgeInsets.only(left: 256.0 * point.x, top: 256.0 * point.y),
- child: MyImage(channel, point),
- ));
- Set.from(elements2.keys).difference(newSet).forEach(
- (point) => elements2.remove(point),
- );
- // print(elements2.length);
- WebRTCChannels(channel).connect();
- return elements2;
- }
- void _onInteractionEnd(ScaleEndDetails details) => _onUpdate();
- }
- Point<int> _offsetToIndex(Offset offset) {
- offset = offset / 256.0;
- var x = offset.dx.floor();
- var y = offset.dy.floor();
- if (x < 0) x = 0;
- if (y < 0) y = 0;
- return Point<int>(x, y);
- }
- class MyImage extends StatefulWidget {
- GrpcWebClientChannel channel;
- Point<int> tile;
- MyImage(this.channel, this.tile, {Key? key}) : super(key: key);
- @override
- MyImageState createState() => MyImageState();
- }
- class MyImageState extends State<MyImage> {
- late ResponseStream<MonitorReply> responses;
- ui.Image? img;
- Uint8List data = Uint8List(256 * 256 * 4);
- void makeImage() {
- ui.decodeImageFromPixels(data, 256, 256, ui.PixelFormat.rgba8888, setImg,
- allowUpscaling: false);
- }
- void setImg(ui.Image data) {
- if (mounted) {
- setState(() {
- img = data;
- });
- }
- }
- @override
- void initState() {
- final stub = PaintClient(widget.channel,
- options: CallOptions(timeout: Duration(seconds: 300)));
- MonitorSub outgoingNotes =
- MonitorSub(tile: BPoint(x: widget.tile.x, y: widget.tile.y));
- responses = stub.monitor(outgoingNotes);
- grpc();
- super.initState();
- }
- Future<void> grpc() async {
- responses.listen((value) {
- BPoint? lastPosition;
- for (var point in value.points) {
- if (point.point != null) {
- lastPosition = point.point;
- }
- var p = lastPosition ?? point.point;
- final startByte = (p.x + p.y * 256) * 4;
- List.generate(
- 4,
- (index) =>
- data[startByte + index] = point.color.rgba >> 8 * index & 0xff);
- }
- makeImage();
- });
- }
- @override
- Widget build(BuildContext context) {
- return RawImage(
- image: img,
- filterQuality: FilterQuality.none,
- );
- }
- @override
- void dispose() {
- super.dispose();
- print("ok");
- }
- }
- class WebRTCChannels {
- GrpcWebClientChannel channel;
- bool _inCalling = false;
- RTCPeerConnection? _pc;
- RTCDataChannel? _dc;
- PaintClient? _grpcClient;
- WebRTCChannels(this.channel);
- final Map<String, dynamic> _iceServers = {
- 'iceServers': [
- {
- 'url': 'stun:melvans.com:3478',
- 'username': 'b84e4232f84505e3c3967184df374f2cdaa954f1',
- 'credential': 'afc39ae0591c132292f73a87f6f3725311fbafa0'
- },
- ]
- };
- final Map<String, dynamic> _config = {
- 'mandatory': {},
- 'optional': [
- {'DtlsSrtpKeyAgreement': true},
- ],
- };
- final Map<String, dynamic> _constraints = {
- 'mandatory': {
- // 'OfferToReceiveAudio': true,
- // 'OfferToReceiveVideo': true,
- },
- 'optional': [],
- };
- Future<void> connect() async {
- print("Connecting GRPC...");
- _grpcClient = PaintClient(channel);
- RTCPeerConnection pc = await createPeerConnection(
- _iceServers,
- );
- pc.onIceCandidate = (candidate) {
- print('pc2: onIceCandidate: ${candidate.candidate}');
- };
- pc.onDataChannel = (channel) {
- RTCDataChannel dc = channel;
- print('\nnew channel');
- dc.onDataChannelState = (state) {
- print('\ndc2: Received message: ${state.toString()}');
- };
- dc.onMessage = (data) {
- print('\ndc2: Received message: ${data.text}');
- // setState(() {
- // dctatus += '\ndc2: Received message: ${data.text}';
- // });
- // dc.send(
- // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
- };
- };
- pc.onIceGatheringState = (state) async {
- if (state == RTCIceGatheringState.RTCIceGatheringStateComplete) {
- print('RTCIceGatheringStateComplete');
- RTCSessionDescription? sdp = await pc.getLocalDescription();
- if (sdp == null) {
- return;
- }
- // _send(sdp.sdp!);
- }
- };
- // RequestRTCRequest rtpcrequest = RequestRTCRequest();
- var request = StreamController<RTCData>();
- final call = _grpcClient!.requestRTC(request.stream);
- await for (var data in call) {
- print("GRPC value: ${data}");
- // pc.setRemoteDescription(RTCSessionDescription(value.data, value.event));
- }
- print("Closed GRPC");
- // createSubscriber();
- }
- static Future<void> answer(RTCPeerConnection pc) async {
- var answer = await pc.createAnswer();
- await pc.setLocalDescription(answer);
- print("STARTING!!!");
- }
- // static Future<void> startwebrtc(RequestRTCReply value) async {
- // print("STARTING!!!");
- // RTCPeerConnection pc = await createPeerConnection(_iceServers,);
- // pc.onIceCandidate = (candidate) {
- // print('pc2: onIceCandidate: ${candidate.candidate}');
- // };
- // pc.onDataChannel = (channel) {
- // RTCDataChannel dc= channel;
- // dc.onDataChannelState = (state) {
- // // setState(() {
- // // dctatus += '\ndc2: state: ${state.toString()}';
- // // });
- // };
- // dc.onMessage = (data) {
- // print('\ndc2: Received message: ${data.text}');
- // // setState(() {
- // // dctatus += '\ndc2: Received message: ${data.text}';
- // // });
- // // dc.send(
- // // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
- // };
- // };
- // await pc.setRemoteDescription(RTCSessionDescription(value.data, value.event));
- // var answer = await pc.createAnswer();
- // print('pc2 answer: ${answer.sdp}');
- // await pc.setLocalDescription(answer);
- // }
- // void _onMessage(data) {
- // if (_pc == null) return;
- // _pc!.setRemoteDescription(new RTCSessionDescription(data, 'answer'));
- // }
- // void createSubscriber() async {
- // if (_inCalling) {
- // return;
- // }
- // _pc = await createPeerConnection(_iceServers, _config);
- // _dc = await _pc!.createDataChannel('data', RTCDataChannelInit());
- // _pc!.onDataChannel = (channel) {
- // RTCDataChannel dc = channel;
- // dc.onDataChannelState = (state) {
- // print('\ndc2: Received message: ${state.toString()}');
- // };
- // dc.onMessage = (data) {
- // print('\ndc2: Received message: ${data.text}');
- // // setState(() {
- // // dctatus += '\ndc2: Received message: ${data.text}';
- // // });
- // // dc.send(
- // // RTCDataChannelMessage('(dc2 ==> dc1) Hello from dc2 echo !!!'));
- // };
- // };
- // // _dc.onMessage
- // _pc!.onIceGatheringState = (state) async {
- // if (state == RTCIceGatheringState.RTCIceGatheringStateComplete) {
- // print('RTCIceGatheringStateComplete');
- // RTCSessionDescription? sdp = await _pc!.getLocalDescription();
- // if (sdp == null) {
- // return;
- // }
- // _send(sdp.sdp!);
- // }
- // };
- // RTCSessionDescription description = await _pc!.createOffer(_constraints);
- // print('Subscriber createOffer');
- // _pc!.setLocalDescription(description);
- // _inCalling = true;
- // }
- // void _send(String data) {
- // print('send: ' + data);
- // if (_grpcClient != null) {
- // RequestRTCRequest rtpcrequest = RequestRTCRequest(data: data);
- // _grpcClient!.requestRTC(rtpcrequest).listen((value) {
- // print(value);
- // _pc!.setRemoteDescription(RTCSessionDescription(value.data, 'answer'));
- // // startwebrtc(value);
- // });
- // }
- // }
- }
|