Corridor path by line path and width - javascript

I need to write a function, which generates corridor path by line path and width.
For example, I have array of coords of polyline:
var coords = [
50, 50,
150, 50,
250, 100,
220, 200,
350, 100,
];
https://i.stack.imgur.com/FCCxb.png
Then I use function to get corridor path:
var width = 10;
var corridorPath = getCorridorPath(coords, width);
Function should return something like that:
[
40, 50,
50, 40,
150, 40,
157, 41,
255, 90,
260, 105,
240, 170,
340, 90,
360, 90,
360, 110,
220, 215,
205, 205,
233, 107,
147, 62,
50, 60,
40, 50,
]
https://i.stack.imgur.com/v3wxN.png

Calculate parallel polylines and conjunctions.
For two neighbor edges AB and BC find normalized vectors ab and cb.
Calculate unit bisector vector:
b = (ab + cb).normalized
Calculate length of bisector segments as
len = d / sin(fi)
where d is offset (halfwidth), and fi is angle between vectors b and ab:
fi = atan2(crossproduct(b,ab), dotproduct(b,ab))
Find offset points (left and right ones) as
B' = B + l * b
B'' = B - l * b
Note that you want to cut long triangles, so offset point is valid for side with acute angle, and you need to recalculate two points for cut cap - for example, get central point B''' = B - d * b and add vectors, perpendicular to bisector.

Related

How to make drawImage position a variable inside canvas

The problem is that the "position" variable is not beeing updated in order to make the digit display next to each other and not on top of each other.
I also tryed to put it inside the prototype function thinking that if it's beeing updated right before it runs the drawImage function it will work...but it does not and I do not understand what the problem is.
<pre>
var sprite = new Image();
sprite.src = "Flappy_Bird_Sprite.png"
function drawSprite (img, x_crop, y_crop, x_width, y_height, x_pos, y_pos, x_posWidth, y_posHeight) {
this.img = img;
this.x_crop = x_crop;
this.y_crop = y_crop;
this.x_width = x_width;
this.y_height = y_height;
this.x_pos = x_pos;
this.y_pos = y_pos;
this.x_posWidth = x_posWidth;
this.y_posHeight = y_posHeight;
}
var position = 10;
drawSprite.prototype.draw = function () {
ctx.drawImage(this.img, this.x_crop, this.y_crop, this.x_width, this.y_height, this.x_pos, this.y_pos, this.x_posWidth, this.y_posHeight);
}
var arr = [
new drawSprite(sprite, 137, 306, 7, 10, position, 50, 20, 40),
new drawSprite(sprite, 139, 477, 5, 10, position, 50, 15, 40),
new drawSprite(sprite, 292, 160, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 306, 160, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 320, 160, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 334, 160, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 292, 184, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 306, 184, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 320, 184, 12, 18, position, 50, 15, 40),
new drawSprite(sprite, 334, 184, 12, 18, position, 50, 15, 40),
]
var increment = 0;
function animate () {
requestAnimationFrame(animate);
increment++;
if (increment % 10 == 0){
score++;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
var tString = score.toString(); //Makeing the score into a string so I can select each number with the substring function
var length = tString.length; //Finding out the length of the string(in our case is 2)
for(var i = 0; i < length; i++){
var substring = tString.substring(i,i+1); //Selecting each digit of that number in order to draw each of them separately
var toNumber = parseInt(substring); //Setting that "string" back into a number so I can acces that number in the array
if( length == 2 ) { //I am trying to make this work for 2 digit number(10-99) and if this works, then I will add 3 digits and so on, I know that this should work only for numbers from 10-99
if( i == 0){ // Position the first digit of that number at the position ....
position = 180;
}
if( i == 1){ // Position the second digit of that number at the position ....
position = 105;
}
}
arr[toNumber].draw();
}
}
animate();
<code>
Your calls to new drawSprite all use the same value for position, which is fixed at the point you create those objects.
You could try updating the sprite instance before drawing:
arr[toNumber].x_pos = position;
arr[toNumber].draw();

fabric.js limit rotation to x degrees with rotation handle

I am using fabric.js and trying to allow rotation for any object on my canvas, not freely 360 degrees, but only 15 degrees at a time, using the rotation handle. I searched really hard but couldn't find an answer so far. Is this possible?
Shorter solution:
canvas.on('object:rotating', function(options) {
var step = 15;
options.target.angle = Math.round(options.target.angle / step) * step;
});
UPD: since 1.6.7 you can just use fabric.Object.snapAngle property:
someFabricObject.snapAngle = 15;
You can use canvas.on("object:rotating") to interfer with rotation of all objects.
I defined a set of valid angles, and then on the rotation event i checked which one to use.
var angles = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345, 360];
canvas.on("object:rotating", function(rotEvtData) {
var targetObj = rotEvtData.target;
var angle = targetObj.angle % 360;
for (var i=0; i < angles.length; i++) {
if (angle <= angles[i]) {
targetObj.angle = angles[i];
break;
}
}
});
var canvas = new fabric.Canvas("c");
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 90,
height: 90
});
canvas.add(rect);
var angles = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345, 360];
canvas.on("object:rotating", function(rotEvtData) {
var targetObj = rotEvtData.target;
var angle = targetObj.angle % 360;
for (var i=0; i < angles.length; i++) {
if (angle <= angles[i]) {
targetObj.angle = angles[i];
break;
}
}
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<canvas id="c" width="400" height="400"></canvas>

How to make a decahedron

Hi I've found some code that animates 3d shapes and even gives an example of making and animating an icosahedron I'm trying to turn it in to a decahedron though and my geometry is pretty bad. The code I have for the icosahedron is:
// draw a icosahedron
var tau = 1.6180,
phi = 20.90515745, // (180-138.1896851)/2
rt3 = Math.sqrt(3),
d = sideLen/2,
foldTbl = [ 60, -60, 60, -60,
-60, -60, 60, 60,
60, -60, 60, -60,
-60, -60, 60, 60,
60, -60, 60, -60],
moveTbl = [ 0, 2*d, 0, 2*d,
2*d, 2*d, 0, 0,
0, 2*d, 0, 2*d,
2*d, 2*d, 0, 0,
0, 2*d, 0, 2*d],
triangle = ['M',0,0,0, 'L', d*rt3,d,0, 0,2*d,0, 'z'],
tri,
faces = g.createGroup3D(),
bend = -2*phi,
i;
for (i=0; i<20; i++)
{
// create the next face
tri = g.compileShape3D(triangle, "red", null, 1); // backColor irrelevant
faces.addObj(tri);
faces.translate(0, -moveTbl[i], 0);
faces.rotate(0, 0, 1, foldTbl[i]);
faces.rotate(0, 1, 0, bend);
faces.translate(0, moveTbl[i], 0);
}
return faces;
i'm sure there must be an easy way to make this a decahedron but if anyone has any advice that'd be amazing - thanks!
If you have coordinates for an icosahedron but want to draw a dodecahedron, you can make use of the duality between those two. Take the icosahedron, and put a new vertex in the middle of every one of its triangular faces. Connect two new vertices with an edge if the corresponding faces of the icosahedron had an edge in common. You will obtain a dodecahedron, with one vertex for every face of the icosahedron, and one face for every vertex.

How can I add a marker to a donut chart created with Raphael.js?

I need help adding a marker to this donut chart script I've modified utilizing raphael.js. I've got most everything ready to go except for a way to dynamically generate a triangular marker.
JSFiddle: http://jsfiddle.net/aP7MK/73/
function donutChart(total, goal, avg){
var paper = Raphael("canvas", 400, 400);
paper.customAttributes.arc = function (xloc, yloc, value, total, R) {
var alpha = 360 / total * value,
a = (90 - alpha) * Math.PI / 180,
x = xloc + R * Math.cos(a),
y = yloc - R * Math.sin(a),
path;
if (total == value) {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, 1, 1, xloc - 0.01, yloc - R]
];
} else {
path = [
["M", xloc, yloc - R],
["A", R, R, 0, +(alpha > 180), 1, x, y]
];
}
return {
path: path
};
};
var backCircle = paper.circle(100, 100, 40).attr({
"stroke": "#7BC2E5",
"stroke-width": 14
});
var theArc = paper.path().attr({
"stroke": "#f5f5f5",
"stroke-width": 14,
arc: [100, 100, 0, 100, 40]
});
//event fired on each animation frame
eve.on("raphael.anim.frame.*", onAnimate);
//text in the middle
theText = paper.text(100, 100, "0%").attr({
"font-size": 18,
"fill": "#f5f5f5",
"font-weight": "bold"
});
//the animated arc
theArc.rotate(0, 100, 100).animate({
arc: [100, 100, ((total/goal) * 100), 100, 40]
}, 1900);
//on each animation frame we change the text in the middle
function onAnimate() {
var howMuch = theArc.attr("arc");
theText.attr("text", Math.floor(howMuch[2]) + "%");
}
}
donutChart(80, 140, 40);
Here's what I'm eventually trying to create:
I'm not worried about the styling, just need help with the marker element, which will denote where the avg argument being passed to the donutChart function lies within the chart.
As #Ian said you can use path to draw triangle:
// triangle
var tri = paper.path("M100 50 L90 40 L110 40 L100 50 Z");
See the docs about using path (its commands).
Then you need to rotate/translate (again as #Ian said) but SVG helps you here providing rotate method which takes not only angle of rotation but also coordinates of point to rotate around (it translates coordinates for you)
tri.rotate((howMuch[2] - prev_percent) * 3.6, 100, 100);
The only thing here to note is that you need difference of previous and current percentage.
The working fiddle

Canvas triangle without linejoin and stroke width

I have this triangle that I have rounded corners on but I'm using the arcTo:
context.moveTo(140, 0);
context.arcTo(180, 100, 100, 100, 4);
context.arcTo(100, 100, 140, 0, 4);
context.arcTo(140, 0, 180, 100, 4);
As you will see the top angle looks a bit messed up. Any ideas how to fix it? Seems like there needs to be some calculations for the initial moveTo(x, y) but 140, 0 is where it should start.
I just got rid of the moveTo and arced them to eachother. Started the first part at 174, 176 (180-4 radius) works but 174 had no overlap at all.
Live Demo
​var canvas = document.getElementsByTagName("canvas")[0],
ctx = canvas.getContext("2d");
canvas.width = canvas.height = 400;
ctx.beginPath();
ctx.arcTo(174, 100, 100, 100, 4);
ctx.arcTo(100, 100, 140, 0, 4);
ctx.arcTo(140, 0, 180, 100, 4);
ctx.arcTo(180, 100, 100, 100, 4);
ctx.stroke();
​
Here is what I came up with:
var r = 8;
var offset = (6 * r / 4) - 1;
context.arcTo((180 - offset), 100, 100, 100, r);
context.arcTo(100, 100, 140, 0, r);
context.arcTo(140, 0, 180, 100, r);
context.arcTo(180, 100, 100, 100, r);
Using part of what Loktar provided, I've modified it slightly to use a ratio of what I know works for a given diameter, and used this as the offset. With a diameter of 4, I know 6 works.
Seems like there should be a better way but I'm happy with this.

Categories