Calculate 2D rotation javascript HTML canvas - javascript

Implemented a canvas, Drawing a square there and get the calculated coordinates,
Can see on the following pic the drawing:
I'm calculating and getting the upleft point X and Y coordinates,
And for the down right coordinates that i need, I'm adding the height and width, as follows:
{ upLeft: { x: position.x, y: position.y }, downRight: { x: position.x + position.width, y: position.y + position.height } },
Now i want to get the same dimensions when i'm rotating the canvas clockwise or anti-clockwise.
So i have the angle, And i try to calculate via the following function:
function getRotatedCoordinates(cx, cy, x, y, angle) {
let radians = (Math.PI / 180) * angle,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = (cos * (x - cx)) - (sin * (y - cy)) + cx,
ny = (cos * (y - cy)) + (sin * (x - cx)) + cy;
return [nx, ny];
}
And i'm calling the function via the following args and using it.
let newCoords = getRotatedCoordinates(0, 0, position.x, position.y, angle);
position.x = newCoords[0];
position.y = newCoords[1];
So firstly, I'm not sure that the cx and cy points are correct, I'm always entering 0 for both of them.
Secondly, I'm not getting the desired results, They are getting changed but i'm pretty sure that something is wrong with the x and y, So i guess that the function is wrong.
Thanks.

Here is how I would do it:
function getRectangeCoordinates(x, y, width, height, angle) {
let points = [ [x, y] ]
let radians = (Math.PI / 180) * angle;
for (let i = 0; i < 3; i++) {
x += Math.cos(radians) * ((i == 1) ? height : width);
y += Math.sin(radians) * ((i == 1) ? height : width);
points.push([x, y])
radians += Math.PI / 2
}
return points
}
let canvas = document.createElement("canvas");
canvas.width = canvas.height = 140
let ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
function draw(coords, radius) {
for (let i = 0; i < 4; i++) {
ctx.beginPath();
ctx.arc(coords[i][0], coords[i][1], radius, 0, 8);
ctx.moveTo(coords[i][0], coords[i][1]);
let next = (i + 1) % 4
ctx.lineTo(coords[next][0], coords[next][1]);
ctx.stroke();
}
}
let coords = getRectangeCoordinates(20, 10, 120, 40, 15)
console.log(JSON.stringify(coords))
draw(coords, 3)
ctx.strokeStyle = "red";
coords = getRectangeCoordinates(60, 40, 40, 50, 65)
draw(coords, 5)
ctx.strokeStyle = "blue";
coords = getRectangeCoordinates(120, 3, 20, 20, 45)
draw(coords, 2)
In the getRectangeCoordinates I'm returning all corners of a rectangle and the paraments of the function are the top left corner (x, y) the height and width of the rectangle and last the angle.
I'm drawing a few rectangles with different shapes and angles to show how it looks like
The calculations in the function are simple trigonometry here is a visual representation that could help you remember it the next time you need it:

Related

HTML5 Canvas rotate gradient around centre with best fit

