Im trying to write a program to generate random rectangles within a tag,
and then rotate each rectangle within its origin, if I hit the start button again the canvas should clear out to draw a new set of rectangles and rotate them and so on.
pasting my whole program would look hideous, so Ill post what I think is important:
Creating my arrays and initializing some values :
var rectArrayStartX,rectArrayStartY,rectArrayDimY, rectArrayDimX;
function start()
{
if (i >= 1){
canvas.restore()
i--;
}
construct();
window.setInterval( "rotateShape()", 500 );
}
function construct()
{
if ( i < 1) {
canvas.save();
i++
}
var canvas = document.getElementById("gameId");
var context = canvas.getContext("2d");
var k,m;
var points = parseInt(pGame.box.value);
rectArrayStartX[100];
rectArrayStartY[100];
rectArrayDimY[100];
rectArrayDimX[100];
the code goes on but this bit gives me this error:
Uncaught TypeError: Cannot read property '100' of undefined
im trying to create an array for each point, origin x and y + width and height.
then using the fillRect ill pass the array values to draw my rectangles.
Second bit im having problem with is rotating them, im using the following function:
function rotateShape()
{
var randomAngle;
randomAngle = Math.random() * 5 ;
if (randomAngle>3.14)
{
randomAngle=3.14
}
//context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(randomAngle);
}
im calling it in the following function but it does nothing, althought later I need to locate each rect origin and rotate it in the right way but for now I just want to make sure the function works :
function start()
{
if (i >= 1){
canvas.restore()
i--;
}
construct();
window.setInterval( "rotateShape()", 500 );
}
if revising my whole code will make it easier, do let me know to provide it.
thank you for your time and sorry for the long topic.
Here's some code to get you started...
This code will:
Draw a 50x30 rectangle,
That is rotated 30 degrees around its centerpoint,
And is positioned with its centerpoint at canvas coordinate [100,100]
The code:
// save the untransformed context state
context.save();
// move (translate) to the "rotation point" of the rect
// this will be the centerpoint of the rect
context.translate(100,100);
// rotate by 30 degrees
// rotation requires radians so a conversion is required
context.rotate( 30*Math.PI/180 );
// draw a 50 x 30 rectangle
// since rects are positioned by their top-left
// and since we're rotating from the rects centerpoint
// we must draw the rect offset by -width/2 and -height/2
var width=50;
var height=30;
context.fillRect( -width/2, -height/2, width, height );
// restore the context to its untransformed state
context.restore();
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'm create a rectangle in three.js based on 2 coordinates. The first coordinate is the cell of the first user click, the second coordinate is where the user drags the cursor.
The rectanlge that is being created is the correct size, however it 'grows' from it's center, whereas I want it to 'grow' from the corner of the first user click. I've tried a few potential solutions to change the origin of the geometry but I haven't found a fix yet.
The demo can be see here - with the code below.
var startPoint = startPlace;
var endPoint = endPlace;
var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);
var differenceZ = Math.abs(startPlace.z - zIntersect.z);
var differenceX = Math.abs(startPlace.x - xIntersect.x);
floorGeometryNew.rotateX(-Math.PI / 2);
var floorGeometryNew = new THREE.PlaneGeometry(differenceX, differenceZ);
floorGeometryNew.rotateX(-Math.PI / 2);
var x = startPoint.x;
var y = startPoint.y;
var z = startPoint.z;
var voxel = new THREE.Mesh(floorGeometryNew, tempMaterial);
voxel.position.set(x, y, z);
Center of your rectangle is in the middle between startPoint and endPoint and it's the average of them:
voxel.position.addVectors(startPoint, endPoint).divideScalar(2);
Approach 1. Without creating of a new geometry every time when you change size of a rectangle. The idea is:
Create a mesh of a plane once on start with a double-sided material
Set the first vertex of the plane's geometry with the current point of intersection
Track the point of intersection and apply its value to the last vertex of the plane's geometry and change the second and the third vertices accordingly to positions of the first and the last vertices
For example, we created a plane mesh newRect on mouseDown event and set its geometry's first vertex to the point of intersection which was on that moment:
newRectGeom.vertices[0].set(onPlanePoint.x, onPlanePoint.y + .5, onPlanePoint.z);
and then on mouseMove we get the point of intersection and apply its coordinate to the fourth (last) vertex, also we change values of vertices 1 and 2:
newRect.geometry.vertices[1].set(onPlanePoint.x, newRect.geometry.vertices[0].y, newRect.geometry.vertices[0].z);
newRect.geometry.vertices[2].set(newRect.geometry.vertices[0].x, newRect.geometry.vertices[0].y, onPlanePoint.z);
newRect.geometry.vertices[3].set(onPlanePoint.x, onPlanePoint.y + .5, onPlanePoint.z);
It's simplier than it sounds :)
jsfiddle example. Build mode off - OrbitControls are enabled; Build mode on - controls are disabled, you can draw rectangles.
Approach 2. Instead of controlling vertices we can control position and scaling of rectangle.
On mousedown event we'll set the startPoint with the point of intersection
startPoint.copy(onPlanePoint);
and then we'll find position and scaling of our rectangle:
newRect.position.addVectors(startPoint, onPlanePoint).divideScalar(2);
newRect.position.y = 0.5; // to avoid z-fight
newRect.scale.set(Math.abs(onPlanePoint.x - startPoint.x), 1, Math.abs(onPlanePoint.z - startPoint.z))
jsfiddle example. Visually and functionally it's the same as the Approach 1. From my point of view, Approach 2 is simplier.
When you call
voxel.position.set(x, y, z);
then the center of your mesh is setted to this position. So you have to take half of the length and half of the width of your rectangle to add to this position. These values you can get with a bounding box.
var bbox = new THREE.Box3();
bbox.setFromObject( voxel );
var val = bbox.max.x - bbox.min.x;
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.
How to prevent some canvas coords overflowing?
Example case:
The following javascript:
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
context.strokeStyle = "#000";
context.beginPath();
// line from (100, 100) to (100,999999999999.9)
context.moveTo(100.0, 100.0);
context.lineTo(100.0, 999999999999.9);
// line from (200, 200) to (200,9999999999999.9)
context.moveTo(200.0, 100.0);
context.lineTo(200.0, 9999999999999.9);
context.stroke();
gives this canvas output
In these examplex both lines have the end Y-coord positive but the second one seems to be interpreted like -Inf. It seems that internally the values are cast to some unsigned int making some bit value considered like the sign but I found no explicit documentation about it.
UPDATE
What I want is to draw the line correctly not to prevent drawing lines with coords out of canvas image.
I know there is a way doing some linear algebra (calculating the intersection point between the line and the border) but I want to know if there is some simpler method.
tl;dr Jsfiddle
The data type of moveTo and lineTo paramteters is unrestricted double. You can see that it's 32bit floating point number, presumably signed. That means that by specification, there should be no overflow and the error depends on your browser's implementation.
My tests confirmed this, because neither Firefox, Google Chrome or Opera did render anything at all, indicating undefined suspicious behavior. If the coordinate is invalid, I'd expect an error. If I used smaller coordinates (300), they all provided this result (cropped, framed):
I then performed some tests and the last number that can be rendered is:
Math.pow(2,31)-Math.pow(2,6)-1 = 2147483583
So if you're actually trying to solve a problem and not asking out of curiosity, I propose one of these two solutions:
Make a helper function to cap the coordinates.
var max = 2147483583;
var min = -2147483583;
function safeCoordinate(number) {
if(number>max)
return max;
if(number<min)
return min;
return number;
}
Then just call:
context.lineTo(safeCoordinate(x), safeCoordinate(y));
Override actual canvas methods to fix the issue
If you're feeling tough, you can actually go ahead and override canvas context methods. That's something many people would advise you against though, as changing built in stuff often causes mysterious errors that are hard to track down. With this warning out of the way:
(function(max, min, context_proto) {
// Override move to
var old_moveTo =context_proto.moveTo;
context_proto.moveTo = function(x, y) {
return old_moveTo.call(this, safeCoordinate(x), safeCoordinate(y));
}
var old_lineTo =context_proto.lineTo;
context_proto.lineTo = function(x, y) {
return old_lineTo.call(this, safeCoordinate(x), safeCoordinate(y));
}
// Override more methods if needed!
...
// Our helper function
function safeCoordinate(number) {
if(number>max)
return max;
if(number<min)
return min;
return number;
}
})(2147483583,-2147483583, CanvasRenderingContext2D.prototype);
Let's say we have a canvas.height = 500 and canvas.width = 500 and we want to prevent the coords to be within the range 0 to 500. We can use those properties to set a range when the context is called using a function().
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = 500;
canvas.height = 500;
context.strokeStyle = "#000";
letsDraw(100, 200, 800, 300); //Returns Alert Error
function letsDraw(moveToX, moveToY, lineToX, lineToY){
if((moveToX<canvas.width && moveToX>0) && (moveToY<canvas.height && moveToY>0)){
context.beginPath();
context.moveTo(moveToX, moveToY);
if((lineToX<canvas.width && lineToX>0) && (lineToY<canvas.height && lineToY>0)){
context.lineTo(lineToX, lineToY);
context.stroke();
} else {
alert("Range outside Canvas");
return false;
}
} else {
alert("Range outside Canvas");
return false;
}
}
I'm currently working on a basic javascript game that has two sprites that are not to be collided together. However, basic bounding box collision won't suffice as there are portions of the sprites that are transparent and wouldn't 'count' as colliding. I found a solution to the problem that I am having, but I can't get it to work. What I would like to do is calculate the transparent portions of the sprites and make sure that if the transparent portions overlap, that there is no collision detected. Here is what I found that solves the problem.
http://blog.weeblog.net/?p=40#comments
/**
* Requires the size of the collision rectangle [width, height]
* and the position within the respective source images [srcx, srcy]
*
* Returns true if two overlapping pixels have non-zero alpha channel
* values (i.e. there are two vissible overlapping pixels)
*/
function pixelCheck(spriteA, spriteB, srcxA, srcyA, srcxB, srcyB, width, height){
var dataA = spriteA.getImageData();
var dataB = spriteB.getImageData();
for(var x=0; x<width; x++){
for(var y=0; y<height; y++){
if( (dataA[srcxA+x][srcyA+y] > 0) && (dataB[srcxB+x][srcyB+y] > 0) ){
return true;
}
}
}
return false;
}
And for calculating the image data:
/**
* creating a temporary canvas to retrieve the alpha channel pixel
* information of the provided image
*/
function createImageData(image){
$('binaryCanvas').appendTo('body');
var canvas = document.getElementById('binaryCanvas');
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
var canvasData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var imageData = [image.width];
for(var x=0; x<image.width; x++){
imageData[x] = [image.height];
for(var y=0; y<image.height; y++){
var idx = (x + y * image.width) * 4;
imageData[x][y] = canvasData.data[idx+3];
}
}
$("#binaryCanvas").remove();
return imageData;
}
The problem is that I don't know how to implement this solution, or if this is the best solution to my problem. Is this what I'm looking for? And if so, where do I put these methods? The thing that I'm most confused about is what I should be passing to spriteA and spriteB. I've tried passing Images and I've tried passing the imageData returned from the pixelCheck method, but receiving the same error: that the object or image has no method 'getImageData'. What am I doing wrong?
Two things are wrong with this.
You made a function:
function createImageData(image){...}
But what you are calling is:
spriteA.getImageData();
spriteB.getImageData();
The dot notates a property of an object. You were trying to call a function that was never part to the objects. There are some simple fixes.
add the createImageData() function to your constructor :
function Sprite(){
...
this.createImageData = function(image){...};
...
}
or :
Sprite.createImageData = function(image{...};
or just call it correctly:
createImageData(spriteA.image); // or whatever the image is
Second, your function calls for an image parameter, but you aren't supplying one. Simply remember to supply the image when you call it. You could also remove the parameter and get the image from within the function.
Sort of like this:
function Sprite(){
...
this.createImageData = function(){
var image = this.image;
// or just use this.image, or whatever it is
...
}
...
}
Hope this helped.