How to draw arc between two lines on HTML5 canvas? - javascript

I am trying to draw a between two lines but arc is not rendering properly. I am using the following code:
imageContext- Canvas's Context
x1- starting point
y1-starting point
multiPointArr- Array which contains points of line(s)
imageContext.beginPath();
imageContext.moveTo(x1,y1);
if(multiPointArr.length==2)
imageContext.lineTo(x2,y2);
if(multiPointArr.length>2){
for( var ii=1 ; ii < multiPointArr.length ; ii++ ){
imageContext.lineTo(multiPointArr[ii].x,multiPointArr[ii].y);
}
}
imageContext.stroke();
if(multiPointArr.length==3)
{
imageContext.beginPath();
var xArr=[multiPointArr[0].x,multiPointArr[1].x,multiPointArr[2].x];
var yArr=[multiPointArr[0].y,multiPointArr[1].y,multiPointArr[2].y];
var m1 = Math.abs(Math.atan((yArr[1] - yArr[0])/(xArr[1] -xArr[0]))/Math.PI*180.0);
var m2 = Math.abs(Math.atan((yArr[1] - yArr[2])/(xArr[1] -xArr[2]))/Math.PI*180.0);
if(xArr[0] >= xArr[1])
{
if(yArr[0] >= yArr[1]);
else m1 = 360 - m1;
}
else
{
if(yArr[0] >= yArr[1])m1 = 180 - m1;
else m1 = 180 + m1;
}
if(xArr[2] >= xArr[1])
{
if(yArr[2] >= yArr[1]);
else m2 = 360 - m2;
}
else
{
if(yArr[2] >= yArr[1])m2 = 180 - m2;
else m2 = 180 + m2;
}
var arr=[];
arr[0] = Math.min(m1, m2);
arr[1] = Math.abs(m1 - m2);
arr[2] = arr[1];
if(arr[1] > 180)
{
arr[0] = Math.max(m1,m2);
arr[1] = 360 - arr[1];
}
arr[0]=(arr[0]*Math.PI)/180;
arr[1]=(arr[1]*Math.PI)/180;
arr[2]=(arr[2]*Math.PI)/180;
var ArcLen = 30;
imageContext.arc((xArr[1]),(yArr[1]),Math.abs(ArcLen*2),arr[0],arr[1],false);
imageContext.stroke();
}
Using above code arc is not drawing between lines.
It looks like following:
However starting angle is drawing correct but there is a problem with endAngle. Please let me know how I can resolve this problem.

Related

How to clip movements to a bounding box?

