I am creating
"Inscribe a Circle in a Triangle using a canvas". But facing lots of problem. Well I tried to draw and Triangle in the middle of the canvas though its created i am wondering where to start drawing an circle which could be perfectly work for me.
With respective to mathematically I knew to draw circle, but when it comes to java script i am stuck.
kindly help me.
Thanks.
i have tried the following code to draw an traing at center of the canvas:-
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(ctx, 100, c.width/2, c.height/2);
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.save();
}
like this i want..
Ok check this .. Live Demo for equilateral triangle
Radius of circle inscribed in equilateral triangle = Sqrt(3)/6 * side
of triangle;
window.onload = function()
{
var c=document.getElementById("myCanvas");
var context =c.getContext("2d");
check(context,100,c.width/2,c.height/2);
circle(context,100,c.width/2,c.height/2);
}
function check(ctx, side, cx, cy){
var h = side * (Math.sqrt(3)/2);
ctx.strokeStyle = "black";
ctx.save();
ctx.translate(cx, cy);
ctx.beginPath();
ctx.moveTo(0,-h/2);
ctx.lineTo(-side/2, h / 2); // line a
ctx.lineTo(side /2, h / 2); // line b
ctx.lineTo(0,-h /2); // line c
ctx.stroke();
ctx.closePath();
ctx.restore();
}
function circle(ctx,side,cx,cy)
{
var h = side * (Math.sqrt(3)/2);
var radius = Math.sqrt(3)/6 * side; // Radius of the circle
cy = cy + h/2 - radius; // Center Y of circle
ctx.beginPath();
ctx.arc(cx,cy,radius,0,Math.PI * 2,false);
ctx.stroke();
ctx.closePath();
}
Check all formulas to find the radius of circle inscribed in different triangles here
Related
I would like to create an arc distortion of an image with canvas.
My goal is to do the same thing as imagemagick but in javascript with canvas: https://legacy.imagemagick.org/usage/distorts/#circular_distorts
Here is the expected result with the angle parameter that corresponds to the images below:
60°, 120°, 180°, 270°, 360°
I only found two interesting codes that go in the right direction:
This experimental script
which works directly on the pixel array but does not keep the aspect ratio of the original image and the angle given as a parameter does not work well:
https://github.com/sergiks/canvas-text-arc
This other script
which makes a rotation on each column of the image with drawimage but does not allow to configure the angle of the arc, it is a 360° rotation by default:
http://jsfiddle.net/hto1s6fy/
var cv = document.getElementById('cv');
var ig = document.getElementById('ig');
var ctx = cv.getContext('2d');
// draw the part of img defined by the rect (startX, startY, endX, endY) inside
// the circle of center (cx,cy) between radius (innerRadius -> outerRadius)
// - no check performed -
function drawRectInCircle(img, cx, cy, innerRadius, outerRadius, startX, startY, endX, endY) {
var angle = 0;
var step = 1 * Math.atan2(1, outerRadius);
var limit = 2 * Math.PI;
ctx.save();
ctx.translate(cx, cy);
while (angle < limit) {
ctx.save();
ctx.rotate(angle);
ctx.translate(innerRadius, 0);
ctx.rotate(-Math.PI / 2);
var ratio = angle / limit;
var x = startX + ratio * (endX - startX);
ctx.drawImage(img, x, startY, 1, (endY - startY), 0, 0, 1, (outerRadius - innerRadius));
ctx.restore();
angle += step;
}
ctx.restore();
}
var cx = 300,
cy = 300;
var innerRadius = 0;
var outerRadius = 300;
var startX = 0,
endX = 1361,
startY = 0,
endY = 681;
drawRectInCircle(ig, cx, cy, innerRadius, outerRadius, startX, startY, endX, endY);
Imagemagick source code
Finally, I also looked at the C source code of imagemagick but I don't have the skills to transpose it:
https://github.com/imagemagick/imagemagick/blob/main/magickcore/distort.c
(to see what concerns arc distortion, use the keyword "ArcDistortion")
Though this is an interesting topic and I also like to re-invent the wheel sometimes, it isn't necessary in this case. Someone else had a go at it yet and released a JavaScript library called lens, which replicates some of ImageMagick's filters. Luckily the 'Arc distortion' is among those.
Lens offers a method called distort() which accepts an input like a <canvas> element, applies the transformation requested and outputs raw pixel data, which you can then use to make another <canvas>.
Here's a quick example:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.font = "48px sans";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#0";
ctx.fillText("Around the World", canvas.width / 2, canvas.height / 2);
async function makeArc(image, angle, rotate = 0) {
let result = await lens.distort(
image,
lens.Distortion.ARC, [angle, rotate], {
imageVirtualPixelMethod: angle === 360 ? lens.VirtualPixelMethod.HORIZONTAL_TILE : lens.VirtualPixelMethod.TRANSPARENT
}
);
let tempCanv = document.createElement("canvas");
let tempCtx = tempCanv.getContext("2d");
tempCanv.width = result.image.width;
tempCanv.height = result.image.height;
tempCtx.putImageData(result.image.imageData, 0, 0);
document.body.appendChild(tempCanv);
}
makeArc(canvas, 120, 0);
<script src="https://cdn.jsdelivr.net/npm/#alxcube/lens#1.0.0/dist/lens.min.js"></script>
<canvas id="canvas" width="450" height="50"></canvas>
I am trying to using context.clip() to clip a draw arc from other one and fill the clipped result.
But when i clip section & fill it, it gives pixelated fill .
var ctx = document.getElementById("canvas").getContext("2d");
var x = 150 ;
var y = 150 ;
var r = 100 ;
ctx.save() ;
ctx.translate(x,y) ;
ctx.beginPath() ;
ctx.arc(0,0,r,0,2*Math.PI);
ctx.closePath() ;
ctx.fillStyle = "cyan" ;
ctx.fill() ;
ctx.lineWidth = 10;
ctx.stroke();
ctx.restore() ;
ctx.save() ;
ctx.clip() ;
ctx.translate(x,y);
ctx.beginPath();
ctx.moveTo(r,-r-10);
ctx.arc(0,-r-10,r,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore();
https://jsfiddle.net/x0d0n40z/1/
An alternative approach which eliminates the need for clip()/save()/restore() is to use a few steps of compositing.
Clipping mask is anti-aliased in some browsers while in other not. To obtain consistency (and in some cases also performance since save-clip-restore are relative expensive operations) using composition is preferred if possible.
In this case:
Fill main arc in target color
Define a clipping arc
Change composite mode to destination-out and fill (will cut main)
Change composite mode to source-atop and stroke (will outline cut)
Change composite mode to source-over and stroke outline of main circle
Example
Update: Simplified steps (with the last step merged into the process, ref. comments). I also chose to demonstrate use of the Path2D since we can reuse the object without interfering with the path on the ordinary context -
var ctx = c.getContext("2d"),
p = new Path2D(), // this will store main shape for reuse
x = 75, y = 75, radius = 70;
// main arc
p.arc(x, y, radius, 0, 6.28); // store to path object
ctx.fillStyle = "cyan";
ctx.fill(p); // fill path object
// clip top arc
ctx.globalCompositeOperation = "source-atop";
ctx.arc(x, y - radius, radius, 0, 6.28);
ctx.fillStyle = "#09f";
ctx.fill();
ctx.lineWidth = 5;
ctx.stroke();
// stroke main arc
ctx.globalCompositeOperation = "source-over";
ctx.stroke(p); // stroke path object
body {background:#e9e9e9}
<canvas id=c></canvas>
Old version:
var ctx = c.getContext("2d"),
x = 75, y = 75, radius = 70;
// main arc
ctx.arc(x, y, radius, 0, 6.28);
ctx.fillStyle = "cyan";
ctx.fill();
// clipping arc
ctx.beginPath();
ctx.arc(x, y - radius, radius, 0, 6.28);
// cut step
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// stroke gap step
ctx.globalCompositeOperation = "source-atop";
ctx.lineWidth = 10;
ctx.stroke();
// stroke whole outline
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.arc(x, y, radius, 0, 6.28);
ctx.lineWidth = 5;
ctx.stroke();
// if you want to color the clip then use this:
ctx.globalCompositeOperation = "destination-atop";
ctx.fillStyle = "#09f";
ctx.fill();
body {background:#e9e9e9}
<canvas id=c></canvas>
The problem is that the clip boundary is not being anti alised.
To solve you can render the shape without using the clip. The ctx.arc method lets you set the start and end angles so you can get the inset by filling two arcs.
You will need to get the angles where the clip circle and the inset circle intercept.
For this case it is very simple. First get the distance between the circles, and the angle from one to the other. This works only for two circles of same radius.
var c = {x:?,y:?}; // circle one location
var c1 = {x:?,y:?}; // circle two location
var radius = ?; // radius of both
var angle = Math.atan2(c1.y - c.y, c1.x - c.x); // get the angle from one to the next
var dist = Math.hypot(c1.x - c.x, c1.y - c.y); // get the distance. NOTE IE does not have hypot so do it the normal way with Math.sqrt....
Now you have the angle and distance the intercepts are a simple relationship between the distance and the radius
var iAngle = Math.acos(dist / 2 / radius); // the angle from the line between the circles
// to the intercepts
Now you have that angle you can draw the two arcs
ctx.beginPath();
ctx.arc(c.x,c.y,radius,angle - iAngle , angle + iAngle); // first arc
ctx.arc(c1.x,c1.y, radius, angle + Math.PI - iAngle, angle + Math.PI + iAngle); // second arc
ctx.fill();
ctx.stroke();
There is not much you can do to prevent the jaggies from effecting the clip area. Another way to achieve clipping is to use ctx.globalCompositeOperation to render a mask. You can mask in and out, and many more options. This will be a better solution when the clipping area becomes more complex.
I finally figured the right way to correct the bug .
Heres the clean result of what i wanted https://jsfiddle.net/x0d0n40z/6/
Code :
var ctx = document.getElementById("canvas").getContext("2d");
var r = 50
x = ctx.canvas.width/2;
y = ctx.canvas.height/2;
var offset = 60;
ctx.save();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI);
ctx.stroke();
ctx.clip();
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.fillStyle = "cyan";
ctx.fill();
ctx.setTransform(1, 0, 0, 1, x, y);
ctx.beginPath();
ctx.arc(0,-offset,r,0,2*Math.PI,false);
ctx.fillStyle = "#f2f2f2";
ctx.fill();
ctx.lineWidth = 1 ;
ctx.stroke();
ctx.setTransform(1,0,0,1.5,x,y);
ctx.beginPath();
ctx.arc(0,0,r,0,2*Math.PI,false);
ctx.lineWidth = 3 ;
ctx.stroke();
ctx.restore();
Source from were i learned to use clip : http://www.html5canvastutorials.com/advanced/html5-canvas-clipping-region-tutorial/
I'm trying to use a gradient to fill an area of a canvas, but I would like to be able to set the angle of the gradient.
I know this is possible by using different values in the creation of the gradient (ctx.createLinearGradient(x1, y1, x2, y2)) as seen here:
But I can't seem to get my head around the maths needed to convert an angle (radians) to a gradient size that will produce the same angle (The angle I'm referring to is perpendicular to the direction of the gradient, so a 0 radian angle would show the gradient on the right)
In short, how can I convert (quantity) of radians into an X by Y shape?
$(document).ready(function(){
var canvas = document.getElementById("test");
var ctx = canvas.getContext("2d");
var angle = 0.5;
ctx.beginPath();
ctx.moveTo(100, 100);
ctx.arc(100, 100, 100, 0, -angle, true);
ctx.lineTo(100, 100);
ctx.closePath();
// Convert angle into coordinates to tilt the grad
// grad should be perpendicular to the top edge of the arc
var grad = ctx.createLinearGradient(0, 0, 0, 100);
grad.addColorStop(0, "rgba(0,0,0,0)");
grad.addColorStop(1, "rgba(0,0,0,0.8)");
ctx.fillStyle = grad;
ctx.fill();
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="test" width="500" height="500"></canvas>
(So no one wastes their time: I specifically don't want to use a context.rotate() in this case)
You can use the angle with cos and sin to define the line that gives the gradient. The only thing left then is to give the length:
var length = 100, angle = 0;
ctx.createLinearGradient(x, y, x + Math.cos(angle) * length, y + Math.sin(angle) * length);
The gradient will be rendered along (perpendicular) to the line given.
Not stated, but if you need to calculate the length of the line depending on the angle and box you can use the law of sines to do so (used in this way). The example below uses a fixed radius. You can also use max length from (x1, x2) by calculating the hypotenuse: length = Math.sqrt(diffX*diffX + diffY*diffY);.
Example
var ctx = c.getContext("2d"),
x1 = 150, y1 = 150, x2, y2, angle,
length = 150;
render();
cAngle.oninput = render;
function render() {
angle = +cAngle.value / 180 * Math.PI;
// calculate gradient line based on angle
x2 = x1 + Math.cos(angle) * length;
y2 = y1 + Math.sin(angle) * length;
// create and render gradient
ctx.fillStyle = ctx.createLinearGradient(x1, y1, x2, y2);
ctx.fillStyle.addColorStop(0, "#fff");
ctx.fillStyle.addColorStop(1, "#07f");
ctx.fillRect(0, 0, 300, 300);
// show definition line
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
<label>Angle: <input id=cAngle max=359 type=range value=0></label><br>
<canvas id=c height=300></canvas>
I have this animation, but i cant get over the logic. I hope someone can help me here.
Basicly i need this: http://jsfiddle.net/PDE85/9/ but without the arrow doing such crazy moves. It should be attached to the front of the open circle to simulate an expanding arrow.
I got the triangle to turn right here but it doesnt work when i mix it with position logic as seen in the first example.
Here is the code for reference
(function() {
var size = ($(window).height()/5)*4;
$("#intro-container").css('width',size);
$("#intro-canvas").css('width',size);
$("#intro-canvas").css('height',size);
var interval = window.setInterval(draw, 30);
var degrees = 0.0;
var offset = 20;
var rotate = 0;
var canvas = document.getElementById('intro-canvas');
var ctx = canvas.getContext('2d');
canvas.width = size;
canvas.height = size;
draw();
function draw() {
if (canvas.getContext) {
ctx.fillStyle="white";
ctx.strokeStyle="white";
ctx.clearRect(0, 0, size, size);
ctx.save();
ctx.translate(size/2, size/2);
ctx.rotate(-90 * Math.PI / 180);
ctx.beginPath();
ctx.lineWidth = size/8;
ctx.arc(0, 0, size/3, 0, rotate * Math.PI / 180);
//ctx.shadowBlur=1;
//ctx.shadowColor="black";
ctx.stroke();
ctx.restore();
ctx.beginPath();
ctx.save();
// moving logic
ctx.translate(size/2, size/2);
ctx.rotate(-Math.PI / 180 * -rotate+1);
ctx.translate(-size/3, -size/3);
// rotating logic
ctx.translate(size/2, size/2);
ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(size/6,0);
ctx.lineTo(0,size/6);
ctx.lineTo(0,0);
ctx.fill();
ctx.restore();
rotate += 1;
if(rotate > 360){
window.clearInterval(interval)
}
}
}
})();
I believe you are looking for this : http://jsfiddle.net/PDE85/12/
The rotation comes from, the rotate call which is unnecessary.
Plus you need an inverted triangle, hence the coordinates needed an update:
...
// ctx.rotate((rotate * Math.PI + 420) / 180);
ctx.moveTo(0,0);
ctx.lineTo(-size/6,0);
ctx.lineTo(0,-size/6);
...
I want to create a radial gradient stroke around a circle,something similar to
Here is what I've tried so far,
var canvas = document.getElementById("analog-stopwatch");
var context = canvas.getContext('2d');
var cx=200, cy=200, radius=100;
context.beginPath();
context.arc(cx, cy, radius, 0, 2 * Math.PI, false);
// I want the ring width to be 20
var radialGradient = context.createRadialGradient(cx, cy, radius, cx, cy, radius+20);
radialGradient.addColorStop(0, "black");
radialGradient.addColorStop(1, "blue");
//context.lineWidth = 20;
context.strokeStyle = radialGradient;
context.stroke();
One way I could think of is creating a circle with radial gradient & clipping it with a concurrent circle of lesser radius, but is there any simpler way to achieve the same ?
There is a simpler way: draw the outer circle in one direction (ex:clockwise), then draw the inner circle with the other : it will be subtracted from the first path.
Then fill the resulting path :
fiddle :
http://jsbin.com/UCiCaYOn/1/edit?js,output
var canvas = document.getElementById("analog-stopwatch");
var context = canvas.getContext('2d');
var cx=200, cy=200, radius=100;
context.beginPath();
// !!!!
context.arc(cx, cy, radius+30, 0, 2 * Math.PI, false); // !!!
context.arc(cx, cy, radius, 0, 2 * Math.PI, true); // !!!
// !!!!
var radialGradient = context.createRadialGradient(cx, cy, radius, cx, cy, radius+20);
radialGradient.addColorStop(0, "black");
radialGradient.addColorStop(1, "blue");
context.fillStyle = radialGradient;
context.fill();
context.closePath();
Basically all you need to do is to change the rectangle a bit for the gradient and you're good (no need to stroke two arcs etc.):
var lineWidth = 20;
var radialGradient = context.createRadialGradient(cx, cy, radius-lineWidth*0.5,
cx, cy, radius+lineWidth*0.5);
Now you can stroke:
Updated fiddle here