I want to make a gradient that covers the whole canvas whatever the angle of it.
So I used a method found on a Stack Overflow post which is finally incorrect. The solution is almost right but, in fact, the canvas is not totally covered by the gradient.
It is this answer: https://stackoverflow.com/a/45628098/5594331
(You have to look at the last point named "Example of best fit.")
In my code example below, the yellow part should not be visible because it should be covered by the black and white gradient. This is mostly the code written in Blindman67's answer with some adjustments to highlight the problem.
I have drawn in green the control points of the gradient. With the right calculations, these should be stretched to the edges of the canvas at any angle.
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
function bestFitGradient(angle){
var dist = Math.sqrt(w * w + h * h) / 2; // get the diagonal length
var diagAngle = Math.asin((h / 2) / dist); // get the diagonal angle
// Do the symmetry on the angle (move to first quad
var a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
if(a1 > Math.PI){ a1 -= Math.PI }
if(a1 > Math.PI / 2 && a1 <= Math.PI){ a1 = (Math.PI / 2) - (a1 - (Math.PI / 2)) }
// get angles from center to edges for along and right of gradient
var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
var ang2 = Math.abs(diagAngle - Math.abs(a1));
// get distance from center to horizontal and vertical edges
var dist1 = Math.cos(ang1) * h;
var dist2 = Math.cos(ang2) * w;
// get the max distance
var scale = Math.max(dist2, dist1) / 2;
// get the vector to the start and end of gradient
var dx = Math.cos(angle) * scale;
var dy = Math.sin(angle) * scale;
var x0 = w / 2 + dx;
var y0 = h / 2 + dy;
var x1 = w / 2 - dx;
var y1 = h / 2 - dy;
// create the gradient
const g = ctx.createLinearGradient(x0, y0, x1, y1);
// add colours
g.addColorStop(0, "yellow");
g.addColorStop(0, "white");
g.addColorStop(.5, "black");
g.addColorStop(1, "white");
g.addColorStop(1, "yellow");
return {
g: g,
x0: x0,
y0: y0,
x1: x1,
y1: y1
};
}
function update(timer){
var r = bestFitGradient(timer / 1000);
// draw gradient
ctx.fillStyle = r.g;
ctx.fillRect(0,0,w,h);
// draw points
ctx.lineWidth = 3;
ctx.fillStyle = '#00FF00';
ctx.strokeStyle = '#FF0000';
ctx.beginPath();
ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.fill();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 2px solid red;
}
<canvas id="canvas" width="300" height="200"></canvas>
In this fiddle there is a function that calculates the distance between a rotated line and a point:
function distanceToPoint(px, py, angle) {
const cx = width / 2;
const cy = height / 2;
return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}
Which is then used to find the maximum distance between the line and the corner points (only two points are considered, because the distances to the other two points are mirrored):
const dist = Math.max(
distanceToPoint(0, 0, angle),
distanceToPoint(0, height, angle)
);
Which can be used to calculate offset points for the end of the gradient:
const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
width / 2 + ox,
height / 2 + oy,
width / 2 - ox,
height / 2 - oy
)

How to draw triangle pointers inside of circle

