Variable Stroke Width

jersmi's picture

How might one go about drawing onscreen and vary "stroke width", ala yellowtail? Here's the catch -- can't use accumulator paint techniques because I want to use something like cwright's draw in space, which requires a structure output for x,y,z. I was thinking it might be possible to add line width as another structure member, but I don't quite know how to do that or if it's possible with GL Line Structure (or Triangle, maybe Point, too, I guess).

dust's picture
Re: Variable Stroke Width

here is a patch that utilizes a gl line structure to draw with. it also shows how to use a gl line structure to draw without having the last end point connect to the new beginning point meaning when you draw a new line it starts where your new point begins instead of connecting the previous etc...

in addition this also shows how you can get variable width strokes out of the gl line by using a core image filter. this acts more like an ink bleeding into paper as opposed to just changing the line width based on velocity or something. using the ci filter makes this act more like ink or paint. if you adjust the radius you will get a larger variable width.

this was an accident as i was trying to create a smudge like effect but you could certainly just calculate velocity of a stroke based on the time it takes between start and end strokes then adjust the gl line width accordingly.

PreviewAttachmentSize
doodlerGL.qtz20.84 KB

dust's picture
Re: Variable Stroke Width

oops sorry just noticed you where not looking for a accum solution. hmmm calculating velocity for variable width isn't a problem but to get that variable width without accum your going to have put the gl line inside an iterator and use velocity for each line segment or iteration.

meaning as it is now if you change the width it gets applied to the line as a whole but if you are using the line in an iterator and adjusting the width for each segment or iteration you should get a variable width segment to segment.

to get your average mean of xy or z acceleration you would want to subtract first point from last point divided by delta time or the time in between strokes in seconds. just do that for xy and z then add em all together and divide by 3 then you will get an average acceleration value that can be used to calculate your variable width no matter what direction your drawing in.

also you might want to reset your queue when you are done drawing a line so that when you make a new stroke it starts with the new position. you might want at this point save the previous structure down to file then use that file to draw the previous line, kind of like how accum would work.

meaning once you reset it will clear the line structure enabling you to draw a fresh line from a new start point but the old will get cleared. so if your not using feedback or accum to keep the old line the screen you will have by saved structure.

still to get the variable line your going to want to use an iterator. another option is to a ribbon style line like the mouse ribbon example.

don't know if any of this makes since to you or will help you but you can attain variable line widths with filters like the first example posted.

(edit)

it helps to normalize your coordinates 0->1 to calculate acceleration as qc units mess things up by going negative.

just convert units to pixels then divide x by width do the same for height and depth this will give you your coordinates in 0->1. then do the above mentioned last point-first point/delta time at this point you might want do some other math to get the variable width into a range you want. feeding the acceleration values into an interpolation external time should get you a width to deal with. (end edit)

gtoledo3's picture
Re: Variable Stroke Width

GL tools isn't really needed to draw lines; it is just convenient. A line drawer can be made using a line patch and an iterator. So, you just make a queue for line thickness as well, color, or "whatever".

I already have a patch that does this by using entirely stock objects and have meant to post it. I'm helping a friend move today, so I probably will not post it until later tonight or tomorrow.

Btw, I don't think cwright's patch did what it was supposed to do in regards to inverse rotation. I believe it revealed a bug w gl tools that never got fixed before he went to apple (rotation matrix calculation). I may be wrong about that though.

....actually, I may have posted it. Maybe do a search for "stock line drawer" here, or maybe in my box account.

franz's picture
Re: Variable Stroke Width

do you want straight lines only ? if not, why not use yellowtail ? (source code is available, so it is easy to modify according to your needs).

GL Line structure probably uses vertex array for enhanced speed, so there's only one GL call for line width before the other GL commands.

One possible workaround, if you want to use only, say 3, different line widths, is to separate you queue based on line width, and have 3 GLline Structure render your lines.

gtoledo3's picture
Re: Variable Stroke Width

This is a "stock line maker" that uses the normal line patch that QC provides to let you draw freehand in x/y/z, with a 3D transform that allows the line drawing to be rotated.

There is a control for thickness. Notice that you can draw a bunch of thick lines, change line weight, then draw thin lines, change color, etc. Dust explains some concepts behind doing velocity based thickness, which might be helpful if you wish to go that route. In addition, you can do this with quads, which can give a more pen like effect. I have a file that is setup for that, but can't access it at the moment.

Let me know if you have any crashes when you start drawing. Feeding a queue with a nan value allows you to draw non-contiquous line strips, but I believe that if a composition starts and a nan value is the first loaded, it may cause QC to crash. This shouldn't give that problem, but let me know if it does.

Also, this principle can be extended to any object. Actually, the very first thing I did like this was to draw lines of animated md2 birds, or any kind of 3D object - a 3D object pen, if you will. Later on I went ahead and did this.

