In case it's relevant, here is the result of the search:
In case it's relevant, here is the result of the search:
@gferreira Thanks, Gustavo! I don't know how I've managed to go nearly four years without knowing this—I feel like I've used this method in the past to restore a reference. Lesson learned.
a = 1 b = [(a, a), (a + 1, a + 1)] d = 1 g = b print('original:', b, 'new, pending:', g) c = [0, 1] for n in c: g[n] = (g[n] - d, g[n] - d) print('original:', b, 'new:', g)
original: [(1, 1), (2, 2)] new, pending: [(1, 1), (2, 2)] original: [(0, 0), (1, 1)] new: [(0, 0), (1, 1)]
What is going on here? Why does changing g also change b?
I thought after I posted that pens would be part of a more elegant solution—my problem reminded me of some things Just has shown me, and I recall pens coming up in that conversation.
The fix I did find was to create a function that creates and returns a new path from the uncorrected path contour by contour and segment by segment, correcting points as needed by rounding to the nearest implied-grid increment. But now that classes are out I'll take the time to learn about pens (finally).
I'd still like to figure out why I was having the problem in the first place. At 1000pt the glyphs in Kast, my font I've been working with, are all exactly 692pt wide. With the proper linespacing and every other line offset 346pt (the width of the <space> glyph) all the points in adjacent glyphs horizontally and vertically should match exactly to allow me to combine their paths seamlessly.
Looking at the errors—maybe I'll put the analyses up in another post, I saved my test code—I felt like there was something about the formatted string that was introducing systematic misalignment, and removeOverlap() was also doing some math that threw off some points in the combined paths.
For reasons I can explain in detail if needed, I'd like to edit the oncurve points of a BezierPath before drawing the path, checking each point in turn to make sure its coordinates fall on an implied alignment grid and correcting it as needed.
Two methods that apparently don't work are:
for pt in path.onCurvePoints: if test(pt) == Fail or test(pt) == Fail: pt = (correctedCoordinate, correctedCoordinate)
for pt in range(len(path.onCurvePoints)): if test(path.onCurvePoints[pt]) == Fail or test(path.onCurvePoints[pt]) == Fail: path.onCurvePoints[pt] = (correctedCoordinate, correctedCoordinate)
Good to know, thanks. But that means that default linespacing = fontSize + 1?
,Here's something interesting I found while writing code to generate the dimensions of a canvas based on the amount and size of the text I want to set in a square-ish field on the canvas.
fontFamily = 'kast_4_a' fontStyle = 'top_0' fSize = 1000 test_string = FormattedString() test_string.append('a', fontSize = fSize, font = fontFamily + '-%s' %fontStyle) print(test_string.size())
yields the following:
<NSSize width=692.0 height=1001.0>
I can confirm that DrawBot's size() returns a height of any fontSize + 1.
I was hoping to use the size of the text box to calculate the height of the canvas, but now I have to adjust that it seems. What's happening here?
That's a nice workaround, thanks! I didn't consider the resolution parameter since I was thinking in terms of the absolute size of the image in pixels only. Maybe I'll put in a feature request, since pixel dimensions feels like a more intuitive way to characterize output.
Since DB's saveImage() doesn't allow me to resize images as I generate them (and is that right?), I was hoping to use the openCV-python library. But even after installing with pip3.7 and no --user flag I can't get DB to import cv2. Has anyone else run into this problem or gotten openCV to work in DB? Or does anyone have another suggestion for adjusting the size of saved images?
@frederik But I figured it out! Obviously the fill/stroke properties from the formatted strings won't find their way into the Bezier paths, but I can just add them to the paths directly. (I guess I could just use text, but I want some flexibility in the code for when Kast is a variable font.) Just have to write the code to clean/chop up strings into even lines and size the text to position/size the lines to make fields.
A really useful Maurice-bot would tell me to do the work first and then post just to share the results and the code.
Seriously, I should ask for my own category on this forum, where I can post questions as a way of prompting myself to figure out the answers on my own. The solution is to draw the outlines of all the text in each color/layer into a single path, then remove overlaps, fill and stroke as needed to create/reinforce the illusion of continuous solid surfaces. It should be relatively easy to set up basic string operations to slice up sample text and to get the dimensions of the text boxes on the fly to adjust positioning and offset on the canvas before drawing. Unlike the above image, this one is all DrawBot:
This is an old version of Kast, by the way—new versions in different dimensions and side textures coming soon, among other places to a TDC type design competition entry near you …
Okay, figured out the answer to the first question, just didn't know how to phrase the search: draw text into a BezierPath. Alignment will be a fun problem to tackle. Still searching for an answer to the second question, about finding shapes with similar properties and performing pathfinder operations on them.
This is sort of a follow-on to @ThunderNixon Stephen's thread from August: https://forum.drawbot.com/topic/184/efficient-way-to-convert-all-text-to-outlines-in-a-multi-page-doc
Is there any easy, or even not-stupidly-hard way, to:
convert type to outlines, preferably retaining its fill color?
find all shapes with a specified fill color and perform Boolean pathfinder-like operations on them?
I'm asking because once I have revisions to my font Kast complete, I'd like to be able to use it to generate specimen fields with effects like this:
and it would be nice (a) to generate them with scripts and (b) without having to use Illustrator. (I think I speak for most DB users when I say: the less I can get away with having to use Illustrator, or really any Adobe product once PageBot is a bit further along, the better.)
The finished product, a tribute to Bridget Riley's Rustle 6 (2015).
import math canvasX = 600 canvasY = 600 nFrames = 120 side = canvasX/14 height = (side * sqrt(3))/2 center = (side/sqrt(3))/2 nX = 14 nY = 15 offsetX = side offsetY = height from datetime import * time = datetime.now() def interpolate(pt1, pt2, ratio): assert ratio <= 1 return (pt1 + (ratio * (pt2 - pt1)), pt1 + (ratio * (pt2 - pt1))) def triangle(s, h, c, inout): assert inout >= 0 and inout <= 1 start = (-s/2, h/2) pivot = (0, h/2 - (c * 3)) target = (s/2, h/2) control = interpolate((s/2, -c/2), (0, h/2 - c), inout) radius = s # optimal distance to control points is (4/3)*tan(pi/(2n); thank you https://stackoverflow.com/users/16673/suma for answering this question https://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves control_ratio = ((4/3 * tan(pi/12)) * radius) / (2 * c) controlP = interpolate(pivot, control, control_ratio) controlT = interpolate(target, control, control_ratio) triangle = BezierPath() triangle.moveTo(start) triangle.lineTo(pivot) triangle.curveTo(controlP, controlT, target) triangle.closePath() drawPath(triangle) def frame(x, y, m): f = BezierPath() g = BezierPath() f.rect(0, 0, x, y) g.rect(m, m, x - (2 * m), y - (2 * m)) f = f.difference(g) f.closePath() drawPath(f) # original Riley plan for Rustle 6 plan = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 1, 0, 0], [-1, 0, -1, 0, 0, 0, 0, 0, 1, 0, -1, 0, 0], [1, 0, 1, -1, 0, 0, -1, 0, 0, 0, 1, 0, 1, 0], [0, -1, 0, -1, 0, 0, 0, 1, 0, -1, 0, -1, 0], [0, 1, -1, 1, 0, 1, 0, 0, 0, 0, -1, 1, 0, 0], [-1, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0], [0, -1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, -1, 0], [-1, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1], [-1, 0, -1, 0, 1, 0, 1, 0, 0, 0, 1, -1, -1, 0], [0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, -1, -1, 0], [0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ] for n in range(nFrames): newPage(canvasX, canvasY) frameDuration(1/30) ex = sin(radians(n * (360/nFrames))) fill(1) rect(0, 0, canvasX, canvasY) with savedState(): translate(canvasX/2, canvasY/2) fill(0) translate(-(nX - 1) * offsetX/2, (nY - 1) * offsetY/2) for i in range(nY): with savedState(): translate(0, i * -offsetY) if i % 2 == 0: for j in range(nX - 1): with savedState(): translate(offsetX/2 + (j * offsetX), 0) triangle(side, height, center, .5 - (ex * (plan[i][j] * .5))) else: for j in range(nX): with savedState(): translate(j * offsetX, 0) triangle(side, height, center, .5 - (ex * (plan[i][j] * .5))) fill(1) frame(canvasX, canvasY, side/2) saveImage('~/Desktop/riley_tribute_test_01_%s.gif' %time)
The reference image:
I solved a outlining problem from scratch, specifically with rounding corners of an outline with a variable radius, that I talked about on this earlier post here on the forum: https://forum.drawbot.com/topic/181/using-arcto-for-variable-radius-corner-rounding
I wound up using arcTo() to do that (if I get around to it, I'll post the code) and it didn't occur to me that one reason the solution worked was because I wasn't trying to draw anything but a circle-based curve that stayed convex in the same orientation.