Here is my code:
//ct is a canvas context for drawing stuff
//bw is image width, bh is image height
function drawBox() {
ct.translate(x, y)
ct.rotate(rot)
ct.drawImage(box, -bw/2, -bh/2)
ct.fillRect((-bw/2) + (50 * Math.sin(rot)), (-bh/2) - (50 * Math.cos(rot)), 20, 20)
ct.rotate(-rot)
ct.translate(-x, -y)
}
It is supposed to draw the box, and then place the rectangle 50 pixels in front of it. However, it is not working. The rectangle is rotating two times around the image for every time the image rotates once.
I've experimented a bit, and this code works:
function drawBox() {
ct.drawImage(box, x, y)
ct.fillRect((x) + (50 * Math.sin(rot)), (y) - (50 * Math.cos(rot)), 20, 20)
}
I have removed the rotation and changed the coordinates to x and y. If the above code works, why doesn't rotating it and then doing this code work? How can I fix this problem?
I seem to have solved this. I realized the problem was that I was rotating around the wrong point when drawing my other box, so I used this code:
function drawBox() {
ct.translate(x, y)
ct.rotate(rot)
ct.drawImage(box, -halfbw, -halfbh)
ct.rotate(-rot)
ct.translate(-x, -y)
if (sCount > 0) {
sCount --
ct.translate(x, y+halfbh)
ct.rotate(rot+PI)
ct.fillRect((halfbh * Math.sin(rot)), (halfbh * Math.cos(rot))+halfbh, 10, 50)
ct.rotate(-rot-PI)
ct.translate(-x, -y-halfbh)
}
}
(I added a couple of irrelevant things, like sCount, but this is essentially what the problem was.) I had to un-translate and un-rotate so I could re-translate and rotate to where the second box would be at the right place.
Related
I'm writing a simple computer animation, which is a line that rotates around a fixed point at the center of that line. The amount of rotation is based on a gradient noise algorithm (OpenSimplex noise). The line has an origin [x,y] and a nr of the animation frame. These three values plugged into OpenSimplex noise give a rotation value. This part is working perfectly.
The problem is I want to make the line appear to follow the mouse cursor, depending on how far the mouse cursor is from the line. The cursor has coordinates [mx, my] (which change for every frame of animation). I can easily rotate the line and point straight towards the cursor. But I'm having difficulties factoring in the distance. To clarify; the line is rotation on the gradient noise and the mouse cursor alters that rotation to make the line (at [x, y]) point at [mx, my].
Also, the line has an 180 degree identity, so the closest end should point towards the mouse.
Basically what I'm doing now is taking "rotation line" plus "rotation mouse". If it is between 90 and 270 deg the back of the line is closest to the cursor, otherwise the front (for simplicity this is not included in the example code below). So I then take the difference, factor in the distance and substract or add it to the rotation of the line. And this works fairly well except for some artifacts.
let r = OpenSimplexNoise(x, y, frame); // gives current original rotation
let frame = 68; // whichever frame
let x = 60; // some fixed coordinate of line
let y = 60; // some fixed coordinate of line
let mouseX = 10; // changes when the mouse moves
let mouseY = 10; // changes when the mouse moves
let mouseRadius = 200;
let width = 100;
let height = 1;
function distance (x, y, cx, cy) {
return Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy));
}
function angle (x1, y1, x2, y2) {
let dx = x1 - x2;
let dy = y1 - y2;
return 360 + (Math.atan2(dy, dx) * 180 / Math.PI);
}
if (distance(x, y, mouseX, mouseY) <= mouseRadius) {
let dist = distance(x, y, mouseX, mouseY);
let mouseR = angle(x, y, mouseX, mouseY) % 360;
let near = (mouseRadius - dist) / mouseRadius;
let far = 1 - near;
r = (r * far + near * mouseR) % 360;
}
// r now includes mouse
A live version:
https://jsfiddle.net/Ruudt/56pk2wd1/1/
The problem lies in the cases where the mouse passes from being left to right of perpendicular to the (original rotation) line. Here the calculation will nominate the other end as "closests", then calculate the distance and apply this to the rotation. This results in the line jumping from pointing slightly left of the cursor to right of the cursor (or vice versa).
Is there a way to fix this?
I've made an image to illustrate the situation.
The red line represents the line using only the rotation of the gradient noise
The black line is the line that also includes mouse position
the blue arc is the mouse rotation value (right end is origin)
line rotation:
I am running into a weird problem, while performing a simple rotation of an image.
While rotating an image around its center in a canvas, the image is stretched to double of its original width when rotating to 90° and is resizing to its normal width/height rotating to 180° then its stretching again to 270° and resizing back when rotating to 0° again.
Also it seems like the Image is between those 90° steps a parallelogram and not a rectangle.
What I actually do is:
var TO_RADIANS = Math.PI/180;
function drawRotatedImage(x, y, angle, width, height) {
contextL.save();
contextL.translate(x + width/2, y + height/2);
contextL.rotate(angle * TO_RADIANS);
var imgT = new Image();
imgT.src = myPath;
contextL.drawImage(imgT, -width/2, -height/2, width, height);
contextL.restore();
}
ContextL is created like this:
canvasL = document.getElementById("canvasL");
contextL = canvasL.getContext("2d");
I really do not get, what is my fault here. I have read nearly all threads about rotating in canvas and all are doing exactly the same, but it is working. Only special may be, that I use drawImage(image, x, y, width, height) and not drawImage(image, x, y) but is this causing the trouble?
*With drawImage(image, x, y) same trouble is caused
*using contextL.drawImage(imgT, -width/2, -height/2, width/2, height*2); turns the effect around. Starting stretched and regular size at 90°, so it really it is getting the double of its width and half of its height when rotating to 90°.
*Width and Height are calculated as follows:
this.prepareIMG = function(){
this.widthIMG = this.img.width * this.scale;
this.heightIMG = this.img.height * this.scale;
};
This is the function I call every time right before I call drawRotatedImage(...). this.scaleis a value from 0.1to x increasing/decreasing in steps of 0.1.
Also when I log the width and height during the rotation to the console, the value stays always the same!
I do not manipulate the contextLsomewhere outside the function drawRotatedImage(...)!
This is the code which calls the rotate-function
this.rotatePlus = function(){
refreshCanvas();
this.rotation += 5;
this.prepareIMG();
drawRotatedImage(contextL, this.transX, this.transY, this.rotation, this.widthIMG, this.heightIMG/2);
};
Finally refreshCanvas() is defined as follows
function refreshCanvas(){
contextL = canvasL.getContext("2d");
contextL.clearRect(0, 0, canvasL.width, canvasL.height);}
and my canvas is declared in HTML like this <div id="divCanvasL"><canvas id="canvasL"></canvas></div> width & height are set by css.
I followed a tutorial to create a canvas graph using js. The code plotting is this:
function plotData(context, dataSet, sections, xScale) {
context.lineWidth = 1;
context.outlineWidth = 0;
context.strokeWidth = 0;
context.beginPath();
context.moveTo(0, dataSet[0]);
for (i=0; i<sections; i++) {
context.lineTo(i * xScale, dataSet[i]);
}
context.stroke();
}
I am calling this function with an array holding the points where to go.. The x value is calculated per column (xScale) and the resulting graph if i use more than 1 source of data shows up fine. Screenshot when working fine:
http://s21.postimg.org/vlc1qg9iv/Screen_Shot_2016_04_08_at_15_48_42.png
But when i remove the 2 last data lines and leave only 1 line (so when the graph has a smaller difference between graph max and min values it shows up like this:
http://s16.postimg.org/ex0fakef9/Screen_Shot_2016_04_08_at_15_44_21.png
It is in this screenshot that you can clearly see, that while it should draw a line, the line is not really a 1px line but a shape, much like a (badly) distorted line?
I am not sure if i am doing something wrong or i am plainly ignoring something? The height of the canvas is fixed and it is always calculated using:
canvas = $('#canvas-container canvas')[0];
canvas.width = $('#canvas-container').width() * 0.9;
canvas.height = $('#canvas-container').width() / 1.45;
Thanks!
Codepen of the exact effect (from the exact tutorial) can be found here:
https://codepen.io/anon/pen/JXMwBy?editors=1111
(notice there are 2 more lines of graph data i commented out and in doing so i made the Val_max and Val_min vars different to "stretch" the data in the Y line)
You are stretching the Y axis on every operation after this line:
context.scale(1,-1 * yScale);
Instead, remove the line above and multiply the y values when you draw the line in plotData().
// multiply all Y values by -yScale to flip and scale
context.moveTo(0, dataSet[0] * -yScale);
for (i=1;i<sections;i++) {
context.lineTo(i * xScale, dataSet[i] * -yScale);
}
function distance(x1, y1, x2, y2) {
var x = x1 - x2;
var y = y1 - y2;
return(Math.sqrt((x*x) + (y*y)))
};
function collisionCirc(circ1, circ2) {
var d = distance(circ1.x, circ1.y, circ2.x, circ2.y);
var r = circ1.radius + circ2.radius;
return(r > d);
};
function collisionCircPoint(circ1, circ2) {
var cx = ((circ1.x * circ2.radius) + (circ2.x * circ1.radius)) / (circ1.radius + circ2.radius);
var cy = ((circ1.y * circ2.radius) + (circ2.y * circ1.radius)) / (circ1.radius + circ2.radius);
var p = [cx, cy];
return p;
};
function angleDegrees(x1, y1, x2, y2) {
return (Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI) + 180;
};
function updateCollisions() {
var a;
var p;
Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);
if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
Player.sprite.x = p[0];
Player.sprite.y = p[1];
};
};
I have 2 sprites on the map and each has a circle hitArea defined. I am trying to make a smooth circular collision that the player cannot pass through. I thought I could just set the Player.sprite's coordinates to the point of collision but it just warps him to the MapObjects.chest's coordinates, even though the point of collision is correct and is 20 pixels from the MapObject.chest's center. What am I doing wrong or what more information is needed to create a collision much like the JavaScript physics libraries where I can circle around a circle object?
The collision point is between the player and the obstacle. If you move the player towards the collision point, you are actually moving the player closer. For example, if there's exactly 40 px (r1+r2) between the player and the obstacle, the collision point is between them, at only 20 px from the obstacle!
When you have multiple objects, getting it right when the collision has already happened is difficult. If there is only one obstacle nearby, you can simply move the player directly away from the obstacle. However, this way the player might actually end up inside another obstacle.
Another solution is to go back to the start and try smaller movements, until there is no collision. This way you would eventually get it right, but this might also be slow.
The mathematically correct solution is to calculate the maximum distance to move before the collision happens. This is done by solving the following vector equation:
# p = player position before moving
# o = obstacle position
# u = player direction (unit vector)
# d = distance to move
distance(o, p + d * u) = o.radius + p.radius
That's mathematics, you may solve it by yourself or using a tool like Wolfram Alpha.
Solving this equation will give you zero, one or two possible values for the distance. Negative values you can dismiss, as they mean that the player is already past the obstacle. If you get only one value, it means that the player would merely brush the obstacle, which you can also dismiss. Two values mean that the collision happens between these distances; the smaller value is where the collision starts, and the larger value is where the player would already be through the obstacle. Also, if one value is positive and the other is negative, it means that the player is already inside the obstacle, which should never happen.
You should run this check for all nearby obstacles and then move the player according to the smallest non-negative result (that is, zero or positive), or less, if the player can't move that fast.
Finally, to circle around a round object, you can move the player a little bit in a perpendicular direction (either left or right, depending on which side of the obstacle the player will be passing) after a collision, if this doesn't cause any new collisions.
There are many other possible implementations.
Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);
if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
if (Player.sprite.x - MapObjects.chest.x > 0) {
Player.sprite.x += 1;
} else if (Player.sprite.x + MapObjects.chest.x > 0) {
Player.sprite.x -= 1;
};
if (Player.sprite.y - MapObjects.chest.y > 0) {
Player.sprite.y += 1;
} else if (Player.sprite.y + MapObjects.chest.y > 0) {
Player.sprite.y -= 1;
};
};
};
I added that and it actually works well enough minus the player speed being slightly too fast when running into the MapObjects.chest's hitArea at certain angles. Work on that later.
Using the jQuery plugin: imgareaselect (http://odyniec.net/projects/imgareaselect/), I let users select areas of an image to add comments (just like flickr).
I'm aiming to let users draw arrows pointing on specific image areas instead of drawing boxes.
Any idea if (and how) I can modify imgareaselect to draw lines (with an arrow head) instead of selection boxes?
I read that I could use Canvas or processing.js, but AFAIK those either don't work or have limitations on IE.
Thanks,
Yasser
You can make a set of arrow images to overlay, using CSS absolute positioning, on top of the photo. For example, make 18 arrows, each rotated from the last one by 360° / 18 = 20°. Using the CSS sprite technique should allow you to vary the length of the arrow.
In the description that follows, I refer to the start of the arrow as the end near the textbox, and the end as the spot that is pointed to on the picture.
To calculate the (clockwise) arrow angle to use given a pair of x-y coordinates of the pixel pointed to and those of the text box location, we use:
var radians = Math.atan2(startY - endY, startX - endX),
degrees = radians * 180 / Math.PI;
if (degrees < 0) degrees += 360;
Then your script could choose the closest pre-made arrow:
var approxDegrees = Math.round(degrees / 20) * 20;
When the arrow is loaded, position its top-left corner (relative to the end) according to:
var approxRadians = approxDegrees / 180 * Math.PI,
imageX = arrowLength * Math.cos(approxRadians),
imageY = arrowLength * Math.sin(approxRadians);
where l is the length of the arrow.
Finally, trim the arrow:
var width = Math.abs(endX - startX);
var height = Math.abs(endY - startY);
and put the center of the text box on the start of the arrow.
var textX = (startX + textWidth) / 2;
var textY = (startY + textHeight) / 2;