I'm trying to draw an arc on a canvas in Javascript, but I want to get rid of the line that Javascript automatically draws. With the line being drawn, there are two problems: a) fill() malfunctions b) It looks like a bow
Here's an image of what I have and what I want: The left is what I have in JS, the right is what I have in Java. The Java implementation is fairly inefficient and I would like to use the functions already available.
Here are the relevant JS functions:
function pointAt(center, l, theta){
return {
x : Math.sin(theta) * l + center.x,
y : Math.cos(theta) * l + center.y
};
}
function drawArc(ctx, center, l, theta, sweep, label, thickness){
var p0 = pointAt(center, l, theta);
var p1 = pointAt(center, l + thickness, theta);
var p2 = pointAt(center, l + thickness, theta + sweep);
var p3 = pointAt(center, l, theta + sweep);
ctx.fillStyle = "red";
drawPoint(ctx, p0);
ctx.fillStyle = "blue";
drawPoint(ctx, p1);
ctx.fillStyle = "green";
drawPoint(ctx, p2);
ctx.fillStyle = "yellow";
drawPoint(ctx, p3);
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.arc(center.x, center.y, l + thickness, theta, theta + sweep, false);
ctx.moveTo(p2.x, p2.y);
ctx.lineTo(p3.x, p3.y);
ctx.arc(center.x, center.y, l, theta + sweep, theta, true)
ctx.closePath();
ctx.fill();
ctx.stroke();
//TODO: draw label!!!
}
function drawPoint(ctx, p){
ctx.fillRect(p.x - 4, p.y - 4, 8, 8);
}
Here's a working implementation in Java:
private static void drawArc(Graphics g, Point center, int l, double theta, double sweep, String label, int thickness){
for (double i = 0; i < thickness; i+=.5) { //hardcoded
Point last = pointAt(center, l + i, theta);
for(double t = theta; t < theta + sweep; t+=.01){
Point cur = pointAt(center, l + i, t);
g.drawLine(last.x, last.y, cur.x, cur.y);
last = cur;
}
}
double t = theta + sweep / 2;
Point p = pointAt(center, l + 15, t);
int size = g.getFontMetrics().stringWidth(label);
BufferedImage img = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.RED);
double rot = (Math.PI / 2 - t);
// System.out.println(rot / Math.PI + "PI");
boolean b = true;
if(rot < -Math.PI / 2){
rot += Math.PI;
b = false;
}
g2.setTransform(AffineTransform.getRotateInstance(rot, size, size));
g2.drawString(label, size, size);
g2.dispose();
if(!b)
size *= 1.75; //magic value or hardcoded???
g.drawImage(img, p.x - size, p.y - size, null);
}
So my question: How do I turn the banana into a thick arc?
I'm trying to draw an arc on a canvas in Javascript, but I want to get rid of the line that Javascript automatically draws. With the line being drawn, there are two problems: a) fill() malfunctions b) It looks like a bow
Here's an image of what I have and what I want: The left is what I have in JS, the right is what I have in Java. The Java implementation is fairly inefficient and I would like to use the functions already available.
Here are the relevant JS functions:
function pointAt(center, l, theta){
return {
x : Math.sin(theta) * l + center.x,
y : Math.cos(theta) * l + center.y
};
}
function drawArc(ctx, center, l, theta, sweep, label, thickness){
var p0 = pointAt(center, l, theta);
var p1 = pointAt(center, l + thickness, theta);
var p2 = pointAt(center, l + thickness, theta + sweep);
var p3 = pointAt(center, l, theta + sweep);
ctx.fillStyle = "red";
drawPoint(ctx, p0);
ctx.fillStyle = "blue";
drawPoint(ctx, p1);
ctx.fillStyle = "green";
drawPoint(ctx, p2);
ctx.fillStyle = "yellow";
drawPoint(ctx, p3);
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.lineTo(p1.x, p1.y);
ctx.arc(center.x, center.y, l + thickness, theta, theta + sweep, false);
ctx.moveTo(p2.x, p2.y);
ctx.lineTo(p3.x, p3.y);
ctx.arc(center.x, center.y, l, theta + sweep, theta, true)
ctx.closePath();
ctx.fill();
ctx.stroke();
//TODO: draw label!!!
}
function drawPoint(ctx, p){
ctx.fillRect(p.x - 4, p.y - 4, 8, 8);
}
Here's a working implementation in Java:
private static void drawArc(Graphics g, Point center, int l, double theta, double sweep, String label, int thickness){
for (double i = 0; i < thickness; i+=.5) { //hardcoded
Point last = pointAt(center, l + i, theta);
for(double t = theta; t < theta + sweep; t+=.01){
Point cur = pointAt(center, l + i, t);
g.drawLine(last.x, last.y, cur.x, cur.y);
last = cur;
}
}
double t = theta + sweep / 2;
Point p = pointAt(center, l + 15, t);
int size = g.getFontMetrics().stringWidth(label);
BufferedImage img = new BufferedImage(size * 2, size * 2, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.RED);
double rot = (Math.PI / 2 - t);
// System.out.println(rot / Math.PI + "PI");
boolean b = true;
if(rot < -Math.PI / 2){
rot += Math.PI;
b = false;
}
g2.setTransform(AffineTransform.getRotateInstance(rot, size, size));
g2.drawString(label, size, size);
g2.dispose();
if(!b)
size *= 1.75; //magic value or hardcoded???
g.drawImage(img, p.x - size, p.y - size, null);
}
So my question: How do I turn the banana into a thick arc?
Share Improve this question asked Jun 29, 2011 at 18:29 Ryan AmosRyan Amos 5,4524 gold badges38 silver badges56 bronze badges 2- what are you calling drawArc with? – Simon Sarris Commented Jun 29, 2011 at 18:54
- The JS implementation? Ctx is a 2d canvas context, center is the center point of the circle, l is the radius, theta is the start angle, sweep is the angle covered (theta + sweep is the end angle), label is irrelevant, thickness is the thickness of the arc. – Ryan Amos Commented Jun 29, 2011 at 18:56
3 Answers
Reset to default 3Just draw the outer arc clockwise, then the inner arc counter-clockwise. The canvas will automatically close off the ends with straight lines for you.
var c = document.getElementById('c');
var ctx = c.getContext('2d');
ctx.beginPath();
ctx.arc(0,0,50,0,-Math.PI * 0.5, false);
ctx.arc(0,0,25,-Math.PI * 0.5,0, true);
ctx.fillStyle = '#0f0';
ctx.fill();
ctx.strokeStyle = '#0f0';
ctx.stroke();
ctx.closePath();
http://jsfiddle/tMEdq/
Also, here are good references: http://diveintohtml5.ep.io/canvas.html#divingin http://www.nihilogic.dk/labs/canvas_sheet/HTML5_Canvas_Cheat_Sheet.png
You have way too many drawing calls too - this is all you need:
ctx.beginPath();
ctx.arc(center.x, center.y, l + thickness, theta, theta + sweep, false);
ctx.arc(center.x, center.y, l, theta + sweep, theta, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
Just draw the first arc, which will automatically get joined to the next, and then close the path to plete the loop.
You also have your sin
and cos
calls the wrong way around in pointAt()
- sin()
should alter the Y axis coordinate, not the X axis!
Working demo at http://jsfiddle/alnitak/zChSe/
Just do:
function drawArc(ctx, center, l, theta, sweep, label, thickness){
var p0 = pointAt(center, l, theta);
var p1 = pointAt(center, l + thickness, theta);
var p2 = pointAt(center, l + thickness, theta + sweep);
var p3 = pointAt(center, l, theta + sweep);
ctx.fillStyle = "red";
drawPoint(ctx, p0);
ctx.fillStyle = "blue";
drawPoint(ctx, p1);
ctx.fillStyle = "green";
drawPoint(ctx, p2);
ctx.fillStyle = "yellow";
drawPoint(ctx, p3);
ctx.beginPath();
//ctx.moveTo(p0.x, p0.y);
//ctx.lineTo(p1.x, p1.y);
ctx.arc(center.x, center.y, l + thickness, theta, theta + sweep, false);
//ctx.moveTo(p2.x, p2.y);
//ctx.lineTo(p3.x, p3.y);
ctx.arc(center.x, center.y, l, theta + sweep, theta, true);
ctx.closePath();
ctx.fill();
ctx.stroke();
//TODO: draw label!!!
}
The "lateral" lines are implict placed, see http://www.whatwg/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc.