Outline of a Shape

The algorithm for drawing the Outline of a Shape is as follows:

1) The Destination starts filled with whatever background we need to draw on top of. The distance buffer is assumed to be initially filled with 'infinite' (in practice, any sufficiently large value will do).

2) The algorithm continues by following the Trajectory specified by the outline. This is done in discrete steps or jumps. At each step, the pen is moved to a new point in the trajectory, jumping by a fraction of a pixel. A step of about 1/4 of a pixel is small enough. This is done using float (or fixed point) arithmetic.

3) For each step, the pen is at some (float) point. The possibly affected pixels are in the circle defined by the pen outer diameter (w+r). For each of these pixels, the Euclidean Distance from the pixel center to the pen center is computed. This distance is stored in the distance buffer if it is less that the value already there.

4) When the pen has finished following the prescribed trajectory, the distance buffer contains, for each pixel, the minimum distance d to the trajectory. The opacity value is obtained by evaluating the opacity function of the filtering pen at distance d. This opacity value is used to alpha-blend the shape color over the destination frame buffer. Afterwards, the distance buffer is filled with infinite, to be ready for the next call.

[…] Figure 5. Each pixel is painted with the opacity obtained by taking its distance to the path, and using it as the argument to the prefiltering pen function. To illustrate, for some pixels, the small black arrows show this distance to the path. As a further example, for the two arrows closest to the bottom of the figure, the distance is projected (dashed lines) over the filtering pen function, to show how to obtain the opacity values used to paint the pixels. The same procedure is used for all pixels.

chat.openai

Here's a possible implementation of the algorithm in JavaScript:

function drawOutline(destination, trajectory, penOuterDiameter, filteringPen) { const width = destination.width; const height = destination.height; const distanceBuffer = []; for (let i = 0; i < width * height; i++) { distanceBuffer[i] = Number.POSITIVE_INFINITY; } for (let i = 0; i < trajectory.length - 1; i++) { const start = trajectory[i]; const end = trajectory[i + 1]; const step = (end - start) / 4; let current = start; while (current <= end) { const x = Math.round(current.x); const y = Math.round(current.y); const r = penOuterDiameter / 2; for (let i = Math.max(0, x - r); i <= Math.min(width - 1, x + r); i++) { for (let j = Math.max(0, y - r); j <= Math.min(height - 1, y + r); j++) { const distance = Math.sqrt((i - x) ** 2 + (j - y) ** 2); if (distance < distanceBuffer[i + j * width]) { distanceBuffer[i + j * width] = distance; } } } current = current.add(step); } for (let i = 0; i < width * height; i++) { const x = i % width; const y = Math.floor(i / width); const opacity = filteringPen(distanceBuffer[i]); const index = (x + y * width) * 4; destination.data[index + 3] = opacity * 255; } }

~

The graphics engines commonly used to draw vector graphics apply the antialiasing technique known as Pixel Coverage.

kohonen-stars github is an example for our library kohonen github , a basic implementation of SOM algorithm in JavaScript.

pages/outline-of-a-shape

http://code.dreyeck.ch/assets/pages/outline-of-a-shape/outline.html HEIGHT 333

Fixed "Uncaught SyntaxError: missing = in const declaration outline.html:37:9" commit

Uncaught (in promise) ReferenceError: destination is not defined stream http://code.dreyeck.ch/assets/pages/outline-of-a-shape/outline.html:78 <anonymous> http://code.dreyeck.ch/assets/pages/outline-of-a-shape/outline.html:74 outline.html:78:12

[…] async function stream () { while (true) { outline = drawOutline(destination, trajectory, penOuterDiameter, filteringPen); history = Date.now(); refresh(); await delay(norm(1200)); }; […]

Perhaps it was a bit hasty to delete generateGrid, as it could serve as the destination frame buffer (see below: "usually 24 bit RGB") where the drawing is to be done?

Prefiltering Antialiasing for General Vector Graphics ⇒ the pixel grid, see Figure 4. Our sample shape, as drawn by an ideal continuous prefiltering pen, shown over the pixel Grid [⇒ Generate a Rectangular Grid]. The light gray background is more visible near the borders of the outline, where the pen gets progressively translucent.

Prefiltering Antialiasing for General Vector Graphics: The main parts of the disclosed solution are: […] b) The **destination frame buffer** where the drawing is to be done. This is usually 24 bit RGB, or as appropriate for the application. The destination specifies the pixel grid that the shape must be sampled at.

and regarding the grid: could we change our Generate a Rectangular Grid page so that we don't have to click, but use the mouse as a Pen? It would then generate several such ghost pages in the form of a Ghost Lineup instead of one ghost page?!

[…] let destination = [8.5,7.06217782649107]; // Item Clicked on Generate a Rectangular Grid page // replace generateGrid by drawOutline const drawOutline = (destination, trajectory, penOuterDiameter, filteringPen) => { […]

~

Our dreams for this technology are pulled in different directions by competing traditions as to what we put into computers and what we expect to happen there. Hypertext straddles this division and is thus able to go either way. But our dreams are most likely aligned with one or the other.