blob: 1f94fce6f1cd3a320bd48476449758a102a8a1f2 [file] [log] [blame]
part of flutter_sprites;
enum EffectLineWidthMode {
linear,
barrel,
}
enum EffectLineAnimationMode {
none,
scroll,
random,
}
class EffectLine extends Node {
EffectLine({
this.texture: null,
this.transferMode: TransferMode.dstOver,
List<Point> points,
this.widthMode : EffectLineWidthMode.linear,
this.minWidth: 10.0,
this.maxWidth: 10.0,
this.widthGrowthSpeed: 0.0,
this.animationMode: EffectLineAnimationMode.none,
this.scrollSpeed: 0.1,
double scrollStart: 0.0,
this.fadeDuration: null,
this.fadeAfterDelay: null,
this.textureLoopLength: null,
this.simplify: true,
ColorSequence colorSequence
}) {
if (points == null)
this.points = <Point>[];
else
this.points = points;
_colorSequence = colorSequence;
if (_colorSequence == null) {
_colorSequence = new ColorSequence.fromStartAndEndColor(
const Color(0xffffffff),
const Color(0xffffffff)
);
}
_offset = scrollStart;
_painter = new TexturedLinePainter(points, _colors, _widths, texture);
_painter.textureLoopLength = textureLoopLength;
}
final Texture texture;
final TransferMode transferMode;
final EffectLineWidthMode widthMode;
final double minWidth;
final double maxWidth;
final double widthGrowthSpeed;
final EffectLineAnimationMode animationMode;
final double scrollSpeed;
ColorSequence _colorSequence;
ColorSequence get colorSequence => _colorSequence;
List<Point> _points;
List<Point> get points => _points;
void set points(List<Point> points) {
_points = points;
_pointAges = <double>[];
for (int i = 0; i < _points.length; i++) {
_pointAges.add(0.0);
}
}
List<double> _pointAges;
List<Color> _colors;
List<double> _widths;
final double fadeDuration;
final double fadeAfterDelay;
final double textureLoopLength;
final bool simplify;
TexturedLinePainter _painter;
double _offset = 0.0;
void update(double dt) {
// Update scrolling position
if (animationMode == EffectLineAnimationMode.scroll) {
_offset += dt * scrollSpeed;
_offset %= 1.0;
} else if (animationMode == EffectLineAnimationMode.random) {
_offset = randomDouble();
}
// Update age of line points and remove if neccesasry
if (fadeDuration != null && fadeAfterDelay != null) {
// Increase age of points
for (int i = _points.length - 1; i >= 0; i--) {
_pointAges[i] += dt;
}
// Check if the first/oldest point should be removed
while(_points.length > 0 && _pointAges[0] > (fadeDuration + fadeAfterDelay)) {
// Update scroll if it isn't the last and only point that is about to removed
if (_points.length > 1 && textureLoopLength != null) {
double dist = GameMath.distanceBetweenPoints(_points[0], _points[1]);
_offset = (_offset - (dist / textureLoopLength)) % 1.0;
if (_offset < 0.0) _offset += 1;
}
// Remove the point
_pointAges.removeAt(0);
_points.removeAt(0);
}
}
}
void paint(Canvas canvas) {
if (points.length < 2) return;
_painter.points = points;
// Calculate colors
List<double> stops = _painter.calculatedTextureStops;
List<Color> colors = <Color>[];
for (int i = 0; i < stops.length; i++) {
double stop = stops[i];
Color color = _colorSequence.colorAtPosition(stop);
if (fadeDuration != null && fadeAfterDelay != null) {
double age = _pointAges[i];
if (age > fadeAfterDelay) {
double fade = 1.0 - (age - fadeAfterDelay) / fadeDuration;
int alpha = (color.alpha * fade).toInt().clamp(0, 255);
color = new Color.fromARGB(alpha, color.red, color.green, color.blue);
}
}
colors.add(color);
}
_painter.colors = colors;
// Calculate widths
List<double> widths = <double>[];
for (int i = 0; i < stops.length; i++) {
double stop = stops[i];
double growth = math.max(widthGrowthSpeed * _pointAges[i], 0.0);
if (widthMode == EffectLineWidthMode.linear) {
double width = minWidth + (maxWidth - minWidth) * stop + growth;
widths.add(width);
} else if (widthMode == EffectLineWidthMode.barrel) {
double width = minWidth + math.sin(stop * math.PI) * (maxWidth - minWidth) + growth;
widths.add(width);
}
}
_painter.widths = widths;
_painter.textureStopOffset = _offset;
_painter.paint(canvas);
}
void addPoint(Point point) {
// Skip duplicate points
if (points.length > 0 && point.x == points[points.length - 1].x && point.y == points[points.length - 1].y)
return;
if (simplify && points.length >= 2 && GameMath.distanceBetweenPoints(point, points[points.length - 2]) < 10.0) {
// Check if we should remove last point before adding the new one
// Calculate the square distance from the middle point to the line of the
// new point and the second to last point
double dist2 = _distToSeqment2(
points[points.length - 1],
point,
points[points.length - 2]
);
// If the point is on the line, remove it
if (dist2 < 1.0) {
_points.removeAt(_points.length - 1);
}
}
// Add point and point's age
_points.add(point);
_pointAges.add(0.0);
}
double _sqr(double x) => x * x;
double _dist2(Point v, Point w) => _sqr(v.x - w.x) + _sqr(v.y - w.y);
double _distToSeqment2(Point p, Point v, Point w) {
double l2 = _dist2(v, w);
if (l2 == 0.0) return _dist2(p, v);
double t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2;
if (t < 0) return _dist2(p, v);
if (t > 1) return _dist2(p, w);
return _dist2(p, new Point(v.x + t * (w.x - v.x), v.y + t * (w.y - v.y)));
}
}