PreviewAttachmentSize
scribbles.png
scribbles.png49.69 KB
Stock Patch Linemaker_GT-flattened.qtz48.4 KB

gtoledo3's picture
Re: Variable Stroke Width

You know, I was thinking about this as I was doing some errands, and it would probably make sense to not publish iterations, but to have iteration be a function of the queue count by using a structure count off of a queue output. Iteration could go up into oblivion though, so that's a downside. Maybe it could be tied to structure count of the queue, with some reasonable range employed to cap possible amount of iteration.

jersmi's picture
Re: Variable Stroke Width

Thanks for the excellent input, I'm processing the info. In the meantime here's what I have. Disclaimer: there is some messy work in progress in the posted comp, but I commented the relevant parts inside the patch in green. Anyway, it's working.

One problem: I seem to be getting depth buffer blinking/glitching intermittently since I added this new stuff. Any ideas why? It stops when I turn off depth buffer in the RII settings but then there's no depth...

Dust, I need to have a look at that CI filter, and thanks for the accel info. Good stuff.

Franz, just using a few Line (or Point) Structure patches sounds promising for my needs. The structure patches certainly appear to allow for larger queues than the iterated "regular" GL point/line/quad I have here. Also, I looked around for the code to yellowtail a while back but couldn't at the time pull out anything useful. I'll keep at it.

GT, I think I'm on a similar track here to your posted comp?

Another question, this pulled out of Chris' javascript -- how can I implement array shift here to put a limit on queue size and get the drawn stuff to look like its tail is chasing its head?

function (__structure points) main (__number inputX, __number inputY, __number inputZ, __boolean draw, __boolean reset, __number xTheta, __number yTheta, __number zTheta, __number lineWidth, __index queueSize)
{
   if(reset || result.points == undefined)
      result.points = new Array;
   if(draw)
   {
      var point = new Array;
      point[0] = inputX;
      point[1] = inputY;
      point[2] = inputZ;
      point[3] = lineWidth;
      transformPoint(point, xTheta*Math.PI/180, yTheta*Math.PI/180, zTheta*Math.PI/180);
      result.points.push(point);
 
   /*   unresolved shift array when queueSize fills:   
      if (points.length > queueSize) {
      points.shift();
      result.points = points;
      }
   */
   }
 
      return result;
}
PreviewAttachmentSize
Draw_in_Space_CW4.qtz404.13 KB

jersmi's picture
Re: Variable Stroke Width

Here's a link to a javascript implementation of yellowtail, for the record. I'm not ready to tackle this in QC, but maybe there's some details regarding stroke width and animation that could be discussed? It's such a classic...

jersmi's picture
Re: Variable Stroke Width

How would one get the method out of the source code and into a useable form in js or built in patches?

cybero's picture
Re: Variable Stroke Width

Cheers for the reminder on the source code link, vade.

Just downloaded the source, opened in XCode, selected DNYellowtail in the Targets, clicked the i for Information icon, set the Build rules to Native Architecture of Build Machine & Base SDK to 10.6, flagged build active architecture only, clicked the build button, installed with Kineme Plugin Helper and Yellowtail is running sweetly in the 64 bit QC 4.0.

Welcome back Yellowtail

:-)

PreviewAttachmentSize
yellowtail.jpg
yellowtail.jpg68.49 KB

gtoledo3's picture
Re: Variable Stroke Width

When you use it, test it out in an RII and see if you get consistent results. I've had it where background/line color go "wrong" with no rhyme or reason, in SL. I was noticing this on a project a few months ago.

cybero's picture
Re: Variable Stroke Width

Haven't managed to reproduce that, works AOK in an RII for me :-), might be to do with the mask image or if you have turned on the Particle System [maybe, just a guess though]. The results achieved, though slightly different were broadly similar and consistent with the original.

gtoledo3's picture
Re: Variable Stroke Width

It was a really complex setup/composition, so it may have been some problem caused by an obscure combo of scenarios or effects downstream.

cybero's picture
Re: Variable Stroke Width

Just what I was thinking 'cos I'd stumbled up some of my yellowtail derived's misbehaving the same way - iterators also involved.

This is the RII result below of placing the full default patches in the original example into an RII and onto a Billboard.

PreviewAttachmentSize
yellowtailrii.jpg
yellowtailrii.jpg70.83 KB

cybero's picture
Re: Variable Stroke Width

Had another thought.

Maybe to do also in conjunction with the version of Yellowtail you compile.

The original had an issue with mouse co-ordinates that remained unresolved in the DNYellowtailRender.m and a solution was posted by vade but not updated in that .m file.

update(port, (self.inputX+1)*self.imageBounds.size.width*0.5, (self.inputY+1)*self.imageBounds.size.height*0.5, self.inputPressure);
 
on line 42 in DNYellowtailRender.m

& what do you know, whilst trying out that 'instant' theory, I had occasion to run the original and the RII concurrently, serendipitously reproducing the previously irreproducible problem.

