AS3 - bug in Rectangle.inflatePoint() - doesn't deal with negative coordinates well - actionscript-3

this seems a bit nutty:
var r:Rectangle = new Rectangle();
trace("initial rect: " + r); // (x=0, y=0, w=0, h=0)
var p:Point = new Point(-5, -3); // (x=-5, y=-3)
trace("point: " + p);
r.inflatePoint(p);
trace("inflated rect: " + r); // (x=5, y=3, w=-10, h=-6)
i would expect the result to be (x=-5, y=-3, width=5, height=3).
here's an implementation that returns the expected result:
public static function inflateRectByPoint(r:Rectangle,p:Point):void
{
var d:Number;
d = p.x - r.x;
if (d < 0)
{
r.x += d;
r.width -= d;
}
else if (d > r.width)
{
r.width = d;
}
d = p.y - r.y;
if (d < 0)
{
r.y += d;
r.height -= d;
}
else if (d > r.height)
{
r.height = d;
}
}

You're misunderstanding what inflatePoint does.
It's the same as inflate (except taking a Point argument rather than two coordinates) - enlarges the rectangle in every direction.
new Rectangle(0, 0, 2, 5).inflatePoint(new Point(2, 2))
Results in a Rectangle from -2, -2 to 4, 7.
Putting in negative numbers shrinks the rectangle - until it gets smaller than 0, at which point it inverts, as expected.

Related

AS3: Manually calculating RGB multipliers and offsets for brightness from -1 to 1

How would you manually calculate RGB multipliers and offsets to adjust the brightness of a color so that an argument of -1 was all black and 1 was all white?
If it's less than 1, it's easy. R, G, and B are just multiplied by (1 + brightness).
But how would you calculate the offsets for brightness values greater than 0?
It is simple channel per channel interpolation math. It does not look simple only because there are three channels and they need de/serialization for various purposes.
// Usage.
acoolor:uint = parseRGB(200, 150, 100);
trace(colorToRGB(brightNess(acoolor, 0.5)));
trace(colorToRGB(brightNess(acoolor, -0.5)));
// Implementation.
function parseRGB(ared:uint, agreen:uint, ablue:uint):uint
{
var result:uint;
result += (ared << 16) & 0xFF0000;
result += (agreen << 8) & 0xFF00;
result += (ablue) & 0xFF;
return result;
}
function colorToRGB(acolor:uint):Array
{
result = new Array;
result[0] = (acolor >> 16) & 0xFF;
result[1] = (acolor >> 8) & 0xFF;
result[2] = (acolor) & 0xFF;
return result;
}
function RGBToColor(anrgb:Array):uint
{
return parseRGB.apply(this, anrgb);
}
function brightChannel(achannel:uint, adjust:Number):uint
{
if (adjust <= -1) return 0;
if (adjust >= 1) return 255;
if (adjust < 0) return achannel * (1 + adjust);
if (adjust > 0) return achannel + (255 - achannel) * adjust;
// If adjust == 0
return achannel;
}
function brightNess(acolor:uint, adjust:Number):uint
{
var anrgb:Array = colorToRGB(acolor);
for (var i:int = 0; i < 3; i++)
anrgb[i] = brightChannel(anrgb[i], adjust);
return RGBToColor(anrgb);
}

Converting complicated trigonometry from AS2 to AS3

