Modifier Nodes Blackbook

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

Modifier Nodes Blackbook

#1

Post by Kristof » Sun Aug 04, 2019 9:41 am

Maverick @Chad already explored the murky waters of modifier nodes for us right here. I'm all for more visual feedback and while experimenting with different slate setups, I soon felt the need to create "Text" modifier nodes and see what can be done in that context. I'm taking the opportunity to quote myself:
The nodal workflow could do with an overhaul, to try and make it more consistent, dynamic and current (perhaps make it stand out more from the competition):
  • Expose arbitrary input and output parameters in the flow node editor (work panel).
    Expose modifiers too, and make them nodes, instead of hiding them in a tab in the control panel.
    Make the horizontal size of nodes variable to fit longer names.
    Make it possible to hide connections on a node in the work panel, to keep things clean if you wanted to (instead of using wireless nodes, especially since we are missing that über-intelligent wireless node that's driven by an input's datatype instead of a hard-coded one).
    A selection function that allows filters/regular expressions.
Although writing fuses to make a modifier node seems easy, you'll often run into unexpected behavior, especially if you're not that familiar with writing code like myself. You'd need to take care of all the event handling and make sure that the node isn't modifying their incoming parameters, so I'm hoping to get some help from you guys along the way. This will be the place where I'll continue to post my modifier node experiments.

Added in 43 minutes 28 seconds:
My first addition to this toolset is a simple node that will create text and pass it along through its output as such. I dubbed it "Text_Create":
Code: [Select all] [Expand/Collapse] [Download] (text_create.fuse)
  1. -- ============================================================================
  2. -- Blabber
  3. -- ============================================================================
  4.  
  5. --[[
  6. --]]
  7.  
  8. -- ============================================================================
  9. -- Constants
  10. -- ============================================================================
  11.  
  12. VERSION = "v0.1 (2019-0804) by Kristof Indeherberge"
  13. FUSE_NAME = "Text_Create"
  14. DATATYPE = "Text"
  15.  
  16. -- ============================================================================
  17. -- Text Create fuse
  18. -- Fuse that creates text
  19. -- ============================================================================
  20.  
  21. FuRegisterClass( FUSE_NAME , CT_Tool , {
  22.     REGID_DataType      = "Text",
  23.     REGID_InputDataType = "Text",
  24.     --REG_Fuse_NoEdit = true,
  25.     --REG_Fuse_NoReload = true,
  26.     --REG_Fuse_TilePic = fuse_pic,
  27.     --REG_NoBlendCtrls = true,
  28.     REG_NoCommonCtrls = true,
  29.     --REG_NoMotionBlurCtrls = true,
  30.     --REG_NoPreCalcProcess = true,  -- call Process for precalc requests (instead of PreCalcProcess)
  31.     --REG_OpNoMask = true,
  32.     --REG_SupportsDoD = true,   -- this tool supports DoD
  33.     --REG_TimeVariant = true,
  34.     --REG_Unpredictable = true, -- this tool shall never be cached
  35.     --REG_Version = 100,
  36.     REGS_Category = "XMnR\\Text",
  37.     --REGS_Company = "UNT",
  38.     --REGS_HelpTopic = "http://en.wikipedia.org/wiki/Acme_Corporation",
  39.     REGS_Name = FUSE_NAME,
  40.     REGS_OpDescription = "Create text",
  41.     --REGS_OpIconString = FUSE_NAME,
  42.     REGS_OpIconString = "TxtC",
  43.     --REGS_URL = "http://en.wikipedia.org/wiki/Acme_Corporation",
  44.     } )
  45.  
  46. function Create()
  47.     InTextField = self:AddInput( "Text Field" , "TextField" , {
  48.         LINKID_DataType = "Text",
  49.         INPID_InputControl = "TextEditControl",
  50.         TEC_Lines = 1,
  51.         } )
  52.  
  53.     InImage = self:AddInput( "Source" , "Source" , {
  54.         LINKID_DataType = "Image",
  55.         INPID_InputControl = "ImageControl",
  56.         LINK_Visible = false,
  57.         INP_Required = false,
  58.         } )
  59.  
  60.     OutText = self:AddOutput( "Out" , "Out" , {
  61.         LINKID_DataType = "Text",
  62.         LINK_Main = 1,
  63.         } )
  64.  
  65.     --[[
  66.     InLabel = self:AddInput( VERSION , "version" , {
  67.         LINKID_DataType = "Text",
  68.         INPID_InputControl = "LabelControl",
  69.         INP_External = false,
  70.         INP_Passive = true,
  71.         } )
  72.     --]]
  73. end
  74.  
  75. function Process( req )
  76.     local text = InTextField:GetValue( req ).Value
  77.     OutText:Set( req , Text( text ) )
  78. end
