From 322bbcb419b8b3a9a41a5fa809af2d0f9bc6d1e5 Mon Sep 17 00:00:00 2001 From: dvmatyun Date: Mon, 9 Dec 2024 00:12:34 +0400 Subject: [PATCH 1/4] single cube --- example/.vscode/launch.json | 25 ++ example/lib/src/common/widget/routes.dart | 6 + example/lib/src/feature/home/home_screen.dart | 4 + .../square_figure/square_figure_screen.dart | 225 ++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 example/.vscode/launch.json create mode 100644 example/lib/src/feature/square_figure/square_figure_screen.dart diff --git a/example/.vscode/launch.json b/example/.vscode/launch.json new file mode 100644 index 0000000..091adbf --- /dev/null +++ b/example/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "example", + "request": "launch", + "type": "dart" + }, + { + "name": "example (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile" + }, + { + "name": "example (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release" + } + ] +} \ No newline at end of file diff --git a/example/lib/src/common/widget/routes.dart b/example/lib/src/common/widget/routes.dart index f6bee46..2d9f682 100644 --- a/example/lib/src/common/widget/routes.dart +++ b/example/lib/src/common/widget/routes.dart @@ -4,6 +4,7 @@ import 'package:repaintexample/src/feature/fps/fps_screen.dart'; import 'package:repaintexample/src/feature/home/home_screen.dart'; import 'package:repaintexample/src/feature/performance_overlay/performance_overlay_screen.dart'; import 'package:repaintexample/src/feature/shaders/fragment_shaders_screen.dart'; +import 'package:repaintexample/src/feature/square_figure/square_figure_screen.dart'; import 'package:repaintexample/src/feature/sunflower/sunflower_screen.dart'; /// The routes to navigate to. @@ -39,4 +40,9 @@ final Map Function(Map?)> $routes = child: const SunflowerScreen(), arguments: arguments, ), + 'square': (arguments) => MaterialPage( + name: 'square', + child: const SquareFigureScreen(), + arguments: arguments, + ), }; diff --git a/example/lib/src/feature/home/home_screen.dart b/example/lib/src/feature/home/home_screen.dart index 72e0906..6da3418 100644 --- a/example/lib/src/feature/home/home_screen.dart +++ b/example/lib/src/feature/home/home_screen.dart @@ -44,6 +44,10 @@ class HomeScreen extends StatelessWidget { title: 'Sunflower', page: 'sunflower', ), + HomeTile( + title: 'Square figure', + page: 'square', + ), ], ), ), diff --git a/example/lib/src/feature/square_figure/square_figure_screen.dart b/example/lib/src/feature/square_figure/square_figure_screen.dart new file mode 100644 index 0000000..6f9096f --- /dev/null +++ b/example/lib/src/feature/square_figure/square_figure_screen.dart @@ -0,0 +1,225 @@ +import 'dart:typed_data'; +import 'dart:ui'; +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import 'package:repaint/repaint.dart'; +import 'package:repaintexample/src/common/widget/app.dart'; +import 'package:repaintexample/src/feature/performance_overlay/performance_overlay_screen.dart'; + +/// {@template square_figure_screen} +/// SquareFigureScreen widget. +/// {@endtemplate} +class SquareFigureScreen extends StatefulWidget { + /// {@macro square_figure_screen} + const SquareFigureScreen({ + super.key, // ignore: unused_element + }); + + @override + State createState() => _SquareFigureScreenState(); +} + +/// State for widget SquareFigureScreen. +class _SquareFigureScreenState extends State { + final _painter = _SquarePainter(); + final focusNode = FocusNode()..requestFocus(); + + @override + void dispose() { + focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Square'), + leading: BackButton( + onPressed: () => App.pop(context), + ), + ), + body: KeyboardListener( + focusNode: focusNode, + autofocus: true, + onKeyEvent: (key) { + print('Key: ${key.logicalKey.keyId}'); + _painter.autoRotate = false; + switch (key.logicalKey.keyId) { + case 100: // right (d) + _painter.yawAngle -= 1; + case 97: // left (a) + _painter.yawAngle += 1; + case 119: // forward (w) + _painter.pitchAngle -= 1; + case 115: // backward (s) + _painter.pitchAngle += 1; + case 113: // left roll (q) + _painter.rollAngle -= 1; + case 101: // right roll (e) + _painter.rollAngle += 1; + } + }, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Center( + child: AspectRatio( + aspectRatio: 1.0, + child: FittedBox( + alignment: Alignment.center, + fit: BoxFit.scaleDown, + clipBehavior: Clip.none, + child: SizedBox.square( + dimension: 720, + child: RePaint( + painter: _painter, + ), + ), + ), + ), + ), + ), + ), + ), + ); + } +} + +class _SquarePainter extends PerformanceOverlayPainter { + _SquarePainter() + : _positions = Float32List(verticiesAmount * 6), + _colors = Int32List(verticiesAmount * 3) { + _initVertices(); + } + + static const verticiesAmount = 4; + final Float32List _positions; + + final Int32List _colors; + + late Vertices _vertices; + + List _points = []; + + double pitchAngle = 0; // plane nose goes up-down + double yawAngle = 0; // plane nose goes left-right + double rollAngle = 0; // plane rotation around its nose + bool autoRotate = true; + + // Define 3D cube vertices + static const List> verticesCube = [ + [-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], // Bottom face + [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1] // Top face + ]; + + // Define cube edges (pairs of vertex indices) + static const List> edgesCube = [ + [0, 1], [1, 2], [2, 3], [3, 0], // Bottom face + [4, 5], [5, 6], [6, 7], [7, 4], // Top face + [0, 4], [1, 5], [2, 6], [3, 7] // Vertical edges + ]; + + void _initVertices() { + _vertices = Vertices.raw( + VertexMode.triangles, + _positions, + colors: _colors, + ); + } + + // Function to rotate a point in 3D space + List rotate3D(List coords3d, double angleX, double angleY, double angleZ) { + // Convert angles to radians + final radX = (math.pi / 180) * angleX; + final radY = (math.pi / 180) * angleY; + final radZ = (math.pi / 180) * angleZ; + + final x = coords3d[0]; + final y = coords3d[1]; + final z = coords3d[2]; + + // Rotate around X-axis + final y1 = y * math.cos(radX) - z * math.sin(radX); + final z1 = y * math.sin(radX) + z * math.cos(radX); + + // Rotate around Y-axis + final x2 = x * math.cos(radY) + z1 * math.sin(radY); + final z2 = -x * math.sin(radY) + z1 * math.cos(radY); + + // Rotate around Z-axis + final x3 = x2 * math.cos(radZ) - y1 * math.sin(radZ); + final y3 = x2 * math.sin(radZ) + y1 * math.cos(radZ); + + return [x3, y3, z2]; + } + + // Project a 3D point to 2D (ignoring depth) + List projectSimple(List coords3d, {double cameraDistance = 400}) { + const depthModifier = 2; + final scale = 300 / (coords3d[2] / depthModifier + cameraDistance); // Perspective divide (adjust depth) + return [coords3d[0] * scale, coords3d[1] * scale]; + } + + static const double fov = 90; // Field of view in degrees + final fovScale = 1 / (2 * math.tan((fov * 0.5 * math.pi) / 180)); + List projectFov(List coords3d, {double cameraDistance = 400}) { + final scale = (fovScale * cameraDistance) / (coords3d[2] + cameraDistance * 4); + return [coords3d[0] * scale, coords3d[1] * scale]; + } + + Duration elapsedLast = Duration.zero; + @override + void internalUpdate(RePaintBox box, Duration elapsed, double delta) { + final size = box.size; + final dimension = size.shortestSide; + final center = size.center(Offset.zero); + _points = []; + final dt = (elapsedLast.inMilliseconds - elapsed.inMilliseconds) / 1024; + if (autoRotate) { + yawAngle += 4 * dt; + pitchAngle += 6 * dt; + rollAngle += 8 * dt; + } + elapsedLast = elapsed; + + final rotatedVertices = verticesCube + .map((v) => rotate3D(v.map((e) => e * dimension).toList(), pitchAngle, yawAngle, rollAngle)) + .toList(); + final projectedVertices = rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * 2)).toList(); + + for (var i = 0; i < edgesCube.length; ++i) { + final edge = edgesCube[i]; + final start2d = projectedVertices[edge[0]]; + final end2d = projectedVertices[edge[1]]; + _points.add(Offset(center.dx + start2d[0], center.dy - start2d[1])); + _points.add(Offset(center.dx + end2d[0], center.dy - end2d[1])); + } + _vertices = Vertices.raw( + VertexMode.triangles, + _positions, + colors: _colors, + ); + } + + @override + void internalPaint(RePaintBox box, PaintingContext context) { + final canvas = context.canvas; + var paint = Paint() + ..style = PaintingStyle.fill + ..strokeWidth = 4 + ..isAntiAlias = false + ..blendMode = BlendMode.src + ..filterQuality = FilterQuality.none; + + canvas.drawRect( + Offset.zero & box.size, + paint..color = Colors.white, + ); + + paint.color = Colors.black; + for (var i = 0; i < (_points.length - 1); i += 2) { + canvas.drawLine(_points[i], _points[i + 1], paint); + } + } +} From 7581cee619e14fbc36ae46a5777dd836041fe59d Mon Sep 17 00:00:00 2001 From: dvmatyun Date: Mon, 9 Dec 2024 00:37:42 +0400 Subject: [PATCH 2/4] multiple cubes --- .../square_figure/square_figure_screen.dart | 157 +++++++++++++++--- 1 file changed, 134 insertions(+), 23 deletions(-) diff --git a/example/lib/src/feature/square_figure/square_figure_screen.dart b/example/lib/src/feature/square_figure/square_figure_screen.dart index 6f9096f..12af86a 100644 --- a/example/lib/src/feature/square_figure/square_figure_screen.dart +++ b/example/lib/src/feature/square_figure/square_figure_screen.dart @@ -24,8 +24,22 @@ class _SquareFigureScreenState extends State { final _painter = _SquarePainter(); final focusNode = FocusNode()..requestFocus(); + final ValueNotifier _progress = ValueNotifier(1); + + @override + void initState() { + super.initState(); + _progress.addListener(_updateAmount); + _updateAmount(); + } + + void _updateAmount() => _painter.setCubesAmount(_progress.value); + @override void dispose() { + _progress + ..removeListener(_updateAmount) + ..dispose(); focusNode.dispose(); super.dispose(); } @@ -63,21 +77,70 @@ class _SquareFigureScreenState extends State { child: SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), - child: Center( - child: AspectRatio( - aspectRatio: 1.0, - child: FittedBox( - alignment: Alignment.center, - fit: BoxFit.scaleDown, - clipBehavior: Clip.none, - child: SizedBox.square( - dimension: 720, - child: RePaint( - painter: _painter, + child: Column( + children: [ + Expanded( + child: Center( + child: AspectRatio( + aspectRatio: 1.5, + child: RePaint( + painter: _painter, + ), + ), + ), + ), + const SizedBox(height: 8.0), + SizedBox( + height: 72, + child: FittedBox( + alignment: Alignment.center, + fit: BoxFit.scaleDown, + clipBehavior: Clip.none, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 300, + child: ValueListenableBuilder( + valueListenable: _progress, + builder: (context, value, child) => Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Slider( + min: 0, + max: 100, + value: value.toDouble(), + onChanged: (val) => _progress.value = + val.round().clamp(0, 100), + ), + Text( + '$value% (${_painter.cubesToUse} cubes)', + maxLines: 1, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + const SizedBox(width: 8), + SizedBox.square( + dimension: 48, + child: IconButton( + icon: const Icon(Icons.bug_report), + onPressed: () => + _painter.switchPerformanceOverlay(), + ), + ), + ], ), ), ), - ), + ], ), ), ), @@ -168,6 +231,47 @@ class _SquarePainter extends PerformanceOverlayPainter { return [coords3d[0] * scale, coords3d[1] * scale]; } + int calculateDimension(int cubesAmount) { + int dimensionAmount = 1; + for (int i = 0; i < cubesAmount; ++i) { + dimensionAmount = dimensionAmount + 1; + final testNumber = math.pow(dimensionAmount, 3).ceil(); + if (testNumber >= cubesAmount) { + + break; + } + } + return dimensionAmount; + } + + List> verticesCubeForIndex(int idx, int maxIdx) { + final dimensionAmount = calculateDimension(maxIdx); + + int rowIdx = 0; + int colIdx = 0; + int depthIdx = 0; + for (int i = 0; i < idx; ++i) { + colIdx++; + if (colIdx >= dimensionAmount) { + colIdx = 0; + rowIdx++; + if (rowIdx >= dimensionAmount) { + rowIdx = 0; + depthIdx++; + } + } + } + + return verticesCube + .map((v) => [v[0] - colIdx + dimensionAmount ~/ 2, v[1] - rowIdx + dimensionAmount ~/ 2, v[2] + depthIdx]) + .toList(); + } + + int cubesToUse = 1; + void setCubesAmount(int amount){ + cubesToUse = amount * amount * 1; + } + Duration elapsedLast = Duration.zero; @override void internalUpdate(RePaintBox box, Duration elapsed, double delta) { @@ -183,17 +287,24 @@ class _SquarePainter extends PerformanceOverlayPainter { } elapsedLast = elapsed; - final rotatedVertices = verticesCube - .map((v) => rotate3D(v.map((e) => e * dimension).toList(), pitchAngle, yawAngle, rollAngle)) - .toList(); - final projectedVertices = rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * 2)).toList(); - - for (var i = 0; i < edgesCube.length; ++i) { - final edge = edgesCube[i]; - final start2d = projectedVertices[edge[0]]; - final end2d = projectedVertices[edge[1]]; - _points.add(Offset(center.dx + start2d[0], center.dy - start2d[1])); - _points.add(Offset(center.dx + end2d[0], center.dy - end2d[1])); + final dimensionAmount = calculateDimension(cubesToUse); + + + + for (int cubeIdx = 0; cubeIdx < cubesToUse; ++cubeIdx) { + final movedVertices = verticesCubeForIndex(cubeIdx, cubesToUse); + final rotatedVertices = movedVertices + .map((v) => rotate3D(v.map((e) => e * dimension).toList(), pitchAngle, yawAngle, rollAngle)) + .toList(); + final projectedVertices = rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * (2 + dimensionAmount / 4))).toList(); + + for (var i = 0; i < edgesCube.length; ++i) { + final edge = edgesCube[i]; + final start2d = projectedVertices[edge[0]]; + final end2d = projectedVertices[edge[1]]; + _points.add(Offset(center.dx + start2d[0], center.dy - start2d[1])); + _points.add(Offset(center.dx + end2d[0], center.dy - end2d[1])); + } } _vertices = Vertices.raw( VertexMode.triangles, From 8b5ceb74eac3687d47f574f96d1fba3855e4d7f1 Mon Sep 17 00:00:00 2001 From: dvmatyun Date: Mon, 9 Dec 2024 00:44:47 +0400 Subject: [PATCH 3/4] update key added --- .../square_figure/square_figure_screen.dart | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/example/lib/src/feature/square_figure/square_figure_screen.dart b/example/lib/src/feature/square_figure/square_figure_screen.dart index 12af86a..b93d35f 100644 --- a/example/lib/src/feature/square_figure/square_figure_screen.dart +++ b/example/lib/src/feature/square_figure/square_figure_screen.dart @@ -84,8 +84,8 @@ class _SquareFigureScreenState extends State { child: AspectRatio( aspectRatio: 1.5, child: RePaint( - painter: _painter, - ), + painter: _painter, + ), ), ), ), @@ -114,8 +114,7 @@ class _SquareFigureScreenState extends State { min: 0, max: 100, value: value.toDouble(), - onChanged: (val) => _progress.value = - val.round().clamp(0, 100), + onChanged: (val) => _progress.value = val.round().clamp(0, 100), ), Text( '$value% (${_painter.cubesToUse} cubes)', @@ -132,8 +131,7 @@ class _SquareFigureScreenState extends State { dimension: 48, child: IconButton( icon: const Icon(Icons.bug_report), - onPressed: () => - _painter.switchPerformanceOverlay(), + onPressed: () => _painter.switchPerformanceOverlay(), ), ), ], @@ -237,7 +235,6 @@ class _SquarePainter extends PerformanceOverlayPainter { dimensionAmount = dimensionAmount + 1; final testNumber = math.pow(dimensionAmount, 3).ceil(); if (testNumber >= cubesAmount) { - break; } } @@ -268,35 +265,44 @@ class _SquarePainter extends PerformanceOverlayPainter { } int cubesToUse = 1; - void setCubesAmount(int amount){ + void setCubesAmount(int amount) { cubesToUse = amount * amount * 1; } Duration elapsedLast = Duration.zero; + String _lastKey = ''; @override void internalUpdate(RePaintBox box, Duration elapsed, double delta) { final size = box.size; final dimension = size.shortestSide; final center = size.center(Offset.zero); - _points = []; + final dt = (elapsedLast.inMilliseconds - elapsed.inMilliseconds) / 1024; + if (autoRotate) { yawAngle += 4 * dt; pitchAngle += 6 * dt; rollAngle += 8 * dt; } + + String getKey() => '$yawAngle-$pitchAngle-$rollAngle-$cubesToUse'; + final key = getKey(); + if (key == _lastKey) { + return; + } + _points = []; + _lastKey = key; elapsedLast = elapsed; final dimensionAmount = calculateDimension(cubesToUse); - - for (int cubeIdx = 0; cubeIdx < cubesToUse; ++cubeIdx) { final movedVertices = verticesCubeForIndex(cubeIdx, cubesToUse); final rotatedVertices = movedVertices .map((v) => rotate3D(v.map((e) => e * dimension).toList(), pitchAngle, yawAngle, rollAngle)) .toList(); - final projectedVertices = rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * (2 + dimensionAmount / 4))).toList(); + final projectedVertices = + rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * (2 + dimensionAmount / 4))).toList(); for (var i = 0; i < edgesCube.length; ++i) { final edge = edgesCube[i]; From 0afc52a5b623058e3c432a57c83f0f3abd1bf407 Mon Sep 17 00:00:00 2001 From: dvmatyun Date: Wed, 11 Dec 2024 19:53:23 +0400 Subject: [PATCH 4/4] use `drawRawPoints` to draw square skeletons --- .../square_figure/square_figure_screen.dart | 58 +++++++++++++------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/example/lib/src/feature/square_figure/square_figure_screen.dart b/example/lib/src/feature/square_figure/square_figure_screen.dart index b93d35f..ee864ae 100644 --- a/example/lib/src/feature/square_figure/square_figure_screen.dart +++ b/example/lib/src/feature/square_figure/square_figure_screen.dart @@ -57,7 +57,6 @@ class _SquareFigureScreenState extends State { focusNode: focusNode, autofocus: true, onKeyEvent: (key) { - print('Key: ${key.logicalKey.keyId}'); _painter.autoRotate = false; switch (key.logicalKey.keyId) { case 100: // right (d) @@ -147,21 +146,24 @@ class _SquareFigureScreenState extends State { } } +/// uses `drawRawPoints` to draw square skeletons class _SquarePainter extends PerformanceOverlayPainter { _SquarePainter() - : _positions = Float32List(verticiesAmount * 6), + : _positionsVerticies = Float32List(verticiesAmount * 6), + _positionsPoints = Float32List(verticiesAmount * 6), _colors = Int32List(verticiesAmount * 3) { _initVertices(); } static const verticiesAmount = 4; - final Float32List _positions; + final Float32List _positionsVerticies; final Int32List _colors; + // ignore: unused_field Later will experiment with vertices late Vertices _vertices; - List _points = []; + Float32List _positionsPoints; double pitchAngle = 0; // plane nose goes up-down double yawAngle = 0; // plane nose goes left-right @@ -184,7 +186,7 @@ class _SquarePainter extends PerformanceOverlayPainter { void _initVertices() { _vertices = Vertices.raw( VertexMode.triangles, - _positions, + _positionsVerticies, colors: _colors, ); } @@ -271,12 +273,26 @@ class _SquarePainter extends PerformanceOverlayPainter { Duration elapsedLast = Duration.zero; String _lastKey = ''; + + bool _computed = true; @override void internalUpdate(RePaintBox box, Duration elapsed, double delta) { + if (!_computed) { + return; + } + _computed = false; + _compute(box, elapsed, delta).then((_) { + _computed = true; + }); + } + + Future _compute(RePaintBox box, Duration elapsed, double delta) async { final size = box.size; final dimension = size.shortestSide; final center = size.center(Offset.zero); - + + final cubesAmount = cubesToUse; + final dt = (elapsedLast.inMilliseconds - elapsed.inMilliseconds) / 1024; if (autoRotate) { @@ -285,36 +301,42 @@ class _SquarePainter extends PerformanceOverlayPainter { rollAngle += 8 * dt; } - String getKey() => '$yawAngle-$pitchAngle-$rollAngle-$cubesToUse'; - final key = getKey(); + String getKey() => '$yawAngle-$pitchAngle-$rollAngle-$cubesAmount'; + final key = getKey(); // Not rebuild UI for static cubes if (key == _lastKey) { return; } - _points = []; _lastKey = key; elapsedLast = elapsed; - final dimensionAmount = calculateDimension(cubesToUse); + final dimensionAmount = calculateDimension(cubesAmount); - for (int cubeIdx = 0; cubeIdx < cubesToUse; ++cubeIdx) { - final movedVertices = verticesCubeForIndex(cubeIdx, cubesToUse); + final positionsPoints = Float32List(cubesAmount * edgesCube.length * 4); + for (int cubeIdx = 0; cubeIdx < cubesAmount; ++cubeIdx) { + // This one can help us to avoid blocking the UI thread: + // await Future.delayed(Duration.zero); + final movedVertices = verticesCubeForIndex(cubeIdx, cubesAmount); final rotatedVertices = movedVertices .map((v) => rotate3D(v.map((e) => e * dimension).toList(), pitchAngle, yawAngle, rollAngle)) .toList(); final projectedVertices = - rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * (2 + dimensionAmount / 4))).toList(); + rotatedVertices.map((v) => projectSimple(v, cameraDistance: dimension * (2 + dimensionAmount / 2))).toList(); for (var i = 0; i < edgesCube.length; ++i) { final edge = edgesCube[i]; final start2d = projectedVertices[edge[0]]; final end2d = projectedVertices[edge[1]]; - _points.add(Offset(center.dx + start2d[0], center.dy - start2d[1])); - _points.add(Offset(center.dx + end2d[0], center.dy - end2d[1])); + final shift = edgesCube.length * cubeIdx * 4; + positionsPoints[shift + i * 4] = center.dx + start2d[0]; + positionsPoints[shift + i * 4 + 1] = center.dy - start2d[1]; + positionsPoints[shift + i * 4 + 2] = center.dx + end2d[0]; + positionsPoints[shift + i * 4 + 3] = center.dy - end2d[1]; } } + _positionsPoints = positionsPoints; _vertices = Vertices.raw( VertexMode.triangles, - _positions, + _positionsVerticies, colors: _colors, ); } @@ -335,8 +357,6 @@ class _SquarePainter extends PerformanceOverlayPainter { ); paint.color = Colors.black; - for (var i = 0; i < (_points.length - 1); i += 2) { - canvas.drawLine(_points[i], _points[i + 1], paint); - } + canvas.drawRawPoints(PointMode.lines, _positionsPoints, paint); } }