Is this the sort of thing you were mentioning?

Ironically enough it isn't happening in the RII, is it, see Window titles for proof, but in the original output test. Running concurrently does it. version of .m actually makes no difference. I have tried winding back to the co-ordinate issue version as it happens.

Your curiosity today has been answered by serendipitous chance in pursuance of a wayward theory.

Is that computer science ? •~ :-)

PreviewAttachmentSize
yellowtailriindorig-bg-problem.jpg
yellowtailriindorig-bg-problem.jpg73.88 KB

usefuldesign.au's picture
Re: Variable Stroke Width

cybero wrote:
Is that computer science ? •~ :-)

More natural law I think. Well done — you still get the credit, cybero.

cybero's picture
Re: Variable Stroke Width

LOL.

I was thinking of the matter of seeking truth even if it involves disproving one's ancient or instant theories ..

gtoledo3's picture
Re: Variable Stroke Width

Yep, that's the problem I was seeing. I'm really glad the designer didn't like the flying sperm look, ultimately. As a matter of fact, anything spermy.... usually a "no-go".

What was answered though? Did you come up with a solution (I'm in coffee drinking mode... I feel like I may have glossed over something)? I already knew it was screwy...

gtoledo3's picture
Re: Variable Stroke Width

Also, just because it isn't happening in an RII for you now doesn't mean it "can't" (but, maybe it "can't" on your machine?). When I fired it up earlier this morning, it did what I'm talking about the first time, with a scene in a RII. Then, I resized the window, and that alone got it rendering correctly. After that, it's worked ok. I feel trepidation about using it for anything as is.

I feel like I might have had some problems like that with it in Leopard, that may have revealed themselves in less obvious ways.

mradcliffe's picture
Re: Variable Stroke Width

Subscribe. (ignore this)

usefuldesign.au's picture
Re: Variable Stroke Width

cybero wrote:
I was thinking of the matter of seeking truth even if it involves disproving one's ancient or instant theories ..
Even so. :-)

cybero's picture
Re: Variable Stroke Width

For me, what was answered was how to improve the mouse co-ordination of and make a 10.6 conformant Yellowtail plugin that didn't crash me out.

Regards your experience to date with such problems, I'd not covered all the available scenarios before posting up my findings, but I was equally surprised to see it happen.

More a matter of report and confirmation and possible clue to what can cause this, with a concurrent side thread about the plugin compilation.

I discovered how the odd rendering issue happened for me. The only solution that works for me is to attach the clouds image to the image input on the top level sprite, then it masks up with the yellowtail image correctly, even though it does function correctly, on its own without an RII version concurrently open.

With an RII version concurrently open and no input on the top level sprite image input , the effect is reproducible, as is the solving of the issue with putting the clouds image to the Sprite image input port, actually, any image seems to do, just gives that sprite something to play with and have masked.

In fact scratch that, any time I add another open version viewer window in live rendering mode, I get a similar rendering failure and the workaround that I proposed being given to the original so it could render right with an RII version, fails to mask nicely when another original version of the test file is opened and rendered concurrently.

Definitely works in a single standalone render at present & who knows, I might even discover that it messes up when used in isolation too. Good fun though.

Also what about putting the Yellowtail directly to the top Sprite through the image input and no top Sprite masking?

Tried and with three including one RII, one original and one with proxy to image and image to mask on the Top Sprite, works with modified original and RII version, but with the three running, similar rendering problems.

Works well enough alone [ I think ]

jersmi's picture
Re: Variable Stroke Width

The yellowtail plugin has always worked well for me. How does yellowtail address the issue of stroke width when a line is drawn? Is it manipulation of quads? It would be amazing to truly bring in some of the yellowtail method to a QC javascript patch. Maybe using a Quad Structure patch would allow manipulation of the stroke width in a more deliberate manner, since each corner can be addressed?

Reasons I'm using Chris' javascript:

  1. I have made it so with mouse down I can draw on the 2D viewer then what is drawn gets animated through 3D space until the next drawn thing. His method works so well for this.

  2. The js works by manipulating the matrix. I like this method.

  3. As it turns out (as franz pointed out) the GL Line/Point/Triangle/Quad Structure patches aren't as costly as iterated single object patches.

(Edit: I'm not sure how I feel about having that numbered list auto-formatted...)

And, GT, mouse drawing (and tracking using openCV) using Kineme GL Inverse Rotation works perfectly for me with the draw in space patch.

gtoledo3's picture
Re: Variable Stroke Width

(good to know about the inverse rotation; I still didn't think it worked correctly because of a composition I was working on that used sprites with it... this encourages me to look again).

On my machine, I don't get any performance hit from the iteration (it works absurdly fast, to my surprise), and I just like to do things stock if possible. I can imagine the iteration might be costly depending on OS or maybe system.