how would a go about drawing the inner blue slice of this circle, to simulate varying stroke weight.
I have tried a approach where i draw the stroke by drawing small circles on each angle of the circle and increasing the radius on certain parts of the circle. But this doesnt give the right result because the circle gets "pixelated" in the edge, and it skews the circle outwards.
There is no easy way to accomplish this. Part of the difficulty is that Canvas, the underlying technology that p5.js uses to draw graphics, doesn't support variable stroke weights either. In Scalable Vector Graphics, which has similar limitations, the best way to accomplish this would be to describe the shape as the outer perimeter, and the perimeter of the inner void, and then fill the shape without any stroke. I think Canvas would support this approach, but I don't think it can be done easily with p5.js because there's now way to jump to a new position when drawing bezier curves with beginShape()/bezierVertex(). However, one way you could do this in p5.js would be to fill the outer shape and then "remove" the inner void. If you want to draw this on top of other existing graphics then the best way is to draw this shape to a separate p5.Graphics object which you then draw to your main canvas with image():
let sprite;
function setup() {
createCanvas(windowWidth, windowHeight);
sprite = createGraphics(100, 100);
sprite.noStroke();
sprite.fill('black');
sprite.angleMode(DEGREES);
sprite.circle(50, 50, 100);
// switch to removing elements from the graphics
sprite.erase();
// Translate and rotate to match the shape you showed in your question
sprite.translate(50, 50);
sprite.rotate(-45);
// Remove a perfect semi circle from one half, producing regular 5px stroke circle
sprite.arc(0, 0, 90, 90, -90, 90);
// Remove a half-ellipse from the other side of the circle, but this time the
// height matches the previous arc, but the width is narrower.
// Note: the angles for this arc overlap the previous removal by a few degrees
// to prevent there from being a visible seam in between the two removed shapes.
sprite.arc(0, 0, 70, 90, 85, 275, OPEN);
}
function draw() {
background('lightgray');
image(sprite, mouseX - 50, mouseY - 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Related
What value fed to strokeWidth() will give a stroke width of one pixel regardless of the current scale() setting?
I think strokeWeight(0) should work. Here is an example:
void setup() {
size(100,100);
noFill();
scale(10);
// 1st square, stroke will be 10 pixels
translate(3,3);
strokeWeight(1);
beginShape();
vertex(-1.0, -1.0);
vertex(-1.0, 1.0);
vertex( 1.0, 1.0);
vertex( 1.0, -1.0);
endShape(CLOSE);
// 2nd square, stroke will be 1 pixel
translate(3,3);
strokeWeight(0);
beginShape();
vertex(-1.0, -1.0);
vertex(-1.0, 1.0);
vertex( 1.0, 1.0);
vertex( 1.0, -1.0);
endShape(CLOSE);
}
Kevin did offer a couple of good approaches.
Your question doesn't make it clear what level of comfort you have with the language. My assumption (and I could be wrong) is that the layers approach isn't clear as you might have not used PGraphics before.
However, this option Kevin provided is simple and straight forward:
multiplying the coordinates manually
Notice most drawing functions take not only the coordinates, but also dimensions ?
Don't use scale(), but keep track of a multiplier floating point variable that you use for the shape dimensions. Manually scale the dimensions of each shape:
void draw(){
//map mouseX to a scale between 10% and 300%
float scale = map(constrain(mouseX,0,width),0,width,0.1,3.0);
background(255);
//scale the shape dimensions, without using scale()
ellipse(50,50, 30 * scale, 30 * scale);
}
You can run this as a demo bellow:
function setup(){
createCanvas(100,100);
}
function draw(){
//map mouseX to a scale between 10% and 300%
var scale = map(constrain(mouseX,0,width),0,width,0.1,3.0);
background(200);
//scale the shape dimensions, without using scale()
ellipse(50,50, 30 * scale, 30 * scale);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.min.js"></script>
Another answer is in the question itself: what value would you feed to strokeWidth() ? If scale() is making the stroke bigger, but you want to keep it's appearance the same, that means you need to use a smaller stroke weight as scale increases: the thickness is inversely proportional to the scale:
void draw(){
//map mouseX to a scale between 10% and 300%
float scale = map(constrain(mouseX,0,width),0,width,0.1,3.0);
background(255);
translate(50,50);
scale(scale);
strokeWeight(1/scale);
//scaled shape, same appearing stroke, just smaller in value as scale increases
ellipse(0,0, 30, 30);
}
You can run this bellow:
function setup(){
createCanvas(100,100);
}
function draw(){
//map mouseX to a scale between 10% and 300%
var scaleValue = map(constrain(mouseX,0,width),0,width,0.1,3.0);
background(240);
translate(50,50);
scale(scaleValue);
strokeWeight(1/scaleValue);
//scale the shape dimensions, without using scale()
ellipse(0,0, 30, 30);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.7/p5.min.js"></script>
Kevin was patient, not only to answer your question, but also your comments, being generous with his time. You need to be patient to carefully read and understand the answers provided. Try it on your own then come back with specific questions on clarifications if that's the case. It's the best way to learn.
Simply asking "how do I do this ?" without showing what you're tried and what your thinking behind the problem is, expecting a snippet to copy/paste will not get your very far and this is not what stackoverflow is about.
You'll have way more to gain by learning, using the available documentation and especially thinking about the problem on your own first. You might not crack the problem at the first go (I know I certainly don't), but reasoning about it and viewing it from different angles will get your gears going.
Always be patient, it will serve you well on the long run, regardless of the situation.
Update Perhaps you mean by
What value fed to strokeWidth() will give a stroke width of one pixel regardless of the current scale() setting?
is how can you draw without anti-aliasing ?
If so, you can disable smoothing via a line: calling noSmooth(); once in setup(). Try it with the example code above.
None.
The whole point of scale() is that it, well, scales everything.
You might want to draw things in layers: draw one scaled layer, and one unscaled layer that contains the single-pixel-width lines. Then combine those layers.
That won't work if you need your layers to be mixed, such as an unscaled line on top of a scaled shape, on top of another scaled line. In that case you'll just have to unscale before drawing your lines, then scale again to draw your shapes.
I'm generating a falloff texture by adding gradient part to the white image I have. If implementation is relevant, I'm doing it with HTML5 canvas. For some reason I'm getting weird ray like while artifacts where it's supposed to be gradient smooth. I couldn't find any way to take care of that on implementation level, so I have to get rid of them after generating. Question is, if I have per pixel access to the image, how do I recognize those white pixels and replace with pixels to keep the gradient smooth?
The rays are caused by overlaps and rounding errors. They can be removed or at least reduced by using a Gaussian blur filter (which in effect act as a low-pass filter).
To avoid new problems such as the inner shape's black pixels leaking into the gradient, I'd suggest these steps:
Fill inner shape in the same color as the start color of the gradient.
Produce gradients
Apply Gaussian blur using either the filter property of context (f.ex context.filter = "blur(7px)";, reset by setting it to none), or by using a manual implementation
Redraw the inner shape in the destination color.
Now it's a simple matter of experimenting with the blur radius to find an optimal value. Note that blurring will add to the gradient so you might want to link the two so that the radius of the gradient is reduced when blur radius is increased.
Pro-tip: you can also drop the gradient production all together and simply make the glow effect using Gaussian blur (run example below).
var ctx = c.getContext("2d");
ctx.moveTo(300, 50);
ctx.quadraticCurveTo(325, 300, 550, 550);
ctx.quadraticCurveTo(300, 500, 50, 550);
ctx.quadraticCurveTo(250, 300, 300, 50);
ctx.closePath();
// blur next drawings
ctx.filter = "blur(20px)"; // glow radius
// produce a full base using fill and heavy stroke
ctx.fillStyle = ctx.strokeStyle = "#fff";
ctx.fill();
ctx.lineWidth = 40; // thicker = stronger spread
ctx.stroke();
// final, fill center in destination color
ctx.filter = "none";
ctx.fillStyle = "#000";
ctx.fill();
#c {background:#000}
<canvas id=c width=600 height=600></canvas>
I want to run a for loop that will have a line with a color of brown. This line will get smaller and smaller, but not too small.
The final image will look like this, but with the table top being colored in brown:
//Back wall
fill(102, 102, 102);
rect(50,50,300,300);
//Top Left Corner
line(50,50,0,0);
//Top Right Corner
line(350,50,400,0);
//Bottom Left Corner
line(350,350,400,400);
//Bottom Riight Corner
line(50,350,0,400);
//Table
//Top Left
fill(48, 17, 0);
rect(163,312,3,38);
//Top Right
fill(48, 17, 0);
rect(230,312,3,38);
//Mesa
fill(48, 17, 0);
rect(126,322,142,5);
//Right Side
line(126,322,168,312);
//Top Side
line(234,312,168,312);
//Right Side
line(269,322,232,312);
//Bottom Left Leg
rect(126,327,5,41);
line(126,368,126,322);
//Bottom Right Leg
rect(263,327,5,41);
line(269,368,268,322);
I have tried this for loop:
for(var x = 200; x > 100; x--){
stroke(61, 34, 0);
line(x,200,x,200);
}
The x value will decrease until x = 100. But, it is not showing the the line getting smaller EVEN after making sure the background(); is out of the loop.
P.S. The pieces of code given are separate.
You're only changing the x coordinate of the lines you're drawing, so the line is moving horizontally, not vertically.
If you want the line to move vertically (to color in the table) and horizontally (to make it smaller as it gets "further away"), you'll have to change both the x and y values you pass into the line() function.
But you're making this harder than it needs to be. There is no reason for you to draw a bunch of lines to get this shape. Just use the beginShape() function to draw the polygon directly. Something like this:
beginShape();
vertex(100, 100); //upper-left
vertex(200, 100); //upper-right
vertex(250, 200); //lower-right
vertex(50, 200); //lower=left
endShape(CLOSE);
Note that this is just an example, and you'll have to play around with the values to draw it in the correct location. But the point is that you don't have to use a for loop to draw lines just to draw a polygon.
Since you're trying to draw a 3D scene, you should also note that you can simply use 3D coordinates along with the vertex() function to draw in 3D. No need to try to force the perspective yourself.
I am trying to create horizontal cylinder. I have found the below link which is used for creating vertical cylinder.
How to draw a cylinder on html5 canvas
can someone tell me the changes to create a horizontal cylinder?
In general, if you have a drawing you want to rotate, you can use transforms.
Transforms move, rotate (and scale) the canvas without the need to recode your desired drawing.
A Demo: http://jsfiddle.net/m1erickson/RU26r/
This transform will rotate your original drawing 90 degrees:
drawRotatedCylinder(100,100,50,30,90);
function drawRotatedCylinder(x,y,w,h,degreeAngle){
// save the context in its unrotated state
context.save();
// translate to the rotation point
// your object will rotate around the rotation point
// so if you want it to rotate from the center then
// translate to x+width/2, y+height/2
context.translate(x+w/2,y+h/2);
// rotate by 90 degrees
// rotate() takes radians, so convert to radians
// with radians==degrees*Math.PI/180
context.rotate(degreeAngle*Math.PI/180);
// draw your original shape--no recoding required!
drawCylinder(-w/2,-h/2,w,h);
// restore the context to its untransformed state
context.restore();
}
Please check this neat piece of code I found:
glEnable(GL_LINE_SMOOTH);
glColor4ub(0, 0, 0, 150);
mmDrawCircle( ccp(100, 100), 20, 0, 50, NO);
glLineWidth(40);
ccDrawLine(ccp(100, 100), ccp(100 + 100, 100));
mmDrawCircle( ccp(100+100, 100), 20, 0, 50, NO);
where mmDrawCircle and ccDrawLine just draws these shapes [FILLED] somehow... (ccp means a point with the given x, y coordinates respectively).
My problem .... Yes, you guessed it, The line overlaps with the circle, and both are translucent (semi transparent). So, the final shape is there, but the overlapping part becomes darker and the overall shape looks ugly.. i.e, I would be fine if I was drawing with 255 alpha.
Is there a way to tell OpenGL to render one of the shapes in the overlapping parts??
(The shape is obviously a rectangle with rounded edges .. half-circles..)
You could turn on GL_DEPTH_TEST and render the line first and a little closer to the camera. When you then render the circle below, the fragments of the line won't be touched.
(You can also use the stencil buffer for an effect like this).
Note that this might still look ugly. If you want to use anti-aliasing you should think quite hard on which blending modes you apply and in what order you render the primitives.