I realize this is a simple Trigonometry question, but my high school is failing me right now.
Given an angle, that I have converted into radians to get the first point. How do I figure the next two points of the triangle to draw on the canvas, so as to make a small triangle always point outwards to the circle. So lets say Ive drawn a circle of a given radius already. Now I want a function to plot a triangle that sits on the edge of the circle inside of it, that points outwards no matter the angle. (follows the edge, so to speak)
function drawPointerTriangle(ctx, angle){
var radians = angle * (Math.PI/180)
var startX = this.radius + this.radius/1.34 * Math.cos(radians)
var startY = this.radius - this.radius/1.34 * Math.sin(radians)
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
}
Example
You don't say what type of triangle you want to draw so I suppose that it is an equilateral triangle.
Take a look at this image (credit here)
I will call 3 points p1, p2, p3 from top right to bottom right, counterclockwise.
You can easily calculate the coordinate of three points of the triangle in the coordinate system with the origin is coincident with the triangle's centroid.
Given a point belongs to the edge of the circle and the point p1 that we just calculated, we can calculate parameters of the translation from our main coordinate system to the triangle's coordinate system. Then, we just have to translate the coordinate of two other points back to our main coordinate system. That is (x1,y1) and (x2,y2).
You can take a look at the demo below that is based on your code.
const w = 300;
const h = 300;
function calculateTrianglePoints(angle, width) {
let r = width / Math.sqrt(3);
let firstPoint = [
r * Math.cos(angle),
r * Math.sin(angle),
]
let secondPoint = [
r * Math.cos(angle + 2 * Math.PI / 3),
r * Math.sin(angle + 2 * Math.PI / 3),
]
let thirdPoint = [
r * Math.cos(angle + 4 * Math.PI / 3),
r * Math.sin(angle + 4 * Math.PI / 3),
]
return [firstPoint, secondPoint, thirdPoint]
}
const radius = 100
const triangleWidth = 20;
function drawPointerTriangle(ctx, angle) {
var radians = angle * (Math.PI / 180)
var startX = radius * Math.cos(radians)
var startY = radius * Math.sin(radians)
var [pt0, pt1, pt2] = calculateTrianglePoints(radians, triangleWidth);
var delta = [
startX - pt0[0],
startY - pt0[1],
]
pt1[0] = pt1[0] + delta[0]
pt1[1] = pt1[1] + delta[1]
pt2[0] = pt2[0] + delta[0]
pt2[1] = pt2[1] + delta[1]
ctx.beginPath();
// This gives me my starting point on the outer edge of the circle, plotted at the angle I need
ctx.moveTo(startX, startY);
[x1, y1] = pt1;
[x2, y2] = pt2;
// HOW DO I THEN CALCULATE x1,y1 and x2, y2. So that no matter what angle I enter into this function, the arrow/triangle always points outwards to the circle.
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.closePath();
ctx.fillStyle = '#FF0000';
ctx.fill();
}
function drawCircle(ctx, radius) {
ctx.beginPath();
ctx.arc(0, 0, radius, 0, 2 * Math.PI);
ctx.closePath();
ctx.fillStyle = '#000';
ctx.fill();
}
function clear(ctx) {
ctx.fillStyle = '#fff';
ctx.fillRect(-w / 2, -h / 2, w, h);
}
function normalizeAngle(pointCoordinate, angle) {
const [x, y] = pointCoordinate;
if (x > 0 && y > 0) return angle;
else if (x > 0 && y < 0) return 360 + angle;
else if (x < 0 && y < 0) return 180 - angle;
else if (x < 0 && y > 0) return 180 - angle;
}
function getAngleFromPoint(point) {
const [x, y] = point;
if (x == 0 && y == 0) return 0;
else if (x == 0) return 90 * (y > 0 ? 1 : -1);
else if (y == 0) return 180 * (x >= 0 ? 0: 1);
const radians = Math.asin(y / Math.sqrt(
x ** 2 + y ** 2
))
return normalizeAngle(point, radians / (Math.PI / 180))
}
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.querySelector('canvas');
const angleText = document.querySelector('.angle');
const ctx = canvas.getContext('2d');
ctx.translate(w / 2, h / 2);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, 0);
canvas.addEventListener('mousemove', _.throttle(function(ev) {
let mouseCoordinate = [
ev.clientX - w / 2,
ev.clientY - h / 2
]
let degAngle = getAngleFromPoint(mouseCoordinate)
clear(ctx);
drawCircle(ctx, radius);
drawPointerTriangle(ctx, degAngle)
angleText.innerText = Math.floor((360 - degAngle)*100)/100;
}, 15))
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>
<canvas width=300 height=300></canvas>
<div class="angle">0</div>
reduce the radius, change the angle and call again cos/sin:
function drawPointerTriangle(ctx, angle)
{
var radians = angle * (Math.PI/180);
var radius = this.radius/1.34;
var startX = this.center.x + radius * Math.cos(radians);
var startY = this.center.y + radius * Math.sin(radians);
ctx.moveTo(startX, startY);
radius *= 0.9;
radians += 0.1;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
radians -= 0.2;
var x1 = this.center.x + radius * Math.cos(radians);
var y1 = this.center.y + radius * Math.sin(radians);
ctx.lineTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(startX, startY);
}
the resulting triangle's size is proportional to the size of the circle.
in case you need an equilateral, fixed size triangle, use this:
//get h by pythagoras
h = sqrt( a^2 - (a/2)^2 );)
//get phi using arcustangens:
phi = atan( a/2, radius-h );
//reduced radius h by pythagoras:
radius = sqrt( (radius-h)^2 + (a/2)^2 );
radians += phi;
...
radians -= 2*phi;
...

How to get random point near edges of a square in javascript