I'm struggling to find the logic to output a series of pen positions based on some input format.
The pen's position data should be converted in a way that maintains a state of 'pen-down' and 'pen-up' (I have that working), and that clips any pen-movement beyond the rectangle area for the input (x = -8192, y = -8192) .. (8191, 8191). I could not make that clipping work.
See below instructions for how that clipping is supposed to happen:
Instructions
Example of expected output
inputData:
F0A0417F40004000417FC067086708804001C0670840004000187818784000804000
inputData:
F0A0417F41004000417FC067086708804001C067082C3C18782C3C804000
//For decode inputData//
let commands =[] , command , allPos = "", lastPos;
function proDecode(hex) {
for (let i = 0, len; i < hex.length; i+=len) {
// Opcodes take 1 byte (i.e. 2 hex characters), and
// numbers take 2 bytes (4 characters)
len = hex[i] >= '8' ? 2:4;
let num = hex.slice(i,i+len)
if (len === 2) { // opcode
command = []
commands.push(command)
}
else{
num = penDecode(num);
console.log(num);
}
command.push(num)
}
console.log(commands);
return commands;
}
//for outPut//
unction proOutput(commands){
let isPenDown = false;
let x = 0, y = 0;
let output = "";
let color = ""
for (let [opcode, ...args] of commands) {
if (opcode === 'F0') {
x = y = 0;
isPenDown = false;
color = "CO" + 0 + 0 + 0 + 255 + ";\n";
output += "CLR;\n";
} else if (opcode === '80') {
isPenDown = args[0] > 0;
output += "PEN " + (isPenDown ? "DOWN" : "UP") + ";\n";
} else if (opcode === 'A0') {
color = "CO " + args.join(" ") + ";\n";
output += color
} else if (opcode === 'C0') {
let allPos = "", lastPos;
for (let i = 0; i < args.length; i+=2) {
x += args[i];
y += args[i+1];
if(x <-8192){
x= -8192
} else if (x>8191){
x= 8191
lastPos = ` (${x}, ${y})`;
}else if (y<-8192){
y= -8192
}else if (y>8191){
y= 8191
output += "y PEN UP" + ";\n";
} else{
}
lastPos = ` (${x}, ${y})`;
if (isPenDown) allPos += lastPos;
}
output += "MV" + (allPos || lastPos) + ";\n";
} // else: ignore unknown commands
}
Expected Result
Create the same output of the example above.
You would need to find the intersection point with the bounding box whenever the coordinates move out of bounds (or come back within bounds). In some cases a line segment could even both enter and exit the box, giving two intersections.
To calculate an intersection point between a line segment (from x1, y1 to x2, y2) and a horizontal line at y, you would use this formula for the x-coordinate:
x = x1 + (y - y1) * (x2 - x1) / (y2 - y1)
Obviously the y coordinate of the intersection point is already given by y.
There would be no intersection when one of the following conditions is true:
y1 < y and y2 < y
y1 > y and y2 > y
y1 = y2
So to determine whether a line segment crosses one of the four sides of the bounding box (-8192, -8192) to (8191, 8191), you would check for 4 intersections. You could then find 0, 1 or 2 of such intersections.
Here is the code, which I adapted from my answer to your previous question (on hex encoding):
function intersectionWithAxis(x1, y1, x2, y2, y) {
if (y1 < y && y2 < y || y1 > y && y2 > y || y1 === y2) return []; // No intersection
return [Math.round(x1 + (y - y1) * (x2 - x1) / (y2 - y1)), y];
}
function decode(hex) {
let commands = [];
for (let i = 0, len, command; i < hex.length; i += len) {
// Opcodes take 1 byte (i.e. 2 hex characters), and
// numbers take 2 bytes (4 characters)
len = hex[i] >= "8" ? 2 : 4;
let num = parseInt(hex.slice(i, i+len), 16);
if (len === 2) { // Opcode
command = []; // start a new command
commands.push(command);
} else { // Number. Encoded in offset-binary, using 2 x 7 bits
num = ((num & 0x7F00) >> 1) + (num & 0x7F) - 0x2000;
}
command.push(num); // Push opcode or argument in current command
}
return commands;
}
function disassemble(hex) {
let isPenDown = false;
let isPenInBox = true;
let x = 0, y = 0;
let output = "";
let commands = decode(hex);
for (let [opcode, ...args] of commands) {
if (opcode === 0xF0) {
x = y = 0;
isPenDown = false;
isPenInBox = true;
output += "CLR;\n";
} else if (opcode === 0x80) {
isPenDown = args[0] > 0;
if (isPenInBox) output += "PEN " + (isPenDown ? "DOWN" : "UP") + ";\n";
} else if (opcode === 0xA0) {
output += "CO " + args.join(" ") + ";\n";
} else if (opcode === 0xC0) {
if (!isPenDown) {
for (let i = 0; i < args.length; i += 2) {
let [dx, dy] = args.slice(i, i + 2);
x += dx;
y += dy;
}
output += `MV (${x}, ${y});\n`;
} else {
let buffer = "";
for (let i = 0; i < args.length; i += 2) {
let [dx, dy] = args.slice(i, i + 2);
let toX = x + dx;
let toY = y + dy;
// Get intersections with top, bottom, left and right side of box:
let intersections = [
intersectionWithAxis(x, y, toX, toY, -8192),
intersectionWithAxis(x, y, toX, toY, 8191),
intersectionWithAxis(y, x, toY, toX, -8192).reverse(),
intersectionWithAxis(y, x, toY, toX, 8191).reverse()
].filter(p =>
// Only keep the intersection points
p.length && p.every(x => x >= -8192 && x <= 8191)
);
if (intersections.length === 0) { // Position remains at same side of the box (inside or outside)
if (isPenInBox) buffer += ` (${x}, ${y})`;
} else if (intersections.length === 1) { // Moving from outside to inside of box, or vice versa
// Flush previous positions to output, move to the intersection point, and toggle the pen
output += `MV${buffer} (${intersections[0].join(", ")});\nPEN ${(isPenInBox ? "UP" : "DOWN")};\n`
isPenInBox = !isPenInBox;
// Start new series with positions
buffer = isPenInBox ? ` (${toX}, ${toY})` : "";
} else { // Moving from outside the box, through the box, and outside again
output += `MV (${intersections[0].join(", ")});\nPEN DOWN;\nMV (${intersections[1].join(", ")});\nPEN UP;\n`;
}
x = toX;
y = toY;
}
// Flush previous positions to output
if (buffer) output += `MV${buffer};\n`;
}
} // else: ignore unknown commands
}
return output;
}
// Two samples as in question:
console.log(disassemble("F0A0417F40004000417FC067086708804001C0670840004000187818784000804000"));
console.log("---");
console.log(disassemble("F0A0417F41004000417FC067086708804001C067082C3C18782C3C804000"));
The code you posted in your question has some issues (which is why I used the original code without your changes to it):
It is bad practice to use global variables like commands, command, allPos and lastPos: you change their values within your functions, which gives those functions side effects;
The color variable is set in the following statement, but this value is never used:
color = "CO" + 0 + 0 + 0 + 255 + ";\n";
Also, that value does not follow the required syntax (spaces are missing between the RGB values). Anyway, it serves no purpose in your function, so you can omit it.
The following string does not include the value of y, but a literal "y":
output += "y PEN UP" + ";\n";
Also, generating the output at that spot in the code is wrong: you would need to flush the already gathered coordinates to the output first. And outputting y or its value at this point is not in line with the output syntax either.

Get tang point in svg path

i want optimize function about tang of path at specific point
is there a way to optimize this code
but it not optimazed in big paths
i want optimie this to work with large svg path in short time
function getTangent(pathNode, point){
var line_path = pathNode;
var length_at_point = 0,
total_length = line_path.getTotalLength();
while ((Math.trunc(
line_path.getPointAtLength(length_at_point).x) != Math.trunc(point[0]) ||
Math.trunc(line_path.getPointAtLength(length_at_point).y) != Math.trunc(point[1])) &&
length_at_point < total_length) {
length_at_point++;
};
var point = line_path.getPointAtLength(length_at_point),
prev = {},
next = {},
delta = {};
if (length_at_point > 1 && length_at_point < (total_length - 1)) {
prev = line_path.getPointAtLength(length_at_point - 1);
next = line_path.getPointAtLength(length_at_point + 1);
delta = {
x: next.x - prev.x,
y: next.y - prev.y
}
} else {
// don't worry about the first and last pixel or so
return;
};
var LENGTH = 700; // length of tangent line
return {
id:'tangent'
, "stroke-width":0.5
, stroke:'rgb(6,120,155)'
, x1:(point.x - delta.x * LENGTH)
, y1:(point.y - delta.y * LENGTH)
, x2:(point.x + delta.x * LENGTH)
, y2:(point.y + delta.y * LENGTH)
};
}

Three.js collision detection issue

I'm making a simple game where you can add objects(cubes) at the raycaster(mouse) position when you click on a big plane(ground). I don't want that objects(cubes) can placed in each other. I made a simple collision detection. I know this isn't the best way to do this, but this is the way I understand what I'm doing.. I am a beginner.
In the following code I check the positions of both objects. With these positions I check the distance between it. If the distance is smaller than 4098(which is 64*64) it add the object to the scene.
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( clickObjects );
var intersects2 = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) { // Clicking on the ground?
if ( intersects2.length > 0 ) { // Clicking on an object?
}else{
var geometry = new THREE.BoxGeometry( 64, 64, 64);
var cube = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: 0xFBF5D7, opacity: 1 } ) );
cube.position.copy(intersects[0].point);
cube.position.y = 30;
cube.flipSided = true;
cube.doubleSided = true;
var validposition = true;
var i;
for (i = 0; i < objects.length; i++) {
var dx = cube.position.x - objects[i].position.x;
var dy = cube.position.y - objects[i].position.y;
var dz = cube.position.z - objects[i].position.z;
var distance = dx*dx+dy*dy+dz*dz;
if(distance < 4096) {
validposition = false;
}
}
if(validposition == true) {
objects.push(cube);
scene.add(cube);
}
}
}
}
The problem is that this only works when it is a square object. Can anyone help me how I can think in which way for a rectangle object like THREE.BoxGeometry( 64, 64, 400)?
I wrrote some time ago a remake of the frogger game from C64. There I had to control that my frog has no contact to the cars.
var img1_x1 = parseInt(idImg.style.left);
var img1_x2 = img1_x1 + idImg.width - 1;
var img1_y1 = parseInt(idImg.style.top);
var img1_y2 = img1_y1 + idImg.height - 1;
img2_x1 = parseInt(idImg2.style.left);
img2_x2 = img2_x1 + idImg2.width - 1;
img2_y1 = parseInt(idImg2.style.top);
img2_y2 = img2_y1 + idImg2.height - 1;
if (((img2_x1 <= img1_x2 && img1_x2 <= img2_x2) || (img2_x1 <= img1_x1 && img1_x1 <= img2_x2) || (img1_x1 <= img2_x1 && img2_x1 <= img1_x2))
&& ((img2_y1 <= img1_y2 && img1_y2 <= img2_y2) || (img2_y1 <= img1_y1 && img1_y1 <= img2_y2) || (img1_y1 <= img2_y1 && img2_y1 <= img1_y2)))
{
alert('boom');
}
Look at my code perhaps there is something usefull for you. I first calculate the position of my frog (img1) and than use a for-loop over all my test-objects (here not displayed). In this loop I calculate the position for the object (img2) and afterwards I make the control of overlapping.
You had to add the third dimension to the conditions which I didn'tz needed.
Note: For img2 I don't use the var in my for-loop I declared it outside in the function.
I copied here only a few sections from my code and cut off confusing parts.

