Let users draw a line (arrow) on a div/img - javascript

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;

Related

Rotating line smoothly towards vector (2D)

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:

Dynamically compute SVG Path for a full circle with JavaScript

I'm trying to draw the perimeter of a circle depending on the angle inputed by the user. The angle determines the perimeter completion : 360° being the full circle, 180 half of the circle, and so on.
My problem is : given the radius, the angle and the center coordinates of the circle, how can I dynamically compute the path of the perimeter ?
I know it's probably basic math but everything I tried so far didn't work.
Here is my fiddle : https://jsfiddle.net/Hal_9100/L311qq88/
My problem is finding the right formula for the x and y coordinates of the path :
var x = i * (radius * Math.cos(angle)) + centerX;
var y = i * (radius * Math.sin(angle)) + centerY;
Am I going all wrong here ?
Here is an example of what I'm trying to do : please note that only the black perimeter should be drawn : I just used the dotted red lines to give a visual example of how the perimeter should be drawn depending on the value given by the user.
Yes, the problem is your maths. Here is the correct way to calculate the x,y coordinate pairs (note that the iteration is from zero to the required angle, not from zero to the radius):
for (var i = 0; i <= angle; i++) {
var x = (radius * Math.cos((i-90)*Math.PI/180)) + centerX;
var y = (radius * Math.sin((i-90)*Math.PI/180)) + centerY;
Your fiddle works fine if you substitute these three lines.

Calculating relative item positions based on camera position and rotation

For a 2d game, I have these concepts:
stage: container in which items and cameras are placed.
item: a visible entity, located on a certain point in the world, anchored from center.
camera: an invisible entity, used to generate relative images of world, located on a certain point in the world, anchored from center.
In the illustrations, you can see how they are related, and what the end result should be.
Here is the code I have: (dumbed down to make it easier to read)
Note1: This is not happening on canvas, so I will not use canvas translation or rotation (and even then, I don't think it would make the problem any easier).
Note2: Item and camera positions are center coordinates.
var sin = Math.sin(rotationRad);
var cos = Math.cos(rotationRad);
var difX = item.x - camera.x;
var difY = item.y - camera.y;
var offsetX = camera.width / 2;
var offsetY = camera.height / 2;
var view.x = (cos * difX) - (sin * difY) + _Ax + _Bx;
var view.y = (sin * difX) + (cos * difY) + _Ay + _By;
This is supposed to calculate an items new position by:
calculating new position of item by rotating it around camera center
(_A*) adjusting item position by offsetting camera position
(_B*) adjusting item position by offsetting camera size
I tried several different solutions to use for _A* and _B* here, but none of them work.
What is the correct way to do this, and if possible, what is the explanation?
You first subtract new origin position from object position. You then rotate it by the inverse of the rotation. New origin can be camera position of top left corner of viewport. Of course if you know viewport center its top left corner is computed by subtracting half of its dimensions.
Like this:
var topLeft = Camera.Position - Camera.Size / 2;
var newPosition = Object.Position - topLeft;
newPosition = Rotate(newPosition, -camera.Angle);
Rotation is very simple:
rotatedX = x * cos(angle) - y * sin(angle)
rotatedY = y * cos(angle) + x * sin(angle)

Rotate arrow on hover over to land at certain degree and load image

I'm looking for someone to help guide me in the right direction for a function I'm trying to create.
I need to create an arrow that when at a certain point of degree, a tree grows, I have created 7 different heights and 7 different images for the tree's for a clean look.
Basically you know how you can have an image and rotate it using
<script type="text/javascript">
var img = $('.image');
if(img.length > 0){
var offset = img.offset();
function mouse(evt){
var center_x = (offset.left) + (img.width()/2);
var center_y = (offset.top) + (img.height()/2);
var mouse_x = evt.pageX; var mouse_y = evt.pageY;
var radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
var degree = (radians * (180 / Math.PI) * -1) + 90;
img.css('-moz-transform', 'rotate('+degree+'deg)');
img.css('-webkit-transform', 'rotate('+degree+'deg)');
img.css('-o-transform', 'rotate('+degree+'deg)');
img.css('-ms-transform', 'rotate('+degree+'deg)');
}
$(document).mousemove(mouse);
}
</script>
But how do I get my arrow to stop at 20 degree's, 25 degree's, 30 degree's etc etc.. while at the same time loading the new image i have assigned to that certain degree (whatever the tip of the arrow is pointed at) all doing this by hover over.
And not only do stop and load the new image, but also once the user clicks submit it adds data to my tree table within my db. So basically, its an arrow, the tip of the arrow gets set at a certain degree, loads the new image, takes the height of the image (i need some way of assigning the height var to each individual image im guessing?) then query that into my tree table under the tree height field.
Any help, links to get me started would be greatly appreciated.
Also, is there a way to do this with Canvas or SVG? Instead of using a arrow image as my arrow? For a more clean look.
Here is an example of how you might render an arrow following the mouse using canvas.
http://jsbin.com/inufoy/edit
Locking the rotation of the arrow to certain points is as simple as filtering the parameter on the draw function. eg:
var segs = 7;
var coefficient = Math.PI / segs;
r -= ((r + coefficient) % (coefficient * 2)) - coefficient;
From there all you have to do is assign each image a rotation, and check when the arrow is pointing towards the tree, then load the tree's image.
Edit:
Here's another version of that script with a static arrow base such as your description:
http://jsbin.com/inufoy/5/edit

Points on a (un)rotated rectangle

I found this excellent question and answer which starts with x/y (plus the center x/y and degrees/radians) and calculates the rotated-to x'/y'. This calculation works perfectly, but I would like to run it in the opposite direction; starting with x'/y' and degrees/radians, I would like to calculate the originating x/y and the center x/y.
(x', y') = new position
(xc, yc) = center point things rotate around
(x, y) = initial point
theta = counterclockwise rotation in radians (radians = degrees * Pi / 180)
dx = x - xc
dy = y - yc
x' = xc + dx cos(theta) - dy sin(theta)
y' = yc + dx sin(theta) + dy cos(theta)
Or, in JavaScript/jQuery:
XYRotatesTo = function($element, iDegrees, iX, iY, iCenterXPercent, iCenterYPercent) {
var oPos = $element.position(),
iCenterX = ($element.outerWidth() * iCenterXPercent / 100),
iCenterY = ($element.outerHeight() * iCenterYPercent / 100),
iRadians = (iDegrees * Math.PI / 180),
iDX = (oPos.left - iCenterX),
iDY = (oPos.top - iCenterY)
;
return {
x: iCenterX + (iDX * Math.cos(iRadians)) - (iDY * Math.sin(iRadians)),
y: iCenterY + (iDX * Math.sin(iRadians)) + (iDY * Math.cos(iRadians))
};
};
The math/code above solves for the situation in Figure A; it calculates the position of the destination x'/y' (green circle) based on the known values for x/y (red circle), the center x/y (blue star) and the degrees/radians.
But I need math/code to solve for Figure B; where I can find not only the destination x/y (green circle), but also the destination center x/y (green star) from the known values of the starting x/y (grey circle, though probably not needed), the destination x'/y' (red circle) and the degrees/radians.
The code above will solve for the destination x/y (green circle) via iDegrees * -1 (thanks to #andrew cooke's answer which has since been removed by him), but in order to do that I need to feed into it the location of the destination center x/y (green star), and that is the calculations I'm currently missing, as you can see in Diagram C, below:
So... how do I find the coordinates ?/? (green star) given n, A (angle) and x'/y' (red circle)?
You're trying to find an inverse transformation. You start with the composition of two linear transformations, a translation T and a rotation R. You apply R first to a vector x and T second, so the expression is y = TRx. To solve the inverse problem you need the inverse of TR, written (TR)-1, which is equal to R-1T-1. The inverse of the rotation R is just the rotation by the negative of the angle (which you mention). The inverse of the translation is, similarly, the original translation multiplied by -1. So your answer is x = R-1T-1y.
In your present situation, you're given the rotation by means of its angle, but you'll need to compute the translation. You'll need the grey circle, which you didn't think you would need. Apply the rotation R (not its inverse) to the gray circle. Subtract this point from the red circle. This is the original translation T. Reverse the sign to get T-1.

Categories

Resources