I just tried to use bezierPath.polygon()
since bezierPath.rect()
and bezierPath.oval()
work.
this does not work (and can be solved in other ways), but maybe it would be nice and somewhat logic since rect and oval do work?
Posts made by jo

bezierPath.polygon()?

RE: How can I split a path into multiple segments?
@jansindl3r keep in mind that in a Bezier curve time and distance do not correlate. if you construct a point at time .1 it will not be at one tenth of the curve length.
afaik the most common technique to get similar distances is to calculate lots of points on the curve(s), calculate some distances and select the closest points.
this link has some very helpful information.
good luck.

RE: Output text
@gferreira
probably not pythonic
but i was surprised this works
(if you keep things black and white)coin = randint(0, 1) color1 = coin, color2 = not coin,

Lissajous table
Here is some code to draw a Lissajous table.
Change thefunc_x
and/orfunc_y
(sin or cos) and/or add some value to thedelta
to get other curves.
Well, actually the curves are just polygons.#  # lissajous table #  # settings cols = 12 rows = 8 cell_s = 80 r_factor = .8 # fraction of the cell size to have a gap between them func_x = cos # should be `sin` or `cos` func_y = sin # should be `sin` or `cos` delta = 0 #pi/3 # some angle in radians density = 360 # amount of points per cell – higher values make nicer curve approximations #  # calculated settings radius = (cell_s * r_factor) / 2 step = (2 * pi) / density pw = cell_s * (cols + 1) ph = cell_s * (rows + 1) x_coords = { (col, d) : func_x(step * (col + 1) * d + pi/2 + delta) * radius for col in range(cols) for d in range(density) } y_coords = { (row, d) : func_y(step * (row + 1) * d + pi/2) * radius for row in range(rows) for d in range(density) } #  # function(s) def draw_cell(pos, col, row): cx, cy = pos points = [(cx + x_coords[(col, f)], cy + y_coords[(row, f)]) for f in range(density)] polygon(*points) #  # drawings newPage(pw, ph) rect(0, 0, pw, ph) fontSize(12) translate(0, ph) fill(1) text('δ\n{0:.2f}°'.format(degrees(delta)), (cell_s * (1  r_factor)/2, 20)) for col in range(1, cols+1): cx = col * cell_s + cell_s * (1  r_factor)/2 text('{0}\n{1}'.format(func_x.__name__, col), (cx, 20)) for row in range(1, rows + 1): cy = row * cell_s + cell_s * (1  r_factor) text('{0}\n{1}'.format(func_y.__name__, row), (cell_s * (1  r_factor)/2, cy)) fill(None) stroke(.5) strokeWidth(1) for col in range(cols): for row in range(rows): draw_cell((cell_s * col + cell_s * 1.5,  cell_s * row cell_s * 1.5), col, row)

RE: How to mirror/flip a object?
@habakuk nice to read that you can use some of the code!
about the lissajous: I am not sure what exactly you want to achieve. the german wiki entry has vizualisations for a few values. you might want to change the value for
a
andb
and alsodelta
, eg:a = 1 b = 3 delta = pi/4
I do not have your variable font so I cannot test this but I was wondering why you added the plus and minus 20 here:
curr_axis1 = ip(axis1_min + 20, axis1_max  20, x) curr_axis2 = ip(axis2_min + 20, axis2_max  20, y)
lastly if you want to draw the shadow in the back, just put the lines of code before you call the pink drawing. you might then use the
savedState()
so draw it without the mirroring.good luck, jo!

RE: How to mirror/flip a object?
the mirrored drawing is probably happening outside your canvas. keep in mind that every transformation (scale, skew, rotation, etc) is always starting from the origin. so a mirroring from the default origin (left, bottom corner) will be to the left or below your canvas. see the example below with a shifted origin.
pw = 1000 ph = 400 txt = "SHADE" newPage(pw, ph) translate(0, 160) # shift the origin up a bit fontSize(300) text(txt, (pw/2, 0), align = 'center') scale(x=1, y=.5) # the mirroring text(txt, (pw/2, 0), align = 'center')

