【Flutter】 모방 변환을 통해 별 회전
만든 물건
컨디션
기계: M1 맥북 에어
편집기:VScode
보관소: https://github.com/kenta-wakasa/flutter_playground
대충 모방해서 바꾸면
공식으로 간단하게 모방 변환이 이렇다는 것을 나타낸다.
y = Ax + t\tag1
이것이 무엇을 의미하는지 말하자면 x는 x에 어떤 변화를 가져오는 계수 A와 평행이동을 나타내는 t에 따라 y로 변한다.
컴퓨터 도형의 세계에서 이 얻기 어려운 공식은 확대, 회전, 평행 이동 도형을 할 수 있다.
예를 들어 (1)의 공식을 x 좌표, y 좌표에 적용하여 고려하면 다음과 같다.
\begin{cases}
x' = ax+by+x_0\\
y' = cx+dy+y_0
\end{cases}
이거 줄 서서 쓰면 이런 느낌이야.나는 내가 펼쳐 보면 매우 명백하다고 생각한다.
\begin{bmatrix}
x'\\
y'\\
1
\end{bmatrix}
=
\begin{bmatrix}
a & b & x_0\\
c & d & y_0\\
0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
x\\
y\\
1
\end{bmatrix}
이 a, b, c, d의 부분은 (1) 중의 A에 해당한다
A =
\begin{bmatrix}
a & b\\
c & d\\
\end{bmatrix}
그렇습니다.x, y만\theta 회전시키려면 다음과 같은 행렬을 나타낼 수 있습니다.
A =
\begin{bmatrix}
\cos\theta & -\sin\theta\\
\sin\theta & \cos\theta\\
\end{bmatrix}
이른바 회전 행렬이다.
여기에 상수를 더하면 x와 y 방향에서 같은 비율로 확대하고 축소할 수 있다.
즉, 상수가 a라면 이렇게 쓴다.
A = a
\begin{bmatrix}
\cos\theta & -\sin\theta\\
\sin\theta & \cos\theta\\
\end{bmatrix}
그다음에 이거만 코드에 넣으면 돼!
소스 코드 작성
모방 변환은 이렇다.
그렇긴 하지만 확대, 축소, 회전, 평행 이동만 할 뿐 잘라내지 않았다.
변환된 부분을 모방하다
Offset _affinTranslate(
Offset pos, {
double radians = 0,
double scale = 1.0,
double tx = 0,
double ty = 0,
}) {
final dx = scale * (pos.dx * cos(radians) - pos.dy * sin(radians)) + tx;
final dy = scale * (pos.dx * sin(radians) + pos.dy * cos(radians)) + ty;
return Offset(dx, dy);
}
컨트롤러
별이 적혀있어서 이 부분도 참고할 수 있을 것 같아요.
전선으로 도형을 그리니 매우 즐겁다.
canvas_controller.dart
import 'dart:math';
import 'package:flutter/material.dart';
class CanvasController extends ChangeNotifier {
CanvasController() {
_radians = 0;
_scale = 1;
_tx = 0;
_ty = 0;
_initOffset = const Offset(0, -10); // 星型の最初の頂点を決める。
_sourceOffsetList.add(_initOffset);
// 星型は同一円の円周上を 4π/5 ずつ回転させた点を結ぶことで描ける。
for (var radians = 0.0; radians <= 4 * pi; radians += 4 * pi / 5) {
_sourceOffsetList.add(_affinTranslate(_initOffset, radians: radians));
}
_destinationOffsetList = _sourceOffsetList;
}
double _radians;
double get radians => _radians;
set radians(double radians) {
_radians = radians;
_update();
}
double _scale;
double get scale => _scale;
set scale(double scale) {
_scale = scale;
_update();
}
double _tx;
double get tx => _tx;
set tx(double tx) {
_tx = tx;
_update();
}
double _ty;
double get ty => _ty;
set ty(double ty) {
_ty = ty;
_update();
}
Offset _initOffset;
final _sourceOffsetList = <Offset>[];
List<Offset> _destinationOffsetList;
List<Offset> get destinationOffsetList => _destinationOffsetList;
/// 拡大・回転・平行移動のアフィン変換は次のようになっている
/// ```
/// dx = scale * ( x*cosθ - y*sinθ ) + tx
/// dy = scale * ( x*sinθ + y*cosθ ) + ty
/// ```
Offset _affinTranslate(
Offset pos, {
double radians = 0,
double scale = 1.0,
double tx = 0,
double ty = 0,
}) {
final dx = scale * (pos.dx * cos(radians) - pos.dy * sin(radians)) + tx;
final dy = scale * (pos.dx * sin(radians) + pos.dy * cos(radians)) + ty;
return Offset(dx, dy);
}
/// すべての offset に対してアフィン変換をかける
void _update() {
_destinationOffsetList = _sourceOffsetList
.map((s) =>
_affinTranslate(s, radians: radians, scale: scale, tx: tx, ty: ty))
.toList();
notifyListeners();
}
}
페이지
슬라이더나 문자 상자에 를 입력합니다.
canvas_page.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'canvas_controller.dart';
import 'painter.dart';
final canvasProvider = ChangeNotifierProvider.autoDispose<CanvasController>(
(ref) => CanvasController(),
);
class CanvasPage extends ConsumerWidget {
const CanvasPage({Key key}) : super(key: key);
static const String title = 'アフィン変換';
static const textStyle = TextStyle(fontSize: 24, fontWeight: FontWeight.bold);
Widget build(BuildContext context, ScopedReader watch) {
final _provider = watch(canvasProvider);
return Scaffold(
appBar: AppBar(
title: const Text(title,
style: TextStyle(fontWeight: FontWeight.bold))),
body: SafeArea(
child: Stack(children: [
Center(
child: CustomPaint(
painter: Painter(offsetList: _provider.destinationOffsetList)),
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(
height: 80,
width: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Slider(
onChanged: (value) => _provider.radians = value,
value: _provider.radians,
min: 0,
max: 2 * pi),
const Text('Rotation', style: textStyle)
])),
SizedBox(
height: 80,
width: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Slider(
value: _provider.scale,
onChanged: (value) => _provider.scale = value,
min: 1,
max: 20),
const Text('Scale', style: textStyle)
]))
]),
Align(
alignment: Alignment.bottomCenter,
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
SizedBox(
width: 160,
child: TextField(
style: const TextStyle(fontSize: 36),
decoration: const InputDecoration(
labelText: 'tx',
hintText: '0',
border: OutlineInputBorder()),
onChanged: (tx) {
_provider.tx = double.parse(tx);
},
),
),
const SizedBox(width: 16, height: 500),
SizedBox(
width: 160,
child: TextField(
style: const TextStyle(fontSize: 36),
decoration: const InputDecoration(
labelText: 'ty',
hintText: '0',
border: OutlineInputBorder(),
),
onChanged: (ty) {
_provider.ty = double.parse(ty);
}))
]))
])));
}
}
페인트
그림 그리는 중입니다.
이번에 오프셋에서 점군 데이터를 준비했습니다.
그것들을 연결해서 선분으로 그려라.
Path()..addPolygon
점을 연결해서 선을 그리면 됩니다.painter.dart
import 'package:flutter/material.dart';
class Painter extends CustomPainter {
Painter({ this.offsetList});
List<Offset> offsetList;
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.orange
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
final path = Path()..addPolygon(offsetList, false);
canvas.drawPath(path, paint);
}
bool shouldRepaint(Painter oldDelegate) {
return true;
}
}
최후
Offset
반에는 비례자와 평행으로 이동하는 방법이 있지만 원점을 중심으로 회전하는 방법은 없다.그냥 끼는 것도 좋을 것 같아요.
Reference
이 문제에 관하여(【Flutter】 모방 변환을 통해 별 회전), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/pressedkonbu/articles/affin-translate텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)