text formating
New to Nodebox (and programming).
Trying to change the typeface in the packed text visualization but can't figure it out. Textpath specifies a font file but converts the list to a shape ... which isn't an accepted data type.
(http://support.nodebox.net/discussions/show-your-work/140-packed-text)
1) can someone show me how to specify the typeface of the text?
2) can I randomly change the typeface of each line of text from a list of typefaces?
Thank you --
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by john on 21 Jan, 2019 02:25 AM
Hi MJ,
Welcome to NodeBox!
You are right: textpath turns a string with font definition into a shape (a path). Once it's a path you can no longer access properties like font.
So you need to fiddle with things before they get turned into paths.
The first step is finding the textpath node where this happens. In this network the textpath node is buried in a sub-sub-network. If you open "network2" on the top level (by control-clicking and choosing Edit Children), then open network1 you will find the textpath node.
We want to make font a parameter on the top level so that we can change it as we wish. To do this you control-click on the font port of that textpath node and choose publish. If you go back up a level you will see that network1 now has a font port. Control click that to publish it again and now the top level network2 has a font port as well.
You can verify this works by changing the font setting in network2: the whole packed poster will change to whatever font you specify.
Now all we have to do is feed it a random list of fonts. I did this by using a make_strings node to create a list of five fonts installed in my system (I changed its name to font_list so I could find it again). I then plugged that into a repeat node set to 10 to make a list of 50 fonts, and plugged that into a shuffle node to randomize it.
Just plug that randomized list of 50 fonts into the new font port of network2 and you're done! See attached screenshot for the final result.
I made all of these modifications for you using the 1000_films.ndbx network and saved it as 1000_films_random_fonts.ndbx. I then zipped the whole folder up and attached it to this note. You can play with my modified network and change the font list to whatever you want. If you want to try doing all this yourself (a good way to learn) you can make the same modifications to the untouched "1000_films_sorted.ndbx" network.
Hope that clears things up for you. I think it's great that you are modifying other people's stacks like this. If you run into any other conundrums please don't hesitate to ask for help. It's how we all learn.
John
2 Posted by Noah Way on 22 Jan, 2019 12:41 AM
John - thanks for that.
Right under my nose, LOL. At least some of it. Interesting way to work, eliminates or at least simplifies literal syntax. Still grappling with reverse engineering some things, but I'm muddling my way.
I'll be back, no doubt.
3 Posted by Noah Way on 24 Jan, 2019 10:30 PM
I'm beginning to understand some of this but have reached a stumbling block:
Colors are assigned by lookup2 bounds.width -> convert_range > hsb_color1. This colors blocks of text according to their width, resulting in color code by size. How would I apply colors randomly without regard to the size of the objects?
I've tried shuffling data at lookup2, which results in random colors applied to every word instead of every title.
Some other questions, if you don't mind:
In network2 the purpose of slice is unclear and changing it seems to have no effect on output.
In network1, the sting Alpha;Beta;Gamma in make_strings1 seems without purpose.
Also, I cannot discern the logic of the fit nodes in network1.
Thanks ---
Support Staff 4 Posted by john on 25 Jan, 2019 10:40 PM
Hi Noah,
Great questions!
Q1. How do you apply colors randomly?
In this case the hsb_color node is set to assign colors in a range of 0 to 16. The lookup and convert nodes are sending a list of width-based values, each ranging from 0 to 16.
To replace this with random colors, just feed a random_numbers node into the hsb_color node instead. Set the random_numbers node to start at 0 and end at 16. Then choose an amount. If you set amount to 3 you will get the same three random hues applied over and over again.
Change the seed value to verify that these hues are random. Each see will produce a different set of 3 hues.
If you want more colors, choose a higher amount. You could add a count node to see how many words there are (3358 in this case) and set the amount to that. That way each title would get its own random color.
Another option would be to choose, say, 5 possible hues at random, but apply them in a different order each time so the same hues don't keep repeating. To do this take the total amount of titles, divide by 5, then feed that result as the amount of a repeat node. Feed your five random hues from your random_numbers node into the repeat node to create a list of 3360 hues. These are still repeating, but now you can feed THAT list into a shuffle node to give the whole thing one more shuffle.
In this case a single random_numbers node with a sufficiently large amount is all you really need. But in other cases you may need to refine exactly what you mean by "random colors".
Q2. How do I apply this at the title level instead of the word level?
The network2 node upstream of your coloring nodes produces one row of the poster at a time (in this case 34 rows). When this is ungrouped is become a list of 3358 separate words. That's what you've been coloring.
In order to color by title instead of by word, you'll need to do your coloring earlier in the process, at the point where each title is formed. This happens INSIDE the network2 node.
If you look inside network2 you see that each title is formed by network1. Those titles are stacked horizontally to form each row. All you need to do is add a colorize node just below Network1. Feed your random_numbers and hsb_color node into the fill port of colorize.
Now network2 produces titles already colored at random. You can remove the coloring nodes you placed below it; the coloring has already been done.
There is a problem, however. Since network2 stacks titles into rows, the random colors get repeated across rows and the reset and repeated in the same order across every successive row. The result is sort of random, but not really random enough.
To fix this, you need to change the random seed for each row. So in that random_numbers node you added inside network2, control click on the seed port and choose Publish. Go back up to the top. Network2 now has a new port called "seed".
The range node just above network2 already has a nice list of seed values, one for each row. Just feed the output of that into your new seed port and Voila! Randomly-colored titles.
Q3. What does that slice node do?
Network2 receives a list of 1177 movie titles. It wants to produce one poster row each time it fires. So it needs to take those 1177 titles and break them into slices. The first slice will start at title 0 and include 34 titles; the next slice will start at title 34 and include the next 34 titles, the next will start at 68 and another 34 titles - and so on.
The range and integer nodes feeding into network 2 provide these start and size values. Each time network2 fires it reads in one start value, one size value, and the entire list of all 1177 titles.
The reason for this is one of the most subtle and confusing parts of Nodebox. If you select network2 and click the MetaData button atop the parameter pane, then click on each port in the port list, you will see that the first port, "list", has a range of List (as opposed to Value). This means that each time the node fires it reads in the entire list. The other ports all have a range set to Value, so they are read in one a time.
So now, inside of network2, each time it fires it has to slice up that list of 1177 of titles into one row, based on the start and size values it receives each time. This is how network2 is able to work on one row at a time.
Q4. How come changing that slice node has no effect?
When you select the slice node it shows you all the port values and allows you to change them. Normally changing those values would have a dramatic effect. But in this case each value is already being set by an incoming connection from above. Those incoming connections are what actually determines the slice.
You can think of those values in the slice node as its default values. By changing them you are changing the defaults, but in this case those defaults are overridden.
Q5. What is the purpose of Alpha;Beta;Gamma in make_strings1?
See previous answer. Alpha;Beta;Gamma is the default value included whenever you place a strings node. But since the actual value is connected from above, those default values are not used.
One subtlety: the Separator port in that strings node looks empty, but it's not. It has a space in it. Network1 is receiving titles one a time. Each title consists of a set of words separated by spaces. What this strings node is doing is turning each title string into a list of separate words.
Q6. What does the fit node do?
Network1 constructs title blocks by placing the words of that title in a vertical stack. In order to make them fill the block in needs to make short words bigger than long words such that they all wind up with the same width. In then needs to make all the blocks the same height in order to form tidy rows.
The fit node takes an arbitrary shape (path) and resizes it to fit a given width and height. It also has Keep Proportions option which, if checked, makes sure the shape extends to which ever boundary (width or height) it hits first and then leaves the other boundary alone so that the shape retains its original proportions.
So the first thing network1 does is use fit to expand each word to maximum of either 300 pixels wide or 1200 pixels tall. For these text paths it will always hit that width boundary first. So each word will come out 300 pixels wide with a proportional height in each case. Now all the words have an equal width, so stacking them south produces a clean top-to-bottom stack.
But because each title has a different number of words, these neat stacks will vary in height - which would disrupt the even rows we want. So before concluding, network1 groups the stack into a single shape and applies the fit node again, this time with a big width of 1200 and a small height of 100. In this case the stacks will hit that height boundary first, guaranteeing that they all wind up the same height while retaining the proportions obtained in the previous step.
On the top level, once all the rows are assembled, they are put through another fit node to make sure all the rows have the same width.
There is one more fit node at the very bottom, but it doesn't change the shape of the poster; all it does is center it. I would have used an align node instead to make the intent clearer.
Whew. Quite a long lesson but very useful, I hope. If you absorb all this you will be well on your way to mastering NodeBox.
Modified stack attached.
John
5 Posted by Noah Way on 02 Feb, 2019 09:50 PM
I'm beginning to grasp some of the simpler ideas here but need some help making the attached work.
The idea is a random placement of text matching rgb codes. I've managed to scatter the text and apply color BUT the output is one formatted line of text repeated instead of the entire list.
Feeling pretty dense ...
Support Staff 6 Posted by john on 03 Feb, 2019 08:47 AM
Noah,
You were actually pretty close.
The problem was with the "Network1" subnetwork you borrowed from the previous example. It is not normally necessary to do a slice inside a subnetwork the way that one did - in fact in my experience it's quite rare. Normally a subnetwork can work with the incoming values just as they are.
Your situation this time is one of these normal cases. You just want that network to take in a short phrase like "Fresh Cut Grass" and spit out a text block with all words the same width. So you don't need the size and start_index parameters which you weren't feeding anyway.
(The reason you got the same phrase over and over was that you were doing an identical slice each time using the default values of 1,1. This told network1 to always take 1 phrase from the list starting with position 1 - the second phrase "Light Yellow Green" since numbering starts at 0. )
See my attached revision. All I did was simplify Network1 by removing that slice node at the top. The subnetwork (which I renamed make_block) now takes one phrase at a time from your list and turns it into a block.
The only other change I made was to adjust the fit node below that. You had it set to 300,300 which didn't really change anything. Changing that to 1000,140 guarantees that the height boundary will be hit first, causing all the blocks to have the same height. 140 looked about right to me but of course you can adjust that up or down.
I added a few other nodes just to make this network easier to test and adjust; they have no effect on the final output.
Hope that helps. There is no need to feel dense. It took me quite a while to master the way NodeBox works. I really appreciate your openness in asking these questions; everyone can learn from our exchanges.
John
7 Posted by Noah Way on 03 Feb, 2019 05:42 PM
Thanks again. The colors are not meant to be random, but rather to be associated with their names. I've tried various ways to match colors to names both within and without of the network without success. Applying color within disrupts the formatting unless applied after grouping, and in no case do I get correct color match.
Looking at the data confirms that the color values are correct but the result is scrambled. I've run through the examples and made done some color matching samples but can't seem to get this one to work.
FYI the application is generative art.
8 Posted by Noah Way on 03 Feb, 2019 07:26 PM
BINGO
Somehow I managed to make this work by coming at it from a different direction. I started by matching color to text, then modified the file to format the text.
FYI the application is generative art. Thanks for your help.
9 Posted by Noah Way on 09 Feb, 2019 06:30 PM
Here is another text formatting challenge:
Place text strings in quadrilateral shapes and a radial pattern such that the quadrilateral shape is defined by the degrees of rotation and length of the text string.
Example: 36 strings, angle = 10 degrees.
This is way above my pay grade. I'm going to assume requires MATH.
10 Posted by Noah Way on 09 Feb, 2019 10:35 PM
Attached is a scalable quadrilateral shape using points and coordinates with inputs for length and angle.
Fit_To does only fits the text to the shape in one direction. I am stuck, trying to get the text shape to match the geometric shape.
Support Staff 11 Posted by john on 10 Feb, 2019 07:16 AM
Noah,
What you are trying to do should be easy to do in NodeBox but it is not. The fit_to node fits to to the *bounding box* of another shape, not to the shape itself. Nodebox can skew text and scale text, but has no built-in way of radiating text (making the letters grow bigger as they radiate outwards so that they seem to occupy a trapezoid).
What you need is a "radiate_string" node. I made one (attached) but it was hard to make and required a custom node.
My radiate_string node has 6 parameters:
- string(s)
- font name
- font size
- growth factor (1 = no growth, 10 = rapid growth of each successive letter)
- kern (the amount of space between each letter)
- slant (lets you adjust where the center line falls within the chosen font)
I suggest you play with all these parameters and see what happens. For the demo I used 12 months for the strings and wrapped them around an almost-full arc. This caused some months to appear upside-down (unavoidable if you want text to radiate outward around a full circle). Depending on your application you could radiate across a smaller arc to make all the words easy to read.
Let me know if this achieves the effect you were trying for. If you don't care how I made this node feel free to skip the rest of this note.
John
The ugly details...
The radiate_string node was tricky to make because text is tricky. Fonts are made out of bezier curves with some letters that include contours (like the little triangle shape inside a capital A). Some contours are inside a larger letter, some letters have two contours (like a capital B), and some letters have disjoint contours (like the dot floating above a lower case i). To make matters worse, different fonts draw contours in different orders. A lower case Verdana "a" is drawn outside-then-inside but the same letter in Verdana Bold is drawn inside-then-outside.
The reason we care about contours is that, in order to distort the letters, we have to break them into individual points, modify those points, then reconnect them. But once a letter is turned into points there are no more contours - just points. And when you connect those points annoying lines will appear connecting contour shapes with surrounding shapes.
To get around this we have to break a string into individual letters, then break each letter into separate contours BEFORE we muck with the points. This requires a custom node, "contours", which uses a simple Python script that must be added to your code library. Frederick (the inventor of NodeBox) provided this custom node years ago but few know of its existence.
To compensate for fonts which draw contours in the "wrong" order, you need to sort the contours by their area in descending order - a whole puzzle in itself. I used my handy area node and made an order table to slice the contours into the correct order.
Once you have the contours separated and sorted, you run into another problem. When you reduce a shape made with bezier curves into a set of points you lose the control points of each curve. When you reconnect the remaining points the shape will appear as a series of straight edges - which makes the beautiful letters look awful. To get around this we can resample the angular path so there are many short lines connecting points instead of a few long lines. I used an arbitrary resolution of 200 points per contour when resampling; this works well enough as long as the font size doesn't get too big.
Now we are finally ready to adjust the points. This is where the math comes in. Basically I magnify the Y coordinates based on the magnitude of the X coordinates - the farther along each letter is in the string the taller it gets. The growth factor adjusts how quickly this happens. I also align all the letters beforehand using the slant parameter to adjust where on the font the midline of the trapezoid falls. Different fonts require slightly different slant values; you may also need to adjust the slant if your string has lower case letters with descenders.
Once the points have been adjusted I use a connect node to reform each letter. But here we run into yet another problem: To avoid those nasty lines connecting inner contours to outer contours we have to reform the letter into a single shape. This requires knowing how many contours (if any) there are, and whether they are inner or disjoint. Compound nodes are used both to detect "inside-ness" and to reform the contours into a single shape.
This approach is a bit of a kludge. It would be better if we didn't have to separate the contours and if we could adjust the control points of the bezier curves instead of losing them. Compound nodes can be slow and long strings with resampled letters could end up with tens of thousands of points - which means that the node will start to bog down if fed many long strings. Most fonts will look OK from a distance, but if you zoom in you may see minor distortions here and there due to the resampling.
The bottom line: you can solve this problem in NodeBox reasonably well but the result is a bit of a kludge and requires someone as crazy as I am to overcome all the problems.
Thanks for posing such an interesting challenge.
12 Posted by Noah Way on 10 Feb, 2019 05:04 PM
Thanks, John. I was right about one thing - that's way above my pay grade.
I'm exploring Illustrator-like text effects with volumes of data as a component of my new work. This is amazing simple and easily modified in Nodebox, although it seems I am quickly discovering the limits of the platform.
Support Staff 13 Posted by john on 11 Feb, 2019 09:13 AM
After years I am only beginning to discover what NodeBox can do. I use it at work for diverse and impressive visualizations and create networks with thousands of nodes. With determination and the occasional custom node there is almost nothing I can't do.
Yet it is also true that NodeBox is riddled with surprising holes and gaps. It can do some things with ease that other languages can barely do at all, but sometimes it can barely do things that are easy in almost any other language. As an open source language with few resources and a tiny following it hovers precariously on the brink of extinction. Yet it has become my tool of choice.
I love it in part for its odd limitations. As a creative person I've learned that limitations which force you to approach problems from unusual directions can be a boon to the creative process. When faced with a blank canvas a perfect tool would be paralyzing. Better to limit your choices in order to move forward.
Keep exploring!
John
14 Posted by Noah Way on 11 Feb, 2019 02:02 PM
I'm not going to pretend to understand much of what you've done, but it is fascinating and full of lessons to explore. Thanks again!
One more twist if you're up for it - how to add different colors to each individual string?
Support Staff 15 Posted by john on 12 Feb, 2019 05:17 AM
Adding colors to each individual string is easy!
Make your list of colors. One way to do that is by hooking a random node (set to 12 values ranging from 0 to 255) to the hue port of an HSB color node (don't forget to set brightness and saturation to somewhere near 255).
Now just feed that into the fill port of that colorize node at the very bottom. The 12 strings will be colored with those 12 random colors.
That's it.