How to draw an arbitrary irregular polygon with n sides? - javascript

I am trying to find an algorithm of how to draw a simple (no lines are allowed to intersect), irregular polygon.
The number of sides should be defined by the user, n>3.
Here is an intial code which only draws a complex polygon (lines intersect):
var ctx = document.getElementById('drawpolygon').getContext('2d');
var sides = 10;
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(0, 0);
for(var i=0;i<sides;i++)
{
var x = getRandomInt(0, 100);
var y = getRandomInt(0, 100);
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
// https://stackoverflow.com/a/1527820/1066234
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
JSFiddle: https://jsfiddle.net/kai_noack/op2La1jy/6/
I do not have any idea how to determine the next point for the connecting line, so that it does not cut any other line.
Further, the last point must close the polygon.
Here is an example of how one of the resulting polygons could look like:
Edit: I thought today that one possible algorithm would be to arrange the polygon points regular (for instance as an rectangle) and then reposition them in x-y-directions to a random amount, while checking that the generated lines are not cut.

I ported this solution to Javascript 1 to 1. Code doesn't look optimal but produces random convex(but still irregular) polygon.
//shuffle array in place
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
/** Based on Sander Verdonschot java implementation **/
class Point {
constructor(x, y) {
this.x = x;
this.y = y
}
}
function generateRandomNumbersArray(len) {
const result = new Array(len);
for (let i = 0; i < len; ++i) {
result[i] = Math.random();
}
return result;
}
function generateRandomConvexPolygon(vertexNumber) {
const xPool = generateRandomNumbersArray(vertexNumber);
const yPool = generateRandomNumbersArray(vertexNumber);
// debugger;
xPool.sort();
yPool.sort();
const minX = xPool[0];
const maxX = xPool[xPool.length - 1];
const minY = yPool[0];
const maxY = yPool[yPool.length - 1];
const xVec = []
const yVec = [];
let lastTop = minX;
let lastBot = minX;
xPool.forEach(x => {
if (Math.random() >= 0.5) {
xVec.push(x - lastTop);
lastTop = x;
} else {
xVec.push(lastBot - x);
lastBot = x;
}
});
xVec.push(maxX - lastTop);
xVec.push(lastBot - maxX);
let lastLeft = minY;
let lastRight = minY;
yPool.forEach(y => {
if (Math.random() >= 0.5) {
yVec.push(y - lastLeft);
lastLeft = y;
} else {
yVec.push(lastRight - y);
lastRight = y;
}
});
yVec.push(maxY - lastLeft);
yVec.push(lastRight - maxY);
shuffle(yVec);
vectors = [];
for (let i = 0; i < vertexNumber; ++i) {
vectors.push(new Point(xVec[i], yVec[i]));
}
vectors.sort((v1, v2) => {
if (Math.atan2(v1.y, v1.x) > Math.atan2(v2.y, v2.x)) {
return 1;
} else {
return -1;
}
});
let x = 0, y = 0;
let minPolygonX = 0;
let minPolygonY = 0;
let points = [];
for (let i = 0; i < vertexNumber; ++i) {
points.push(new Point(x, y));
x += vectors[i].x;
y += vectors[i].y;
minPolygonX = Math.min(minPolygonX, x);
minPolygonY = Math.min(minPolygonY, y);
}
// Move the polygon to the original min and max coordinates
let xShift = minX - minPolygonX;
let yShift = minY - minPolygonY;
for (let i = 0; i < vertexNumber; i++) {
const p = points[i];
points[i] = new Point(p.x + xShift, p.y + yShift);
}
return points;
}
function draw() {
const vertices = 10;
const _points = generateRandomConvexPolygon(vertices);
//apply scale
const points = _points.map(p => new Point(p.x * 300, p.y * 300));
const ctx = document.getElementById('drawpolygon').getContext('2d');
ctx.fillStyle = '#f00';
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y);
for(let i = 1; i < vertices ; ++i)
{
let x = points[i].x;
let y = points[i].y;
ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fill();
}
draw();
<canvas id="drawpolygon"></canvas>

You could generate random points and then connect them with an approximate traveling salesman tour. Any tour that cannot be improved by 2-opt moves will not have edge crossings.

If it doesn't need to be random, here's a fast irregular n-point polygon:
Points are:
(0,0)
((i mod 2)+1, i) for 0 <= i <= n-2
Lines are between (0,0) and the two extreme points from the second row, as well as points generated by adjacent values of i.

Related

rotation of a 3d cube relative to a specific point on the canvas

Using certain methods, I drew a 3D cube.
My task is to rotate it around the z axis. I also need to rotate the cube relative to the coordinates (0,0,0) (on my canvas, these will be the coordinates (150,150,0)). Now it rotates correctly, but only changes its angle relative to the 'top left corner of my canvas'.
I think the solution to my problem is as follows:
To rotate the cube relative to the given coordinates, the cube must first be translated to the negative coordinates of this point, and by changing the angle, it must be translated back, but only to the positive coordinates of this point.
In my case the pseudo code would be like this:
// if I want to flip the cube around the coordinates (150,150,0) (this is the center of my canvas), then I need to do the following
translate(-150,-150,0);
rotate();
translate(150,150,0);
In the commented parts of the code, I tried to do this, but when I translate the cube, it gets stuck somewhere in the corner and I don't know how to fix it.
function randInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) ) + min;
}
function randomColor() {
return "rgb(" + randInt(0,255) + "," + randInt(0,255) + "," + randInt(0,255) + ")";
}
function ctg(x) {
return 1 / Math.tan(x);
}
function draw() {
let canvas = document.getElementById("canvas");
let context = canvas.getContext("2d");
let canvasWidth = 300;
canvas.width = canvas.height = canvasWidth;
function drawUW() {
context.lineWidth = 0.1;
context.beginPath();
context.moveTo(0,canvas.height/2);
context.lineTo(canvas.width,canvas.height/2);
context.moveTo(canvas.width/2,0);
context.lineTo(canvas.width/2,canvas.height);
context.stroke();
}
function updateCanvas() {
context.clearRect(0,0,canvas.width,canvas.height);
drawUW();
}
let x = 50, y = 50, z = 50;
let d = 100;
let x0 = canvas.width/2;
let y0 = canvas.height/2;
let x1 = x0+x;
let y1 = y0;
let x2 = x0;
let y2 = y0-y;
let x3 = x0+x;
let y3 = y0-y;
// x' = xd/(z+d)
// y' = yd/(z+d)
let x0p=x0*d/(z+d);
let y0p=y0*d/(z+d);
let x1p=x1*d/(z+d);
let y1p=y1*d/(z+d);
let x2p=x2*d/(z+d);
let y2p=y2*d/(z+d);
let x3p=x3*d/(z+d);
let y3p=y3*d/(z+d);
function getWalls() {
wall01 = [[x0,x2,x3,x1],[y0,y2,y3,y1]];
wall02 = [[x0p,x2p,x2,x0],[y0p,y2p,y2,y0]];
wall03 = [[x0p,x2p,x3p,x1p],[y0p,y2p,y3p,y1p]];
wall04 = [[x1p,x3p,x3,x1],[y1p,y3p,y3,y1]];
wall05 = [[x2,x2p,x3p,x3],[y2,y2p,y3p,y3]];
wall06 = [[x0,x0p,x1p,x1],[y0,y0p,y1p,y1]];
}
function drawWall(wall) {
context.fillStyle = randomColor();
context.strokeStyle = "black";
context.lineWidth = 0.5;
context.beginPath();
context.moveTo(wall[0][0],wall[1][0]);
context.lineTo(wall[0][1],wall[1][1]);
context.lineTo(wall[0][2],wall[1][2]);
context.lineTo(wall[0][3],wall[1][3]);
context.lineTo(wall[0][0],wall[1][0]);
context.fill();
context.stroke();
}
function rotatePoint(x,y,z,fi) {
fi*=Math.PI/180;
arr = [x,y,z,1];
newX = 0;
newY = 0;
newZ = 0;
tx = -150, ty = -150, tz = -150;
arrTranslate = [
[1,0,0,tx],
[0,1,0,ty],
[0,0,1,tz],
[0,0,0,1]
];
// z
arrRotate = [
[Math.cos(fi),-Math.sin(fi),0,0],
[Math.sin(fi),Math.cos(fi),0,0],
[0,0,1,0],
[0,0,0,1]
];
// translate
// for (let i = 0; i < arr.length; i++) {
// newX += arr[i] * arrTranslate[0][i];
// newY += arr[i] * arrTranslate[1][i];
// newZ += arr[i] * arrTranslate[2][i];
// }
//rotate
for (let i = 0; i < arr.length; i++) {
newX += arr[i] * arrRotate[0][i];
newY += arr[i] * arrRotate[1][i];
newZ += arr[i] * arrRotate[2][i];
}
tx = 150, ty = 150, tz = 150;
// translate
// for (let i = 0; i < arr.length; i++) {
// newX += arr[i] * arrTranslate[0][i];
// newY += arr[i] * arrTranslate[1][i];
// newZ += arr[i] * arrTranslate[2][i];
// }
x = newX;
y = newY;
z = newZ;
return [newX,newY,newZ];
}
function rotatePoints(fi) {
x0 = rotatePoint(x0,y0,z,fi)[0];
y0 = rotatePoint(x0,y0,z,fi)[1];
x1 = rotatePoint(x1,y1,z,fi)[0];
y1 = rotatePoint(x1,y1,z,fi)[1];
x2 = rotatePoint(x2,y2,z,fi)[0];
y2 = rotatePoint(x2,y2,z,fi)[1];
x3 = rotatePoint(x3,y3,z,fi)[0];
y3 = rotatePoint(x3,y3,z,fi)[1];
x0p = rotatePoint(x0p,y0p,z,fi)[0];
y0p = rotatePoint(x0p,y0p,z,fi)[1];
x1p = rotatePoint(x1p,y1p,z,fi)[0];
y1p = rotatePoint(x1p,y1p,z,fi)[1];
x2p = rotatePoint(x2p,y2p,z,fi)[0];
y2p = rotatePoint(x2p,y2p,z,fi)[1];
x3p = rotatePoint(x3p,y3p,z,fi)[0];
y3p = rotatePoint(x3p,y3p,z,fi)[1];
}
let inFi = document.getElementById("fi");
inFi.addEventListener("change", updateImage);
function updateImage() {
rotatePoints(inFi.value);
getWalls();
updateCanvas();
drawWall(wall06);
drawWall(wall04);
drawWall(wall03);
drawWall(wall02);
drawWall(wall05);
drawWall(wall01);
}
updateImage();
}
<body onload="draw();">
<canvas id="canvas"></canvas>
<div><input type="number" id="fi" value="0"></div>
</body>