I'm trying to make a game, following this tutorial.
The issue comes from the fact that I am using ActionScript 3.0 whereas the tutorial was written using ActionScript 2.0.
Regarding the sight of the enemy, I have turned this code:
onClipEvent (enterFrame) {
dist_x = _root.hero._x-_x;
dist_y = _root.hero._y-_y;
dist = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
angle = Math.atan(dist_y/dist_x)/(Math.PI/180);
if (dist_x<0) {
angle += 180;
}
if (dist_x>=0 && dist_y<0) {
angle += 360;
}
wall_collision = 0;
for (x=1; x<=dist; x++) {
point_x = _x+x*Math.cos(angle*Math.PI/180);
point_y = _y+x*Math.sin(angle*Math.PI/180);
if (_root.wall.hitTest(point_x, point_y, true)) {
wall_collision = 100;
break;
}
}
_root.line._x = _x;
_root.line._y = _y;
_root.line._rotation = angle;
_root.line._alpha = 100-wall_collision;
}
Into that:
// calculate rotation based on target
_dx = this.x - _root.hero.x;
_dy = this.y - _root.hero.y;
// which way to rotate
_rotateTo = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (_rotateTo > barrel.rotation + 90) _rotateTo -= 360;
if (_rotateTo < barrel.rotation - 90) _rotateTo += 360;
// ease rotation
_trueRotation = (_rotateTo - barrel.rotation) / _rotateSpeedMax;
// update rotation
barrel.rotation += _trueRotation;
wall_collision = 0;
OuterLoop: for (var xi=1; xi<=_dx; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_rotateTo);
var point_y:Number = this.y + xi*Math.sin(_rotateTo);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation += _trueRotation;
_root.sight.alpha = 100 - wall_collision;
But the it does not work.
The rotation do work fine, but the whole "alpha = 0 if player is behind a wall" does not work.
Please help me resolving the issue.
Try the following:
// calculate rotation based on target
_dx = _root.hero.x-this.x;
_dy = _root.hero.y-this.y;
// The full distance is missing from your AS3 code
_dist = Math.sqrt(_dx*_dx+_dy*_dy);
// Return the old good approach for finding angle
angle = Math.atan(_dy/_dx)/(Math.PI/180);
if (_dx<0) {
_angle += 180;
}
if (_dx>=0 && _dy<0) {
_angle += 360;
}
wall_collision = 0;
OuterLoop: for (var xi=1; xi<=_dist; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_angle*Math.PI/180);
var point_y:Number = this.y + xi*Math.sin(_angle*Math.PI/180);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation = _angle;
// Alpha changed from [0, 100] scale to [0, 1] scale.
_root.sight.alpha = (100 - wall_collision) * 0.01;
Information on alpha in ActionScript 3.0.
As per AS3 reference, alpha is from 0 to 1, not 0 to 100. That would suggest
`_root.sight.alpha = (100 - wall_collision)/100.0ยด
might work.
Can You try the following code. I have no prev exp with flash, but seems like You missed something.
The iterator xi should take values in range of distance, not only by one axis dx.
// calculate rotation based on target
_dx = this.x - _root.hero.x;
_dy = this.y - _root.hero.y;
// the iteration is by distance in original article mentioned so
// keep dist
//=================================
_dist = Math.sqrt(_dx*_dx+_dy*_dy);
// which way to rotate
_rotateTo = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (_rotateTo > barrel.rotation + 90) _rotateTo -= 360;
if (_rotateTo < barrel.rotation - 90) _rotateTo += 360;
// ease rotation
_trueRotation = (_rotateTo - barrel.rotation) / _rotateSpeedMax;
// update rotation
barrel.rotation += _trueRotation;
wall_collision = 0;
// xi iterations are to a distance
//== =======
OuterLoop: for (var xi=1; xi<=_dist; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_rotateTo);
var point_y:Number = this.y + xi*Math.sin(_rotateTo);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation += _trueRotation;
// EDITED AFTER OTHERS SOLVED
// was
//_root.sight.alpha = 100 - wall_collision;
// should be:
// Alpha changed from [0, 100] scale to [0, 1] scale.
_root.sight.alpha = (100 - wall_collision) * 0.01;
// END OF SOLUTION
There is only slight modification to Your original code, marked by preceding //=====
EDIT:
And the winner is transparency range. Still, I do recommend to iterate to a distance, not to _dx.

Processing to Processing.js, PVector array

I just wrote a code in Processing(with some help, because I'm a beginner!!) and it doesn't work in Processing.js any idea how I can fix this code.
the code is a diagram showing intersection between one moving line and four other fixed lines.
I really appreciate your prompt answers , I have a deadline in two days and I'm a real beginner!!!
PVector[] lineCoordinates = new PVector[5];
PVector mStart, mEnd;
void setup() {
PFont font;
font = loadFont("ArialNarrow-12.vlw");
textFont(font, 12);
size(600, 200);
lineCoordinates[0] = new PVector(width/5, height/5); // start
lineCoordinates[1] = new PVector(width-10, height-(height/3)); // line 1
lineCoordinates[2] = new PVector(width-(width/5)-10, height-(height/3)); // line 2
lineCoordinates[3] = new PVector(width-(2*(width/5))-10, height-(height/3)); // line 3
lineCoordinates[4] = new PVector(width-(3*(width/5))-10, height-(height/3)); // line 4
mStart = new PVector(width/5, height-(height/3));
mEnd = new PVector();
fill(0);
strokeWeight(1);
}
void draw() {
background(255);
fill(0);
text("The Eye", (width/5)-10,( height/5)-5);
text("Picture Plane", mouseX, mouseY);
text("Horizon Line", 5, height-(height/3)-5);
text("x", width-(2*(width/5))+40, height-(height/3));
text("x", width-(3*(width/5))+40, height-(height/3));
text("x", width-(width/5)+40, height-(height/3));
fill(255,0,0);
noStroke();
ellipse(width-(width/5)-10, height-(height/3),5,5);
ellipse(width-(2*(width/5))-10, height-(height/3),5,5);
ellipse(width-(3*(width/5))-10, height-(height/3),5,5);
ellipse(width-10, height-(height/3),5,5);
fill(0);
stroke(1);
line(0, height-(height/3), width, height-(height/3)); // Horizon Line
mEnd.set(mouseX, mouseY, 0);
line(mStart, mEnd);
for (int i=1; i<lineCoordinates.length; i++) {
line(lineCoordinates[0], lineCoordinates[i]);
PVector is = lineIntersection(lineCoordinates[0], lineCoordinates[i], mStart, mEnd);
if (is!=null) { ellipse(is.x, is.y, 5, 5); }
}
}
void line(PVector s, PVector e) {
line(s.x, s.y, e.x, e.y);
}
PVector lineIntersection(PVector p1, PVector p2, PVector p3, PVector p4)
{
PVector b = PVector.sub(p2, p1);
PVector d = PVector.sub(p4, p3);
float b_dot_d_perp = b.x * d.y - b.y * d.x;
if (b_dot_d_perp == 0) { return null; }
PVector c = PVector.sub(p3, p1);
float t = (c.x * d.y - c.y * d.x) / b_dot_d_perp;
if (t < 0 || t > 1) { return null; }
float u = (c.x * b.y - c.y * b.x) / b_dot_d_perp;
if (u < 0 || u > 1) { return null; }
return new PVector(p1.x+t*b.x, p1.y+t*b.y);
}
Start by adding the following at the bottom:
void mouseMoved() {
x = mouseX;
y = mouseY;
redraw();
}
and replacing mouseX and mouseY with the x y vars.
At least this makes "Picture Plane" follow the mouse...

How can I implement Lanczos resampling after every canvas transform without having to make a new canvas?

UPDATE: Once I got this demo working... holy smokes, it's SLOW, like 12-16 seconds for only a level 2 render (when image is around 1000x2000 pixels). This is not even worth bothering with.
I found this really awesome and hopeful looking code in the top answer here: Resizing an image in an HTML5 canvas
//returns a function that calculates lanczos weight
function lanczosCreate(lobes){
return function(x){
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
}
}
//elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes){
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width: sx,
height: Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}
thumbnailer.prototype.process1 = function(self, u){
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}
if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self){
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
}
...
img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
//but feel free to raise it up to 8. Your client will appreciate
//that the program makes full use of his machine.
document.body.appendChild(canvas);
}
However, this implementation loads an image and renders it, end of story.
I have been trying to re-implement this code so that it does the filtering every time an existing canvas is scaled (think, zooming in and out of an image or document) without having to load a new image or create a new canvas.
How can I adapt it to work this way? Or is that even possible?
What you want to do is something like a singleton to reuse your canvas object. This will let you save the cost of create a new canvas object each time and you will reuse the same object
function getCanvas(){
var canvas;
if (typeof canvas === "undefined"){ canvas = document.createElement("canvas");}
return canvas;
}
img.onload = function() {
var canvas = getCanvas("canvas");
.... THE REST OF YOUR CODE .......
}
.
However this is not what slows your code, image scaling Algorithms are really heavy algorithms with intensive cpu use "usually make use of gpu acceleration at a really low level", and use advanced techniques like multiple bufferr and so others. here is a interesting tutorial in java.net on how image scaling works, it is in java but you can interpolate to any language.
Javascript is not ready for this techniques, so I recommend you to use the transformations available in the canvas api, as in the tutorial you read the efficient way is using the canvas2Dcontext.
var ctx = canvas.getContext("2d");
ctx.scale(2,2);

Box to Circle Collision AS3

I'm trying to implement a aabb to circle collision.
Here's my code:
//From another file
radius = (sprite.width + sprite.height) / 4;
private function BoxToCircleCollision(box1:BoundingBox, circle1:BoundingCircle):Boolean
{
var nBoxMinX:Number = box1.sprite.x - box1.sprite.width / 2;
//var nBoxMinY:Number = box1.sprite.x + box1.sprite.width / 2;
var nBoxMaxX:Number = box1.sprite.y - box1.sprite.height / 2;
//var nBoxMaxY:Number = box1.sprite.y + box1.sprite.height / 2;
var nCirMinX:Number = circle1.sprite.x - circle1.radius / 2;
//var nCirMinY:Number = circle1.sprite.y - circle1.radius;
var nCirMaxX:Number = circle1.sprite.x + circle1.radius / 2;
//var nCirMaxY:Number = circle1.sprite.y + circle1.radius;
if (nBoxMaxX, 2 > nCirMinX))
{
Main.WriteDebug("Box max: " + nBoxMaxX + " | Circle min: " + nCirMinX);
return true;
}
else
{
Main.WriteDebug("Box max: " + nBoxMaxX + " | Circle min: " + nCirMinX);
return false;
}
}
Somehow the collision does work as expected.
Either they never move at all and "collided" was traced, or they'll continue moving and never collide when I tried swapping values around.
Is there something i'm missing in my logic???
My box-box and circle-circle collision are working fine.
Thanks in advance for your help.
This row doesn't look at all correct:
var nBoxMaxX:Number = box1.sprite.y - box1.sprite.height / 2;
Maybe you meant this:
var nBoxMaxX:Number = box1.sprite.x + box1.sprite.width / 2;
This line won't compile:
if (nBoxMaxX, 2 > nCirMinX))
Edit:
Here's a function to help you get the AABB <-> Circle collision right. It's not a complete solution but you can combine it with the calculations you have for the AABB min and max values, should be trivial:
private function collideAABBandCircle(c : Circle, aabb:AABB) : Boolean {
var sqDist : Number = sqDist(c.centerPoint, aabb);
return sqDist <= c.radius * c.radius:
}
private function sqDist(p : Point, aabb:AABB) : Number {
/* CALCULATE min and max values of aabb bounds */
var sqDist : Number = 0.0;
if(p.x < minX) {
sqDist += (minX - p.x) * (minX - p.x);
}
if(p.x > maxX) {
sqDist += (p.x - maxX) * (p.x - maxX);
}
if(p.y < minY) {
sqDist += (minY - p.y) * (minY - p.y);
}
if(p.y > maxY) {
sqDist += (p.y - maxY) * (p.y - maxY);
}
return sqDist;
}
There's a good article about using Separating Axis Theorem for 2d collision detection. Great read if you want to find out how 2d collision detection works.