RE: More variable fonts nonsense
@Christine hello!
I just downloaded the zip from github and did test a few of the scripts in drawbot without any issue. did you change the folder structure and / or do you get an error message? what OS are you using? 
RE: Random Lines with no intersection
ok if i got you right you want the function to
return
a value — in your case the height of the legs. this value could then be used in awhile
loop. while loops are quite helpful but might run forever if done incorrect. I adapted the code a bit so the legs function returns the length of the legs which is then added to the y value in the while loop. There is a check inside the while loop so it will run for a maximum of 3000 times.#  # settings pw = ph = 500 amount_x = 10 cell_w = pw / amount_x max_h = 50 gap = max_h * .2 #  # fucntion(s) def legs(pos, max_w, max_h): x, y = pos length = random() * max_h step_s = random() * max_w polygon((x  step_s, y) , (x, y + length), (x + step_s, y), close = False) return length #  # drawings newPage(pw, ph) fill(None) stroke(0) strokeWidth(2) for x in range(amount_x): count = 0 y = random() * max_h while y < ph: y += legs((x * cell_w + cell_w/2, y), cell_w/2, max_h) y += gap count += 1 if count > 3000: break
and the while loop without the check:
for x in range(amount_x): y = random() * max_h while y < ph: y += legs((x * cell_w + cell_w/2, y), cell_w/2, max_h) y += gap

Vera Molnar recoded
Vera Molnar is a great computer generated arts pioneer and is still working. I could not resist to emulate and try to recode some of her artworks. Using drawbot to do so is great fun and pretty easy — one can only wonder about the struggles she had to overcome. With all due respect here is a link to github with my humble attempts.

Solar eclipse
if you are not in chile or argentina for the solar eclipse this code can gif you solace:
#  # settings pw = 800 ph = 500 dia = pw * .45 frames = 20 #  # function(s) def a_page(): newPage(pw, ph) rect(0, 0, pw, ph) radialGradient((pw/2, ph/2), (pw/2, ph/2), [(1, 0.8, 0), (0.9, 0.6, 0) ]) oval(pw/2dia/2, ph/2dia/2, dia, dia) def ip(a, b, f): return a + (ba)*f #  # drawings for frame in range(frames): a_page() f = frame / frames x = ip(pw/4, pw/4*3, f) y = ip(0, ph, f) fill(0) if abs(x  pw/2) < .2: shadow((0, 0), dia/3, (1, 1, 1)) oval(x  dia/2, y  dia/2, dia, dia) # saveImage('solar_eclipse.gif')

RE: Random Lines with no intersection
@mrrouge
I am not quite sure if this is what you are asking for but you either need totranslate(x,y)
the maximum leg length or width or just draw the legs at the position you want them to. In yourlegs()
function you do have the parametersx
andy
but they are not used inside the function.Also I do recommend to use a variable if you repeatedly use the same value. So instead of
randint(20, 100)
you could usemin_val = 20 max_val = 100 randint(min_val, max_val)
I put together a simple “legs” drawing that might help.
Good luck#  # settings pw = ph = 500 amount_x = 10 amount_y = 4 cell_w = pw / amount_x cell_h = ph / amount_y #  # fucntion(s) def legs(pos, max_w, max_h): x, y = pos length = random() * max_h step_s = random() * max_w polygon((x  step_s, y  length) , pos, (x + step_s, y  length), close = False) #  # drawings newPage(pw, ph) fill(None) stroke(0) strokeWidth(2) for x in range(amount_x): for y in range(amount_y): legs((x * cell_w + cell_w/2, y * cell_h + cell_h/2), cell_w/2, cell_h/2)

RE: Tutorial request: how to animate a variable font
to center the text on the page you should set the align parameter to center and set the x coordinate of the text to half of the page width:
text(txt, (width()/2, 70), align ='center')