Difficulty in computing the algorithm for collision detection (ball with paddle and bricks) in JavaScript breakout game

I'm creating a Javascript breakout game but have an issue with detecting the collisions between the ball and both the paddle and bricks.
On running the program, I expect the ball to bounce off the paddle and the bricks if they collide. And if the ball collides with the bricks, the bricks would be removed.
I have tried executing the function checkForCollision(gw, paddle) within the animation code, setting that if the collider is the paddle, the ball will simply bounces off, and if not, it must be a brick. However, unfortunately, the ball simply passed through the colliders (paddle or bricks) and behaved as if the colliders did not exist.
Previously, I've tried several if/else variations to mend it, and have tried another method which specifies the bricks that were collided using its index position (e.g. [Not sure if this is constructed correctly] collidedBrick = bricks[i][j], where i and j is the number of rows and columns of bricks respectively). But the ball in the game still couldn't detect the existence of the colliders.
JavaScript code as follows:
const GWINDOW_WIDTH = 360;
const GWINDOW_HEIGHT = 600;
const N_ROWS = 10;
const N_COLS = 10;
const BRICK_ASPECT_RATIO = 4 / 1;
const BRICK_TO_BALL_RATIO = 3 / 2;
const BRICK_TO_PADDLE_RATIO = 2 / 3;
const BRICK_SEP = 2;
const TOP_FRACTION = 0.1;
const BOTTOM_FRACTION = 0.05;
const N_BALLS = 3;
const TIME_STEP = 10;
const INITIAL_Y_VELOCITY = 3.0;
const MIN_X_VELOCITY = 1.0;
const MAX_X_VELOCITY = 3.0;
const BRICK_WIDTH = (GWINDOW_WIDTH - (N_COLS + 1) * BRICK_SEP) / N_COLS;
const BRICK_HEIGHT = BRICK_WIDTH / BRICK_ASPECT_RATIO;
const PADDLE_WIDTH = BRICK_WIDTH / BRICK_TO_PADDLE_RATIO;
const PADDLE_HEIGHT = BRICK_HEIGHT / BRICK_TO_PADDLE_RATIO;
const PADDLE_Y = (1 - BOTTOM_FRACTION) * GWINDOW_HEIGHT - PADDLE_HEIGHT;
const BALL_SIZE = BRICK_WIDTH / BRICK_TO_BALL_RATIO;
/* Main program */
function Breakout() {
let gw = GWindow(GWINDOW_WIDTH, GWINDOW_HEIGHT);
let bricks = createBricks(gw);
let paddle = createPaddle(gw);
let ball = createBall(gw);
}
function createBricks(gw) {
let x0 = (gw.getWidth() - (N_COLS * BRICK_WIDTH) - (N_COLS - 1) * BRICK_SEP)/2;
let y0 = TOP_FRACTION * gw.getHeight();
let color = ["Red", "Orange", "Green", "Cyan", "Blue"];
for (let i = 0; i < N_ROWS; i++) {
for (let j = 0; j < N_COLS; j++) {
let x = x0 + j * (BRICK_WIDTH + BRICK_SEP);
let y = y0 + i * (BRICK_HEIGHT + BRICK_SEP);
let bricks = GRect(x, y, BRICK_WIDTH, BRICK_HEIGHT);
bricks.setFilled(true);
bricks.setColor("White");
bricks.setFillColor(color[Math.floor(i/2)]);
gw.add(bricks);
}
}
}
function createPaddle(gw) {
let x0 = (gw.getWidth() - PADDLE_WIDTH) / 2;
let y0 = PADDLE_Y;
let paddle = GRect(x0, y0, PADDLE_WIDTH, PADDLE_HEIGHT);
paddle.setFilled(true);
gw.add(paddle);
let mouseMoveAction = function(e) {
let x = e.getX() - (PADDLE_WIDTH / 2);
let y = PADDLE_Y;
if (x > 0 && x <= gw.getWidth() - PADDLE_WIDTH) {
paddle.setLocation(x, y);
}
};
gw.addEventListener("mousemove", mouseMoveAction);
}
function createBall(gw) {
let centerX = gw.getWidth() / 2;
let centerY = gw.getHeight() / 2;
let x0 = centerX - (BALL_SIZE / 2);
let y0 = centerY - (BALL_SIZE / 2);
let ball = GOval(x0, y0, BALL_SIZE, BALL_SIZE);
ball.setFilled(true);
gw.add(ball);
let clickAction = function(e) {
let vy = INITIAL_Y_VELOCITY;
let vx = randomReal(MIN_X_VELOCITY, MAX_X_VELOCITY);
if (randomChance()) vx = -vx;
let step = function() {
if (ball.getX() > 0 && ball.getY() > 0 &&
ball.getX() < gw.getWidth() - BALL_SIZE &&
ball.getY() < gw.getHeight() - BALL_SIZE) {
ball.move(vx, vy);
let collision = checkForCollision(gw, paddle);
}
if (ball.getX() < 0 || ball.getX() > gw.getWidth() - BALL_SIZE) {
vx = -vx;
ball.move(vx, vy);
}
if (ball.getY() < 0) {
vy = -vy;
ball.move(vx, vy);
}
if (ball.getY() > gw.getHeight()) {
clearInterval(timer);
}
};
let timer = setInterval(step, TIME_STEP);
};
gw.addEventListener("click", clickAction);
}
function checkForCollision(gw, paddle) {
let collider = getCollidingObject(gw, ball);
if (collider !== null) {
if (collider !== paddle) {
vy = -vy;
ball.move(vx, vy);
gw.remove(collider);
}
else {
vy = -vy;
ball.move(vx, vy);
}
}
function getCollidingObject(gw, ball) {
let x = ball.getX();
let y = ball.getY();
let object = null;
object = gw.getElementAt(x, y);
if (object !== null) {
return object;
}
object = gw.getElementAt(x, y + BALL_SIZE);
if (object !== null) {
return object;
}
object = gw.getElementAt(x + BALL_SIZE, y);
if (object !== null) {
return object;
}
object = gw.getElementAt(x + BALL_SIZE, y + BALL_SIZE);
if (object !== null) {
return object;
}
}
Below is the html file which directs to JavaScript Graphics library.
<!DOCTYPE html>
<html>
<head>
<title>Breakout</title>
<script type='text/javascript'
src='https://cs106ax.stanford.edu/jslib/JSGraphics.js'></script>
<script type='text/javascript'
src='https://cs106ax.stanford.edu/jslib/RandomLib.js'></script>
<script type='text/javascript'
src='Breakout.js'></script>
</head>
<body onload='Breakout()'></body>
</html>
Your help is much appreciated. Thanks!

How to calculate intersection point of a line on a circle using p5.js

I have a line (se) that I know starts inside a circle, and I know ends outside of a circle. I'm trying to find a point l where the line crosses the circle.
I'm using the p5.js library and have access to all of its Vector functions.
My thoughts were, that if I can make a right angle on the line, to the radius, I can start solving some things.
// Get the vector between s and c
let scVector = p5.Vector.sub(start, circleCenter);
// Get the angle between se and sc
let escAngle = this.v.angleBetween(scVector);
// Get the distance between t (where the right angle hits the center of the circle) and c
let tcDistance = Math.sin(escAngle) * scVector.mag();
// Get the distance between t and where the line intersects the circle
let tlDistance = Math.sqrt(Math.pow(hole.r, 2) - Math.pow(tcDistance, 2));
// Get the distance between the start point and t
let stDistance = Math.sqrt(Math.pow(scVector.mag(), 2) - Math.pow(tcDistance, 2));
// Add the two distances together, giving me the distance between s and l
let totalDistance = tcDistance + stDistance;
// Create a new Vector at this angle, at the totalDistance Magnitude, then add it to the current position
let point = p5.Vector.fromAngle(this.v.heading(), totalDistance).add(start);
// Mark a point (hopefully l) on the edge of the circle.
points.push({
x: point.x,
y: point.y,
fill: '#ffffff'
})
Unfortunately, as my objects pass through the circle, they aren't leaving dots on the edge, but further away from the circle's edge.
The tiny dots are the marked positions, the coloured dots are the objects (which have a start and end point)
I have a demo here, the questionable bit is line 42 onwards:
https://codepen.io/EightArmsHQ/pen/be0461014f9868e3462868776d9c8f1a
Any help would be much appreciated.
To find the intersection of a point and a line, I recommend to use an existing algorithm, like that one of WolframMathWorld - Circle-Line Intersection.
The algorithm is short, well explained an can be expressed in an short function. The input parameters p1, p2, and cpt are of type p5.Vector, r is a scalar. This parameters define an endless line form p1 to p2 and a circle with the center point cpt and the radius r. The function returns a list of inter section points, with may be empty:
intersectLineCircle = function(p1, p2, cpt, r) {
let sign = function(x) { return x < 0.0 ? -1 : 1; };
let x1 = p1.copy().sub(cpt);
let x2 = p2.copy().sub(cpt);
let dv = x2.copy().sub(x1)
let dr = dv.mag();
let D = x1.x*x2.y - x2.x*x1.y;
// evaluate if there is an intersection
let di = r*r*dr*dr - D*D;
if (di < 0.0)
return [];
let t = sqrt(di);
ip = [];
ip.push( new p5.Vector(D*dv.y + sign(dv.y)*dv.x * t, -D*dv.x + p.abs(dv.y) * t).div(dr*dr).add(cpt) );
if (di > 0.0) {
ip.push( new p5.Vector(D*dv.y - sign(dv.y)*dv.x * t, -D*dv.x - p.abs(dv.y) * t).div(dr*dr).add(cpt) );
}
return ip;
}
If you want to verify, if a point is "in between" to other points, you can use the Dot product. If you know that 3 points on a line, then it is sufficient to calculate the distances between the points, to determine, if 1 point is in between the 2 other points.
p1, p2, and px are of type p5.Vector. The points are on the same line. The function returns true, if px is between p1 and p2 and false else:
inBetween = function(p1, p2, px) {
let v = p2.copy().sub(p1);
let d = v.mag();
v = v.normalize();
let vx = px.copy().sub(p1);
let dx = v.dot(vx);
return dx >= 0 && dx <= d;
}
See the example, which I've implemented to test the function. The circle is tracked by the mouse and is intersected by an randomly moving line:
var sketch = function( p ) {
p.setup = function() {
let sketchCanvas = p.createCanvas(p.windowWidth, p.windowHeight);
sketchCanvas.parent('p5js_canvas')
}
let points = [];
let move = []
// Circle-Line Intersection
// http://mathworld.wolfram.com/Circle-LineIntersection.html
p.intersectLineCircle = function(p1, p2, cpt, r) {
let sign = function(x) { return x < 0.0 ? -1 : 1; };
let x1 = p1.copy().sub(cpt);
let x2 = p2.copy().sub(cpt);
let dv = x2.copy().sub(x1)
let dr = dv.mag();
let D = x1.x*x2.y - x2.x*x1.y;
// evaluate if there is an intersection
let di = r*r*dr*dr - D*D;
if (di < 0.0)
return [];
let t = p.sqrt(di);
ip = [];
ip.push( new p5.Vector(D*dv.y + sign(dv.y)*dv.x * t, -D*dv.x + p.abs(dv.y) * t).div(dr*dr).add(cpt) );
if (di > 0.0) {
ip.push( new p5.Vector(D*dv.y - sign(dv.y)*dv.x * t, -D*dv.x - p.abs(dv.y) * t).div(dr*dr).add(cpt) );
}
return ip;
}
p.inBetween = function(p1, p2, px) {
let v = p2.copy().sub(p1);
let d = v.mag();
v = v.normalize();
let vx = px.copy().sub(p1);
let dx = v.dot(vx);
return dx >= 0 && dx <= d;
}
p.endlessLine = function(x1, y1, x2, y2) {
p1 = new p5.Vector(x1, y1);
p2 = new p5.Vector(x2, y2);
let dia_len = new p5.Vector(p.windowWidth, p.windowHeight).mag();
let dir_v = p5.Vector.sub(p2, p1).setMag(dia_len);
let lp1 = p5.Vector.add(p1, dir_v);
let lp2 = p5.Vector.sub(p1, dir_v);
p.line(lp1.x, lp1.y, lp2.x, lp2.y);
}
p.draw = function() {
if (points.length == 0) {
points = [];
move = [];
for (let i=0; i < 2; ++i ) {
points.push( new p5.Vector(p.random(p.windowWidth-20)+10, p.random(p.windowHeight-20)+10));
move.push( new p5.Vector(p.random(2)-1, p.random(2)-1) );
}
points.push( new p5.Vector(p.mouseX, p.mouseY));
}
else
{
for (let i=0; i < 2; ++i ) {
points[i] = points[i].add(move[i]);
if (points[i].x < 10 || points[i].x > p.windowWidth-10)
move[i].x *= -1;
if (points[i].y < 10 || points[i].y > p.windowHeight-10)
move[i].y *= -1;
move[i].x = Math.max(-1, Math.min(1, move[i].x+p.random(0.2)-0.1))
move[i].y = Math.max(-1, Math.min(1, move[i].y+p.random(0.2)-0.1))
}
points[2].x = p.mouseX;
points[2].y = p.mouseY;
}
let circle_diameter = p.min(p.windowWidth, p.windowHeight) / 2.0;
let isectP = p.intersectLineCircle(...points, circle_diameter/2.0);
// draw the scene
p.background(192);
p.stroke(0, 0, 255);
p.fill(128, 128, 255);
for (let i=0; i < points.length; ++i ) {
p.ellipse(points[i].x, points[i].y, 10, 10);
}
for (let i=0; i < isectP.length; ++i ) {
if (p.inBetween(points[0], points[1], isectP[i])) {
p.stroke(255, 0, 0);
p.fill(255, 128, 0);
} else {
p.stroke(255, 128, 0);
p.fill(255, 255, 0);
}
p.ellipse(isectP[i].x, isectP[i].y, 10, 10);
}
p.stroke(0, 255, 0);
p.noFill();
p.endlessLine(points[0].x, points[0].y, points[1].x, points[1].y)
p.ellipse(points[2].x, points[2].y, circle_diameter, circle_diameter);
}
p.windowResized = function() {
p.resizeCanvas(p.windowWidth, p.windowHeight);
points = [];
}
p.mouseClicked = function() {
points = [];
}
p.keyPressed = function() {
points = []
}
};
var circle_line = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
<div id="p5js_canvas"></div>
Demo

How to code an nth order Bezier curve

Trying to code an nth order bezier in javascript on canvas for a project. I want to be able to have the user press a button, in this case 'b', to select each end point and the control points. So far I am able to get the mouse coordinates on keypress and make quadratic and bezier curves using the built in functions. How would I go about making code for nth order?
Here's a Javascript implementation of nth order Bezier curves:
// setup canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
ctx.fillText("INSTRUCTIONS: Press the 'b' key to add points to your curve. Press the 'c' key to clear all points and start over.", 20, 20);
// initialize points list
var plist = [];
// track mouse movements
var mouseX;
var mouseY;
document.addEventListener("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
});
// from: http://rosettacode.org/wiki/Evaluate_binomial_coefficients#JavaScript
function binom(n, k) {
var coeff = 1;
for (var i = n - k + 1; i <= n; i++) coeff *= i;
for (var i = 1; i <= k; i++) coeff /= i;
return coeff;
}
// based on: https://stackoverflow.com/questions/16227300
function bezier(t, plist) {
var order = plist.length - 1;
var y = 0;
var x = 0;
for (i = 0; i <= order; i++) {
x = x + (binom(order, i) * Math.pow((1 - t), (order - i)) * Math.pow(t, i) * (plist[i].x));
y = y + (binom(order, i) * Math.pow((1 - t), (order - i)) * Math.pow(t, i) * (plist[i].y));
}
return {
x: x,
y: y
};
}
// draw the Bezier curve
function draw(plist) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var accuracy = 0.01; //this'll give the 100 bezier segments
ctx.beginPath();
ctx.moveTo(plist[0].x, plist[0].y);
for (p in plist) {
ctx.fillText(p, plist[p].x + 5, plist[p].y - 5);
ctx.fillRect(plist[p].x - 5, plist[p].y - 5, 10, 10);
}
for (var i = 0; i < 1; i += accuracy) {
var p = bezier(i, plist);
ctx.lineTo(p.x, p.y);
}
ctx.stroke();
ctx.closePath();
}
// listen for keypress
document.addEventListener("keydown", function(e) {
switch (e.keyCode) {
case 66:
// b key
plist.push({
x: mouseX,
y: mouseY
});
break;
case 67:
// c key
plist = [];
break;
}
draw(plist);
});
html,
body {
height: 100%;
margin: 0 auto;
}
<canvas id="canvas"></canvas>
This is based on this implementation of cubic Bezier curves. In your application, it sounds like you'll want to populate the points array with user-defined points.
Here is a code example for any number of points you want to add to make a bezier curve. Here points you will pass is an array of objects containing x and y values of points. [ { x: 1,y: 2 } , { x: 3,y: 4} ... ]
function factorial(n) {
if(n<0)
return(-1); /*Wrong value*/
if(n==0)
return(1); /*Terminating condition*/
else
{
return(n*factorial(n-1));
}
}
function nCr(n,r) {
return( factorial(n) / ( factorial(r) * factorial(n-r) ) );
}
function BezierCurve(points) {
let n=points.length;
let curvepoints=[];
for(let u=0; u <= 1 ; u += 0.0001 ){
let p={x:0,y:0};
for(let i=0 ; i<n ; i++){
let B=nCr(n-1,i)*Math.pow((1-u),(n-1)-i)*Math.pow(u,i);
let px=points[i].x*B;
let py=points[i].y*B;
p.x+=px;
p.y+=py;
}
curvepoints.push(p);
}
return curvepoints;
}