Convert a number into sum of two other numbers so the difference is minimum

In Mars, there are only two denominations of currency ,x and y. A
Marsian goes to a bar and the bill is "z". Using x and y he has to pay
the bill. But the bar doesn't tender change, any extra money will be
taken as tips.
So write a function in JavaScript that helps the marsian to reduce the
tips.
The function takes in x, y, z and returns the amount of tip he has to
pay.
Example 1
Input: 2, 5, 109
Output: 0
Explanation: 21 coins of 5, and 2 coins of 2
Example 2
Input: 5, 7, 43
Output: 0
Explanation: 4 coins of 7, and 3 coins of 5
Example 3
Input: 15, 19, 33
Output: 1
Explanation: 1 coin of 15 and 1 coin of 19
Solution: I think this is level one DP problem, something like subset sum. Like for finding the optimal tip for the larger number, knowing the optimal tip for all the below numbers would help.
const coinA = 2
const coinB = 5
const sum = 13
var arr = [];
arr[0] =0;
console.log(getMyTip(coinA, coinB, sum));
function getMyTip(){
for(var i=1; i<= sum; i++){
var minA, minB;
if( i < coinA){
minA = coinA - i;
}else{
minA = arr[i - coinA];
}
if( i < coinB){
minB = coinB - i;
}else{
minB = arr [i - coinB]
}
arr[i] = Math.min(minA, minB);
}
return arr[sum];
}
Jsfiddle: https://jsfiddle.net/7c4sbe46/
But I'm not sure why it is not getting accepted. Please let me know if I'm missing something with the logic here.
It is more related to diophantine equations, i.e. is there a solution to a.x+b.y=z ? The answer is yes if z is a multiple of the greatest common divisor of x and y (called it gcd). If not, your tip will be the difference between 1. the smaller number divisible by gcd and greater than z
and 2. z.
Once you know the value of the tip, you can even easily know the number of x and y that you need by slightly modifying the value of z to (z+tip).
#include <stdio.h>
int main()
{
int curr1, curr2, bill;
scanf("%d %d %d",&curr1,&curr2,&bill);
int gcd, tip=0;
int x=curr1;
int y=curr2;
while(x!=y)
{
if(x > y)
x -= y;
else
y -= x;
}
gcd=x;
if((bill%curr1==0) || (bill%curr2==0) || (bill%(curr1 + curr2)==0)){
tip = 0;
} else if(bill>(curr1 + curr2) && (bill % gcd==0)) {
tip = 0;
} else if((curr1 + curr2) > bill){
if(curr2 > curr1){
tip = (bill % (curr2-curr1));
}else{
tip = (bill % (curr1-curr2));
}
}
printf("%d",tip);
return 0;
}
There is no need to use dp for this. Here is the simple solution -
// x -> first currency denomination
// y -> second currency denomination
// z -> total bill
var calculateTip = function(x,y,z) {
var xMax = Math.floor(z/x);
var tip = y;
if(xMax == 0) {
tip = (x-z) < (Math.ceil(z/y)*y - z) ? (x-z) : (Math.ceil(z/y)*y - z);
}
while (xMax>=0) {
var tempTip = xMax*x + Math.ceil((z-xMax*x)/y)*y - z;
if(tempTip < tip) {
tip = tempTip;
}
xMax--;
}
return tip;
}
var minimumTip = function(x,y,z) {
if(x>y) {
return calculateTip(x,y,z);
} else {
return calculateTip(y,x,z);
}
}
console.log(minimumTip(2, 5, 109));
var findTip = function(x=2, y=5, z=13){
var x = x;
var y = y;
var z = z;
var tip ;
var temp1 = x;
var temp2 = y
function findNumber(num,total){
if(num > total){
return num-total;
}
else{
var q = Math.floor(total/num);
return ((q+1)*num)-total;
}
}
function findMin(a,b,c){
var min ;
if(a<b && a<c){
min = a
}else{
if(b<c){
min = b;
}else{
min = c;
}
}
return min;
}
while(temp1!=temp2)
{
if(temp1 > temp2)
temp1 -= temp2;
else
temp2 -= temp1;
}
var factor =temp1;
if(z%x == 0 || z%y == 0 || z%(x+y) == 0) {
tip = 0;
}else if(z%factor == 0 && z>=x*y - x -y){
tip = 0;
}
else {
var minX= findNumber(x,z);
var minY = findNumber(y,z);
var minXY = findNumber(x+y,z);
console.log(minX,minY,minXY)
tip = findMin(minX,minY,minXY);
}
alert('the tip is '+ tip.toString());
return tip;
}
findTip(21, 11, 109);

