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