this is the sample code provided in the LinePath2D class
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.motionPaths.*;
import flash.geom.Point;
//create a LinePath2D with 5 Points
var path:LinePath2D = new LinePath2D([new Point(0, 0),
new Point(100, 100),
new Point(350, 150),
new Point(50, 200),
new Point(550, 400)]);
//add it to the display list so we can see it (you can skip this if you prefer)
addChild(path);
//create an array containing 30 blue squares
var boxes:Array = [];
for (var i:int = 0; i < 30; i++) {
boxes.push(createSquare(10, 0x0000FF));
}
//distribute the blue squares evenly across the entire path and set them to autoRotate
path.distribute(boxes, 0, 1, true);
//put a red square exactly halfway through the 2nd segment
path.addFollower(createSquare(10, 0xFF0000), path.getSegmentProgress(2, 0.5));
//tween all of the squares through the path once (wrapping when they reach the end)
TweenMax.to(path, 20, {progress:1});
//while the squares are animating through the path, tween the path's position and rotation too!
TweenMax.to(path, 3, {rotation:180, x:550, y:400, ease:Back.easeOut, delay:3});
//method for creating squares
function createSquare(size:Number, color:uint=0xFF0000):Shape {
var s:Shape = new Shape();
s.graphics.beginFill(color, 1);
s.graphics.drawRect(-size / 2, -size / 2, size, size);
s.graphics.endFill();
this.addChild(s);
return s;
}
what would be the simplest way of fading the elements in and out?
should I add a tween to each of the items, or should i hook into the update listener?
I got it working with this
TweenMax.to(path, $speedNorm, { progress:1, ease:Linear.easeNone, repeat: -1, onUpdate:function():void
{
for (var j:int = 0; j < path.followers.length; j++)
{
var $follower:PathFollower = path.followers[j];
if ($follower.progress < 1/$numPoints) {
$follower.target.alpha = $follower.progress * $numPoints;
}
else if ($follower.progress > 1-1/$numPoints) {
$follower.target.alpha = (1-$follower.progress) * $numPoints;
}
}
}
});
where $numpoints would be 30 based on the example
Related
So, I'm trying to make a grid of rectangles each get more transparent the closer the mouse is to it.
Using some basic maths, I thought I had got it, but instead it seems I got a weird graphic bug(maybe?) shown here:
The middle of the rings is where the mouse is.
Part of code that deals with transparency:
private function update(e:Event = null):void
{
for (var i:int = 0; i < buttons.length; i++) {
lightFact = getDistance(buttons[i])
lightBrightness = lightPower - (lightFact * 10)
buttons[i].alpha = lightBrightness
}
}
getDistance is just getting distance from the block to the mouse.
Each rectangle is a movie clip, if that matters.
If you are trying to do this:
Then I think your problem is basically that your alpha value is ranging from 0 to about 3000 or something like that. That's going to cause strange effects. The value needs to range smoothly from 0 to 1 (so it needs to be a floating point number as in Number).
Here is the code which generated the image above which I wrote for you that will get you started in the right direction:
package
{
import flash.display.*;
import flash.events.*;
public class lightFactTest extends MovieClip
{
private var boxesArray: Array = new Array();
private var xDist: Number = 0;
private var yDist: Number = 0;
private var d: Number = 0;
private var size_Glow : Number = 0;
private var size_Radius : Number = 0;
public function lightFactTest(): void
{
// creates a background for rectangles array.
var BG_box: Sprite = new Sprite();
BG_box.graphics.lineStyle();
BG_box.graphics.beginFill(0x080839);
BG_box.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
BG_box.graphics.endFill();
addChild(BG_box);
//# creates a grid of sprites (rectangles).
for (var i:int = 0; i < (stage.stageWidth / 10); i++)
{
for (var j:int = 0; j < (stage.stageHeight / 10); j++)
{
var box: Sprite = new Sprite();
box.graphics.lineStyle();
box.graphics.beginFill(0xFFFFFF);
box.graphics.drawRect(0, 0, 10, 10);
box.graphics.endFill();
addChild(box);
box.x += i*10; //+ 50;
box.y += j*10; //+ 50;
boxesArray.push(box);
}
}
addEventListener(Event.ENTER_FRAME, lightCalc);
}
private function lightCalc(e: Event): void
{
size_Glow = 3.5;
size_Radius = 0.64;
//# iterates through the array calculating each distance and then alpha.
for (var i:int = 0; i < boxesArray.length; i++)
{
xDist = Math.abs(stage.mouseX - boxesArray[i].x);
yDist = Math.abs(stage.mouseY - boxesArray[i].y);
//var d: Number = Math.pow(xDist * xDist + yDist * yDist, 0.5);
d = Math.sqrt(xDist * xDist + yDist * yDist) / (size_Radius / 5);
//# This is the code that you really need to focus on...
boxesArray[i].alpha = Math.min(1 / d * 10, 1 ) * (Math.PI / 0.5 - Math.min(size_Radius, 0) ) * size_Glow;
}
}
}
}
Hope that helps!
Having a bit of a problem with some code I've written. Basically what it does is take 3 values that constantly change and graphs them over time in the form of a cumulative line graph. It almost works except I get this weird line drawn across the entire stage and further and I can't figure out what the issue is. The full code is below, you can run it by pasting it into flash.
import flash.display.Shape;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.utils.Timer;
var y1:Array = new Array();
var y2:Array = new Array();
var y3:Array = new Array();
var avg:Array = new Array();
var y1Shape:Shape = new Shape();
var y2Shape:Shape = new Shape();
var y3Shape:Shape = new Shape();
var avgShape:Shape = new Shape();
var container:Sprite = new Sprite();
var scale:uint = 1;
var redrawGraph:int = setInterval(reDraw,500);
var y1Int:int = 0;
var y2Int:int = 0;
var y3Int:int = 0;
container.addChild(y1Shape);
container.addChild(y2Shape);
container.addChild(y3Shape);
container.addChild(avgShape);
this.addChild(container);
function reDraw():void
{
y1Shape.graphics.clear();
y2Shape.graphics.clear();
y3Shape.graphics.clear();
avgShape.graphics.clear();
y1Shape.graphics.lineStyle(1, 0x0066FF, 1);
y1Shape.graphics.beginFill(0x0066FF, 0.5);
y2Shape.graphics.lineStyle(1, 0x009900, 1);
y2Shape.graphics.beginFill(0x009900, 0.5);
y3Shape.graphics.lineStyle(1, 0x990000, 1);
y3Shape.graphics.beginFill(0x990000, 0.5);
avgShape.graphics.lineStyle(1, 0x000000, 1);
y1Int = rand();
y2Int = rand();
y3Int = rand();
trace(y1Int, y2Int, y3Int);
y1.unshift(y1Int);
y2.unshift(y2Int);
y3.unshift(y3Int);
popOut(y1);
popOut(y2);
popOut(y3);
var i:uint,sum:uint,aLength:uint,len:uint = y1.length,max:int = 0,height_:int = 400;
scale = 10;
for (i=0; i<len; i++)
{
max = Math.max(y1[i] + y2[i] + y3[i],max);
}
for (i=0; i<len; i++)
{
sum += y1[i] + y2[i] + y3[i];
}
avg.unshift(Math.round(sum/len));
/*--------------------------------MATCHED GRAPH------------------------------------------*/
var y1_commands:Vector.<int> = new Vector.<int>();
var y1_coord:Vector.<Number>= new Vector.<Number>();
var y1_coord_rev:Vector.<Number> = new Vector.<Number>();
y1_commands.push(1);
y1_coord.push(400,height_);
for (i=0; i<len; i++)
{
y1_commands.push(2);
y1_coord.push((400-i*scale),height_-(Math.round((y1[i]/max)*height_)));
y1_coord_rev.unshift((400-i*scale),height_-(Math.round((y1[i]/max)*height_)));
}
for (i=len; i>0; i--)
{
y1_commands.push(2);
y1_coord.push(400 - i*scale,height_);
}
y1_commands.push(2);
y1_coord.push(400,height_);
/*--------------------------------MATCHED GRAPH------------------------------------------*/
/*----------------------------------BUSY GRAPH-------------------------------------------*/
var y2_commands:Vector.<int> = new Vector.<int>();
var y2_coord:Vector.<Number>= new Vector.<Number>();
var y2_coord_rev:Vector.<Number> = new Vector.<Number>();
y2_commands.push(1);
y2_coord.push(400,height_-(Math.round((y1[i]/max)*height_)));
for (i=0; i<len; i++)
{
y2_commands.push(2);
y2_coord.push((400-i*scale),height_-(Math.round(((y1[i]+y2[i])/max)*height_)));
y2_coord_rev.unshift((400-i*scale),height_-(Math.round(((y1[i]+y2[i])/max)*height_)));
}
for (i=len; i>0; i--)
{
y2_commands.push(2);
y2_coord.push(400 - i*scale, height_-(Math.round((y1[i]/max)*height_)));
}
y2_commands.push(2);
y2_coord.push(400,height_-(Math.round((y1[i]/max)*height_)));
/*----------------------------------BUSY GRAPH-------------------------------------------*/
/*----------------------------------VAC GRAPH-------------------------------------------*/
var y3_commands:Vector.<int> = new Vector.<int>();
var y3_coord:Vector.<Number>= new Vector.<Number>();
var y3_coord_rev:Vector.<Number> = new Vector.<Number>();
y3_commands.push(1);
y3_coord.push(400,height_-(Math.round(((y1[i]+y2[i])/max)*height_)));
for (i=0; i<len; i++)
{
y3_commands.push(2);
y3_coord.push((400-i*scale),height_-(Math.round(((y1[i]+y2[i]+y3[i])/max)*height_)));
y3_coord_rev.unshift((400-i*scale),height_-(Math.round(((y1[i]+y2[i]+y3[i])/max)*height_)));
}
for (i=len; i>0; i--)
{
y3_commands.push(2);
y3_coord.push(400 - i*scale, height_-(Math.round(((y1[i]+y2[i])/max)*height_)));
}
y2_commands.push(2);
y2_coord.push(400,height_-(Math.round(((y1[i]+y2[i])/max)*height_)));
/*----------------------------------BUSY GRAPH-------------------------------------------*/
//y3Shape.graphics.drawPath(y3_commands, y3_coord);
y2Shape.graphics.drawPath(y2_commands, y2_coord);
y1Shape.graphics.drawPath(y1_commands, y1_coord);
}
function popOut(a:Array):void
{
if (a.length >=Math.ceil(400/scale))
{
a.pop();
}
}
function rand():int
{
return Math.floor(Math.random() * (1 + 5 - 0) + 0);
}
y3Shape is commented out until the problem with y2Shape is fixed (having both drawn just makes the problem harder to figure out).
Any ideas what could be up?
Thanks
If you insert trace your vectors near .drawPath, you'll see something like that:
trace(y2_commands); // 1,2,2,2,2
trace(y2_coord); // 400,171,400,57,390,NaN,400,171,400,57
So, NaN (Not a Number) means, that you have error in coordinates calculating.
ps. y1[i] in first calculating of BUSY GRAPH is undefined
You seem to be using beginFill(), when you draw a path that's not looped, Flash requires your path to get looped in order to fill it with something. So, it loops it implicitly by adding a line to your starting point and filling it. In order to receive a non-overlapping path, add two points to your path that will be right below 0 point in X axis, one right under the last point on your graph, and one right under the FIRST point on the graph.
Actually, you already have them installed, but for some reason you place not 2 points, but a whole lot of em, and height position of that line is plainly wrong. Your code states:
for (i=len; i>0; i--)
{
y2_commands.push(2);
y2_coord.push(400 - i*scale, height_-(Math.round((y1[i]/max)*height_)));
}
You should have:
for (i=len; i>0; i--)
{
y2_commands.push(2);
y2_coord.push(400 - i*scale, height_);
}
Or even better:
y2_commands.push(2);
y2_commands.push(2);
y2_coord.push(400 - len*scale, height_);
y2_coord.push(400, height_);
Say I have drawn a triangle with:
import flash.geom.Matrix;
function drawTriangle(sideLength:uint):void {
var triangleHeight:uint = Math.sqrt(Math.pow(sideLength,2) - Math.pow(sideLength / 2,2));
var triangleShape:Shape = new Shape();
triangleShape.graphics.beginFill(0x2147AB);
triangleShape.graphics.lineStyle(1,0xff00ff00);
triangleShape.graphics.moveTo(sideLength/2, 0);
triangleShape.graphics.lineTo(sideLength, triangleHeight);
triangleShape.graphics.lineTo(0, triangleHeight);
triangleShape.graphics.lineTo(sideLength/2, 0);
addChild(triangleShape);
var matrix:Matrix = new Matrix;
matrix.translate(50, 50);
transform.matrix = matrix;
}
drawTriangle(400);
How can I achieve the following:
When the user clicks a point inside the triangle, we will get the x and y coordinates, do some calculation with those values and get some (lots) of pixel coordiantes accordingly (all of those calculated points will be within the triangle). And finally, change the color of those points (something different than triangleShape fill color).
Here's a solution using a triangle drawn to some BitmapData, added to a Bitmap, and then contained in a Sprite.
var box:Sprite = new Sprite();
box.graphics.beginFill(0x000000, 1);
box.graphics.lineStyle(1, 0x000000, 1);
box.graphics.moveTo(100, 50);
box.graphics.lineTo(50, 100);
box.graphics.lineTo(150, 100);
box.graphics.lineTo(100, 50);
box.graphics.endFill();
addChild(box);
var boxCopied:BitmapData = new BitmapData(box.width, box.height, true, 0x00000000);
var matr:Matrix = new Matrix();
matr.tx = -50;
matr.ty = -50;
boxCopied.draw(box, matr);
box.graphics.clear();
var boxCopy:Bitmap = new Bitmap(boxCopied);
box.addChild(boxCopy);
box.addEventListener(MouseEvent.CLICK, clicked, false, 0, true);
function clicked(evt:MouseEvent):void
{
for(var i=0;i<50;i++)
{
var pixel = new Point(Math.floor(Math.random() * boxCopy.width), Math.floor(Math.random() * boxCopy.height));
if(boxCopied.hitTest(new Point(boxCopy.x, boxCopy.y), 1, pixel))
{
boxCopied.setPixel32(pixel.x, pixel.y, Math.random() * 0xFFFFFFFF);
}
}
}
http://www.swfupload.com/view/162170.htm
Note I'm using setPixel32 to send a 32-bit integer (essentially an ARBG instead of RGB) to manipulate alpha as well.
50 random pixels are being generated. If they're inside of the triangle, they're kept.
I am trying to create a Colorpicker control for my application. I prefer pure actionscript. Does someone know how to create a picker like this: http://parasolarchives.com/tools/colorpicker/
Most interestingly I am interested how to draw the gradient because the gradient has a saturation,
this is a picker i use myself, hope it'll be useful:
package lazylib.ui.generated
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.GradientType;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import lazylib.broadcast.evts.ColorEvent;
import lazylib.broadcast.Radio;
/**
* ...
* #author www0z0k
*/
public class Picker extends Sprite
{
private var bmpc: Bitmap;
private var bmp:BitmapData;
private var id:String;
private var w:int;
private var h:int;
public static const COLOR_PICKED_EVT_TYPE: String = 'picked by picker';
public function Picker(_id:String, _w:int = 256, _h:int = 256, _x:int = 0, _y:int = 0)
{
id = _id;
w = _w;
h = _h;
x = _x;
y = _y;
bmp = new BitmapData(w, h);
bmpc = new Bitmap(bmp);
addChild(bmpc);
refillBmp(0x7f7f7f);
}
public function get ID():String { return id; }
private function refillBmp(overColor:int = 0x7f7f7f, alphaStep:Number = 0.006):void {
var rtspr:Sprite = new Sprite();
var spr:Sprite = new Sprite();
var colors:Array = new Array(0xff0000,0xff7f00,0xffff00,0x7fff00,0x00ff00, 0x00ff7f, 0x00ffff,0x007fff,0x0000ff, 0x7f00ff,0xff00ff, 0xff007f, 0xff0000);
var alphas: Array = new Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
var ratios: Array = new Array(10, 30, 50, 70, 90, 110, 130, 150, 170, 190, 210, 230, 250);
var matrix:Matrix = new Matrix();
matrix.createGradientBox(w, h);
spr.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, matrix);
spr.graphics.drawRect(0, 0, w, h);
spr.graphics.endFill();
rtspr.addChild(spr);
var spr2:Sprite = new Sprite();
rtspr.addChild(spr2);
var startA: Number = 1;
for (var i:int = 0; startA > 0; i++) {
startA -= alphaStep;//orig 0.004!!!
spr2.graphics.lineStyle(1, overColor, startA);
spr2.graphics.moveTo(0, h - i);
spr2.graphics.lineTo(w, h - i);
}
bmp.draw(rtspr, new Matrix());
graphics.beginFill(0, 0);
graphics.drawRect(0, 0, w, h);
graphics.endFill();
buttonMode = true;
addEventListener(MouseEvent.CLICK, onClick);
}
public function adjustDarkness(percent:int):void {
var colorValue: int = int(255 * percent / 100);
var currentGray:int = colorValue + colorValue << 8 + colorValue << 16;
refillBmp(colorValue);
}
private function onClick(e:MouseEvent):void {
var col: int = bmp.getPixel(e.localX, e.localY);
Radio.broadcast(new ColorEvent(COLOR_PICKED_EVT_TYPE + id, col));
dispatchEvent(new ColorEvent(COLOR_PICKED_EVT_TYPE + id, col));
}
}
}
Radio is a global dispatcher class (posted here if you need it), ColorEvent is just an event with an int color field, adjustDarkness is usually called from an external scrollbar. Let me know if a working example is needed.
I'm not sure this would be too accurate, but I think I can give you a few ideas:
You could grab that same squared image you are showing (or photoshop's for that matter) and change it's hue to get the whole color range.
If you want to draw it, mathematically I don't think it's too hard to do... for the cyan, for instance, the Y axis should control the maximum amount of color in all channels, and the X axis should control the proportion of red in the mix.
i've come across this ActionScript sample, which demonstrates drawing of the color spectrum, one line at a time via a loop, using waveforms.
however, the waveform location of each RGB channel create a color spectrum that is missing colors (pure yellow, cyan and magenta) and therefore the spectrum is incomplete.
how can i remedy this problem so that the drawn color spectrum will exhibit all colors?
// Loop through all of the pixels from '0' to the specified width.
for(var i:int = 0; i < nWidth; i++)
{
// Calculate the color percentage based on the current pixel.
nColorPercent = i / nWidth;
// Calculate the radians of the angle to use for rotating color values.
nRadians = (-360 * nColorPercent) * (Math.PI / 180);
// Calculate the RGB channels based on the angle.
nR = Math.cos(nRadians) * 127 + 128 << 16;
nG = Math.cos(nRadians + 2 * Math.PI / 3) * 127 + 128 << 8;
nB = Math.cos(nRadians + 4 * Math.PI / 3) * 127 + 128;
// OR the individual color channels together.
nColor = nR | nG | nB;
}
UPDATED SOLUTION
for anyone interested, below is the solution i wrote to address the above problem. RGB waveforms are not used to create the full color spectrum. also, the code is flexible so you can assign your own size and color variables for the produced sprite. the color variables in this example are red, yellow, green, cyan, blue, magenta, red to produce the complete color spectrum
/*
//SpectrumGradient Object Call
var spectrum:SpectrumGradient = new SpectrumGradient(stage.stageWidth, stage.stageHeight, 0xFF0000, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF, 0xFF00FF, 0xFF0000);
this.addChild(spectrum);
*/
package
{
import flash.display.BitmapData;
import flash.display.CapsStyle;
import flash.display.GradientType;
import flash.display.LineScaleMode;
import flash.display.Sprite;
import flash.geom.Matrix;
public class SpectrumGradient extends Sprite
{
public function SpectrumGradient(spriteWidth:Number, spriteHeight:Number, ...spriteColors)
{
//Setup spectrum sprite
var spectrum:Sprite = new Sprite();
var spectrumAlphas:Array = new Array();
var spectrumRatios:Array = new Array();
var spectrumPartition:Number = 255 / (spriteColors.length - 1);
for (var pushLoop:int = 0; pushLoop < spriteColors.length; pushLoop++)
{
spectrumAlphas.push(1);
spectrumRatios.push(pushLoop * spectrumPartition);
}
//Create spectrum sprite as evenly distributed linear gradient using supplied spriteColors
var spectrumMatrix:Matrix = new Matrix();
spectrumMatrix.createGradientBox(spriteWidth, spriteHeight);
spectrum.graphics.lineStyle();
spectrum.graphics.beginGradientFill(GradientType.LINEAR, spriteColors, spectrumAlphas, spectrumRatios, spectrumMatrix);
spectrum.graphics.drawRect(0, 0, spriteWidth, 1);
spectrum.graphics.endFill();
//Assign bitmapData to the spectrum sprite
var bitmapData:BitmapData = new BitmapData(spectrum.width, spectrum.height, true, 0);
bitmapData.draw(spectrum);
var pixelColor:Number;
for (var i:int = 0; i < spriteWidth; i++)
{
//Retrieve the color number for each pixel of the spectrum sprite
pixelColor = bitmapData.getPixel(i, 0);
//Create new matrices for the white and black gradient lines
var matrixWhite:Matrix = new Matrix();
matrixWhite.createGradientBox(1, spriteHeight / 2, Math.PI * 0.5, 0, 0);
var matrixBlack = new Matrix();
matrixBlack.createGradientBox(1, spriteHeight / 2, Math.PI * 0.5, 0, spriteHeight / 2);
//Each slice of the sprite is composed of two vertical lines: the first fades from white to the pixelColor, the second fades from pixelColor to black
graphics.lineStyle(1, 0, 1, false, LineScaleMode.NONE, CapsStyle.NONE);
graphics.lineGradientStyle(GradientType.LINEAR, [0xFFFFFF, pixelColor], [100, 100], [0, 255], matrixWhite);
graphics.moveTo(i, 0);
graphics.lineTo(i, spriteHeight / 2);
graphics.lineGradientStyle(GradientType.LINEAR, [pixelColor, 0], [100, 100], [0, 255], matrixBlack);
graphics.moveTo(i, spriteHeight / 2);
graphics.lineTo(i, spriteHeight);
}
}
}
}
you can't have all colors at once. all RGB colors, that's 256 x 256 x 256, so you'd need 4096 x 4096 pixels for showing all of them.
Also, there is no "natural"/sensible way of displaying them all. At least until now, nobody has come up with a 2 dimensional color space that really makes sense. For displaying colors, you'll always have to pick 2. That's why common color choosers either use a hue slider and a lightness/saturation plane or a hue/saturation plane and a lightness slider.
please also note that the first (rectangular) spectrum can be easily drawn with 2 superposed gradients. a horizontal one for the hue, and a vertical (semitransparent) for lightness. its faster and completely smooth (if you zoom you don't see the individual lines).
edit: here's a working example of how this can be achieved with a single gradient, which is preferable for obvious reasons:
package {
import flash.display.*;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
public class GradientTest extends Sprite {
public function GradientTest() {
var colors:Array = [0xFF0000, 0xFFFF00, 0x00FF00, 0x00FFFF, 0x0000FF, 0xFF00FF, 0xFF0000];
var part:Number = 0xFF / (colors.length-1);
var ratios:Array = [], alphas:Array = [];
var m:Matrix = new Matrix();
m.createGradientBox(500, 20);
for (var i:int = 0; i < colors.length; i++) {
ratios.push(part * i);
alphas.push(100);
}
this.graphics.beginGradientFill(GradientType.LINEAR, colors, alphas, ratios, m);
this.graphics.drawRect(0, 0, 500, 20);
//just to get the RGB values under the mouse:
var b:BitmapData = new BitmapData(this.width, this.height, true, 0);
b.draw(this);
stage.addEventListener(MouseEvent.MOUSE_MOVE, function (e:Event):void {
if (hitTestPoint(mouseX, mouseY)) {
var s:String = b.getPixel(mouseX, mouseY).toString(16);
while (s.length < 6) s = "0" + s;
trace("#" + s);
}
});
}
}
}
the approach using waveforms is a bit like a hammer in search of a nail. just because bit operations and trigonometry are great tools, doesn't mean you should prefer them to a solution that is much simpler.