RE: Tutorial request: how to animate a variable font
@Anna hi
do you get error messages?
if so you should post them.
and maybe post some code.
what is going wrong and what would you like it to do? 
listInstances(font)?
i hope i am not missing this in the documentation …
I was wondering if there is a way to list all instances of a variable font? and then possibly get the values of the axes at these instances.
thanks j 
Drawing a circle with Beziers
You cannot draw a circle with Beziers.
When I first read this some time ago I was a bit confused.
Why is this not a circle. It sure looks like one.After learning a bit more about curve descriptions and calculations I kind of agree but the quote should better say:
You cannot draw a perfect circle with Beziers.
Getting the best tangent values.
We have a formula to calculate points on a Bezier Curve.
x = (1t)**3*p1[0] + 3*(1t)**2*t*t1[0] + 3*(1t)*t**2*t2[0] + t**3*p2[0] y = (1t)**3*p1[1] + 3*(1t)**2*t*t1[1] + 3*(1t)*t**2*t2[1] + t**3*p2[1]
p1 and p2 are the two endpoints and t1 and t2 the two tangent points. t is the factor (01) at which we want the point on the curve.
On a bezier curve with the following coördinates:
p1 = (1, 0) p2 = (0, 1) t1 = (1, tangent_val) t2 = (tangent_val, 1)
we can calculate the tangent_val if we assume that the point at t=0.5 (halfway the curve) is exactly on the circle. Halfway of a quartercircle is 45 degrees so x and y are both at 1/sqrt(2). If we plug these numbers into the formula we can calculate the tangent value:
1/sqrt(2) = (1.5)**3*1 + 3*(1.5)**2*.5*1 + 3*(1.5)*.5**2*tangent_val + .5**3*0 ... tangent_val = 4*(sqrt(2)1)/3 ≈ 0.5522847493
With this tangent_val we can calculate other points on the bezier curve. Of which we can calculate the angle in respect to the center point at (0, 0). And with this angle we can use sine and cosine to get the point that would be on a circle.
We can also calculate the distance of the bezier point and see where the offset is the biggest. As we can see the difference is really tiny. With a radius of 1 the biggest off value is 1.0002725295591013 (not even one permille). The difference is hardly visible so I added only the top part of a really long bar chart to make it show.
See the image below:
Code
#  # settings pw, ph = 595, 842 #A4 margin = 70 tang = 4*(sqrt(2)1)/3 r = pw  2 * margin density = 90 # amount of points dia_ = 1 # diameter to draw the points r_plot = 100000 # this is a magnifying factor to make the tiny difference visible. r_plot_y = margin  r_plot #radius plotting c_plot_y = ph  3 * margin  r # circle plotting #  # functions def get_bezier_p(p1, t1, t2, p2, t): ''' get the point on a bezier curve at t (should be between 0 and 1) ''' x = (1  t)**3 * p1[0] + 3*(1  t)**2 * t * t1[0] + 3*(1  t)* t**2 * t2[0] + t**3 * p2[0] y = (1  t)**3 * p1[1] + 3*(1  t)**2 * t * t1[1] + 3*(1  t)* t**2 * t2[1] + t**3 * p2[1] return x, y def get_dist(p1, p2): ''' returns the distance between two points (x, y)''' return sqrt((p2[0]p1[0])**2 + (p2[1]p1[1])**2) #  # drawings newPage(pw, ph) fill(1) rect(0, 0, pw, ph) translate(margin, margin) # draw the tangent points rect(r  dia_, c_plot_y + tang*r  dia_, dia_*2, dia_*2) rect(tang*r dia_, c_plot_y + r  dia_, dia_*2, dia_*2) max_off = 1 for i in range(density+1): #  # bezier # calculate the factor f f = i/density fill(0) if i == 0 or i == (density): dia = dia_ * 4 else: dia = dia_ # get the coordinates for the point at factor f x_b, y_b = get_bezier_p( (1, 0), (1, tang), (tang, 1), (0, 1), f ) # get the distance of the point from the center at (0, 0) r_b = get_dist((0, 0), (x_b, y_b)) max_off = max(max_off, r_b) # get the angle of the point angle = atan2(y_b, x_b) # draw the point and a rect for the bar chart oval(x_b * r  dia/2, c_plot_y + y_b * r  dia/2, dia, dia) rect((angle/(pi/2)) * r  dia_/2, r_plot_y, dia_, r_plot * r_b) #  # circle fill(1, 0, 0) # get the point on a circle x_c, y_c = cos(angle), sin(angle) # draw the point and a rect for the bar chart oval(x_c * r  dia/2, c_plot_y + y_c * r  dia/2, dia, dia) rect((angle/(pi/2)) * r  dia_/2, r_plot_y, dia_, r_plot) fill(.3) if i % 10 == 0: x_p, y_p = r * f, margin #* 1.5 rect(x_p  .25, y_p, .5, 30) text('%d°' % (f * 90), (x_p2, y_p * 1.5)) print (max_off) text('%.6f' % max_off, (50, max_off * r_plot  r_plot + margin  2)) text('%.6f' % 1, (50, margin  2))
The maybe more interesting peculiarity of bezier curves is the difference between time and distance. We can compare the bezier distance at factor f (01) to the circle if we use the same factor to get the appropriate fraction of 90 degrees.
Code
#  # settings pw, ph = 595, 842 #A4 margin = 70 tang = 4*(sqrt(2)1)/3 r = pw  2 * margin density = 90 # amount of points dia_ = 1 # diameter to draw the points r_plot = 100000 # this is a magnifying factor to make the tiny difference visible. r_plot_y = margin  r_plot #radius plotting c_plot_y = ph  3 * margin  r # circle plotting #  # functions def get_bezier_p(p1, t1, t2, p2, t): ''' get the point on a bezier curve at t (should be between 0 and 1) ''' x = (1  t)**3 * p1[0] + 3*(1  t)**2 * t * t1[0] + 3*(1  t)* t**2 * t2[0] + t**3 * p2[0] y = (1  t)**3 * p1[1] + 3*(1  t)**2 * t * t1[1] + 3*(1  t)* t**2 * t2[1] + t**3 * p2[1] return x, y def get_dist(p1, p2): ''' returns the distance between two points (x, y)''' return sqrt((p2[0]p1[0])**2 + (p2[1]p1[1])**2) #  # drawings newPage(pw, ph) fill(1) rect(0, 0, pw, ph) translate(margin, margin) # draw the tangent points rect(r  dia_, c_plot_y + tang*r  dia_, dia_*2, dia_*2) rect(tang*r dia_, c_plot_y + r  dia_, dia_*2, dia_*2) for i in range(density+1): #  # bezier # calculate the factor f f = i/density fill(0) if i == 0 or i == (density): dia = dia_ * 4 else: dia = dia_ # get the coordinates for the point at factor f x_b, y_b = get_bezier_p( (1, 0), (1, tang), (tang, 1), (0, 1), f ) # get the distance of the point from the center at (0, 0) r_b = get_dist((0, 0), (x_b, y_b)) # get the angle of the point angle = atan2(y_b, x_b) # draw the point and a rect for the bar chart oval(x_b * r  dia/2, c_plot_y + y_b * r  dia/2, dia, dia) rect((angle/(pi/2)) * r  dia_/2, r_plot_y, dia_, r_plot) #  # circle fill(1, 0, 0) # get the point on a circle x_c, y_c = cos( pi/2*f ), sin( pi/2*f ) # draw the point and a rect for the bar chart oval(x_c * r  dia/2, c_plot_y + y_c * r  dia/2, dia, dia) rect(f * r  dia_/2, r_plot_y, dia_, r_plot) fill(.3) if i % 10 == 0: x_p, y_p = r * f, margin #* 1.5 rect(x_p  .25, y_p, .5, 30) text('%d°' % (f * 90), (x_p2, y_p * 1.5))

