Created
June 17, 2022 23:01
-
-
Save sooxt98/08032ef271eddac1c6ab78f806e9e305 to your computer and use it in GitHub Desktop.
Phyllotaxis Visualizer Flutter
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
// Copyright 2019 the Dart project authors. 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:math' as math; | |
import 'package:flutter/material.dart'; | |
const Color primaryColor = Colors.orange; | |
const TargetPlatform platform = TargetPlatform.android; | |
void main() { | |
runApp(Sunflower()); | |
} | |
class SunflowerPainter extends CustomPainter { | |
final double seedRadius; | |
final double scaleFactor; | |
final double angle; | |
static const tau = math.pi * 2; | |
static final phi = (math.sqrt(5) + 1) / 2; | |
final int seeds; | |
SunflowerPainter(this.seeds, this.seedRadius, this.scaleFactor, this.angle); | |
@override | |
void paint(Canvas canvas, Size size) { | |
final center = size.width / 2; | |
for (var i = 0; i < seeds; i++) { | |
final theta = i * tau * (angle/360) / phi; | |
final r = math.sqrt(i) * scaleFactor; | |
final x = center + r * math.cos(theta); | |
final y = center - r * math.sin(theta); | |
final offset = Offset(x, y); | |
if (!size.contains(offset)) { | |
continue; | |
} | |
drawSeed(canvas, x, y); | |
} | |
} | |
@override | |
bool shouldRepaint(SunflowerPainter oldDelegate) { | |
return oldDelegate.seeds != seeds; | |
} | |
// Draw a small circle representing a seed centered at (x,y). | |
void drawSeed(Canvas canvas, double x, double y) { | |
final paint = Paint() | |
..strokeWidth = 2 | |
..style = PaintingStyle.fill | |
..color = primaryColor; | |
canvas.drawCircle(Offset(x, y), seedRadius, paint); | |
} | |
} | |
class Sunflower extends StatefulWidget { | |
@override | |
State<StatefulWidget> createState() { | |
return _SunflowerState(); | |
} | |
} | |
class _SunflowerState extends State<Sunflower> { | |
double seeds = 100.0; | |
double seedRadius = 2.0; | |
double scaleFactor = 4.0; | |
double angle = 1.0; | |
int get seedCount => seeds.floor(); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData().copyWith( | |
platform: platform, | |
brightness: Brightness.dark, | |
sliderTheme: SliderThemeData.fromPrimaryColors( | |
primaryColor: primaryColor, | |
primaryColorLight: primaryColor, | |
primaryColorDark: primaryColor, | |
valueIndicatorTextStyle: const DefaultTextStyle.fallback().style, | |
), | |
), | |
home: Scaffold( | |
appBar: AppBar( | |
title: const Text("Sunflower"), | |
), | |
drawer: Drawer( | |
child: ListView( | |
children: const [ | |
DrawerHeader( | |
child: Center( | |
child: Text( | |
"Sunflower 🌻", | |
style: TextStyle(fontSize: 32), | |
), | |
), | |
), | |
], | |
), | |
), | |
body: Container( | |
constraints: const BoxConstraints.expand(), | |
decoration: BoxDecoration( | |
border: Border.all( | |
color: Colors.transparent, | |
), | |
), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.center, | |
mainAxisAlignment: MainAxisAlignment.start, | |
children: [ | |
Container( | |
decoration: BoxDecoration( | |
border: Border.all( | |
color: Colors.transparent, | |
), | |
), | |
child: SizedBox( | |
width: 400, | |
height: 400, | |
child: CustomPaint( | |
painter: SunflowerPainter(seedCount, seedRadius, scaleFactor, angle), | |
), | |
), | |
), | |
Text("Showing $seedCount seeds"), | |
Text("Angle $angle seeds"), | |
ConstrainedBox( | |
constraints: const BoxConstraints.tightFor(width: 1300), | |
child: Slider.adaptive( | |
min: 1, | |
max: 1000, | |
value: seeds, | |
onChanged: (newValue) { | |
setState(() { | |
seeds = newValue; | |
}); | |
}, | |
), | |
), | |
ConstrainedBox( | |
constraints: const BoxConstraints.tightFor(width: 1300), | |
child: Slider.adaptive( | |
min: 1, | |
max: 10000, | |
value: seedRadius, | |
onChanged: (newValue) { | |
setState(() { | |
seedRadius = newValue; | |
}); | |
}, | |
), | |
), | |
ConstrainedBox( | |
constraints: const BoxConstraints.tightFor(width: 1300), | |
child: Slider.adaptive( | |
min: 1, | |
max: 100, | |
value: scaleFactor, | |
onChanged: (newValue) { | |
setState(() { | |
scaleFactor = newValue; | |
}); | |
}, | |
), | |
), | |
ConstrainedBox( | |
constraints: const BoxConstraints.tightFor(width: 1300), | |
child: Slider.adaptive( | |
min: 1, | |
max: 360, | |
value: angle, | |
onChanged: (newValue) { | |
setState(() { | |
angle = newValue; | |
}); | |
}, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment