Happy Easter!

Feel happy for this title, my first idea was "He died for your sins". This would not have matched the mood of my topic.

So I felt like I ought to do something for Easter and having began being "artsy" with my Piet Mondrian series, I thought I continue on this path.

So the topic today are ornaments. I googled a lot of pictures for inspiration.

The first element needed would be a nice spline with smooth curvature transitions. The builtin tool for splines is not only difficult to handle, but the documentation of splines and bezier curves is bad. I haven't for instance found a definition of the knot cell in a spline row.

So I built a helper shape consisting of a straight line, with control points that I used as array with the drawspline function.

Since the sort order of the points matters I wanted to show their indices. I used smarttags. They display the hint text only when you hover over them, well better than no info at all.

`def draw_constructor():`

x1 = W/2 - W/10

x2 = W/2 + W/10

y1 = y2 = H/2

shp = vPg.DrawLine(x1,y1,x2,y2)

shp.AddSection(visSectionProp)

shp.AddSection(visSectionControls)

shp.AddSection(visSectionSmartTag)

row = shp.AddNamedRow(visSectionProp, 'num_points', visTagDefault)

N = 10

shp.Cells('prop.num_points.Label').Formula = chr(34) + 'num_points' + chr(34)

shp.Cells('prop.num_points.Type').Formula = 1

shp.Cells('prop.num_points.Format').Formula = chr(34) + ';'.join([str(i+1) for i in range(N)]) + chr(34)

shp.Cells('prop.num_points').Formula = 1

for i in range(N):

shp.AddRow(visSectionControls, visRowLast, visTagDefault)

shp.Cells('Controls.Row_' + str(i+1) + '.XCon').Formula = 'if(prop.num_points>=' + str(i+1) + ';0;6)'

shp.AddRow(visSectionSmartTag, visRowLast, visTagDefault)

shp.Cells('SmartTags.Row_' + str(i+1)).Formula = 'Controls.Row_' + str(i+1)

shp.Cells('SmartTags.Row_' + str(i+1) + '.Y').Formula = 'Controls.Row_' + str(i+1) + '.Y'

shp.Cells('SmartTags.Row_' + str(i+1) + '.DisplayMode').Formula = 2

shp.Cells('SmartTags.Row_' + str(i+1) + '.Disabled').Formula = 'if(prop.num_points>=' + str(i+1) + ';False;True)'

shp.Cells('SmartTags.Row_' + str(i+1) + '.Description').Formula = chr(34) + str(i+1) + chr(34)

return shp

And the whole exercise makes only sense if you can randomize the points, so I added an according function.

`def randomize_points(shp, N = 10, dy = None):`

len_ = ((shp.Cells('EndX').ResultIU - shp.Cells('BeginX').ResultIU) ** 2 + (shp.Cells('EndY').ResultIU - shp.Cells('BeginY').ResultIU) ** 2) ** 0.5

d = len_ / N

if not dy:

dy = d

distribute_points(shp, N)

for i in range(N):

shp.Cells('Controls.Row_' + str(i+1)).Formula = shp.Cells('Controls.Row_' + str(i+1)).ResultIU + d * (random.uniform(0,1))

shp.Cells('Controls.Row_' + str(i+1) + '.Y').Formula = dy * (0.5 - random.uniform(0,1))

The distribute_points function does what its name suggests. I place first the points in equal distances on the line, then I randomize their position.

`def distribute_points(shp, N = 10):`

len_ = ((shp.Cells('EndX').ResultIU - shp.Cells('BeginX').ResultIU) ** 2 + (shp.Cells('EndY').ResultIU - shp.Cells('BeginY').ResultIU) ** 2) ** 0.5

d = len_ / N

for i in range(N):

shp.Cells('Controls.Row_' + str(i+1)).Formula = d * i

shp.Cells('Controls.Row_' + str(i+1) + '.Y').Formula = d

Now you may want to not use all the 10 points provided. So you chose the desired number in the props of the helper shape and write a function that returns only the points that are within the chosen range.

(You will recall from the code above, that the points not in use get a "XCon" value of 6, so checking if their value is zero means they are visible.)

`def active_points(shp):`

N = 10

L = []

for i in range(N):

if shp.Cells('Controls.Row_' + str(i+1) + '.XCon').ResultStr('') == '0':

L.append([shp.Cells('Controls.Row_' + str(i+1)).ResultIU, shp.Cells('Controls.Row_' + str(i+1) + '.Y').ResultIU])

return L

The coordinates you get back from your points are relative to their parent - the helper shape.

We need to translate them into absolute ones.

As long as I kept the line horizontal the conversion was easy.

But as soon you inclined it, the formulas broke. I needed to refer to matrix transformations.

Nice topic:

https://en.wikipedia.org/wiki/Transformation_matrixBut I cheated and found a ready formula.

You will furthermore need a function to flatten the list of lists, as Visio expects an array of consecutive values xi,yi.

`def ar_points(L):`

return [item for sublist in L for item in sublist]

def absolutify(shp):

x1 = shp.Cells('BeginX').ResultIU

y1 = shp.Cells('BeginY').ResultIU

x2 = shp.Cells('EndX').ResultIU

y2 = shp.Cells('EndY').ResultIU

a = shp.Cells('Angle').Result(visRadians)

L1 = active_points(shp)

for i,pt in enumerate(L1):

x_ = pt[0] * math.cos(a) - pt[1] * math.sin(a)

y_ = pt[0] * math.sin(a) + pt[1] * math.cos(a)

L1[i] = [x_,y_]

L1 = [[x + x1, y + y1] for [x,y] in L1]

L = [x1,y1] + ar_points(L1) + [x2,y2]

return L

That was a long introduction. Only now we come to drawing the spline.

And I made two versions of the drawing function. One draws the spline as you prepared it with its control points.

The second scrambles the points before drawing.

`def spline_1(shp):`

pts = absolutify(shp)

shp2 = shp.Parent.DrawSpline(pts,0.1,1)

vApp.ActiveWindow.DeselectAll()

vApp.ActiveWindow.Select(shp, visSelect)

return shp2

def random_spline_1(shp, dy = None):

randomize_points(shp, len(active_points(shp)),dy)

pts = absolutify(shp)

shp2 = vPg.DrawSpline(pts,0.1,1)

vWin.DeselectAll()

vWin.Select(shp, visSelect)

return shp2