RE: More variable fonts nonsense
oh, yes!
that is of course a very nice solution.
will update some of the scripts in the next days.
thanks! 
RE: More variable fonts nonsense
@frederik thank you!
There is one 'issue' that makes it difficult to abstract the code. And I was wondering if this could be implemented differently in drawbot.
when setting the fontVariations the axis tag cannot be a variable:
fontVariations( axisTag = someValue )
It would be quite convenient to do:
myAxis = 'wght' # or whatever # ... do some stuff with myAxis # ... and then just place the variable name fontVariations( myAxis = someValue )
or to just iterate through axes without ever having to type the axistag.

More variable fonts nonsense
After a request to add more axes to the previous simple variable fonts tutorial the follow up escalated a bit. Since there are several files I thought it might be better to put them on github.

RE: Tutorial request: how to animate a variable font
@hanafolkwang hi, this is half a year too late and probably not even what you were looking for but I made a few more examples to generate .gifs of variable fonts.
Since there are several files I put them on github. 
RE: Tutorial request: BezierPath() to pen
thanks @gferreira and @frederik!
great help. i managed to botch my rough attempt at type on a curve.#  # imports from fontTools.pens.basePen import BasePen #  # settings pw = ph = 400 r = 89 angle = pi * 3.1 baseline = ph/2 + r txt = 'HELLO' tang_f = .333 # tangent factor center = pw/2, baseline focus = pw/2, baseline  r #  # functions def ip(a, b, f): return a + (ba) * f def conv_point(p): x, y = p f = (x  pw/2) / (pw/2) a = angle/2 * f x_conv = center[0] + cos(a + pi/2) * (r + y  baseline) y_conv = center[1] + sin(a + pi/2) * (r + y  baseline)  r return x_conv, y_conv class convert_path_pen(BasePen): def __init__(self, targetPen): self.targetPen = targetPen def _moveTo(self, pt): self.targetPen.moveTo( conv_point(pt) ) self.prev_pt = pt def _lineTo(self, pt): if self.prev_pt[0] == pt[0]: self.targetPen.lineTo( conv_point(pt) ) else: tang_1 = ip(self.prev_pt[0], pt[0], tang_f), ip(self.prev_pt[1], pt[1], tang_f) tang_2 = ip(self.prev_pt[0], pt[0], 1tang_f), ip(self.prev_pt[1], pt[1], 1tang_f) self.targetPen.curveTo(conv_point(tang_1), conv_point(tang_2), conv_point(pt)) self.prev_pt = pt def _curveToOne(self, tang_1, tang_2, pt): self.targetPen.curveTo(conv_point(tang_1), conv_point(tang_2), conv_point(pt)) self.prev_pt = pt def _closePath(self): self.targetPen.closePath() #  # drawings path = BezierPath() path.text(txt, font="Helvetica", fontSize = 80, align = 'center', offset=(0, baseline)) newPage(pw, ph) trans_path = BezierPath() trans_pen = convert_path_pen( trans_path ) path.drawToPen( trans_pen ) drawPath(trans_path)
obligatory gifs