Jitter - Circular shape creation. Manipulation of dimensions?

geddy's icon

Hello all,

I am looking to create shapes that look similar to circles, but are not perfectly circular. I have attached an image of the idea that I am going for.

I have had a look at some work by Andrew Benson (Max patch attached) where he uses jit.mesh and jit.exp to create a near perfect circle. My only problem is that as he is using the mathematical equation of PI, the circle is too perfect.

I am looking to manipulate the shape of a circle and jit.nurbs appears to come slightly close to this, but it shows the vector lines running through the circle which is not what I want.

Can anyone recommend how I can go about this?

Max Patch
Copy patch and select New From Clipboard in Max.

IMG_0532-copy.jpg
jpg
metamax's icon
Max Patch
Copy patch and select New From Clipboard in Max.

geddy's icon

Thanks metamax for the patch, it is very similar to something that I am looking for. I have a few questions.

How does it draw the circle? In the patch that I presented I can see the circle is drawn by vertices using PI. I don't see such things in this patch? I can see that 'p ramp' uses PI to give the circle movement.

Jit.world and jit.gl.pix are new to me. I would appreciate some pointers.

Thanks,
Ged

Rob Ramirez's icon

nice one metamax!

metamax's icon

Ged,

The pi function in [p ramp] is almost entirely inconsequential. It just keeps the time parameter in a range that prevents the wiggle and wobble from getting too scrunchy. Turn off the ramp and manually change the time param to see what I mean. The actual circle is derived within [jit.gl.pix].

Here are a few things that might make it easier to understand..

Pix objects are all about creating rules that apply to every pixel. You can't tell one pixel to do one thing and another pixel to do something else because everything inside of [jit.gl.pix] is stateless. All you have is the context to which it is rendered. That means you have to come up with rules that use your screen dimensions and coords as your base values.

The most common approach is to draw a distinction from one corner of the screen to the other.. dividing the screen into two triangles. That is what is meant by "norm". The coordinates divided by the dimensions (cell/dim). Once you do that, it's game on. You have 2000 years of trigonometry at your disposal. It's also the most common way to draw gradients. All you need is out = norm and you have something pretty to look at.

The circularity in the above patch is introduced with the length operator. You can use out = length(norm); to generate a circular gradient centered at the screen origin (in our case, the upper left hand corner)....or out = length(1-norm); to invert the center to the lower right hand corner. Because norm is a two-element vector, the latter is the same as out = length(vec(1, 1)-norm); ... iow out = length(vec(1, 1,)- vec(norm.x, norm.y));

If we want to move the center of the circular gradient to the middle of the screen we can use out = length(vec(0.5, 0.5,)- vec(norm.x, norm.y)); which is the same as out = length(vec(0.5, 0.5)-norm); ... or better yet out = length(0.5-norm); So there you have it... the birth of your circle in just a handful of characters.

The smoothstep function is responsible for transforming the gradient into a "line" by redefining and interpolating where the gradient begins and ends. This is also how you antialias hard edges in various shapes.

example circle:

circle = length(0.5-norm);
out = smoothstep(0.3-0.01, 0.3, circle) - smoothstep(0.3, 0.3+0.01, circle);

To better understand what smoothstep is doing, it might help to contrast the above example with the following...

gradient = norm;
out = smoothstep(0.3-0.01, 0.3, gradient) - smoothstep(0.3, 0.3+0.01, gradient);

Okay, gotta stop here. You can replace the codebox contents in the original patch with any of the code snippets in this post to see the examples. fwiw, I have learned a ton porting various GL shaders to Gen - collecting hundreds of functions and code fragments along the way. The original patch I posted is cobbled/modded from (I think) a shadertoy shader - possibly glsandbox. I'm usually better about citing sources. Be sure to check out both sites for more examples. Good luck.

edit: okay.. the original shader is by "phil" https://www.shadertoy.com/view/ltBXRc

Roman Thilenius's icon

"It just keeps the time parameter in a range that prevents the wiggle and wobble from getting too scrunchy."

this sounds like what they teach at IRCAM on a daily basis.

metamax's icon

Roman, you're a funny mf'er. I never did learn to say 'scrunchy' in French.

geddy's icon

Thank you very much for your response, it has been quite an eye opener. You have persuaded me to upgrade to a subscription service of Max 7 to enable me to edit Gen. I have a few questions.

Pix objects are all about creating rules that apply to every pixel. You can’t tell one pixel to do one thing and another pixel to do something else because everything inside of [jit.gl.pix] is stateless. All you have is the context to which it is rendered. That means you have to come up with rules that use your screen dimensions and coords as your base values.

I already have a jit.window object with jit.sketch objects. With what you have said above, how would I go about porting this gradient circle into my jit.window object, along with all of my other drawings. As the circle is drawn by clever use of a colour gradients, is there any way to make the black shade transparent?

The circularity in the above patch is introduced with the length operator. You can use out = length(norm); to generate a circular gradient centered at the screen origin

I understand that the 'length()' operator returns the length of the vector, but how does this create a slight circle gradient?

Thanks,
Ged

metamax's icon

Ged, check out [jit.cellblock] to see the numerical values of a matrix and experiment with the effects of different operators such as norm, snorm, etc. Once you see how they affect the numbers in each cell/pixel, their ability to produce gradients will make more sense. Essentially, they generate a gradual progression from one value to another which is expressed visually as a gradient.

As for your other questions, I think someone else is probably better suited to answer. I am only just figuring out much of Jitter myself. But if you catch Rob Ramirez on a good day, he will show you how to do all of that AND make your morning coffee with just two or three objects.

Federico-AmazingMaxStuff's icon
Max Patch
Copy patch and select New From Clipboard in Max.

Great MetaMax! This is how you use this generated texture as a normal object. I made the black color transparent. Clever how you used the dot product to get a varying angle, I substituted this part with an atan2 function to get a similar result.

metamax's icon

Nice frederico! That is helpful. I am just getting started with a lot of this so your patch clarified several things. As I mentioned, my success has been limited to porting code from other sources, mashing up existing solutions, translating equations into genexpr, etc. so I can't really take credit for the ultra-slick use of the dot product function. I'm actually not entirely clear why it works. Your alternative makes sense though.. and it eliminates the 'scrunching' effect generated by param input that exceeds signed pi values.

Your youtube videos are excellent btw. I will be posting a bunch of my shader ports on a new thread.. please chime in so there is someone who actually knows what they are talking about.

Federico-AmazingMaxStuff's icon
Max Patch
Copy patch and select New From Clipboard in Max.

It will be a pleasure for me Metamax! Anyway the dot product of two normalized vectors is the cosine of the angle between them. You move the second vector with the time parameter, and then get the sine of this -1, 1 value. The code uses two different vectors so it gets a phase difference between the sines. Here's a little patch that shows the angle between vectors (right, green) and the cosine of this angle (left, red) using the dot product. You can see that when the angle is near 0 or PI you have a cosine of 1 (i used the absolute value to avoid minus), and when it's near PI/2 or (3/2)PI you get a cosine of 0. I hope this is clear enough. Thank you for the appreciation on my tuts!

Andrew Benson's icon

Going back to the OP, I wanted to toss in this pretty simple Gen patch that uses additive synthesis to make a wobbly circle. Enjoy:

Max Patch
Copy patch and select New From Clipboard in Max.