Script newbie: How to set bezier path points of a PolylineMask?

User avatar
Cambiata
Posts: 9
Joined: Fri May 31, 2019 3:59 am

Script newbie: How to set bezier path points of a PolylineMask?

#1

Post by Cambiata » Thu Aug 08, 2019 9:21 am

Hi!

Getting the bezier path points of a PolylineMask (Polygon node) is easy using the PolylineMask:GetBezierPolyline() method:
  1. ==Polygon1:GetBezierPolyline()
However, there is seems to be no corresponding SetBezierPolyline method... :(

There might be an alternative approach based on the fact that a PolylineMask "wraps" a BezierSpline object holding the polyline points data - I found that out by examining the macro code for at Polyline node - and there is a BezierSpline:SetKeyFrames() method.

So, is there a way to programmatically "reach" the BezierSpline "inside" the PolylineMask, and set the keyframe data (containing the polyline points)? Alternatively, set the PolylineMask Input SourceOp with a new BezierSpline with the right keyframe data?

Other ideas?

Best! / Jonas

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

Re: Script newbie: How to set bezier path points of a PolylineMask?

#2

Post by SirEdric » Thu Aug 08, 2019 10:56 am

Hi Jonas.

This boy does a *lot* of polyline handling...:-)
viewtopic.php?f=6&t=2622
Maybe it gets you started.

User avatar
Cambiata
Posts: 9
Joined: Fri May 31, 2019 3:59 am

Re: Script newbie: How to set bezier path points of a PolylineMask?

#3

Post by Cambiata » Fri Aug 09, 2019 8:38 am

Thanks a lot, Sir!

se_EasyNow's a lot of great stuff to digest. I'm sure it will lead me closer to success..! :-)

Added in 55 minutes 8 seconds:
Just found your SE_ChemJson_import script, SirEdric. Cool stuff in the direction where I'm heading!

Actually, my goal is to script-generate music notation graphics that should be animated and synchronized to audio.
(Audio stuff has to be delt with outside Fusion, as far as I have understood.)

Thanks again!

User avatar
AndrewHazelden
Fusionator
Posts: 1535
Joined: Fri Apr 03, 2015 3:20 pm
Answers: 8
Location: West Dover, Nova Scotia, Canada
Been thanked: 101 times
Contact:

Re: Script newbie: How to set bezier path points of a PolylineMask?

#4

Post by AndrewHazelden » Fri Aug 09, 2019 9:40 am

Cambiata wrote:
Fri Aug 09, 2019 9:33 am
Actually, my goal is to script-generate music notation graphics that should be animated and synchronized to audio.
(Audio stuff has to be delt with outside Fusion, as far as I have understood.)

Thanks again!
Hi @Cambiata

You might get some neat ideas from this WSL post that deals with scripting MIDI data in Fusion:
viewtopic.php?p=24362#p24362

User avatar
Cambiata
Posts: 9
Joined: Fri May 31, 2019 3:59 am

Re: Script newbie: How to set bezier path points of a PolylineMask?

#5

Post by Cambiata » Sun Aug 11, 2019 4:08 am

Thank you, Andrew - will delve into that asap!

Regarding my path points problem, it seems that the BezierSpline keyframe data for a keyframing displacement is set up differently from the data for a graphic polyline path:

This is the BezierSpline content for a keyframing displacement:
  1. Path1Displacement = BezierSpline {
  2.             SplineColor = { Red = 255, Green = 0, Blue = 255 },
  3.             NameSet = true,
  4.             KeyFrames = {
  5.                 [0] = { 0, RH = { 7.33333333333333, 0.333333333333333 }, Flags = { Linear = true, LockedY = true } },
  6.                 [22] = { 1, LH = { 14.6666666666667, 0.666666666666667 }, Flags = { Linear = true, LockedY = true } }
  7.             }
  8.         }
The KeyFrames table is basically an array where the important stuff are the actual vaules ([0] = 0, [22]=1 in the example above). The RH and Flags values seem to be infered by the system.

This is the BezierSpline content for a graphic polyline path:
  1.         Polygon1Polyline = BezierSpline {
  2.             SplineColor = { Red = 173, Green = 255, Blue = 47 },
  3.             NameSet = true,
  4.             KeyFrames = {
  5.                 [0] = { 0, Flags = { Linear = true, LockedY = true }, Value = Polyline {
  6.                         Points = {
  7.                             { Linear = true, X = -0.299475759267807, Y = 0.32517483830452, RX = 0.137177808790449, RY = -0.159285163661724 },
  8.                             { Linear = true, X = 0.112057663500309, Y = -0.152680650353432, LX = -0.137177808790449, LY = 0.159285163661724 }
  9.                         }
  10.                     } }
  11.             }
  12.         }
In this case the KeyFrames table array is more complex, where the interesting stuff for each KeyFrame index is the Value property, wich is of type Polyline, wich in holds the Points property, which seems to be an table/object/something where the actual coordinates live. :-)

Trying to "reverse engineere" this, creating a Point from the console works fine:
  1.     point = Point(0.3, 0.7)
  2.     ==point.Y
Also creating a Polyline works fine...
  1.     -- works fine:
  2.     polyline = Polyline()
  3.  
  4.     -- works fine - Not sure if this is at all a way to go!
  5.     polyline = Polyline({Point(0.3, 0.7), Point(0.4, 0.8)})
however, trying to "reach" the polyline.Points property for reading or setting its data doesn't work: :(
  1.     ==polyline.Points
  2.     nil
  3.  
  4.     polyline.Points = {Point(0.3, 0.7), Point(0.4, 0.8)}
  5.     ==polyline.Points
  6.     nil
Working from the opposite end, the following code lets me reach the keyframe numeric value for frame 0 (wich is 0), but it won't let me reach the Flags or Polyline properties (wich are null):
  1.     activeTool = comp.ActiveTool
  2.     -- dump(activeTool)
  3.  
  4.     for key,inp in pairs(activeTool:GetInputList()) do
  5.         if inp:GetConnectedOutput() then
  6.             spline = inp:GetConnectedOutput():GetTool()
  7.             keyframes = spline:GetKeyFrames(true)
  8.             dump(keyframes[0])  -- >0
  9.             dump(keyframes[0].Flags) -- >nil
  10.             dump(keyframes[0].Points) -- >nil
  11.  
  12.         end
  13.     end
So, I'm stuck here. :-(
Any further ideas how to programmatically build the path of graphical Polygon tool?

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

Re: Script newbie: How to set bezier path points of a PolylineMask?

#6

Post by SirEdric » Tue Aug 13, 2019 2:33 am

Have a look here: viewtopic.php?f=16&t=2449&p=21837#p21826
The trick with Polylines is, that you have to copy the entire tool in order to modify it.

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

Re: Script newbie: How to set bezier path points of a PolylineMask?

#7

Post by SirEdric » Tue Aug 13, 2019 5:56 am

In your case, something like this should work for simple masks.
It's a Comp-Script.

Code: Select all

-- create a new PolylineMask
mask = comp.PolylineMask()
ct = comp.CurrentTime -- KeyFrames will have this index, when we create the Polyline!
-- copy the tool, so we can modify the entire table shizbang
maskTool = comp:CopySettings(mask)
maskPoly = mask.Polyline:GetConnectedOutput():GetTool()
polyName = maskPoly:GetAttrs().TOOLS_Name

NewPoints = {
	{ X = -0.16, Y = 0.23},
	{ X = -0.29, Y = 0.00},
	{ X = -0.16, Y = -0.32},
	{ X = 0.17, Y = -0.25}
}

maskTool.Tools[polyName].KeyFrames[ct].Value['Closed'] = true
maskTool.Tools[polyName].KeyFrames[ct].Value.Points = NewPoints

