266 lines
8.2 KiB
Dart
266 lines
8.2 KiB
Dart
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
part of flutter_sprites;
|
|
|
|
/// Used by [EffectLine] to determine how the width of the line is calculated.
|
|
enum EffectLineWidthMode {
|
|
|
|
/// Linear interpolation between minWidth at the start and maxWidth at the
|
|
/// end of the line.
|
|
linear,
|
|
|
|
/// Creates a barrel shaped line, with minWidth at the end points of the line
|
|
/// and maxWidth at the middle.
|
|
barrel,
|
|
}
|
|
|
|
/// Used by [EffectLine] to determine how the texture of the line is animated.
|
|
enum EffectLineAnimationMode {
|
|
|
|
/// The texture of the line isn't animated.
|
|
none,
|
|
|
|
/// The texture of the line is scrolling.
|
|
scroll,
|
|
|
|
/// The texture of the line is set to a random position at every frame. This
|
|
/// mode is useful for creating flashing or electricity styled effects.
|
|
random,
|
|
}
|
|
|
|
/// The EffectLine class is using the [TexturedLine] class to draw animated
|
|
/// lines. These can be used to draw things such as smoke trails, electricity
|
|
/// effects, or other animated types of lines.
|
|
class EffectLine extends Node {
|
|
|
|
/// Creates a new EffectLine with the specified parameters. Only the
|
|
/// [texture] parameter is required, all other parameters are optional.
|
|
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;
|
|
}
|
|
|
|
/// The texture used to draw the line.
|
|
final Texture texture;
|
|
|
|
/// The transfer mode used to draw the line, default is
|
|
/// [TransferMode.dstOver].
|
|
final TransferMode transferMode;
|
|
|
|
/// Mode used to calculate the width of the line.
|
|
final EffectLineWidthMode widthMode;
|
|
|
|
/// The width of the line at its thinnest point.
|
|
final double minWidth;
|
|
|
|
/// The width of the line at its thickest point.
|
|
final double maxWidth;
|
|
|
|
/// The speed at which the line is growing, defined in points per second.
|
|
final double widthGrowthSpeed;
|
|
|
|
/// The mode used to animate the texture of the line.
|
|
final EffectLineAnimationMode animationMode;
|
|
|
|
/// The speed of which the texture of the line is scrolling. This property
|
|
/// is only used if the [animationMode] is set to
|
|
/// [EffectLineAnimationMode.scroll].
|
|
final double scrollSpeed;
|
|
|
|
/// Color gradient used to draw the line, from start to finish.
|
|
ColorSequence get colorSequence => _colorSequence;
|
|
|
|
ColorSequence _colorSequence;
|
|
|
|
/// List of points that make up the line. Typically, you will only want to
|
|
/// set this at the beginning. Then use [addPoint] to add additional points
|
|
/// to the line.
|
|
List<Point> get points => _points;
|
|
|
|
set points(List<Point> points) {
|
|
_points = points;
|
|
_pointAges = <double>[];
|
|
for (int i = 0; i < _points.length; i++) {
|
|
_pointAges.add(0.0);
|
|
}
|
|
}
|
|
|
|
List<Point> _points;
|
|
|
|
List<double> _pointAges;
|
|
List<Color> _colors;
|
|
List<double> _widths;
|
|
|
|
/// The time it takes for an added point to fade out. It's total life time is
|
|
/// [fadeDuration] + [fadeAfterDelay].
|
|
final double fadeDuration;
|
|
|
|
/// The time it takes until an added point starts to fade out.
|
|
final double fadeAfterDelay;
|
|
|
|
/// The length, in points, that the texture is stretched to. If the
|
|
/// textureLoopLength is shorter than the line, the texture will be looped.
|
|
final double textureLoopLength;
|
|
|
|
/// True if the line should be simplified by removing points that are close
|
|
/// to other points. This makes drawing faster, but can result in a slight
|
|
/// jittering effect when points are added.
|
|
final bool simplify;
|
|
|
|
TexturedLinePainter _painter;
|
|
double _offset = 0.0;
|
|
|
|
@override
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
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);
|
|
}
|
|
|
|
/// Adds a new point to the end of the line.
|
|
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)));
|
|
}
|
|
}
|