Question about jit.gl.textmult
I have tested out jit.gl.textmult, which is an extraordinary text collage tool, but I am trying to do something very specific, essentially break up words in a sentence or letters in a word, I couldn’t figure out how to get from the help patch to where I want to go. Big thanks to Rob who is always there when you need help!!
Thanks, Randall
jit.gl.textmult basically creates individual jit.gl.text for each item of a list prepended by the word "text". So if you send "text hello world" it will create two instances of jit.gl.text, one for "hello" and the other for "world".
The thing is, you need to manually set the position of each jit.gl.text, and you have no idea of the size of the rendered word's mesh. The only way I know to get that size is to use [jit.gl.text @mode 3D] and send a "getbounds" message to it. From that you could compute the word's size, and so be able to place each word relatively to each other to recreate your sentence. However, from my understanding, jit.gl.textmult doesn't allow to send messages to its inner jit.gl.text. So what you ask for would require to either:
- Modify the existing js code of jit.gl.text so that you can get the size of each word and place them accordingly
- Recreate your own jit.gl.textmult tailored for your very own use (you have multiple ways of programmatically creating several objects)
- Use a completely different approach such as jit.gl.sketch in combination with jit.gl.text2D or jit.gl.text3D, or if you want to stay in 2D, you could use the MGraphics API which allows to draw text and has the convenient text_measure(text) method which returns the computed text size with defined font, and font size.
Thanks TFL!
Yes, I need to interact with each jit.gl.text independently so I can control all the various parameters for each. I'm interested in being able to pull a word or words apart by the letters, and then put it all back together again. In other words, a collage of letters might enter in, randomly distributed, but then coalesce to form the original word(s). Or perhaps with a longer text, the words enter individually, and then coalesce into a sentence. This would require complete control over each word/letter with all the various jit.gl.text parameters. Maybe its too much to ask, but then it seems like anything is possible with jitter.
I think the idea of creating a custom jit.gl.textmult is the best idea, though I'm not sure I am have the chops to do that. Perhaps if I had a simple example I could take it from there...
If you're comfortable with js, modifying the existing jit.g.textmult should not be too hard.
For the first step (getting size of each jit.gl.text), I would add
this.GetBounds = function()
{
return this.glText.bounds;
}
into the TextObj() function, and modify the SetTextAndDrawBang() function to call g_textObj.GetBounds for each created g_textObj. From here, depending on how you want to handle each words position, you could compute each word's size from its bounds and store that size either in js or output it in Max using secondary outlet.
Another simpler solution could be to use a mono font that that any character will always be the same width, so the letter/word position calculation could be pretty straightforward.
Thanks again! Unfortunately my javascript skills are fairly non-existent, however if there is a straightforward Jitter solution I could mostly likely implement that.
I managed to modify jit.gl.textmult to get each inner jit.gl.text bounds. From that I managed to get a matrix with as much cells as there is words, and each cell is the dimension of the word.
Now, the hard part is to set the position of each of these words accordingly. Hard to do in jitter, as it is a more sequential problem (you want to accumulate the x dimension of each previous word, + adding a space between each of them, and the result of that big sum is the position of the current word).
I tried something for the positionning (see below) but it doesn't work as expected, don't know why (maybe i get the sampling coordinates wrong?). Anyway, you might be able to do something from that starting point! Curious to see the/any result!
//Genexpr code to compute position of each word from a matrix with dimensions of each of them.
//DOES NOT WORK AS EXPECTED - here just to get the idea
cursor = 0;
if (cell.x >0) {
for (i=0; i<cell.x; i+=1) {
prev_item_coord = vec( i/dim.x , 0 );
cursor += sample(in1, prev_item_coord).x;
cursor += space;
}
}
out1 = vec(cursor, 0, 0);
From the beginning I speak about words, but the logic is the same if you want your words to get independant letters.
Great work TFL! This is definitely a start. However for me, what is most important is having control over each word so that they can be split apart and independently manipulated. Right now, if I change the position for example, all the words move together. How is it possible to change the position of each fragment of text? It may be that what I am looking for is fairly straightforward, since the goal isn't perfect alignment of the sentence, but rather creatively positioning and animating its parts.
I will provide an example of what I am doing with one word (I'll have to prepare the patch first) so you can imagine the same thing with multiple words that all belong to the same phrase or sentence.
Right now, if I change the position for example, all the words move together.
Yes, this is because of the jit.gen and my weird code to try to position words one after the other, but you still have complete control over the absolute position of each word. Oh and maybe that the named matrix "dims" that might be confusing. (as I'm filling it at the bottom of the patch, but using its values to compute words positions.
But you can send the positionning matrix into the first inlet of jit.gl.textmult. This matrix has to be of 3 planes (for x, y and z position), and of dim "N 1" where N is the number of inidivual text elements (hence the zl.len after the message object with the text to display). First cell is the position of the first word, and so on.
Here is a stripped down example allowing you to set the position of the first to words independently (first you need to save the patch into the same folder as the other files I sent you in my previous post, close the patcher, then re-open it)
We're definitely making progress! As you can see below, when I added additional setcell's, I was able to move each of the words around. It looks like for each word, no matter how many, it inhabits its own jit.gl.text? You have XY positioning working for each word, but what about all the other jit.gl.text paramaters (I am using 3D mode) so that would include using a jit.anim.drive object for animation. How would you connect the animation object to each individual instance? Also, I ran into an error, see below.

When I set the window to floating, something was broken in the patch, looks like the binding was disabled. I wasn't able to control the words so they are still collapsed.

jit.gl.textmult functions similarly to jit.gl.multiple, so if you want additional transform parameters you provide those as arguments to the glparams attribute in the object box, preceded by a number indicating the number of matrix inputs you want.
If you want to control these transforms via anim.drive, you should create an anim.drive and anim.node for each word you want control over, and send their tranform values to the corresponding matrix cell every frame. This is perfectly suited for something like poly~, where each instance of the poly contains the anim.node and anim.drive combo, and the poly~ index is used to determine which cell of the matrix to set.
The context errors when floating is toggled are unfortunate side effects of the underlying tranform-feedback. The workaround is to re-initialize the text after toggling floating.
Basic example of controlling 3 words below.
Rob, thanks so much. This is precisely what I was looking for, allowing me to set up independent animation and geometric control over a collection of word cutups. William Burroughs would be so envious!!
I managed to make pretty good progress with Rob's excellent example for animating individual words with jit.gl.textmult. I created an abstraction for each word and I got all the parameters working needed except scale. Scale only seems to work for the whole jit.gl.textmult.
Also, I have hit a wall trying to connect jit.gl.textmult to videoplane and node objects in order to integrate with a larger patch. It seems jit.gl.textmult is not an ob3D object? That is the error message I get when connecting a node object. The videoplane simply didn't work at all.
It would be great to get advice as to how to add the videoplane and node objects, as well as scale each word independently. Thanks!
Main Patch:
Abstraction:
About individual scale for each word: you were close! Send a matrix to the corresponding inlet, just like you do for position and rotatexyz. Also, once the matrix is sent, you need to bang the leftmost inlet of the [jit.gl.textmult]. Not sure if it's intentionnal behavior or not though.
About rendering context: you can use names instead of connections! The first argument of a gl object is the name of the context it draws to. The only exception is jit.world, as its first argument is the actual name of its own rendering context. In my example, I've set [jit.world w], so the main rendering context is "w". I've created a [jit.gl.node w @name n] which name is "n" and draws to "w". And the textmult is declared as [jit.gl.textmult n 2 @glparams position scale] so it draws to "n" and its second inlet is for the scale param of each word.
EDIT: missing the "anim-cutup" patcher in your example, so I don't really know what's going on.
TFL, incredible simplification of Rob's patch! I was in fact using named contexts for both jit.world and jit.gl.node but it wasn't working because I was trying to use the usual method of connecting jit.gl.node to the jit.gl.textmult object, which doesn't behave like a normal gl object. In terms of scaling (and other parameters, I am not just interested in randomizing with matrix objects, but having precise independent control over each letter or word. Would it be too much trouble to add the abstraction I created for anim.drive and anim.node? With the abstraction I can use anim controls such as turn, move, etc. on each letter/word. But I was having trouble with position and scaling because I am not sure where to send the paramters for independent control. Sorry, I forgot to include the abstraction, so I have added it to my post above and well as below. Thanks!!
anim-cutup abstraction:
Like so?
As I said, you can address scale just like you already do with position and rotatexyz.
I use random values with jit.noise just for the sake of the example, but given that the control input is a matrix, you have total control over its content, thus individual control for each word/letter.
You just need to think about how you actually want to modify these position/rotatexyz/scale parameters, and if/how your patcher is supposed to scale or not. Because with your current base, you're stuck with 3 words/letters only, and need to duplicate anim-cutup manually for any new word/letter you might want to add. This is where poly~ might become handy!
To sum things up: You have one matrix per parameter (position, rotatexyz, scale...), and for each of the matrix, one cell is for one word/letter. Now it's up to you to use [jit.gen], [jit.anim.drive], some [multislider] or whatever control you would like to set different values for each cell.
(also: there is a mistake in your last post: you copied my previous example instead of your abstraction, but i saw it on your edited previous post!)
EDIT: modified the first screenshot (forgot to bang the leftmost inlet after updating the scale matrix)
Thanks TFL! Sorry about the absent minded wrong patch. I have replaced it with the right one.
In my original patch, scale wasn't working with anim.node, only position and rotation, even though I added the parameter. On the other hand, all of the animated effects are working fine with anim.drive. Although I am comfortable with abstractions, I am curious how this could be done with poly~ which I haven't worked with. I do in fact want to be able to accommodate sentences with varying numbers of words, so it would be great to be able to automate that, tracking the number of words or letters in a sentence and then automating the patch to allow for that.
If I paid you a modest sum, could you revise the patch in poly~, with the above requirements? I am not sure how complex this would be, but I'm sure you could accomplish it much faster. I would also be open to having an exchange via email: rpacker@zakros.com. Thanks! Randall
For the record, here is a rather minimalistic patcher to use jit.gl.textmult and control each word individually with jit.anim.drive (using poly~) for scaling.
Main patcher:
poly~ patcher (save it as anim-abs.maxpat):
@Rob Ramirez
In your example, I try to set a parent jit.anim.node for all jit.anim.node of each word, but it doesn't seem to work. In the [jit.anim.node @name parent] paramaters are correctly updated according to messages (position, turn...), but it has no effect on subsequent [jit.anim.node @anim parent]. What am I missing?
I tried with patchcords instead of naming, but no success.
was a solution found to getting the total length of a set of words between the beginning of the first word gl object and the end of the last word gl object? Is there a jitter object that reports on all the information for a certain jit.world environment?
was a solution found to getting the total length of a set of words between the beginning of the first word gl object and the end of the last word gl object?
That total length you're talking about doesn't exist on its own, you need to calculate it yourself. It is basically the sum of each words width (you can calculate those from the words bounds you get from my modified version of jit.gl.textmult) and add to this the required distance for all white spaces.
In other words:
total_length = sum(word[i].x2 - word[i].x1) + whiteSpaceDistance*(numberOfWords-1)
where word[i] is the bound box you get for word i .
Depending on your skills, it might be easier to do that calculation directly into multiple_text_custom.js
Is there a jitter object that reports on all the information for a certain jit.world environment?
Not that I'm aware of, but you can get any objects attribute value with binded pattr object, like so:
You can also get all objects attributes with an [autopattr @greedy 1] with its left inlet connected to all objects you want to know their attribute of. But currently recalling presets with exposed attributes that way is broken.
Thanks for your answers @TFL
quick question - can you actually record the output of the jit.gl.textmult? I can't get it to work either recording as texture or as matrix output from jit.world. Any clues for this?
You can record that just like any other jit.gl stuff. The simplest solution (but with worse performance impact) is by using setting @output_matrix 1
on your [jit.world] (and also maybe play with @matrix_mode_async
) and connect its leftmost outlet to a [jit.record].
The other solution is to use @output_texture 1
instead and send that texture to an external recorder either using Spout (Windows) or Syphon (macOS). For this to work you need to get the proper Max external for spout or syphon (you'll find them in the Max package manager), and then an app to receive and record the feed.
@tfl @wil did you try this specifically with the output of jit.gl.textmult - I'm able to record other gl objects with either jit.record or with syphon recorder but the textmult content is missing. I've tried directly with the jit.gl.textmult help patch using both jit.record and jit.gl.syphonserver and the text doesn't appear.
At this stage, I can capture only with a screen recorder (eg. OBS).

Max 8.6.4
Apple M2
MacOS 14.7
I got it working using the jit.gl.textmult help file.
add @drawto ****yourworldname**** to the jit.gl.textmult
in this case:
[jit.gl.textmult 4 @glparams position scale rotatexyz color @drawto home]
then [jit.world home]

Cheers Wil - thanks for the quick response - I'll try it soon ( - :