Is it possible to programmatically edit the Bezier control handles for the node of a Microsoft Office shape?

Microsoft Office applications allow you to edit the node points of shapes as illustrated below:
Bezier control handlesEach node is defined by a set of 3 coordinates. X1 & Y1 defined the location of the node point while X2 & Y2 and X3 & Y3 define the location of the two Bezier control handles. By right clicking on a freeform shape and selecting Edit Points all 3 coordinated for a given node can be changed.

It's possible to create a new shape programmatically and specify the optional Bezier handles using the AddNodes method:
AddNodes method
It's also possible to change the node coordinates for an existing shape using the SetPosition method:
SetPosition method
But I can't see any way to edit the X2,Y2,X3,Y3 coordinated for the Bezier handles anywhere in the Object Model. Is it possible to read and write these values for an existing shape?
LVL 14
Jamie GarrochPowerPoint Consultant & DeveloperAsked:
Who is Participating?
 
Claire StrebConnect With a Mentor Software GuruCommented:
If you had copied and pasted my code above and put a break point on line 19, you would have seen the number of myShape.Nodes.

in other words:

Dim xColumns as String: xColumn = ""
Dim yColumns as String: yColumn = ""
Dim myNode as ShapeNode
For each myNode in myShape.Nodes
    xColumns = xColumns & myNode.Points(1, 1) & ","
    yColumns = yColumns & myNode.Points(1, 2) & ","
Next
xColumns = Left(xColumns, Len(xColumns) - 1)
yColumns = Left(yColumns, Len(yColumns) - 1)
Dim xValues as Variant: xValues = Split(xColumns, ",")
Dim yValues as Variant: yValues = Split(yColumns, ",")
Dim i as Integer
For i = 0 to myShape.Nodes.Count
    Debug.Print "(X" & i+1 & ",Y" & i+1 & ") = (" & xValues(i) & "," & yValues(i) & ")"
Next i

Open in new window

0
 
Claire StrebSoftware GuruCommented:
Yes, you get the Shape.Nodes collection which is a ShapeNodes object (https://msdn.microsoft.com/en-us/vba/powerpoint-vba/articles/shapenodes-object-powerpoint), wich is a collection of ShapeNode objects (https://msdn.microsoft.com/en-us/vba/powerpoint-vba/articles/shapenode-object-powerpoint), and then you can do things like:

Dim freeFormBuilder As FreeformBuilder
Dim mySlide As Slide
Set mySlide = ActivePresentation.Slides(ActiveWindow.View.Slide.SlideIndex)
Set freeFormBuilder = mySlide.Shapes.BuildFreeform(msoEditingCorner, 266.8235, 104.3088)
With freeFormBuilder
    .AddNodes msoSegmentLine, msoEditingAuto, 536.6986, 89.93378
    .AddNodes msoSegmentLine, msoEditingAuto, 521.4486, 288.4338
    .AddNodes msoSegmentLine, msoEditingAuto, 484.1986, 288.4338
    .AddNodes msoSegmentLine, msoEditingAuto, 484.1986, 308.1838
    .AddNodes msoSegmentLine, msoEditingAuto, 454.5735, 288.4338
    .AddNodes msoSegmentLine, msoEditingAuto, 289.5735, 288.4338
    .AddNodes msoSegmentLine, msoEditingAuto, 266.8235, 104.3088
End With

Dim myShape As Shape
Set myShape = freeFormBuilder.ConvertToShape
'Now you can do a bunch of stuff with myShape, such as myShape.Fill, myShape.TextFrame, etc.
'and the nodes can be examined and changed such as the following (which can be see here even though it is Excel, https://msdn.microsoft.com/en-us/vba/excel-vba/articles/shapenode-points-property-excel):
With myShape.Nodes
    pointsArray = .Item(1).Points
    currXvalue = pointsArray(1, 1)
    currYvalue = pointsArray(1, 2)
    .SetPosition 1, currXvalue + 1, currYvalue
    .SetPosition 1, currXvalue, currYvalue
End With

Open in new window

0
 
Jamie GarrochPowerPoint Consultant & DeveloperAuthor Commented:
Thanks for the code examples Claire. What I'm looking for is the points array for X2,Y2 and X3,Y3. The second part of your code access the node location only via the X1,Y1 properties. I can't find out how to read & write the coordinates for the two Bezier handles.
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

 
Claire StrebSoftware GuruCommented:
I was giving you examples and references thinking you could extrapolate it from there.  With myShape.Nodes is iterating all of the nodes.  Did you try it?
0
 
Jamie GarrochPowerPoint Consultant & DeveloperAuthor Commented:
Hi Claire. I had already tried quite a few variations of the code I originally posted which is similar to what you provided and have been unable to expose the 3 sets of coordinated for a single node (X1-X3 & Y1-Y3). The issue seems to be that the points array only contains X1 & Y1. Are you able to read all 6 coordinate properties for each node somehow?
0
 
Jamie GarrochPowerPoint Consultant & DeveloperAuthor Commented:
Ahhh. Lightbulb moment. I had incorrectly assumed that a node on a freeform shape comprised three pairs of coordinates. One pair for the point on the path and two points for the two Bezier control handles. That assumption came from the AddNodes method which has 6 coordinate properties. But in reality, this method creates one, two or three nodes depending on how it's used.

So in my original illustration, not only are there 4(+1) nodes for the 4 points of the path but there are 12 additional nodes that describe the Bezier handles:

Bezier control handles are nodes
So there is no API to get the point coordinates and the two Bezier handle coordinates from a single "Node" object because in fact, they are all nodes in the nodes collection for the shape object. Doh!

Thank you for sticking with me Claire :-)

FYI, I updated your code snippet for anyone else looking as the Nodes collection enumerates from 1 and not 0:

Sub ShowAllNodes(Optional myShape As Shape)
  Dim xColumns As String: xColumns = ""
  Dim yColumns As String: yColumns = ""
  Dim myNode As ShapeNode
  
  If myShape Is Nothing Then Set myShape = ActiveWindow.Selection.ShapeRange(1)
  
  For Each myNode In myShape.Nodes
    xColumns = xColumns & myNode.points(1, 1) & ","
    yColumns = yColumns & myNode.points(1, 2) & ","
  Next
  
  xColumns = Left(xColumns, Len(xColumns) - 1)
  yColumns = Left(yColumns, Len(yColumns) - 1)
  
  Dim xValues As Variant: xValues = Split(xColumns, ",")
  Dim yValues As Variant: yValues = Split(yColumns, ",")
  Dim i As Integer
  
  For i = 1 To myShape.Nodes.Count
    Debug.Print "(X" & i & ",Y" & i & ") = (" & xValues(i - 1) & "," & yValues(i - 1) & ")"
  Next i
End Sub

Open in new window


This free PowerPoint resource file is why I needed to understand this part of the OM.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.