If you drag it to the viewer, you can see its output:

Image

If you want to use in an existing tool that accepts a modifier input, do Connect To and select these puppies from a nice menu. No need to rebuild expressions or whatever if you need it elsewhere.

Image

By specifying the "REGID_DataType" in FuRegisterClass(), you'll end up with a node that's smaller than a regular one, so visually it will stand out too.

Image
Last edited by Kristof on Sun Aug 11, 2019 2:35 pm, edited 1 time in total.

User avatar
SecondMan
Site Admin
Posts: 3478
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 5
Location: Vancouver, Canada
Been thanked: 92 times
Contact:

Re: My Modifier Nodes Blackbook

#2

Post by SecondMan » Mon Aug 05, 2019 1:40 pm

Oooh! Another terrific topic! Thanks! :cheer:

I can see a lot of good tools and ideas coming from this one. Speaking for myself I'll chip in whenever and wherever I can.

User avatar
Chad
Fusionator
Posts: 1409
Joined: Fri Aug 08, 2014 1:11 pm
Been thanked: 14 times

Re: My Modifier Nodes Blackbook

#3

Post by Chad » Mon Aug 05, 2019 5:50 pm

Hmmm... Text could be really interesting. Like you could have tools that build the strings by concatenation and variables.

Like you could have a "line return" tool that took the input string, added a line return, and output that string.

Make lua or python via nodes?

Or build a shader for 3CuS via nodes.

No, this is probably getting too silly.

User avatar
SirEdric
Fusionator
Posts: 1873
Joined: Tue Aug 05, 2014 10:04 am
Answers: 2
Real name: Eric Westphal
Been thanked: 112 times
Contact:

Re: My Modifier Nodes Blackbook

#4

Post by SirEdric » Mon Aug 05, 2019 9:49 pm

Chad wrote:
Mon Aug 05, 2019 5:50 pm
Or build a shader for 3CuS via nodes.

No, this is probably getting too silly.
Not. At. All.....:-)

User avatar
SecondMan
Site Admin
Posts: 3478
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 5
Location: Vancouver, Canada
Been thanked: 92 times
Contact:

Re: My Modifier Nodes Blackbook

#5

Post by SecondMan » Tue Aug 06, 2019 12:39 am

I agree with Eric. This cannot possibly get silly enough.

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

Re: My Modifier Nodes Blackbook

#6

Post by Kristof » Tue Aug 06, 2019 3:04 am

Hear, hear!

Another cool thing you can do with the Text Create node: manage your font selection in one place and have it exposed as a node in the comp. Connect your Text+ node to sample the value coming from two nodes where you define the name and style of the font:

Code: Select all