Ball Bounce - javascript

function randomXToY(minVal,maxVal,floatVal)
{
var randVal = minVal+(Math.random()*(maxVal-minVal));
return typeof floatVal=='undefined'?Math.round(randVal):randVal.toFixed(floatVal);
}
Ball = (function() {
// constructor
function Ball(x,y,radius,color){
this.center = {x:x, y:y};
this.radius = radius;
this.color = color;
this.dx = 2;
this.dy = 2;
this.boundaryHeight = $('#ground').height();
this.boundaryWidth = $('#ground').width();
this.dom = $('<p class="circle"></p>').appendTo('#ground');
// the rectange div a circle
this.dom.width(radius*2);
this.dom.height(radius*2);
this.dom.css({'border-radius':radius,background:color});
this.placeAtCenter(x,y);
}
// Place the ball at center x, y
Ball.prototype.placeAtCenter = function(x,y){
this.dom.css({top: Math.round(y- this.radius), left: Math.round(x - this.radius)});
this.center.x = Math.round(x);
this.center.y = Math.round(y);
};
Ball.prototype.setColor = function(color) {
if(color) {
this.dom.css('background',color);
} else {
this.dom.css('background',this.color);
}
};
// move and bounce the ball
Ball.prototype.move = function(){
var diameter = this.radius * 2;
var radius = this.radius;
if (this.center.x - radius < 0 || this.center.x + radius > this.boundaryWidth ) {
this.dx = -this.dx;
}
if (this.center.y - radius < 0 || this.center.y + radius > this.boundaryHeight ) {
this.dy = -this.dy;
}
this.placeAtCenter(this.center.x + this.dx ,this.center.y +this.dy);
};
return Ball;
})();
var number_of_balls = 5;
var balls = [];
$('document').ready(function(){
for (i = 0; i < number_of_balls; i++) {
var boundaryHeight = $('#ground').height();
var boundaryWidth = $('#ground').width();
var y = randomXToY(30,boundaryHeight - 50);
var x = randomXToY(30,boundaryWidth - 50);
var radius = randomXToY(15,30);
balls.push(new Ball(x,y,radius, '#'+Math.floor(Math.random()*16777215).toString(16)));
}
loop();
});
loop = function(){
for (var i = 0; i < balls.length; i++){
balls[i].move();
}
setTimeout(loop, 8);
};
I have never used in oops concepts in javascript. How do I change the ball color when the balls touches each other?
This is the link : http://jsbin.com/imofat/1/edit
You currently don't have any interaction with the balls. What you can do is checking whether two balls are "inside" each other, and change colors in that case: http://jsbin.com/imofat/1491/.
// calculates distance between two balls
var d = function(a, b) {
var dx = b.center.x - a.center.x;
var dy = b.center.y - a.center.y;
return Math.sqrt(dx*dx + dy*dy);
};
and:
// for each ball
for(var i = 0; i < balls.length; i++) {
// check against rest of balls
for(var j = i + 1; j < balls.length; j++) {
var a = balls[i];
var b = balls[j];
// distance is smaller than their radii, so they are inside each other
if(d(a, b) < a.radius + b.radius) {
// set to some other color using your random color code
a.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
b.setColor('#'+Math.floor(Math.random()*16777215).toString(16));
}
}
}
Still, there are things for improvement:
Balls are changing colors as long as they are inside each other, not just once.
If you want them to "touch", you might want to implement some kind of bouncing effect to make it more realistic.

Categories