I just started playing around with pixi and have drawn multiple rectangles from an array with pixel coordinates like this:
var rectangle = [....];
....
var stage = new PIXI.Stage();
var renderer = PIXI.autoDetectRenderer(wrapper.getWidth(), wrapper.getHeight(), { transparent: true });
....
var graphics = new PIXI.Graphics();
graphics.interactive = true;
graphics.on("mouseover", function(e) {
this.alpha = 0.5;
}).on("mouseout", function() {
this.alpha = 1;
});
graphics.beginFill(0xFFFFFF);
graphics.lineStyle(2, 0x000000);
for (var i = 0; i < rectangle.length; i++) {
graphics.drawRect(rectangle[i][0], rectangle[i][1], 10, 10);
}
graphics.endFill();
stage.addChild(graphics);
renderer.render(stage);
The events are triggered but the object I get by "e" or "this" inside the callback is the object for all graphics. I want to get that single "mouseovered" rectangles object I can see in the graphicsData, but there is no id or anything to identify it by. How can I do this?
Performance is of essence as I'm going to render 20k+ rectangles or circles.
Without drawing each rectangle onto it's own PIXI.Graphics object you won't be able to get individual mouseover events. This is because as far as PIXI is concerned the Graphics object is a single bitmap image.
I would suggest performing your own hit tests inside the mouseover function to detect which rectangle the cursor is over.
If you are using PIXI.Rectangles you can take advantage of the built in Rectangle.Contains function to check if a point (in this case the mouse position) is inside the bounds.
Related
As you can see on attached image it has the rhombus with the ellipse inside which is almost transparent.
But this is just an image.
How can I create this with createjs?
A more detailed description of the problem:
What you see in the picture is not exactly what I need.
Ideally, my task is to make two triangles out of this rhombus with an ellipse inside.
The ellipse should create some kind of transparency in the triangle so that all the elements that will be under this triangle are visible through.
My implementation:
I make a triangle according to this example:
(thank to this fiddle)
(createjs.Graphics.Polygon = function(x, y, points) {
this.x = x;
this.y = y;
this.points = points;
}).prototype.exec = function(ctx) {
// Start at the end to simplify loop
var end = this.points[this.points.length - 1];
ctx.moveTo(end.x, end.y);
this.points.forEach(function(point) {
ctx.lineTo(point.x, point.y);
});
};
createjs.Graphics.prototype.drawPolygon = function(x, y, args) {
var points = [];
if (Array.isArray(args)) {
args.forEach(function(point) {
point = Array.isArray(point) ? {x:point[0], y:point[1]} : point;
points.push(point);
});
} else {
args = Array.prototype.slice.call(arguments).slice(2);
var px = null;
args.forEach(function(val) {
if (px === null) {
px = val;
} else {
points.push({x: px, y: val});
px = null;
}
});
}
return this.append(new createjs.Graphics.Polygon(x, y, points));
};
stage = new createjs.Stage("demoCanvas");
poly1 = new createjs.Shape();
poly1.graphics.beginFill("Red").drawPolygon(0,0,10,10,10,40,40,30,60,5,30,0);
poly1.x = 10;
poly1.y = 10;
stage.addChild(poly1);
stage.update();
(if there is a more convenient or even correct way to make a triangle and this will help in solving my problem, I will gladly accept your solution).
Next, I simply overlay the ellipse drawn with drawEllipse on top of this triangle.
I understand that I may be doing something wrong, and that is why I am here.
Any help will be accepted!
I assume you are using the Graphics API to draw your content. If so, you simply need to ensure the "hole" draws with reverse winding. This just means the shape needs to be drawn in the reverse direction.
For example, the Canvas2D rect method draws clockwise, so to subtract from them, the instructions need to be drawn in the other direction.
var s = new createjs.Shape();
s.graphics.beginFill("red")
.drawRect(0,0,300,300) // Draw a square
// Then draw a smaller square
.moveTo(100,100) // Top left
.lineTo(100,200) // Bottom left
.lineTo(200,200) // Bottom right
.lineTo(200,100) // Top right
.lineTo(100,100) // Top left
.closePath(); // Optional if you are done
The drawEllipse has an anticlockwise parameter which does the trick as well. Here is a jsfiddle sample, which actually draws it the other way (small cut-out first), but with the same result.
UPDATE
In order for the shape to "cut out" the other one, it has to be part of the same graphics instance, and part of the same path instructions. If you closePath() after any drawing instructions, any new instructions are drawn on top of that without cutting it out. Using separate shape instances does this automatically.
Using the updated code, I added a simple drawEllipse() call using default clockwise winding, and it cut out the circle: https://jsfiddle.net/lannymcnie/yd25h8se/ -- Note that I scaled up the coordinates from above x10 to make it more visible.
Cheers,
I made a code with the intention of having a square appear where the mouse is pressed down, stay in that spot despite mouse movement and not disappear when the mouse it released.
THIS IS P5.JS ! https://p5js.org/reference/
Instead, the square follows the mouse until it is released then it disappears!
I believe that my code keeps declaring a new constant and deleting the old one every time the shoot() function is run.
var clocker = 0;// NOT YET USED
var player = {
x:400,
y:400,
};
function shoot(x1, y1, x2, y2, speed, range, power, playerDirection){
var bulletAlive = true;
var bulletDamage = power;
const startX = x1;
const startY = y1;
const destX = x2;
const destY = y2;
var bulletX = startX;
var bulletY = startY;
if(bulletAlive){
fill(0,100,200);
rect(destX-12.5,destY-12.5,25,25);
};
};
function setup() {
createCanvas(1000,650);
}
function draw() {
background(204,204,204);
if(mouseIsPressed){
shoot(player.x,player.y,mouseX,mouseY,2,100,0,"right");
}
}
Perhaps I am using const wrong. If so how should I use it? How can I make it so that destX and destY don't change? (Don't follow mouse or disappear)
PS: sorry for the miscellaneous information, this is supposed to build up to simple bullet physics.
It sounds like there is some confusion about scoping, and there is probably a better way to think about this problem.
First let's look at what is going wrong, and talk through a few details to explain why.
Just like variables (let, var), constants are always declared in a specific scope.
Scopes are like containers for constants and variables. Scopes are private, that is they cannot be accessed from the outside. Scopes can be created and destroyed.
When you declare a constant directly inside a function, the scope is the function itself (like startX inside shoot). (Note that if you declare a constant inside an if statement or other block, the scope is the block. That's not the case here, though.)
Function scopes are created each time the function is called, and destroyed when the function is finished executing.
Each time a function is called and its scope is created, all constants (and variables) are reinitialized with new values.
A constant appearing in your code may have different values during different function calls. It is only constant during its lifetime, which in your case is a single given execution of the function.
This is why your constants aren't staying constant. You are calling shoot() repeatedly while the mouse is down, and so the constants are repeatedly being recreated and assigned new values.
With this information, hopefully you can see the problems with the current approach. As for a solution, let's think about what exactly is happening. shoot() is an action that should be triggered when the user issues a "shoot" command, such as a mouse click. The draw() function is a continuous event triggered to say "hey, update the screen". Putting the shoot action inside the draw event is kind of a mis-match of intentions and is the root of struggles like this.
Instead, let's introduce the idea of a bullet object. A bullet has an x and a y value. A bullet is created when the user shoots, and is given a specific x and y value at the moment of creation. None of this happens inside draw, it happens in another event listener such as "click".
The job of draw is to check to see if there is an active bullet, and if there is one, draw it at the specified x and y coordinate. If there is no bullet, do nothing. (Of course you might need to draw other things as well, but that's unrelated to drawing the bullet).
Keeping object creation and object drawing separate makes it easier to have the kind of control you're looking for.
Edit: Adding some code examples
Here's what the code would look like to do exactly what you asked, using the bullet object idea above. The inline comments should explain each step.
// This is where we'll store an active bullet object.
// The `shoot()` function is responsible for setting this.
// `draw()` is responsible for rendering the bullet.
// Initially we'll set the value to `null` to explicitly
// indicate that there is no bullet.
let activeBullet = null;
// The purpose of `shoot()` is to create a bullet
// and make it available to be rendered.
function shoot(x, y) {
// Create the bullet object.
const newBullet = {
x: x,
y: y,
size: 25
};
// Set the active bullet to the new bullet. This will
// cause any previously active bullet to disappear.
activeBullet = newBullet;
}
// P5 functions
// ------------
function setup() {
createCanvas(1000, 650);
}
// Shoot when the player clicks.
function mousePressed() {
shoot(mouseX, mouseY);
}
function draw() {
// Always start with a blank canvas.
clear();
// If there is an active bullet, draw it!
// (`null` is "falsy", objects are "truthy", so the
// `if` statement will only run after the `activeBullet`
// variable is assigned a bullet object.)
if (activeBullet) {
fill(0, 100, 200);
rect(
activeBullet.x - activeBullet.size / 2,
activeBullet.y - activeBullet.size / 2,
activeBullet.size,
activeBullet.size
);
}
}
You also mentioned you wanted to build up to simple bullet physics. Just to show how the bullet object idea works nicely, here's a demo where you can click to shoot multiple bullets, they all move independently, and collide with a wall at which point they are removed. There's a lot more involved in building games, but hopefully it's an inspiring starting point :)
// Store canvas dimensions globally so we have easy access.
const canvasWidth = 1000;
const canvasHeight = 650;
// We'll add a "wall" object so we have something the bullets can
// collide with. This value is the X position of the wall.
const wallX = canvasWidth - 200;
// Instead of a single bullet, using an array can accommodate
// multiple bullets. It's empty to start, which means no bullets.
// We can also use `const` for this, because we won't ever assign
// a new value, we'll only modify the contents of the array.
const activeBullets = [];
function shoot(x, y) {
// Create the bullet object.
const newBullet = {
x: x,
y: y,
size: 25,
speed: 4
};
// Instead of overwriting a single bullet variable, we'll push
// the new bullet onto an array of bullets so multiple can exist.
activeBullets.push(newBullet);
}
// P5 functions
// ------------
function setup() {
createCanvas(canvasWidth, canvasHeight);
}
// Shoot when the player clicks.
function mousePressed() {
shoot(mouseX, mouseY);
}
function draw() {
// Always start with a blank canvas.
clear();
// Draw our "wall".
fill(50);
rect(wallX, 0, 60, canvasHeight);
// Set the fill color once, to use for all bullets. This doesn't
// need to be set for each bullet.
fill(0, 100, 200);
// Loop through the array of bullets and draw each one, while also
// checking for collisions with the wall so we can remove them. By
// looping backwards, we can safely remove bullets from the array
// without changing the index of the next bullet in line.
for (let i=activeBullets.length-1; i>=0; i--) {
// Grab the current bullet we're operating on.
const bullet = activeBullets[i];
// Move the bullet horizontally.
bullet.x += bullet.speed;
// Check if the bullet has visually gone past the wall. This
// means a collision.
if (bullet.x + bullet.size / 2 > wallX) {
// If the bullet has collided, remove it and don't draw it.
activeBullets.splice(i, 1);
} else {
// If the bullet hasn't collided, draw it.
rect(
bullet.x - bullet.size / 2,
bullet.y - bullet.size / 2,
bullet.size,
bullet.size
);
}
}
}
The const declaration exists only within the scope of shoot. So once the shoot function is finished executing, startX startY destX destY, being const, are deleted.
Possible fix:
var didShootAlready = false;
var startX, startY, destX, destY;
function shoot(/*params*/){
if(!didShootAlready){
didShootAlready = true;
startX = x1;
startY = y1;
destX = x2;
destY = y2;
}
//do the rest
}
I am struggling with camera functionality that (I think) would provide a way to force my polygon to stick to the top of my house on zoom-out, zoom-in, and rotation (or camera move).
This question follows an earlier question that was resolved. Now I need a little help resolving my next issue.
The sample code I am trying to follow is located in the gold standard that appears to be baked into the existing camera controller here.
pickGlobe is executed with the parameters of the viewer, the correct mousePosition in world coordinates and a result parameter, which I don't care about right now. scene.pickPosition takes the c2position (Cartesian2) and should return the scratchDepthIntersection (Cartesian3). Instead, the returned value is undefined.
Here is my code:
function clickAction(click) {
var cartesian = scene.camera.pickEllipsoid(click.position, ellipsoid);
if (cartesian) {
var setCartographic = ellipsoid.cartesianToCartographic(cartesian);
collection.latlonalt.push(
Cesium.Math.toDegrees(setCartographic.latitude).toFixed(15),
Cesium.Math.toDegrees(setCartographic.longitude).toFixed(15),
Cesium.Math.toDegrees(setCartographic.height).toFixed(15)
);
lla.push(Cesium.Math.toDegrees(setCartographic.longitude), Cesium.Math.toDegrees(setCartographic.latitude));
if (lla.length >= 4) {
console.log((lla.length / 2) + ' Points Added');
}
enableDoubleClick();
enableDraw();
testMe(click.position); <--------------------- straight from the mouse click
}
}
var pickedPosition;
var scratchZoomPickRay = new Cesium.Ray();
var scratchPickCartesian = new Cesium.Cartesian3();
function testMe(c2MousePosition) { <--------------------- straight from the mouse click
if (Cesium.defined(scene.globe)) {
if(scene.mode !== Cesium.SceneMode.SCENE2D) {
pickedPosition = pickGlobe(viewer, c2MousePosition, scratchPickCartesian);
} else {
pickedPosition = camera.getPickRay(c2MousePosition, scratchZoomPickRay).origin;
}
}
}
var pickGlobeScratchRay = new Cesium.Ray();
var scratchRayIntersection = new Cesium.Cartesian3();
var c2position = new Cesium.Cartesian2();
function pickGlobe(viewer, c2MousePosition, result) { <--------------------- straight from the mouse click
c2position = c2MousePosition; <--------------------- setting to Cartesian2
var scratchDepthIntersection = new Cesium.Cartesian3();
if (scene.pickPositionSupported) {
scratchDepthIntersection = scene.pickPosition(c2MousePosition); <--------------------- neither works!
}
}
Here are my variables:
Here is the result:
Here are my questions to get this code working:
1. Why is scratchDepthIntersection not getting set? c2position is a Cartesian2 and c2MousePosition is straight from the mouse.click.position and scratchDepthIntersection is a new Cartesian3.
The correct value for mousePosition is a Cartesian2 containing window coordinates, not a Cartesian3. Such mouse coordinates usually come from a callback from Cesium.ScreenSpaceEventHandler, but can also be constructed from native JavaScript mouse/touch events.
If you inspect the contents of mousePosition, you should find x and y values in window pixel coordinates.
I see you edited the question to include the contents of mousePosition, and it looks like the mouse coordinates have already been converted into ellipsoid Cartesian3 coordinates, which will prevent this code from working. You want original mouse coordinates going directly into scene.pickPosition for this to work.
I create a little html5 game with canvas.
In the canvas, there are many displayed sprites and one of them move automatically from left to right. The others are statics.
When I move the mouse onto the canvas, I draw all sprites in a temporary canvas and I use getImageData to find the sprite onto which the mouse is over.
But getImageData make slow anormally the moving sprite in Firefox.
So what is the solution to avoid this deceleration ?
Here my code :
function getSelectedObject(array_objects)
{
//Clear the temporary canvas :
tmpcx.clearRect(0, 0, tmpc.width, tmpc.height);
/*Search the right sprite object :*/
for(var i = array_objects.length-1; i >= 0; i--)
{
array_objects[i].draw(tmpcx);
imageData = tmpcx.getImageData(mouse_x, mouse_y, 1, 1);
component = imageData.data;
if(component[3] > 0)
{
//Return the sprite object found :
return array_objects[i];
}
else
{
tmpcx.clearRect(0, 0, tmpc.width, tmpc.height);
}
}
return false;
}
canvas.onmousemove = function(event)
{
selectedObject = getSelectedObject(array_objects);
}
Not sure how much of a performance gain you'd get with this - no need to clear the temp canvas between sprites .... the pixel is clear until a sprite is painted on it!
I've referenced a function called checkBoundingBoxisOver - not sure if you could write this function, but I can't right now - besides, I don't even know what your array_objects are!!!
I would think it were simple, just need the x, y, width, height of a sprite to do a preliminary check if the sprite could even possibly be under the mouse before doing the expensive draw
function getSelectedObject(array_objects) {
//Clear the temporary canvas :
tmpcx.clearRect(0, 0, tmpc.width, tmpc.height);
var sprite;
/*Search the right sprite object :*/
for (var i = array_objects.length - 1; i >= 0; i--) {
sprite = array_objects[i];
if (checkBoundingBoxisOver(sprite, mouse_x, mouse_y)) {
sprite.draw(tmpcx);
imageData = tmpcx.getImageData(mouse_x, mouse_y, 1, 1);
component = imageData.data;
if (component[3] > 0) {
return sprite;
}
}
}
return false;
}
I ran into a similar issue reading pixels from a large bitmap every frame of the animation. In my case it is a black and white image showing where the world is water or land.
getImageData is extremely slow on Firefox even when reading just a single pixel.
My solution is to call getImageData only once and store the result in a imageData variable
var imageData = self.context.getImageData(0,0,image.width, image.height);
Then you can make repeated calls to the image data and pull out the part of the image you want. In my case I just need a single pixel or a single color which looks like this
var pixelRed = this.imageData.data[(y* imageWidth * 4) + (x * 4)] == 0;
x and y are self explanatory and since the pixels are 4 byte values (Red, Green, Blue, Alpha) I need to multiply my array index by 4. It proves to be very fast for me.
It be pretty easy to use this code to grab any portion out of the array directly as long as it is not too big.
I'm trying to animate between two complex paths using Paper.js and Tween.js. I've gotten pretty close, I can move all of the points in the path to the correct final positions, but I'm having problems with the handleIn and handleOut for each segment. It doesn't seem to be updating them.
Here's my code: http://codepen.io/anon/pen/rVBaZV?editors=101
var endPathData = 'M740,342.9c-32,...etc...';
var endPath = new Path(endPathData);
endPath.fillColor = '#4CC7A4';
beginPathData = 'M762.8,262.8c-48,...etc...';
var beginPath = new Path(beginPathData);
beginPath.fillColor = '#FFC1D1';
var numberOfSegments = beginPath.segments.length;
for (var i = 0; i < numberOfSegments; i++) {
var tween = new TWEEN.Tween(beginPath.segments[i].point)
.to({
x: endPath.segments[i].point.x,
y: endPath.segments[i].point.y
}, 3000)
.easing(TWEEN.Easing.Linear.None)
.start();
}
view.draw();
view.onFrame = function (event) {
TWEEN.update();
};
I'd like the pink path to end up exactly like the green one, but now I'm stuck. Is there anyway to achieve this?
You need to tween the handles too.
Each segment has two handles: segment.handleIn and segment.handleOut
in your example code you tween the segment.point (the segments position) resulting in the right location of the segments.
I don't know your Tween library, so it is up to you to implement it.
But It looks like you can add to more new tween one for the
beginPath.segments[i].handleIn
and one for the
beginPath.segments[i].handleOut
You can easily check that your code is right by letting paperjs smooth your path and taking care of the handles. By updating the onFrame function like this:
view.onFrame = function (event) {
TWEEN.update();
beginPath.smooth();
endPath.smooth();
};
and this results in the same shaped path.