I want to make a function that gives me a random point near the edges of a rectangle from a point. This is what I came up with so far, but I have absolutely no idea why it is not working.
function Point(x, y) {
this.x = x;
this.y = y;
}
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getRandomPointNearEdges(rectPos, width, height, border) {
var point = new Point(rectPos.x, rectPos.y);
if (randomNumber(0, 1) == 0) {
point.x = randomNumber(rectPos.x, rectPos.x + border);
if (randomNumber(0, 1) == 0) {
point.y = randomNumber(rectPos.y, rectPos.y + border);
}
else {
point.y = randomNumber(rectPos.y + height, (rectPos.y + height) + border);
}
}
else {
point.y = randomNumber(rectPos.y, rectPos.y + border);
if (randomNumber(0, 1) == 0) {
point.y = randomNumber(rectPos.x, rectPos.x + border);
}
else {
point.y = randomNumber(rectPos.x + height, (rectPos.x + width) + border);
}
}
return point;
};
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = 700;
canvas.height = 700;
var ctx = canvas.getContext("2d");
ctx.strokeRect(130, 130, 500, 500);
for (var i = 0; i < 30; i++) {
var point = getRandomPointNearEdges(new Point(130, 130), 500, 500, 100);
ctx.fillRect(point.x, point.y, 2, 2);
}
};
<canvas id="canvas"></canvas>
Just to clarify, the black region in this 'Not to scale' diagram is where I want to allow the point to generate. The width / height of that black region is the border property in the code snippet.
Why is my function not working?
Random with even distribution.
Just to point out that the answer by SimpleJ is statistical flawed with the distribution of random locations having a bias to the corners and then to the shorter sides, even though they cover much less area.
The ideal random location should be spread equally over the area in question, if the height of the box is less than the width then there is less chance of the the sides getting a point.
The example below provides a much faster and a much better distribution. I have added the given answers solution as well so you can compare.
The function that gets a random pos. The arguments x,y top left inside edge of rectangle, w,h inside width and height of the rectangle minDist, maxDist the min and max dist the random point can be from the inside edge of the box. You can also use negative values have the points outside the rectangle. Note that the distances are always from the inside edge of the box. The values are also floored when return (can easily be remove and still works)
function randomPointNearRect(x, y, w, h, minDist, maxDist) {
const dist = (Math.random() * (maxDist - minDist) + minDist) | 0;
x += dist;
y += dist;
w -= dist * 2
h -= dist * 2
if (Math.random() < w / (w + h)) { // top bottom
x = Math.random() * w + x;
y = Math.random() < 0.5 ? y : y + h -1;
} else {
y = Math.random() * h + y;
x = Math.random() < 0.5 ? x: x + w -1;
}
return [x | 0, y | 0];
}
Note there is a slight bias to the inside of the box. It can be removed with a little calculus with the bias rate of change f'(x) = 8*x 8 pixels per pixel inward and the anti derivative f(x)=4*(x**2) + c would directly relate to the distribution. Where x is dist from edge and c is related to perimeter length
Example to compare
The example has two canvases. Many random points are drawn. click the top canvas to add more points. Note how the bottom canvas sides and corners get darker due to the bias of the random points.
const ctx = canvas.getContext("2d");
canvas.onclick = ()=>{
getRandomPointsForBox(200, box,4, 18);
getRandomPoints(200);
}
const edgeClear = 30;
var box = {
x: edgeClear,
y: edgeClear,
w: canvas.width - edgeClear * 2,
h: canvas.height - edgeClear * 2,
edge: 4,
}
function drawBox(box) {
ctx.fillRect(box.x, box.y, box.w, box.h);
ctx.clearRect(box.x + box.edge, box.y + box.edge, box.w - box.edge * 2, box.h - box.edge * 2);
}
function drawPixel(x, y) {
ctx.fillRect(x, y, 1, 1);
}
function getRandomPointsForBox(count, box, min, max) {
min += box.edge;
max += box.edge;
while (count--) {
const [x, y] = randomPointNearRect(box.x, box.y, box.w, box.h, min, max);
drawPixel(x, y);
}
}
drawBox(box);
getRandomPointsForBox(200, box,4, 18);
ctx.font = "18px arial"
ctx.textAlign = "center"
ctx.textBaseline = "middle"
ctx.fillText("Click to add more random points.",canvas.width / 2, canvas.height / 2);
function randomPointNearRect(x, y, w, h, minDist, maxDist) {
const dist = (Math.random() * (maxDist - minDist) + minDist) | 0;
x += dist;
y += dist;
w -= dist * 2
h -= dist * 2
if (Math.random() < w / (w + h)) { // top bottom
x = Math.random() * w + x;
y = Math.random() < 0.5 ? y : y + h -1;
} else {
y = Math.random() * h + y;
x = Math.random() < 0.5 ? x: x + w -1;
}
return [x | 0, y | 0];
}
/* The following is from the answer provided by SimpleJ https://stackoverflow.com/a/49581326/3877726 */
const ctx1 = canvas1.getContext('2d');
const rect = {
x: box.x, y: box.y,
width: box.w, height: box.h,
};
drawRect(rect);
ctx1.font = "18px arial"
ctx1.textAlign = "center"
ctx1.textBaseline = "middle"
ctx1.fillText("SimpleJ's method.",canvas1.width / 2, canvas1.height / 2);
ctx1.fillText("Note density of sides and corners.",canvas1.width / 2, canvas1.height / 2 + 20);
function getRandomPoints(count) {
while (count--) {
drawPoint(randomPointInRect(sample(rects)));
}
}
var rects = getBorderRects(rect, 10);
function getBorderRects(rect, distance) {
const { x, y, width, height } = rect;
return [
{x: x, y: y, width: width, height: distance}, // top
{x: x, y: y + height - distance, width: width, height: distance}, // bottom
{x: x, y: y, width: distance, height: height}, // left
{x: x + width - distance, y: y, width: distance, height: height}, // right
];
}
function sample(array) {
return array[Math.floor(Math.random() * array.length)];
}
function randomPointInRect({x, y, width, height}) {
return {
x: x + (Math.random() * width),
y: y + (Math.random() * height),
};
}
function drawRect({x, y, width, height}) {
ctx1.strokeRect(x, y, width, height);
}
function drawPoint({x, y}) {
ctx1.fillRect(x, y, 1,1);
}
getRandomPoints(200);
<canvas id="canvas" width="500" height="200"></canvas>
<canvas id="canvas1" width="500" height="200"></canvas>
If you think about the problem of getting a random point near an edge as getting a random point in one of four edge rectangles, this problem becomes much easier to break down:
Get edge rectangles.
Pick a random edge rectangle.
Generate a random point in the edge rectangle.
To generate edge rectangles, we need a max distance (how far from the edge can the point be?):
function getBorderRects(rect, distance) {
const { x, y, width, height } = rect;
return [
{x: x, y: y, width: width, height: distance}, // top
{x: x, y: y + height - distance, width: width, height: distance}, // bottom
{x: x, y: y, width: distance, height: height}, // left
{x: x + width - distance, y: y, width: distance, height: height}, // right
];
}
To pick a random rectangle from our array of edge rectangles, we can define a sample function:
function sample(array) {
return array[Math.floor(Math.random() * array.length)];
}
Then to pick a random point in a rectangle, we just need some Math.random:
function randomPointInRect({x, y, width, height}) {
return {
x: x + (Math.random() * width),
y: y + (Math.random() * height),
};
}
And putting everything together:
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const rect = {
x: 10, y: 20,
width: 300, height: 200,
};
drawRect(rect);
drawPoint(
randomPointInRect(
sample(
getBorderRects(rect, 10)
)
)
);
function getBorderRects(rect, distance) {
const { x, y, width, height } = rect;
return [
{x: x, y: y, width: width, height: distance}, // top
{x: x, y: y + height - distance, width: width, height: distance}, // bottom
{x: x, y: y, width: distance, height: height}, // left
{x: x + width - distance, y: y, width: distance, height: height}, // right
];
}
function sample(array) {
return array[Math.floor(Math.random() * array.length)];
}
function randomPointInRect({x, y, width, height}) {
return {
x: x + (Math.random() * width),
y: y + (Math.random() * height),
};
}
function drawRect({x, y, width, height}) {
context.strokeRect(x, y, width, height);
}
function drawPoint({x, y}) {
context.arc(x, y, 1, 0, Math.PI * 2);
context.fill();
}
<canvas width="500" height="500"/>
For anybody here like me, looking for a short, simple solution, this post is closest I found that is not talking trigonometry .. An while what I came up with might not directly be a solution to OPs problem, maybe someone will find this useful..
The approach is fairly simple.
Math.random() a number between 0 & 800. Make use of modulus and divide what's left by 200 to get a random side and axis point. Push the random side all the way, assign the random value to the other axis and yeah, that's about it .. here's an ex:
let rndm = Math.floor(Math.random()*800-1);
let offset = rndm % 200;
let side = (rndm - offset) / 200; // 0:top 1:right 2:btm 3:left
let y = side % 2 > 0 ? offset+1 : 100 * side ;
let x = side % 2 < 1 ? offset+1 : 100 * (side - 1) ;
point.y = y - 100;
point.x = x - 100;
In my case, I needed both negative and positive values with an origin point.
And if you want to spawn a point inside a border, just do another random number spanning the width of the border.
Just remember to adjust the corners.
offset += rndmBorder * 2; // creates an inward line in the corners
point.x = x - 100 + rndmBorder; // still keeping the origin point nice and center
_____________
|\_________/| <-// inward line
| | | |
| | | |
All I was in need for is to offset some letters .. and most of what I found seemed like overkill .. This actually works fairly well, hope it helps.

