Skip to content

Instantly share code, notes, and snippets.

@slightfoot
Created May 7, 2025 18:57
Show Gist options
  • Save slightfoot/40c72242fc9fb0ef8cf3cf5bd75a5883 to your computer and use it in GitHub Desktop.
Save slightfoot/40c72242fc9fb0ef8cf3cf5bd75a5883 to your computer and use it in GitHub Desktop.
3D spinny ball - by Simon Lightfoot :: #HumpdayQandA on 7th May 2025 :: https://www.youtube.com/watch?v=CqmhwUnesvQ
// MIT License
//
// Copyright (c) 2025 Simon Lightfoot
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import 'dart:math' as math;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' show Vector3;
void main() {
runApp(
MaterialApp(
home: AiExample(),
),
);
}
@immutable
class AiExample extends StatefulWidget {
const AiExample({super.key});
@override
State<AiExample> createState() => _AiExampleState();
}
class _AiExampleState extends State<AiExample> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(milliseconds: 10000),
vsync: this,
);
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: CustomPaint(
painter: AiPainter(_controller),
size: Size(300.0, 300.0),
),
),
);
}
}
class AiPainter extends CustomPainter {
AiPainter(this.animation) : super(repaint: animation);
final Animation<double> animation;
@override
void paint(Canvas canvas, Size size) {
final rect = Offset.zero & size;
//canvas.drawRect(rect, Paint()..color = Colors.red);
canvas.save();
canvas.translate(size.width / 2, size.height / 2);
final matrix = Matrix4.identity()
//..setEntry(3, 2, 0.002)
..rotateX(math.pi * 0.2)
..rotateX(math.pi * 2 * animation.value)
..rotateY(math.pi * 2 * animation.value);
final radius = size.shortestSide / 2 - 10.0;
final gradient = ui.Gradient.radial(
Offset.fromDirection(360 * (math.pi / 180) * animation.value, radius * 0.8),
radius,
[Colors.blueAccent, Colors.purpleAccent],
);
canvas.drawCircle(
Offset.zero,
radius,
Paint()
..shader = gradient
..maskFilter = ui.MaskFilter.blur(
ui.BlurStyle.normal,
ui.Shadow.convertRadiusToSigma(10.0),
),
);
final paint = Paint()..color = Colors.white;
for (var inclination = 0.0; inclination < 360.0; inclination += 10) {
for (var azimuth = 0.0; azimuth < 180.0; azimuth += 10) {
final phi = inclination * (math.pi / 180);
final theta = azimuth * (math.pi / 180);
final v3 = Vector3(
radius * math.sin(phi) * math.cos(theta),
radius * math.sin(phi) * math.sin(theta),
radius * math.cos(phi),
);
final v32 = matrix.perspectiveTransform(v3);
final offset = Offset(v32.x, v32.y);
final cr = (3.0 * v32.z) / radius;
if (cr >= 0.5) {
canvas.drawCircle(offset, (3.0 * v32.z) / radius, paint);
}
}
}
canvas.restore();
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment