Skip to content

Instantly share code, notes, and snippets.

@roipeker
Last active November 27, 2020 03:35
Show Gist options
  • Save roipeker/dbf792b862ad8dfb526c227c2e1d4ad9 to your computer and use it in GitHub Desktop.
Save roipeker/dbf792b862ad8dfb526c227c2e1d4ad9 to your computer and use it in GitHub Desktop.
graphx: jelly green
/// roipeker2020
///
/// As other samples, use the `SceneBuilderWidget`.
///
/// web demo:
/// https://roi-graphx-jelly-green.surge.sh
///
/// source:
/// https://gist.github.com/roipeker/dbf792b862ad8dfb526c227c2e1d4ad9
///
import 'package:flutter/material.dart';
import 'package:graphx/graphx.dart';
class DemoJellyDots extends Sprite {
var points = <Dot>[];
int totalPoints = 120;
double size = 18;
double mouseRadius = 80.0, mouseStrength = .06, stiffness = .05;
bool outlineJelly = false;
double get sw => stage.stageWidth;
double get sh => stage.stageHeight;
@override
void addedToStage() {
stage.keyboard.onDown.add((event) {
if (event.isKey(LogicalKeyboardKey.keyD)) {
points.forEach((d) => d.visible = !d.visible);
} else if (event.isKey(LogicalKeyboardKey.keyO)) {
outlineJelly = !outlineJelly;
}
});
points = List.generate(totalPoints, (i) {
var angle = MathUtils.pi2 / totalPoints * i;
var dot = Dot(
px: sw / 2 + cos(angle) * 150 + GameUtils.rndRange(-5, 5),
py: sh / 2 + sin(angle) * 150 + GameUtils.rndRange(-5, 5),
);
dot.visible = false;
addChild(dot);
return dot;
});
}
@override
void update(t) {
if (stage.pointer.isDown) {
size = 20;
stiffness = .15;
} else {
size = 15;
stiffness = .05;
}
for (var i = 0; i < totalPoints - 1; ++i) {
var p0 = points[i];
for (var j = i + 1; j < totalPoints; ++j) {
var p1 = points[j];
compare(p0, p1, j == i + 1);
}
}
compare(points[totalPoints - 1], points[0], true);
updatePoints();
draw(graphics);
}
void updatePoints() {
/// save touch coordinates, to avoid the matrix transformation.
double mx = mouseX;
double my = mouseY;
points.forEach((dot) {
var dx = dot.x - mx;
var dy = dot.y - my;
var dist = sqrt(dx * dx + dy * dy);
if (dist < mouseRadius) {
var tx = mx + dx / dist * mouseRadius;
var ty = my + dy / dist * mouseRadius;
/// the good old Euclidean :) ... distance based easing
dot.vx += (tx - dot.x) * mouseStrength;
dot.vy += (ty - dot.y) * mouseStrength;
}
dot.x += dot.vx;
dot.y += dot.vy;
dot.vx *= dot.friction;
dot.vy *= dot.friction;
});
}
void compare(_Dot p0, _Dot p1, [bool isSibling = false]) {
var dx = p1.x - p0.x;
var dy = p1.y - p0.y;
var dist = sqrt(dx * dx + dy * dy);
if (isSibling) {
var tx = p0.x + dx / dist * size;
var ty = p0.y + dy / dist * size;
var ax = (tx - p1.x) * stiffness;
var ay = (ty - p1.y) * stiffness;
p1.vx += ax;
p1.vy += ay;
p0.vx -= ax;
p0.vy -= ay;
} else if (dist < size * 2) {
var tx = p0.x + dx / dist * size * 2;
var ty = p0.y + dy / dist * size * 2;
var ax = (tx - p1.x) * stiffness;
var ay = (ty - p1.y) * stiffness;
p1.vx += ax;
p1.vy += ay;
p0.vx -= ax;
p0.vy -= ay;
}
}
void draw(Graphics g) {
g.clear();
g.beginFill(Colors.lightGreen.value);
if (outlineJelly) {
g.lineStyle(3, Colors.lightGreen.shade700.value, 1);
}
/// get the middle points to get a smooth cubic bezier...
var pa = points[0];
var pz = points.last;
var mid1x = (pz.x + pa.x) / 2;
var mid1y = (pz.y + pa.y) / 2;
g.moveTo(mid1x, mid1y);
for (var i = 0; i < points.length - 1; ++i) {
var p0 = points[i];
var p1 = points[i + 1];
g.curveTo(
p0.x,
p0.y,
(p0.x + p1.x) / 2,
(p0.y + p1.y) / 2,
);
}
/// close the thing!
g.curveTo(pz.x, pz.y, mid1x, mid1y);
g.endFill();
}
}
/// dot (point) reference.
class Dot extends Shape {
double px, py, vx = 0, vy = 0, friction = .9;
Dot({this.px, this.py}) {
graphics
.beginFill(Colors.yellowAccent.value, 1)
.lineStyle(2, Colors.green.value, .5)
.drawCircle(0, 0, 4)
.endFill();
setPosition(px, py);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment