Created
April 28, 2024 19:04
-
-
Save aakash-pamnani/99461f4302740ef084e77390e9f66dc3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math'; | |
import 'dart:ui'; | |
class ForcePoint { | |
ForcePoint(this.x, this.y); | |
double computed = 0; | |
double force = 0; | |
num x, y; | |
num get magnitude => x * x + y * y; | |
ForcePoint add(ForcePoint point) => ForcePoint(point.x + x, point.y + y); | |
ForcePoint copyWith({num? x, num? y}) => ForcePoint(x ?? this.x, y ?? this.y); | |
} | |
class Ball { | |
Ball(Size size) { | |
double vel({double ratio = 1}) => | |
(Random().nextDouble() > .5 ? 1 : -1) * | |
(.2 + .25 * Random().nextDouble()); | |
velocity = ForcePoint(vel(ratio: 0.25), vel()); | |
var i = .1; | |
var h = 1.5; | |
double calculatePosition(double fullSize) => | |
Random().nextDouble() * fullSize; | |
pos = ForcePoint( | |
calculatePosition(size.width), calculatePosition(size.height)); | |
this.size = size.shortestSide / 15 + | |
(Random().nextDouble() * (h - i) + i) * (size.shortestSide / 15); | |
} | |
late ForcePoint pos; | |
late double size; | |
late ForcePoint velocity; | |
moveIn(Size size) { | |
if (pos.x >= size.width - this.size) { | |
if (pos.x > 0) velocity.x = -velocity.x; | |
pos = pos.copyWith(x: size.width - this.size); | |
} else if (pos.x <= this.size) { | |
if (velocity.x < 0) velocity.x = -velocity.x; | |
pos.x = this.size; | |
} | |
if (pos.y >= size.height - this.size) { | |
if (velocity.y > 0) velocity.y = -velocity.y; | |
pos.y = size.height - this.size; | |
} else if (pos.y <= this.size) { | |
if (velocity.y < 0) velocity.y = -velocity.y; | |
pos.y = this.size; | |
} | |
pos = pos.add(velocity); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
/// Lava clock. | |
/// Copied from https://github.com/RetroMusicPlayer/Paisa/ | |
class LavaAnimation extends StatefulWidget { | |
const LavaAnimation({ | |
super.key, | |
required this.child, | |
this.color, | |
}); | |
final Widget child; | |
final Color? color; | |
@override | |
LavaAnimationState createState() => LavaAnimationState(); | |
} | |
class LavaAnimationState extends State<LavaAnimation> | |
with TickerProviderStateMixin { | |
final Lava lava = Lava(10); | |
late final AnimationController _animation = | |
AnimationController(duration: const Duration(minutes: 10), vsync: this) | |
..repeat(); | |
@override | |
void dispose() { | |
_animation.dispose(); | |
super.dispose(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return LayoutBuilder( | |
builder: (context, constraints) => AnimatedBuilder( | |
animation: _animation, | |
builder: (BuildContext context, _) => CustomPaint( | |
size: const Size(100, 100), | |
painter: LavaPainter( | |
lava, | |
color: widget.color ?? context.primary, | |
), | |
child: widget.child, | |
), | |
), | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
import 'ball.dart'; | |
class LavaPainter extends CustomPainter { | |
LavaPainter(this.lava, {required this.color}); | |
final Color color; | |
final Lava lava; | |
@override | |
void paint(Canvas canvas, Size size) { | |
if (lava.size != size) lava.updateSize(size); | |
lava.draw(canvas, size, color, debug: false); | |
} | |
@override | |
bool shouldRepaint(LavaPainter oldDelegate) { | |
return true; | |
} | |
} | |
class Lava { | |
Lava(this.ballsLength); | |
late List<Ball> balls; | |
int ballsLength; | |
double iter = 0; | |
final ix = [1, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1]; | |
late Map<int, Map<int, ForcePoint>> matrix; | |
final List<int> mscases = [0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 0, 2, 1, 1, 0]; | |
bool paint = false; | |
final List<int> plx = [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0]; | |
final List<int> ply = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1]; | |
late Rect sRect; | |
int sign = 1; | |
Size? size; | |
num step = 5; | |
double get width => size?.width ?? 10; | |
double get height => size?.height ?? 10; | |
double get sx => (width ~/ step).floor().toDouble(); | |
double get sy => (height ~/ step).floor().toDouble(); | |
updateSize(Size size) { | |
this.size = size; | |
sRect = Rect.fromCenter( | |
center: Offset.zero, width: sx.toDouble(), height: sy.toDouble()); | |
matrix = {}; | |
for (int i = (sRect.left - step).toInt(); i < sRect.right + step; i++) { | |
matrix[i] = {}; | |
for (int j = (sRect.top - step).toInt(); j < sRect.bottom + step; j++) { | |
matrix[i]![j] = ForcePoint( | |
(i + sx ~/ 2).toDouble() * step, (j + sy ~/ 2).toDouble() * step); | |
} | |
} | |
balls = List.filled(ballsLength, Ball(const Size(0, 0))); | |
for (var index = 0; ballsLength > index; index++) { | |
balls[index] = Ball(size); | |
} | |
} | |
double computeForce(int sx, int sy) { | |
double force; | |
if (!sRect.contains(Offset(sx.toDouble(), sy.toDouble()))) { | |
force = .6 * sign; | |
} else { | |
force = 0; | |
final ForcePoint? point = matrix[sx]![sy]; | |
for (final ball in balls) { | |
force += ball.size * | |
ball.size / | |
(-2 * point!.x * ball.pos.x - | |
2 * point.y * ball.pos.y + | |
ball.pos.magnitude + | |
point.magnitude); | |
} | |
force *= sign; | |
} | |
matrix[sx]![sy]!.force = force; | |
return force; | |
} | |
List? marchingSquares(List? pathParameters, Path path) { | |
int sx = pathParameters?[0] ?? 0; | |
int sy = pathParameters?[1] ?? 0; | |
int pdir = pathParameters?[2] ?? 0; | |
if (matrix[sx]![sy]!.computed == iter) return null; | |
int dir; | |
int mscase = 0; | |
for (var a = 0; 4 > a; a++) { | |
final dx = ix[a + 12]; | |
final dy = ix[a + 16]; | |
double force = matrix[sx + dx]![sy + dy]!.force; | |
if (force > 0 && sign < 0 || force < 0 && sign > 0 || force == 0) { | |
force = computeForce(sx + dx, sy + dy); | |
} | |
if (force.abs() > 1) mscase += pow(2, a).toInt(); | |
} | |
if (15 == mscase) { | |
return [sx, sy - 1, null]; | |
} else if (5 == mscase) { | |
dir = 2 == pdir ? 3 : 1; | |
} else if (10 == mscase) { | |
dir = 3 == pdir ? 0 : 2; | |
} else { | |
dir = mscases[mscase]; | |
matrix[sx]![sy]!.computed = iter; | |
} | |
final dx1 = plx[4 * dir + 2]; | |
final dy1 = ply[4 * dir + 2]; | |
final pForce1 = matrix[sx + dx1]![sy + dy1]!.force; | |
final dx2 = plx[4 * dir + 3]; | |
final dy2 = ply[4 * dir + 3]; | |
final pForce2 = matrix[sx + dx2]![sy + dy2]!.force; | |
final p = | |
step / ((pForce1.abs() - 1).abs() / (pForce2.abs() - 1).abs() + 1.0); | |
final dxX = plx[4 * dir]; | |
final dyX = ply[4 * dir]; | |
final dxY = plx[4 * dir + 1]; | |
final dyY = ply[4 * dir + 1]; | |
final lineX = matrix[sx + dxX]![sy + dyX]!.x + ix[dir] * p; | |
final lineY = matrix[sx + dxY]![sy + dyY]!.y + ix[dir + 4] * p; | |
if (paint == false) { | |
path.moveTo(lineX, lineY); | |
} else { | |
path.lineTo(lineX, lineY); | |
} | |
paint = true; | |
return [sx + ix[dir + 4], sy + ix[dir + 8], dir]; | |
} | |
draw(Canvas canvas, Size size, Color color, {bool debug = false}) { | |
for (Ball ball in balls) { | |
ball.moveIn(size); | |
} | |
try { | |
iter++; | |
sign = -sign; | |
paint = false; | |
for (Ball ball in balls) { | |
Path path = Path(); | |
List? pathParameters = [ | |
(ball.pos.x / step - sx / 2).round(), | |
(ball.pos.y / step - sy / 2).round(), | |
null | |
]; | |
do { | |
pathParameters = marchingSquares(pathParameters, path); | |
} while (pathParameters != null); | |
if (paint) { | |
path.close(); | |
Paint paint = Paint()..color = color; | |
canvas.drawPath(path, paint); | |
this.paint = false; | |
} | |
} | |
} catch (_) {} | |
if (debug) { | |
for (final ball in balls) { | |
canvas.drawCircle(Offset(ball.pos.x.toDouble(), ball.pos.y.toDouble()), | |
ball.size, Paint()..color = Colors.black.withOpacity(0.5)); | |
} | |
matrix.forEach( | |
(_, item) => item.forEach( | |
(_, point) => canvas.drawCircle( | |
Offset(point.x.toDouble(), point.y.toDouble()), | |
max(1, min(point.force.abs(), 5)), | |
Paint()..color = point.force > 0 ? Colors.blue : Colors.red, | |
), | |
), | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment