Change pixel color with ActionScript 3 - actionscript-3

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.

Related

How do I keep object location from being increased exponentially after each call to draw function?

Simple animation that creates a firework-like effect on the canvas with each click. The issue is the animation is made with a setInterval(draw) and every time the canvas is redrawn the location of each particle is += particle.speed. But with each click the particles move faster and faster as it seems the speed of each particle is not reset.
As you can see with a couple clicks on the working example here: , with the first click the particles move very (correctly) slowly, but with each subsequent click the speed is increased.
JS used is pasted below as well, any help is greatly appreciated!
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
canvas.addEventListener("click", startdraw, false);
//Lets resize the canvas to occupy the full page
var W = window.innerWidth;
var H = window.innerHeight;
canvas.width = W;
canvas.height = H;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, W, H);
//global variables
var radius;
radius = 10;
balls_amt = 20;
balls = [];
var locX = Math.round(Math.random()*W);
var locY = Math.round(Math.random()*H);
//ball constructor
function ball(positionx,positiony,speedX,speedY)
{
this.r = Math.round(Math.random()*255);
this.g = Math.round(Math.random()*255);
this.b = Math.round(Math.random()*255);
this.a = Math.random();
this.location = {
x: positionx,
y:positiony
}
this.speed = {
x: -2+Math.random()*4,
y: -2+Math.random()*4
};
}
function draw(){
ctx.globalCompositeOperation = "source-over";
//Lets reduce the opacity of the BG paint to give the final touch
ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
ctx.fillRect(0, 0, W, H);
//Lets blend the particle with the BG
//ctx.globalCompositeOperation = "lighter";
for(var i = 0; i < balls.length; i++)
{
var p = balls[i];
ctx.beginPath();
ctx.arc(p.location.x, p.location.y, radius, Math.PI*2, false);
ctx.fillStyle = "rgba("+p.r+","+p.g+","+p.b+", "+p.a+")";
ctx.fill();
var consolelogX = p.location.x;
var consolelogY = p.location.y;
p.location.x += p.speed.x;
p.location.y += p.speed.y;
}
}
function startdraw(e){
var posX = e.pageX; //find the x position of the mouse
var posY = e.pageY; //find the y position of the mouse
for(i=0;i<balls_amt;i++){
balls.push(new ball(posX,posY));
}
setInterval(draw,20);
//ball[1].speed.x;
}
After each click startdraw is called, which starts every time a new periodical call (setInterval) for the draw method. So after the 2nd click you have 2 parallel intervals, after the 3rd you have 3 parallel intervals.
It is not exponentially, only linearly increasing :)
A possible dirty fix:
Introduce an interval global variable, and replace this row:
setInterval(draw,20);
with this one:
if (!interval) interval = setInterval(draw,20);
Or a nicer solution is to start the interval at the onLoad event.
setInterval will repeat its call every 20th ms, and returns an ID.
You can stop the repetition by calling clearInterval(ID).
var id = setInterval("alert('yo!');", 500);
clearInterval(id);

dynamically draw circle preloader error 1061 when in document class

I found a tutorial on how to make a dynamic unfilled and filled circle. that will take input from a slider to dertermine how much of the circle is drawn. I wanted to use this for a preloader. Unlike the author I would like to use it inside of a document class. I am getting
1061: Call to a possibly undefined method createEmptyMovieClip through a reference with static type document. and 1120: Access of undefined property circ1. The second is caused from the first. How would I get this to work in my document class? Thanks in advance!
//original code
// x: circles center x, y: circles center y
// a1: first angle, a2: angle to draw to, r: radius
// dir: direction; 1 for clockwise -1 for counter clockwise
MovieClip.prototype.CircleSegmentTo = function(x, y, a1, a2, r, dir) {
var diff = Math.abs(a2-a1);
var divs = Math.floor(diff/(Math.PI/4))+1;
var span = dir * diff/(2*divs);
var rc = r/Math.cos(span);
this.moveTo(x+Math.cos(a1)*r, y+Math.sin(a1)*r);
for (var i=0; i<divs; ++i) {
a2 = a1+span; a1 = a2+span;
this.curveTo(
x+Math.cos(a2)*rc,
y+Math.sin(a2)*rc,
x+Math.cos(a1)*r,
y+Math.sin(a1)*r
);
};
return this;
};
// empty
this.createEmptyMovieClip("circ1",1);
circ1._x = 100;
circ1._y = 150;
circ1.radius = 35;
circ1.onEnterFrame = function(){
this.clear();
var endAngle = 2*Math.PI*percentLoaded;
var startAngle = 0;
if (endAngle != startAngle){
this.lineStyle(2,0,100);
this.CircleSegmentTo(0, 0, startAngle, endAngle, this.radius, -1);
}
}
//filled
this.createEmptyMovieClip("circ2",2);
circ2._x = 220;
circ2._y = 150;
circ2.radius = 35;
/* code in tutorial i left out since its for a second filled in circle
circ2.onEnterFrame = function(){
this.clear();
var endAngle = 2*Math.PI*slider.value/100;
var startAngle = 0;
if (endAngle != startAngle){
this.lineStyle(2,0,100);
this.beginFill(0xFF9999,100);
this.lineTo(this.radius,0);
this.CircleSegmentTo(0, 0, startAngle, endAngle, this.radius, -1);
this.lineTo(0,0);
this.endFill();
}
}
*/
That code you got was made using Actionscript 2, and you're building it for Actionscript 3, so you have to either recode it to Actionscript 3 or compile it for AS2.

nested array does not work

I have the following problem: I have this multi-level array (nested array) which contains two rows of bitmapData. Row 1:360 rotated bitmapData objects; row 2: 360 rotated and colored bitmapData objects.
I try to access row 2 but that doesn't work. There are some mysterious error messages coming up ("TypeError: Error #1034: Type Coercion failed: cannot convert []#36d7e9e9 to flash.display.BitmapData. at BasicBlitArrayObject/updateFrame()").
Please can someone help me out with this problem? Thank you very much.
this function rotates and colors bitmapData; the rotated bitmapData is thrown into an array and the colored bitmapData is thrown into another array; a third array is used as a level array for nesting the other two arrays inside of it
public function createColoredRotationBlitArrayFromBD(sourceBitmapData:BitmapData, inc:int, offset:int = 0, color:Number = 1, $alpha:Number = 1):Array
{
tileList = [];
tileListSec = [];
levelArray = [tileList, tileListSec];
var rotation:int = offset;
while (rotation < (360 + offset))
{
var angleInRadians:Number = Math.PI * 2 * (rotation / 360);
var rotationMatrix:Matrix = new Matrix();
rotationMatrix.translate(-sourceBitmapData.width * .5, -sourceBitmapData.height * .5);
rotationMatrix.rotate(angleInRadians);
rotationMatrix.translate(sourceBitmapData.width * .5, sourceBitmapData.height * .5);
var matrixImage:BitmapData = new BitmapData(sourceBitmapData.width, sourceBitmapData.height,
true, 0x00000000);
matrixImage.draw(sourceBitmapData, rotationMatrix);
tileList.push(matrixImage.clone());
bitmapData = new BitmapData(matrixImage.width, matrixImage.height, true, 0x00000000);
bitmapData = matrixImage;
var colorMatrix:ColorMatrixFilter = new ColorMatrixFilter (
[color, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, $alpha, 0]);
matrixImage.applyFilter(bitmapData, bitmapData.rect, point0, colorMatrix);
tileListSec.push(matrixImage.clone());
rotation += inc;
matrixImage.dispose();
matrixImage = null;
rotationMatrix = null;
bitmapData.dispose();
bitmapData = null;
colorMatrix = null;
}
return(levelArray);
}
creating my rotated and colored bitmapData
animationFrames = tempBlitArrayAsset.createRotationBlitArrayFromBD($bitmapData, 1, 270);
here I try to access the first row of my level array (that doesn't work; I can't access it)
tempEnemy.animationList = animationFrames;
tempEnemy.bitmapData = tempEnemy.animationList[1][tempEnemy.frame];
This function is for updating the frames
public function updateFrame(inc:int, row:int = 0):void
{
frame += inc;
if (frame > animationList.length - 1){
frame = 0;
}
bitmapData = animationList[row][frame];
}
}
this is a line showing how the updateFrame-function is used in my game (trueRotation is 0)
tempEnemy.updateFrame(tempEnemy.trueRotation);
I can't find anything wrong with createColoredRotationBlitArrayFromBD
var $bitmapData:BitmapData = new BitmapData(40,40,false, 0x7f7f7f);
var animationFrames:Array = createColoredRotationBlitArrayFromBD($bitmapData, 1, 270);
trace(animationFrames.length); // 2
trace(animationFrames[0].length); // 360
trace(animationFrames[1].length); // 360
var bitmap:Bitmap = new Bitmap();
this.addChild(bitmap);
bitmap.bitmapData = animationFrames[1][0]; // works..
That seems correct. Right? I get a red tinted bitmap.
The only 'bug' I see in the code you listed is in updateFrame
if (frame > animationList.length - 1){
frame = 0;
}
should probably be:
if (frame > animationList[row].length - 1){
frame = 0;
}
because animationList.length == 2
But everything else looks okay in the code you've provided, so without more code, i'm not sure there is anything to help.

moving an image across a html canvas

I am trying to move an image from the right to the center and I am not sure if this is the best way.
var imgTag = null;
var x = 0;
var y = 0;
var id;
function doCanvas()
{
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
var imgBkg = document.getElementById('imgBkg');
imgTag = document.getElementById('imgTag');
ctx.drawImage(imgBkg, 0, 0);
x = canvas.width;
y = 40;
id = setInterval(moveImg, 0.25);
}
function moveImg()
{
if(x <= 250)
clearInterval(id);
var canvas = document.getElementById('icanvas');
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
var imgBkg = document.getElementById('imgBkg');
ctx.drawImage(imgBkg, 0, 0);
ctx.drawImage(imgTag, x, y);
x = x - 1;
}
Any advice?
This question is 5 years old, but since we now have requestAnimationFrame() method, here's an approach for that using vanilla JavaScript:
var imgTag = new Image(),
canvas = document.getElementById('icanvas'),
ctx = canvas.getContext("2d"),
x = canvas.width,
y = 0;
imgTag.onload = animate;
imgTag.src = "http://i.stack.imgur.com/Rk0DW.png"; // load image
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear canvas
ctx.drawImage(imgTag, x, y); // draw image at current position
x -= 4;
if (x > 250) requestAnimationFrame(animate) // loop
}
<canvas id="icanvas" width=640 height=180></canvas>
drawImage() enables to define which part of the source image to draw on target canvas. I would suggest for each moveImg() calculate the previous image position, overwrite the previous image with that part of imgBkg, then draw the new image. Supposedly this will save some computing power.
Here's my answer.
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var myImg = new Image();
var myImgPos = {
x: 250,
y: 125,
width: 50,
height: 25
}
function draw() {
myImg.onload = function() {
ctx.drawImage(myImg, myImgPos.x, myImgPos.y, myImgPos.width, myImgPos.height);
}
myImg.src = "https://mario.wiki.gallery/images/thumb/c/cc/NSMBUD_Mariojump.png/1200px-NSMBUD_Mariojump.png";
}
function moveMyImg() {
ctx.clearRect(myImgPos.x, myImgPos.y, myImgPos.x + myImgPos.width, myImgPos.y +
myImgPos.height);
myImgPos.x -= 5;
}
setInterval(draw, 50);
setInterval(moveMyImg, 50);
<canvas id="canvas" class="canvas" width="250" height="150"></canvas>
For lag free animations,i generally use kinetic.js.
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var hexagon = new Kinetic.RegularPolygon({
x: stage.width()/2,
y: stage.height()/2,
sides: 6,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
layer.add(hexagon);
stage.add(layer);
var amplitude = 150;
var period = 2000;
// in ms
var centerX = stage.width()/2;
var anim = new Kinetic.Animation(function(frame) {
hexagon.setX(amplitude * Math.sin(frame.time * 2 * Math.PI / period) + centerX);
}, layer);
anim.start();
Here's the example,if you wanna take a look.
http://www.html5canvastutorials.com/kineticjs/html5-canvas-kineticjs-animate-position-tutorial/
Why i suggest this is because,setInterval or setTimeout a particular function causes issues when large amount of simultaneous animations take place,but kinetic.Animation deals with framerates more intelligently.
Explaining window.requestAnimationFrame() with an example
In the following snippet I'm using an image for the piece that is going to be animated.
I'll be honest... window.requestAnimationFrame() wasn't easy for me to understand, that is why I coded it as clear and intuitive as possible. So that you may struggle less than I did to get my head around it.
const
canvas = document.getElementById('root'),
btn = document.getElementById('btn'),
ctx = canvas.getContext('2d'),
brickImage = new Image(),
piece = {image: brickImage, x:400, y:70, width:70};
brickImage.src = "https://i.stack.imgur.com/YreH6.png";
// When btn is clicked execute start()
btn.addEventListener('click', start)
function start(){
btn.value = 'animation started'
// Start gameLoop()
brickImage.onload = window.requestAnimationFrame(gameLoop)
}
function gameLoop(){
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw at coordinates x and y
ctx.drawImage(piece.image, piece.x, piece.y)
let pieceLeftSidePos = piece.x;
let middlePos = canvas.width/2 - piece.width/2;
// Brick stops when it gets to the middle of the canvas
if(pieceLeftSidePos > middlePos) piece.x -= 2;
window.requestAnimationFrame(gameLoop) // Needed to keep looping
}
<input id="btn" type="button" value="start" />
<p>
<canvas id="root" width="400" style="border:1px solid grey">
A key point
Inside the start() function we have:
brickImage.onload = window.requestAnimationFrame(gameLoop);
This could also be written like: window.requestAnimationFrame(gameLoop);
and it would probably work, but I'm adding the brickImage.onload to make sure that the image has loaded first. If not it could cause some issues.
Note: window.requestAnimationFrame() usually loops at 60 times per second.

Drawing Color Spectrum with Waveform

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.