{
	Tools = ordered() {
		FONT_TXT = Fuse.Text_Create {
			NameSet = true,
			CurrentSettings = 2,
			CustomData = {
				Settings = {
					[1] = {
						Tools = ordered() {
							FONT_TXT = Fuse.Text_Create {
								CtrlWZoom = false,
								NameSet = true,
								CustomData = {
								},
								Inputs = {
									TextField = Input { Value = "Nueva Std", },
								},
								ViewInfo = OperatorInfo { Pos = { 648, 145 } },
							}
						}
					},
					[2] = {
						Tools = ordered() {
							FONT_TXT = Fuse.Text_Create {
								CtrlWZoom = false,
								NameSet = true,
								CustomData = {
								},
								Inputs = {
									TextField = Input { Value = "Open Sans", },
								},
								ViewInfo = OperatorInfo { Pos = { 648, 145 } },
								Name = "FONT_TXT"
							}
						}
					},
				}
			},
			Inputs = {
				TextField = Input { Value = "Open Sans", },
			},
			ViewInfo = OperatorInfo { Pos = { 825, 115.5 } },
		},
		FONTSTYLE_TXT = Fuse.Text_Create {
			NameSet = true,
			CurrentSettings = 2,
			CustomData = {
				Settings = {
					[1] = {
						Tools = ordered() {
							FONTSTYLE_TXT = Fuse.Text_Create {
								CtrlWZoom = false,
								NameSet = true,
								CustomData = {
								},
								Inputs = {
									TextField = Input { Value = "Bold Condensed Italic", },
								},
								ViewInfo = OperatorInfo { Pos = { 744, 145 } },
							}
						}
					},
				}
			},
			Inputs = {
				TextField = Input { Value = "Bold", },
			},
			ViewInfo = OperatorInfo { Pos = { 825, 148.5 } },
		},
		Text1 = TextPlus {
			CtrlWZoom = false,
			CurrentSettings = 2,
			CustomData = {
				Settings = {
					[1] = {
						Tools = ordered() {
							Text1 = TextPlus {
								CtrlWZoom = false,
								CustomData = {
								},
								Inputs = {
									Width = Input { Value = 1920, },
									Height = Input { Value = 1080, },
									["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
									Font = Input {
										SourceOp = "FONT_TXT",
										Source = "Out",
									},
									StyledText = Input { Value = "TEST", },
									Style = Input {
										SourceOp = "FONTSTYLE_TXT",
										Source = "Out",
									},
									ManualFontKerningPlacement = Input {
										Value = StyledText {
											Array = {
											},
											Value = ""
										},
									},
								},
								ViewInfo = OperatorInfo { Pos = { 552, 239 } },
							}
						}
					},
					[2] = {
						Tools = ordered() {
							Text1 = TextPlus {
								CtrlWZoom = false,
								CustomData = {
								},
								Inputs = {
									Width = Input { Value = 1920, },
									Height = Input { Value = 1080, },
									["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
									Font = Input {
										SourceOp = "FONT_TXT",
										Source = "Out",
									},
									StyledText = Input { Value = "TEST", },
									Style = Input {
										SourceOp = "FONTSTYLE_TXT",
										Source = "Out",
									},
									ManualFontKerningPlacement = Input {
										Value = StyledText {
											Array = {
											},
											Value = ""
										},
									},
								},
								ViewInfo = OperatorInfo { Pos = { 552, 239 } },
								Name = "Text1"
							}
						}
					},
				}
			},
			Inputs = {
				Width = Input { Value = 1920, },
				Height = Input { Value = 1080, },
				["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
				Font = Input {
					SourceOp = "FONT_TXT",
					Source = "Out",
				},
				StyledText = Input { Value = "TEST", },
				Style = Input {
					SourceOp = "FONTSTYLE_TXT",
					Source = "Out",
				},
				ManualFontKerningPlacement = Input {
					Value = StyledText {
						Array = {
						},
						Value = ""
					},
				},
			},
			ViewInfo = OperatorInfo { Pos = { 825, 214.5 } },
		}
	}
}

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

Re: My Modifier Nodes Blackbook

#7

Post by Kristof » Tue Aug 06, 2019 3:46 pm

Sneak preview, append:

Image

Thanks to @Cedric for adding the switch type approach! Post of actual fuse will follow.

User avatar
Chad
Fusionator
Posts: 1409
Joined: Fri Aug 08, 2014 1:11 pm
Been thanked: 14 times

Re: My Modifier Nodes Blackbook

#8

Post by Chad » Thu Aug 08, 2019 6:55 am

If you hit Ctrl-T, do you get "Less We Suck" or "Suck We Less"?

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

Re: My Modifier Nodes Blackbook

#9

Post by Kristof » Thu Aug 08, 2019 11:16 am

Chad wrote:
Thu Aug 08, 2019 6:55 am
If you hit Ctrl-T, do you get "Less We Suck" or "Suck We Less"?
:D How about a crash? Kidding, first incarnation would but C's version works:

Image

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

My Modifier Nodes Blackbook [Text Append]

#10

Post by Kristof » Thu Aug 08, 2019 12:12 pm

The fuse, written by @Cedric. I'll be using these dynamic inputs in other fuses too, I think.
Code: [Select all] [Expand/Collapse] [Download] (text_append.fuse)
  1. -- ============================================================================
  2. -- Blabber
  3. -- ============================================================================
  4.  
  5. --[[
  6. --]]
  7.  
  8. -- ============================================================================
  9. -- constants
  10. -- ============================================================================
  11.  
  12. VERSION = "v0.1 (2019-0807) by Cédric Duriau"
  13. FUSE_NAME = "Text_Append"
  14. DATATYPE = "Text"
  15.  
  16. -- ============================================================================
  17. -- Fuse
  18. -- ============================================================================
  19.  
  20. FuRegisterClass( FUSE_NAME , CT_Tool , {
  21.     REGID_DataType      = DATATYPE,
  22.     REGID_InputDataType = DATATYPE,
  23.     --REG_Fuse_NoEdit = true,
  24.     --REG_Fuse_NoReload = true,
  25.     --REG_Fuse_TilePic = fuse_pic,
  26.     --REG_NoBlendCtrls = true,
  27.     REG_NoCommonCtrls = true,
  28.     --REG_NoMotionBlurCtrls = true,
  29.     --REG_NoPreCalcProcess = true,  -- call Process for precalc requests (instead of PreCalcProcess)
  30.     REG_OpNoMask = true,
  31.     --REG_SupportsDoD = true,   -- this tool supports DoD
  32.     --REG_TimeVariant = true,
  33.     --REG_Unpredictable = true, -- this tool shall never be cached
  34.     --REG_Version = 100,
  35.     REGS_Category = "XMnR\\DataType\\Text",
  36.     --REGS_Company = "UNT",
  37.     --REGS_HelpTopic = "http://en.wikipedia.org/wiki/Acme_Corporation",
  38.     REGS_Name = FUSE_NAME,
  39.     REGS_OpDescription = "Appends dynamic Text inputs",
  40.     --REGS_OpIconString = FUSE_NAME,
  41.     REGS_OpIconString = "TxtA",
  42.     --REGS_URL = "http://en.wikipedia.org/wiki/Acme_Corporation",
  43.     } )
  44.  
  45. function Create()
  46.     InWhich = self:AddInput("Which", "Which", {
  47.         LINKID_DataType = "Number",
  48.         INPID_InputControl = "SliderControl",
  49.         INP_MinAllowed = 1,
  50.         INP_MaxAllowed = 64,
  51.         INP_MaxScale = 1,
  52.         INP_Integer = true,
  53.         IC_Steps = 1.0,
  54.         IC_Visible = false
  55.     })
  56.  
  57.     InText1 = self:AddInput("Text1", "Text1", {
  58.         LINKID_DataType = "Text",
  59.         LINK_Main = 1,
  60.         INP_Required = false
  61.     })
  62.  
  63.     OutText = self:AddOutput("Output", "Output", {
  64.         LINKID_DataType = "Text",
  65.         LINK_Main = 1
  66.     })
  67.  
  68.     --[[
  69.     InLabel = self:AddInput( VERSION , "version" , {
  70.         LINKID_DataType = "Text",
  71.         INPID_InputControl = "LabelControl",
  72.         INP_External = false,
  73.         INP_Passive = true,
  74.         } )
  75.     --]]
  76. end
  77.  
  78.  
  79. function OnAddToFlow()
  80.     local highest_input = 1
  81.  
  82.     for i = 2, 64 do
  83.         if self:FindInput("Text" .. tostring(i)) ~= nil then
  84.             highest_input = i
  85.         end
  86.     end
  87.  
  88.     for i = 2, highest_input do
  89.         self:AddInput("Text" .. i, "Text" .. i, {
  90.             LINKID_DataType = "Text",
  91.             LINK_Main = i,
  92.             INP_Required = false,
  93.             INP_DoNotifyChanged = true
  94.         })
  95.     end
  96.  
  97.     InWhich:SetAttrs({INP_MaxScale = highest_input, INP_MaxAllowed = highest_input})
  98. end
  99.  
  100. function OnConnected(inp, old, new)
  101.     local inp_nr = tonumber(string.match(inp:GetAttr("LINKS_Name"), "Text(%d+)"))
  102.     local max_nr = tonumber(InWhich:GetAttr("INP_MaxAllowed"))
  103.  
  104.     if inp_nr then
  105.         if inp_nr >= max_nr and max_nr < 64 and new ~= nil then
  106.             InWhich:SetAttrs({INP_MaxScale = inp_nr, INP_MaxAllowed = inp_nr})
  107.  
  108.             self:AddInput("Text" .. (inp_nr + 1), "Text" .. (inp_nr + 1), {
  109.                 LINKID_DataType = "Text",
  110.                 LINK_Main = (inp_nr + 1),
  111.                 INP_Required = false,
  112.                 INP_DoNotifyChanged = true
  113.             })
  114.         end
  115.     end
  116. end
  117.  
  118.  
  119. function Process(req)
  120.     local text = ""
  121.  
  122.     for i = 1, 64 do
  123.         local input = self:FindInput("Text" .. tostring(i))
  124.  
  125.         if input ~= nil then
  126.             local inp_text = input:GetSource(req.Time, req:GetFlags()).Value
  127.             text = text .. inp_text
  128.         end
  129.     end
  130.  
  131.     OutText:Set(req, Text(text))
  132. end

User avatar
Cedric
Posts: 47
Joined: Tue Sep 13, 2016 7:26 am
Answers: 1
Location: Ghent
Been thanked: 7 times

Re: My Modifier Nodes Blackbook

#11

Post by Cedric » Sat Aug 10, 2019 11:52 am

Hi Kristof, da boyz

Hope you all are doing well.

First of all, you know me, I'm not a compositor so my thoughts here could be wrong.
But here are some thoughts of mine regarding the text combine fuse.

The action of the fuse, combining text together, is a neat little feature.
Could be used in many ways in different workflows. To ensure this fuse's action is robust and feature complete, I would love it to give the user the possibility to define a separation character, or some kind of format string, to build a custom combined string as a result.

Adding support for the separation character is an easy feature, just add a text edit control for the user to add some text and use it when combining all the input text parts. Now the combination order or join order in the fuse's current state is based on the input index. Which in itself is a decent implementation and works as expected.
My concern lies in wanting to insert custom text, between specific parts of the combined string with the separators. This is trickier to cover.

With the dynamic inputs, text order is defined by the input index. These get read in order and joined with the separation character to built the output text.
The only way I can think of to build an output string with custom text in between joined parts, is to display a format string. This could be an editable text edit control on the fuse showing the output string in a pre-formatted manner. Something like "{0}-{1}-{2}".

For example:

- input 1 = "foo"
- input 2 = "bar"
- input 3 = "man"
- separation character = "-"
- format = "{1}-{2}-{3}"

Result would be "foo-bar-man".

This would allow the user to customize the format string, giving full control over prefixing, suffixing, overriding the separation character on specific parts or even removing an input (kinda redundant, but possible). By doing this, I think the dynamic input based combine text fuse might work.

Now if you (plural form) find this feature maybe a little overkill or not needed, then I would suggest dumbing down the fuse.
Making it 2 fixed inputs with a separation character(s) option suits the procedural workflow very well. Very easy to write, very robust, easy to use.
Only drawback, bigger flows due to more nodes needed to build string.

What do you guys think?

Cheers
Cedric

PS: might prototype these for you to test, stay tuned
Last edited by Cedric on Mon Aug 12, 2019 10:40 am, edited 1 time in total.

User avatar
SecondMan
Site Admin
Posts: 3478
Joined: Thu Jul 31, 2014 5:31 pm
Answers: 5
Location: Vancouver, Canada
Been thanked: 92 times
Contact:

Re: My Modifier Nodes Blackbook

#12

Post by SecondMan » Sat Aug 10, 2019 11:28 pm

Great ideas. And I find the point about either dumbing down or going all out an interesting one to consider.

To me it sounds like two different Fuses, really. And perhaps the one with the format string could go further still, where the combination types themselves are defined by the format string as well.

Something like a "Text Formatter" Fuse, which can do appends, newlines, tabs,... ?
Cedric wrote:
Sat Aug 10, 2019 11:52 am
da boyz
How androcentric... 😬

Added in 1 minute 45 seconds:
Actually Text Formatter wouldn't be a great name for it. It implies something that would define font styling or markdown.

User avatar
Kristof
Fusionista
Posts: 557
Joined: Thu Aug 07, 2014 1:30 pm
Answers: 1
Been thanked: 18 times

Re: Modifier Nodes Blackbook

#13

Post by Kristof » Sun Aug 18, 2019 3:08 am

I really love these ideas. That format fuse is pretty cool and I would have loved to have this kind of functionality in a Saver or Loader node. @AndrewHazelden 's LifeSaver node allows you to play with something similar (tokens).

@Cedric, as you already know, working together with you on projects has influenced the way I now build my comps. A collabo' between a coder and a compositor is really a nice match: I look at compositing as a visual way of coding (to a certain degree) so there's a lot to learn from somebody like yourself.

User avatar
Cedric
Posts: 47
Joined: Tue Sep 13, 2016 7:26 am
Answers: 1
Location: Ghent
Been thanked: 7 times

Re: My Modifier Nodes Blackbook

#14

Post by Cedric » Sun Aug 25, 2019 5:43 am

SecondMan wrote:
Sat Aug 10, 2019 11:30 pm
Great ideas. And I find the point about either dumbing down or going all out an interesting one to consider.

To me it sounds like two different Fuses, really. And perhaps the one with the format string could go further still, where the combination types themselves are defined by the format string as well.

Something like a "Text Formatter" Fuse, which can do appends, newlines, tabs,... ?
Two fuses makes sense indeed Pieter. One could just handle adding text in input order. The other could format strings with placeholders..
More about this soon, I made some prototypes, @Kristof is playing with them.
SecondMan wrote:
Sat Aug 10, 2019 11:30 pm
Cedric wrote:
Sat Aug 10, 2019 11:52 am
da boyz
How androcentric... 😬
Well considering I was going to start off with "wassup bitches", "da boyz" was already a step forward for me.
Guess I'm not PC enough for this world? :mrgreen:
SecondMan wrote:
Sat Aug 10, 2019 11:30 pm
Added in 1 minute 45 seconds:
Actually Text Formatter wouldn't be a great name for it. It implies something that would define font styling or markdown.
Hmmmm I don't entirely agree with that statement. I do understand that from an artistic/Fusion point of view, text formatting could indeed be 3D text representation with settings like font and such. But the "format" word itself, is used throughout programming languages to build strings with placeholders. So for me it is very logical to call that Fuse FormatText for example.
Kristof wrote:
Sun Aug 18, 2019 3:08 am
@Cedric, as you already know, working together with you on projects has influenced the way I now build my comps. A collabo' between a coder and a compositor is really a nice match: I look at compositing as a visual way of coding (to a certain degree) so there's a lot to learn from somebody like yourself.
Thanks bud, always a pleasure to work with you!
Very happy as well to be helping out recently on your interesting research.
Let's say that I appreciate providing the D part of the R&D.

Cheers
Cedric

PS: don't quote me on providing the D part.. pun not intended..