Why does this code run slow in firefox?

So I wrote this code for a simple game. The code runs at 60 fps in both Chrome and Safari but Firefox barely manages 30-40 fps. The code looks simple enough to me. What could be causing the delay?
I checked in firebug and found out that only one function "follow" is taking up all the time. Here is the code:
function checkCollision (ball0, ball1) {
var dx = ball1.X - ball0.X,
dy = ball1.Y - ball0.Y,
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < ball0.rad + ball1.rad) {
var angle = Math.atan2(dy, dx),
sin = Math.sin(angle),
cos = Math.cos(angle);
var pos0 = {x: 0, y: 0},
pos1 = rotate(dx, dy, sin, cos, true),
vel0 = rotate(ball0.spdX, ball0.spdY, sin, cos, true),
vel1 = rotate(ball1.spdX, ball1.spdY, sin, cos, true),
vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) /
(ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;
var absV = Math.abs(vel0.x) + Math.abs(vel1.x),
overlap = (ball0.rad + ball1.rad) - Math.abs(pos0.x - pos1.x);
pos0.x += vel0.x / absV * overlap;
pos1.x += vel1.x / absV * overlap;
//rotate positions back
var pos0F = rotate(pos0.x, pos0.y, sin, cos, false),
pos1F = rotate(pos1.x, pos1.y, sin, cos, false);
ball1.X = ball0.X + pos1F.x;
ball1.Y = ball0.Y + pos1F.y;
ball0.X = ball0.X + pos0F.x;
ball0.Y = ball0.Y + pos0F.y;
var vel0F = rotate(vel0.x, vel0.y, sin, cos, false),
vel1F = rotate(vel1.x, vel1.y, sin, cos, false);
ball0.spdX = vel0F.x;
ball0.spdY = vel0F.y;
ball1.spdX = vel1F.x;
ball1.spdY = vel1F.y;
}
}
function move()
{
var side,i;
for (i=0 ; i < balls.length; i++)
{
var obj = balls[i];
if (side=obj.edgeX())
{
if (side === 'l')
obj.X = obj.rad;
else if (side === 'r')
obj.X = canvas.width() - obj.rad;
obj.spdX*=-1;
}
if (side=obj.edgeY())
{
if (side === 't')
obj.Y = obj.rad;
else if (side === 'b')
obj.Y = canvas.height() - obj.rad;
obj.spdY*=-1;
}
if (leash == true && i === 0)
{
if (mouse.X>obj.X && (obj.spdX<10))
obj.spdX+=obj.accX;
else if (mouse.X<obj.X && (obj.spdX>-10))
obj.spdX-=obj.accX;
if (mouse.Y>obj.Y && (obj.spdY<10))
obj.spdY+=obj.accY;
else if (mouse.Y<obj.Y && (obj.spdY>-10))
obj.spdY-=obj.accY;
}
obj.X+=obj.spdX;
obj.Y+=obj.spdY;
if (Math.abs(obj.spdX)>0.1)
obj.spdX*=0.98;
else obj.spdX=0;
if (Math.abs(obj.spdY)>0.1)
obj.spdY*=0.98;
else obj.spdY = 0;
};
}
function follow()
{
var ballA, i, ballB,j;
requestAnimationFrame(follow);
//stats.begin();
context.clearRect(0,0,canvas.width(),canvas.height());
move();
for (i = 0, len = balls.length - 1; i < len; i++) {
ballA = balls[i];
for (j = i + 1; j < balls.length; j++) {
ballB = balls[j];
checkCollision(ballA, ballB);
}
}
balls.forEach(function(obj){
drawCircle(obj.X,obj.Y,obj.rad, obj.color);
if (leash == true && obj.control === true)
{drawLeash(mouse.X,mouse.Y,obj.X,obj.Y,obj.color);}
});
//stats.end();
};
Here is the animation: http://ipsumturpis.xtreemhost.com/follower/index.html
I vaguely remembered there used to be a problem in FF regarding with canvas drawing performance, so I have commented out drawCircle(obj.X,obj.Y,obj.rad, obj.color); and poof, magic happened - my frame rate went up from 11 FPS to 60.
Try caching balls length in a variable. Unless it’s absolutely necessary for reasons I don’t see, running balls.length (or any function) in every iteration of a loop is naturally going to be time consuming.
So try something like this;
ballslen = balls.length;
for (j = i + 1; j < ballslen; j++)

Categories

Resources