mask:Delete()
comp:Paste(maskTool)
You do not have the required permissions to view the files attached to this post.

User avatar
Cambiata
Posts: 9
Joined: Fri May 31, 2019 3:59 am

Re: Script newbie: How to set bezier path points of a PolylineMask?

#8

Post by Cambiata » Tue Aug 13, 2019 3:10 pm

Thank you, Eric!

The lack of possibility to directly create/modify the polyline of a Polygon/BSpline is seriously limiting!
(Stuff for another topic: What's the best way to influence BMD to make this important addition to the api? :-))

Thanks to the tool-pasting solution you suggested, it's possible to work around this limitation.
At first, I could not find a way to reference the newly created Polygon by code, but after some hacking around I came up with the solution below.

Once again: Thanks!
  1. -- Create a Polyline and set its graphical path
  2. -- All credits to SidEdric (Eric Westphal)
  3. -- https://www.steakunderwater.com/wesuckless/viewtopic.php?p=24959#p24959
  4. --------------------------------------------------------------------------------
  5. -- Helper functions
  6.  
  7. function deepcopy(orig)
  8.     local orig_type = type(orig)
  9.     local copy
  10.     if orig_type == 'table' then
  11.         copy = {}
  12.         for orig_key, orig_value in next, orig, nil do
  13.             copy[deepcopy(orig_key)] = deepcopy(orig_value)
  14.         end
  15.         setmetatable(copy, deepcopy(getmetatable(orig)))
  16.     else -- number, string, boolean, etc
  17.         copy = orig
  18.     end
  19.     return copy
  20. end
  21.  
  22. function randomString(length)
  23.     math.randomseed(os.time())
  24.     length = length or 1
  25.          if length < 1 then return nil end
  26.          local array = {}
  27.          for i = 1, length do
  28.                  array[i] = string.char(math.random(65, 90))
  29.          end
  30.          return table.concat(array)
  31. end
  32.  
  33. --------------------------------------------------------------------------------
  34.  
  35. -- create a new PolylineMask
  36. mask = comp.PolylineMask()
  37. -- get its name
  38. maskName = mask:GetAttrs().TOOLS_Name
  39. -- copy the PolylineMask as a tool
  40. tool = comp:CopySettings(mask)
  41. -- reach for the Polygon (BezierSpline) connected to the PolylineMask
  42. maskPolygon = mask.Polyline:GetConnectedOutput():GetTool()
  43. -- get its name
  44. maskPolygonName = maskPolygon:GetAttrs().TOOLS_Name
  45. -- remove mask - we don't need it any more...
  46. mask:Delete()
  47.  
  48. -- create a new tool
  49. newTool = {Tools = {}}
  50. -- give it a unique name - to be able to refer to it when it's created...
  51. newMaskName = "Poly" .. randomString(5)
  52. -- copy properties from the old tool
  53. newTool.Tools[newMaskName] = deepcopy(tool.Tools[maskName])
  54. newTool.Tools[maskPolygonName] = deepcopy(tool.Tools[maskPolygonName])
  55.  
  56. -- define coordinates for the new graphical path
  57. NewPoints = {
  58.     { X = -0.16, Y = 0.23},
  59.     { X = -0.29, Y = 0.00},
  60.     { X = -0.16, Y = -0.32},
  61.     { X = 0.17, Y = -0.25}
  62. }
  63. -- add coordinates to the new tool
  64. newTool.Tools[maskPolygonName].KeyFrames[0].Value['Closed'] = true
  65. newTool.Tools[maskPolygonName].KeyFrames[0].Value.Points = NewPoints
  66.  
  67. -- paste the new tool - this gives us a PolylineMask tool including the graphical path
  68. comp:Paste(newTool)
  69. -- grab it for later use
  70. newMask = comp:FindTool(newMaskName)
  71.