How to draw parallel edges (arrows) between vertices with canvas?

I'm working on a flow-network visualization with Javascript.
Vertices are represented as circles and edges are represented as arrows.
Here is my Edge class:
function Edge(u, v) {
this.u = u; // start vertex
this.v = v; // end vertex
this.draw = function() {
var x1 = u.x;
var y1 = u.y;
var x2 = v.x;
var y2 = v.y;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
var dx = x1 - x2;
var dy = y1 - y2;
var length = Math.sqrt(dx * dx + dy * dy);
x1 = x1 - Math.round(dx / ((length / (radius))));
y1 = y1 - Math.round(dy / ((length / (radius))));
x2 = x2 + Math.round(dx / ((length / (radius))));
y2 = y2 + Math.round(dy / ((length / (radius))));
// calculate the angle of the edge
var deg = (Math.atan(dy / dx)) * 180.0 / Math.PI;
if (dx < 0) {
deg += 180.0;
}
if (deg < 0) {
deg += 360.0;
}
// calculate the angle for the two triangle points
var deg1 = ((deg + 25 + 90) % 360) * Math.PI * 2 / 360.0;
var deg2 = ((deg + 335 + 90) % 360) * Math.PI * 2 / 360.0;
// calculate the triangle points
var arrowx = [];
var arrowy = [];
arrowx[0] = x2;
arrowy[0] = y2;
arrowx[1] = Math.round(x2 + 12 * Math.sin(deg1));
arrowy[1] = Math.round(y2 - 12 * Math.cos(deg1));
arrowx[2] = Math.round(x2 + 12 * Math.sin(deg2));
arrowy[2] = Math.round(y2 - 12 * Math.cos(deg2));
context.beginPath();
context.moveTo(arrowx[0], arrowy[0]);
context.lineTo(arrowx[1], arrowy[1]);
context.lineTo(arrowx[2], arrowy[2]);
context.closePath();
context.stroke();
context.fillStyle = "black";
context.fill();
};
}
Given the code
var canvas = document.getElementById('canvas'); // canvas element
var context = canvas.getContext("2d");
context.lineWidth = 1;
context.strokeStyle = "black";
var radius = 20; // vertex radius
var u = {
x: 50,
y: 80
};
var v = {
x: 150,
y: 200
};
var e = new Edge(u, v);
e.draw();
The draw() function will draw an edge between two vertices like this:
If we add the code
var k = new Edge(v, u);
k.draw();
We will get:
but I want to draw edges both directions as following:
(sorry for my bad paint skills)
Of course the vertices and the edge directions are not fixed.
A working example (with drawing vertex fucntion) on JSFiddle:
https://jsfiddle.net/Romansko/0fu01oec/18/
Aligning axis to a line.
It can make everything a little easier if you rotate the rendering to align with the line. Once you do that it is then easy to draw above or below the line as that is just in the y direction and along the line is the x direction.
Thus if you have a line
const line = {
p1 : { x : ? , y : ? },
p2 : { x : ? , y : ? },
};
Convert it to a vector and normalise that vector
// as vector from p1 to p2
var nx = line.p2.x - line.p1.x;
var ny = line.p2.y - line.p1.y;
// then get length
const len = Math.sqrt(nx * nx + ny * ny);
// use the length to normalise the vector
nx /= len;
ny /= len;
The normalised vector represents the new x axis we want to render along, and the y axis is at 90 deg to that. We can use setTransform to set both axis and the origin (0,0) point at the start of the line.
ctx.setTransform(
nx, ny, // the x axis
-ny, nx, // the y axis at 90 deg to the x axis
line.p1.x, line.p1.y // the origin (0,0)
)
Now rendering the line and arrow heads is easy as they are axis aligned
ctx.beginPath();
ctx.lineTo(0,0); // start of line
ctx.lineTo(len,0); // end of line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,0); // tip of arrow
ctx.lineTo(len - 10, 10);
ctx.lineTo(len - 10, -10);
ctx.fill();
To render two lines offset from the center
var offset = 10;
ctx.beginPath();
ctx.lineTo(0,offset); // start of line
ctx.lineTo(len,offset); // end of line
ctx.moveTo(0,-offset); // start of second line
ctx.lineTo(len,-offset); // end of second line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,offset); // tip of arrow
ctx.lineTo(len - 10, offset+10);
ctx.lineTo(len - 10, offset-10);
ctx.fill();
offset = -10;
// add second arrow head
ctx.beginPath();
ctx.lineTo(0,offset); // tip of arrow
ctx.lineTo(10, offset+10);
ctx.lineTo(10, offset-10);
ctx.fill();
And you can reset the transform with
ctx.setTransform(1,0,0,1,0,0); // restore default transform

radial motion in javascript

By default HTML 5 canvas has rectangular shape, though if i do any animation under canvas, it will move into rectangular area.
What if i bound area to radial shape?It shouldn't definitely go out of radial shape.
Can we limit boundary to radial instead of default rectangular shape?
You can look at ball is going out of radial boundary- http://jsfiddle.net/stackmanoz/T4WYH/
I designed boundary in radial shape now i want to limit radial area too.
Limit area for ball-
function bounce() {
if (x + dx > 293 || x + dx < 0) {
dx = -dx;
}
if (y >= 290) {
y = 290;
}
if (y + dy > 290 || y + dy < 0) {
dx *= 0.99;
dy = -dy;
}
if (Math.abs(dx) < 0.01) {
dx = 0;
}
dy++;
}
The cartesian formula for a circle is (x − a)2 + (y − b)2 = r2
So check for this in your bounce condition:
function bounce() {
if( Math.pow(x - 150, 2) + Math.pow(y - 150, 2) > Math.pow(150, 2))
{
dx = -dx * (0.6 + (Math.random() * 0.8));
dy = -dy * (0.6 + (Math.random() * 0.8));
}
}
I am using random bouncing because I could not work out the correct bounce angle using the incident speed and the normal at the bounce point (I'm sure there is somebody ele here who can)
updated fiddle here: http://jsfiddle.net/T4WYH/1/
Few points, use requestAnimationFrame rather than setInterval
I would draw the large circle in the canvas, rather than as a border, then you can use isPointInPath(x, y) to work out if your ball is within the circle (or any other arbitrary path for that matter)
function draw() {
ctx.clearRect(0, 0, 300, 300);
ctx.beginPath()
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.arc(150, 150, 150, 0, 2 * Math.PI, true);
ctx.stroke();
ctx.closePath();
console.log(ctx.isPointInPath(x, y)); //Is (center) of ball in big circle
ctx.beginPath();
ctx.arc(x, y, 10, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.fill();
x += dx;
y += dy;
bounce();
}
I don't want to implement this particular task, but you can get ideas how to bounce from circle from this site: bbdizains.com
And coresponding function:
move = function(){
for (i=1; i<=3; i++) {
A.x[i] = A.x[i] - A.v[i]*Math.sin(A.alfa[i]);
A.y[i] = A.y[i] - A.v[i]*Math.cos(A.alfa[i]);
x = A.x[i]-175;
y = A.y[i]-233;
x2 = x*x;
y2 = y*y;
if (x2+y2>=6561) {
if (A.flag[i]==1) {
gamma = Math.atan(y/x);
A.alfa[i] = - 2*gamma - A.alfa[i];
A.flag[i] = 0;
}
} else {
A.flag[i] = 1;
}
$('#r'+i).css('left',A.x[i]+'px');
$('#r'+i).css('top',A.y[i]+'px');
if ((Math.random()>0.95) && (x2+y2<6000)) {
A.alfa[i] = A.alfa[i] + Math.PI*Math.random()*0.1;
}
}
}

Categories