# OPENRNDR pen plotting tricks (Axidraw etc.)

**URL:** <https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208>\
**Category:** Tutorials\
**Created:** [February 26, 2021, 5:37pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208 "2021-02-26T17:37:43Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [February 26, 2021, 5:37pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/1 "2021-02-26T17:37:43Z")

</div>

Thread for useful tricks when working with plotters and OPENRNDR.

# 1. Boolean operations (clipMode)

A useful operation when generating designs to plot is using boolean operations. Here some examples:

- `ClipMode.REVERSE_DIFFERENCE` draws shapes as occluded by (behind) existing content

 ![latest.Dedupe-2021-02-26-18.24.01](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/e4fc6521bbd7014d118a67d554f7739647986df5.png)

> **imports**
>
> ```kotlin
> import aBeLibs.geometry.dedupe
> import org.openrndr.KEY_SPACEBAR
> import org.openrndr.application
> import org.openrndr.color.ColorRGBa
> import org.openrndr.dialogs.saveFileDialog
> import org.openrndr.shape.ClipMode
> import org.openrndr.shape.drawComposition
> import org.openrndr.svg.saveToFile
> 
> ```

```kotlin
fun main() {
    application {
        program {
            // create design
            val svg = drawComposition {
                fill = null
                circle(width / 2.0 - 100.0, height / 2.0, 100.0)
                circle(width / 2.0 + 100.0, height / 2.0, 100.0)
                clipMode = ClipMode.REVERSE_DIFFERENCE
                circle(width / 2.0, height / 2.0, 100.0)
            }
            extend {
                drawer.clear(ColorRGBa.WHITE)
                // render design
                drawer.composition(svg)
            }
            keyboard.keyDown.listen {
                when (it.key) {
                    KEY_SPACEBAR -> saveFileDialog(supportedExtensions = listOf("SVG" to listOf("svg"))) { file ->

                        // save design for plotting
                        svg.saveToFile(file)
                    }
                }
            }
        }
    }
}

```

- `ClipMode.UNION` combines shapes

 ![latest.Dedupe-2021-02-26-18.22.20](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/44dbb6e8ff5bb90c0526342a93bf0d075302c5af.png)

```kotlin
val svg = drawComposition {
  fill = null // note that there's no fill!
  circle(width / 2.0 - 100.0, height / 2.0, 100.0)
  clipMode = ClipMode.UNION
  circle(width / 2.0, height / 2.0, 100.0)
  circle(width / 2.0 + 100.0, height / 2.0, 100.0)
}

```

- `ClipMode.INTERSECTION` keeps the areas in which a new shape overlaps with existing ones

 ![latest.Dedupe-2021-02-26-18.23.43](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/618ee8725ff47d028c634830ff416862bd2b1390.png)

- `ClipMode.DIFFERENCE` uses a shape to delete parts of the existing shapes

 ![latest.Dedupe-2021-02-26-18.21.12](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/892cba141727608ea70130277968965094158e47.png)

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [February 26, 2021, 5:52pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/2 "2021-02-26T17:52:54Z")

</div>

# 2. Deduplicating segments

When plotting the `REVERSE_DIFFERENCE` example above we might have a small issue: let’s open it in Inkscape and move the shapes a bit:  
 ![image](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/0139c49fa4835972554da62c2cde4016dc78ec4b.png)

The issue is that we have repeated lines. This is not an issue when showing the design on the screen, but when plotting, some lines are plotted twice. This will make plotting slower and some lines may look darker than others. No big deal with 3 circles, but with complex designs it does make a difference.

To get around this issue I wrote a simple function called `.dedupe()`. Using it couldn’t be easier: call the method before saving the file like this:

`svg.dedupe().saveToFile(file)`

Here’s the source code for `Composition.dedupe()` and for the required `Segment.contains()`:

```kotlin
/**
 * For a Composition, filter out bezier segments contained in longer bezier segments.
 * The goal is to avoid drawing lines multiple times with a plotter.
 */
fun Composition.dedupe(err: Double = 1.0): Composition {
    val segments = this.findShapes().flatMap {
        it.shape.contours.flatMap { contour -> contour.segments }
    }
    val deduped = mutableListOf<Segment>()
    segments.forEach { curr ->
        if (deduped.none { other -> other.contains(curr, err) }) {
            deduped.add(curr)
        }
    }
    return drawComposition {
        contours(deduped.map { it.contour })
    }
}

```

```kotlin
/**
 * Simple test to see if a segment contains a different Segment.
 * Compares start, end and two points at 1/3 and 2/3.
 * Returns false when comparing a Segment to itself.
 */
fun Segment.contains(other: Segment, error: Double = 0.5): Boolean =
    this !== other &&
            this.on(other.start, error) != null &&
            this.on(other.end, error) != null &&
            this.on(other.position(1.0 / 3), error) != null &&
            this.on(other.position(2.0 / 3), error) != null

```

It probably doesn’t cover every case but maybe it helps someone?

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [February 26, 2021, 6:23pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/3 "2021-02-26T18:23:53Z")

</div>

# 3. Manipulating SVG files

Instead of writing one program that produces a finished design ready for plotting it can be useful to work in multiple passes. For example, write a program that creates a basic design, then a second one that applies an effect to the output of the first program.

If you have several such effects you can experiment with applying them in different order, or with different parameters, etc.

An example of such an SVG altering effect can look like this:

```kotlin
// load svg
val original = loadSVG("my_generative.svg").findShapes().map { it.shape }

// some kind of contour I will use to cut and bend my svg file
val knife = contour { ... }

// process all the contours from the original file using .split()
val cutLines = original.flatMap {
    it.contours // get contours from shapes
}.flatMap {
    split(it, knife) // For the image below I also apply .bend(), not shown
}

// make a new svg with the modified contours
val modified = drawComposition {
    fill = null
    stroke = ColorRGBa.BLACK
    contours(cutLines)
}

// save result
saveFileDialog(supportedExtensions = listOf("SVG" to listOf("svg"))) {
    it.writeText(writeSVG(modified))
}

```

That’s the approach I used to produce this design:

 ![2021-02-26-190417_694x487_scrot](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/dc313f55efecf51f6b726dc1aa18e2c43a179027.png)

One program produced the base design, a second one was used for cutting and bending segments.

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 6, 2021, 11:29am UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/4 "2021-03-06T11:29:22Z")

</div>

# 4. Find the contour-point nearest to another point

 ![2021-03-06-121214_388x352_scrot](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/f352cac4fd8455e0f6e5f128865149f3e80cdda2.png)

One aspect we can consider when generating a design to plot is this: do previously added lines and curves influence the properties of the line or curve I will add next?

Personally I find it most interesting when curves interact with other curves, when lines start or end at other lines, or cross them at specified locations or angles, or when something happens at the intersections of lines.

OPENRNDR offers many tools (methods) to help with this. We can query distances, intersections, normals, we can offset curves, create sub-curves, sample them at various locations and much more.

In this post I will just mention one of such tools: `ShapeContour.nearest(point: Vector2)`. With it we can ask: what point in this contour is the closest to this other point?

```kotlin
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.shape.Circle
import org.openrndr.shape.LineSegment

fun main() {
    application {
        program {
            // create two nested circles positioned relative to the canvas
            // Circle is a "mathematical circle" (a data type)
           // `.contour` converts it into a drawable one.
            val c1 = Circle(drawer.bounds.position(0.45, 0.45), 150.0).contour
            val c2 = Circle(drawer.bounds.position(0.55, 0.55), 50.0).contour

            // Create a list with 60 LineSegments. Each one connects
            // a position sampled from the first circle with the nearest 
            // point in the second circle.
            val lineCount = 60
            val lines = List(lineCount) {
                val a = c1.position(it / lineCount.toDouble()) // 0.0 .. 1.0
                val b = c2.nearest(a).position
                LineSegment(a, b)
            }

            extend {
                drawer.apply { // make `this` = drawer
                    clear(ColorRGBa.WHITE)
                    fill = null
                    stroke = ColorRGBa.BLACK
                    contour(c1)
                    contour(c2)
                    lineSegments(lines)
                }
            }
        }
    }
}

```

Another result using the same approach:

 ![2021-03-06-121824_413x356_scrot](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/3889793377d59cc6fdf66d2fa2180b7ef210d49d.png)

[More about Shape, ShapeContour and Segment](https://guide.openrndr.org/drawingBasics/complexShapes.html)

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 17, 2022, 3:21pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/5 "2022-03-17T15:21:33Z")

</div>

## 5. Specifying pen height, speed and pauses per layer (v2)

I’ve always wished the Axidraw would allow precise _vertical control_ for plotting with brushes. For example by taking the color of the stroke and mapping that to height. Unfortunately that feature is not there and the makers don’t seem very interested in it for now.

But according to [AxiDraw Layer Control - Evil Mad Scientist Wiki](https://wiki.evilmadscientist.com/AxiDraw_Layer_Control) it is possible to create layers in Inkscape and have special names in the layers which allow configuring the pen height and speed to use when plotting that layer. The layer names also allow introducing delays and waiting for user interaction (to change pens for example).

In the code below I added two methods to OPENRNDR to set Inkscape layer names and to save the SVG file in the right format.

```kotlin
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.drawComposition
import org.openrndr.math.Vector2
import org.openrndr.shape.Composition
import org.openrndr.shape.GroupNode
import org.openrndr.svg.toSVG
import java.io.File

```

```auto
/**
 * ## Helper methods to create SVG files with layer names for Inkscape + AxiDraw
 *
 * Layer names in Inkscape can contain special characters like "+! %" which
 * have meaning when plotting on an AxiDraw device. Those characters can:
 * - pause plotting
 * - delay plotting (in milliseconds)
 * - specify the pen height (0-100%)
 * - specify the pen motion speed (1-100%)
 * - ignore a layer
 *
 * See [AxiDraw Layer Control](https://wiki.evilmadscientist.com/AxiDraw_Layer_Control)
 * for the correct syntax.
 *
 * Here I provide two helper methods to embed layer names in SVG files:
 *
 * 1. `setInkscapeLayer()` can be called on [GroupNode]s to set the desired Inkscape layer name.
 *
 * 2. `saveToInkscapeFile()` saves the SVG file removing the root `<g>` element. The reason for this
 * is that the AxiDraw Inkscape driver does not make use of the special characters in sublayer names,
 * therefore I upgrade sublayers to layers in the root node.
 *
 */

```

```kotlin
fun main() = application {
    program {
        val comp = drawComposition {
            translate(drawer.bounds.center)
            stroke = ColorRGBa.BLACK

            group {
                circle(Vector2.ZERO, 50.0)
            }.setInkscapeLayer("1+D2000+H30+S30 first")

            group {
                circle(Vector2.ZERO, 70.0)
                circle(Vector2.ZERO, 80.0)
            }.setInkscapeLayer("!2 a normal layer")

            group {
                circle(Vector2.ZERO, 90.0)
            }.setInkscapeLayer("%3 do not plot this one")

            group {
                circle(Vector2.ZERO, 110.0)
            }.setInkscapeLayer("!4+H35 higher")
        }
        comp.saveToInkscapeFile(File("/tmp/result_with_layers.svg"))
    }
}

```

```kotlin
private fun GroupNode.setInkscapeLayer(name: String) {
    attributes["inkscape:groupmode"] = "layer"
    attributes["inkscape:label"] = name
}

private fun Composition.saveToInkscapeFile(file: File) = file.writeText(
    toSVG().replace(
        Regex("""(<g\s?>(.*)</g>)""", RegexOption.DOT_MATCHES_ALL), "$2"
    )
)

```

Running this programs produces an SVG file. When opened in Inkscape it looks like this:

 ![image](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/3d789fd3e7e1a0214aaf9538089d362313dd6b64.png)

See those layer names on the top right? With those layer names it is setting a 2000 ms delay for the first layer, a custom pen height for layers 1 and 4, and custom layer speed for layer 1, a pause command on layers 2 and 4 and ignoring completely layer 3.

Now I can create designs where curves are drawn at different speeds or heights. Time to experiment with it 🙂

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 18, 2022, 9:18am UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/6 "2022-03-18T09:18:20Z")

</div>

As an example of the previous helper methods, one could do this:

```auto
fun main() = application {
    program {
        val comp = drawComposition {
            translate(drawer.bounds.center)
            stroke = ColorRGBa.BLACK

            repeat(40) {
                group {
                    circle(Vector2.ZERO, 30.0 + it * 5)
                }.setInkscapeLayer("$it+H${20+it}")

            }
        }
        comp.saveToInkscapeFile(File("/tmp/result_with_layers.svg"))
    }
}

```

to have 40 circles plotted at pen heights 20% to 60%:

 ![image](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/8a6e669d71745377cd18366d360c41c46eca2ca1.png)

We could place a `!` character in front of the layer names to make the plotter pause and wait so we could add ink to the brush 🖌

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [July 8, 2022, 9:03am UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/7 "2022-07-08T09:03:57Z")

</div>

# 6. Hand-written single-stroke text

> [@Doodles Wall c\[\_\]](https://openrndr.discourse.group/t/doodles-wall-c/51/25):
>
> My latest experiment: I wanted to plot text with the axidraw. I didn’t want to use standard fonts but single stroke fonts. In Inkscape I created a bunch of squares and gave them IDs. For the letters the ID is just the same letter. For symbols and numbers I used cNN where NN is the ascii code. For example, c48 for the zero. Then I load that SVG from OPENRNDR creating a map of characters to shapes, searching for the shapes that are located over the square. Late…

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [December 13, 2022, 5:17pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/8 "2022-12-13T17:17:00Z")

</div>

# 7. Reduce pen up/down movements

### Also: “how to clone a List”

In OPENRNDR, `Shape` objects are made out of `ShapeContour`s which are made out of `Segment`s. A `Segment` can be linear or a quadratic (one control point) or cubic (two control points) Bezier curves.

I often generate designs to plot that have many `ShapeContour`s or `Segment`s. The most time-costly operation with a device like the Axidraw is raising and lowering the pen. Therefore it’s tempting to make designs with one huge complex `ShapeContour` instead of thousands of `ShapeContour`s or `Segment`s. But sometimes we can’t choose.

The tip I’m going to describe here applies to collections of `Segment`s in which consecutive ones often start where the previous one ended. This assumes a sorted list. If the list is not sorted, that’s a harder problem in which one needs to search for `Segment`s that start or end in the same locations.

I ran into this situation when scanning positions on a `Segment` pixel by pixel comparing pixel colors in an invisible ColorBuffer (to have an image affect the design).

So one simple program to concatenate `Segment`s may look like this:

```kotlin
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.shape.Circle
import org.openrndr.shape.Segment
import org.openrndr.shape.ShapeContour

fun main() = application {
    // A bunch of segments, this would actually be a generative 
    // design of some kind made out of tons of segments.
    // We simulate such a design with just 3 circles broken into
    // their segments
    val segments = List(3) {
        Circle(100.0 + it * 100.0, 200.0, 30.0).contour.segments
    }.flatten()

    val bucket = mutableListOf<Segment>()
    val result = mutableListOf<ShapeContour>()

    segments.forEach {
        if(bucket.isEmpty() || bucket.last().end.squaredDistanceTo(it.start) < 10E-6) {
            bucket.add(it)
        } else {
            result.add(ShapeContour(bucket.toMutableList(), false))
            bucket.clear()
            bucket.add(it)
        }
    }
    result.add(ShapeContour(bucket, false))

    println("We went from ${segments.size} pen up/down movements to ${result.size}.")

    program {
        extend {
            drawer.fill = null
            drawer.stroke = ColorRGBa.WHITE
            drawer.contours(result)
        }
    }
}

```

It’s a very simple program that reconstructs the original ShapeContour’s out of a list of segments. In this case it wasn’t necessary, because we could have avoided breaking up the `ShapeContour`s in the first place.

Gotcha: the only tricky part is to call `.toMutableList()` to produce a clone of the list. If we don’t do that, the references to previous `ShapeContour`s get overwritten with the later ones, and we would obtain a list with the same circle 3 times. To see what I mean, try deleting `.toMutableList()` and observe the result.

Running the program will print

```auto
We went from 12 pen up/down movements to 3.

```

Note: the Inkscape Axidraw driver has an optimizer. What it does is to sort the segments to avoid unnecessary 2D movement, but it does not avoid pen up/down movements (at least last time I tried during 2022).

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [October 20, 2023, 5:17pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/9 "2023-10-20T17:17:23Z")

</div>

## 8. Creating thumbnails for your SVG files

I have a folder full of SVG files created with my OPENRNDR programs. Being able to view all the thumbnails at once can be useful, which is not always easy if we only have SVG files.

Fortunately Inkscape can be executed from the command line to create PNG images.

```auto
for f in *.svg
    inkscape $f --export-area-drawing --export-background=white --export-overwrite --export-margin=10 -w 300 --export-type=png -o $f.png
end

```

This script works on my Linux box with Fish shell, but it should be easy to adapt to other OSes. The `-w` argument specifies the width in pixels, but you could leave this out, or specify a DPI or height value.

Once you have those PNG images you could use image magick if you want to create contact sheets.

```auto
magick montage *png -tile 10x6 -geometry 200x200+10+10 -pointsize 9 -set label '%f' /tmp/all_%d.png

```

In my case it creates six and this is the second one:

 ![all_2](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/0415f58bcc24ad50aa13e55d35b1c89c362247da.jpeg)

Since the generated SVG files have the name of the program which created them, now I can easily browse these contact sheets, find a thumbnail I like, look at its filename, then open that Kotlin program and iterate on it.

Not really an OPENRNDR tip, but it can be useful if you are creating many vector files for your pen plotter.

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [February 27, 2024, 2:30pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/10 "2024-02-27T14:30:10Z")

</div>

## 9. Hatching

Here goes a very basic example of how one could approach hatching.

 ![axi.PatternBasic-2024-02-27-15.18.04](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/4e9bc53329b7a59ed4ecad80c9d7ccf9de5da54b.png)

> **Import**
>
> ```kotlin
> import org.openrndr.application
> import org.openrndr.color.ColorRGBa
> import org.openrndr.drawComposition
> import org.openrndr.extra.noise.uniform
> import org.openrndr.extra.shapes.hobbycurve.hobbyCurve
> import org.openrndr.math.Polar
> import org.openrndr.shape.ClipMode
> import org.openrndr.shape.CompositionDrawer
> import org.openrndr.shape.Rectangle
> import org.openrndr.shape.draw
> import org.openrndr.svg.saveToFile
> import java.io.File
> 
> ```

```kotlin
fun main() {
    application {
        program {
            val cutterShape = hobbyCurve(
                List(10) {
                    Polar(it * 36.0, 100.0 + (it % 2) * 100.0).cartesian +
                            drawer.bounds.center
                }, true
            ).shape

            val svg = drawComposition {
                fill = null
                messHatching(100, cutterShape.bounds)
                clipMode = ClipMode.INTERSECT
                shape(cutterShape)
            }

            // We can include the cutterShape in the design
            //svg.draw { shape(cutterShape) }

            svg.saveToFile(File("/tmp/messHatching.svg"))

            extend {
                drawer.clear(ColorRGBa.WHITE)
                drawer.composition(svg)
            }
        }
    }
}

// A method to add lines to a `Composition` using a `CompositionDrawer`.
// The `CompositionDrawer` has most drawing methods `Drawer` has.
// For example here we use lineSegment(). We receive `bounds` as an argument
// to create content only in that area.
private fun CompositionDrawer.messHatching(count: Int, bounds: Rectangle) {
    repeat(count) {
        val a = bounds.uniform()
        val b = bounds.uniform()
        lineSegment(a, b)
    }
}

```

The basic idea is to have a shape (in this case a rounded star created with `hobbyCurve`) and fill that shape with lines. The inner lines could be short or long, straight or curved, open or closed and arranged randomly, in a regular pattern, with equal or varying separation between them. Endless possibilities.

The most important part might be the `drawComposition` with `ClipMode.INTERSECT` which is used to eliminate anything that is outside of the `cutterShape`.

An idea for you: create a collection of hatching functions with arguments to control the output.

Also, instead of passing the bounds as an argumnet you could pass the cutter shape itself if you want the hatching algorithm to interact with the orientation of the cutter curve.

---

<div class="post-metadata">

**Author:** ![ronenosity](https://avatars.discourse-cdn.com/v4/letter/r/e480ec/32.png) [@ronenosity](https://openrndr.discourse.group/u/ronenosity)\
**Post date:** [February 28, 2024, 2:19am UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/11 "2024-02-28T02:19:14Z")

</div>

@abe , I really appreciate you posting this example!

---

<div class="post-metadata">

**Author:** ![bluehut](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/bluehut/32/675_2.png) [@bluehut](https://openrndr.discourse.group/u/bluehut)\
**Post date:** [February 29, 2024, 8:46pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/12 "2024-02-29T20:46:06Z")

</div>

## 10. Hidden line removal with an accumulated mask

There are a number of techniques for hidden line removal: some rely on post-processing an SVG file (e.g. [vpype](https://pypi.org/project/vpype/) & [AxiDraw InkScape extension](https://www.evilmadscientist.com/2023/hidden-paths-axidraw/)) and others within OPENRNDR have been discussed earlier in this topic.

Another approach is to maintain a mask as shapes are drawn so that once a shape has been drawn no other shape will intersect with it visually.

 ![maskedcircles](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/814b74aff6fcf5189ea8b73797d13cea08a2000d.png)

> **imports**
>
> ```kotlin
> import org.openrndr.application
> import org.openrndr.color.ColorRGBa
> import org.openrndr.drawComposition
> import org.openrndr.extra.noise.Random
> import org.openrndr.extra.noise.uniform
> import org.openrndr.shape.Circle
> import org.openrndr.shape.Shape
> import org.openrndr.shape.difference
> import org.openrndr.svg.saveToFile
> import java.io.File
> 
> ```

```kotlin
fun main() {
    application {
        program {

            val circles = List(150) {
                Circle(drawer.bounds.uniform(), Random.double(30.0, 60.0))
            }

            val svg = drawComposition {
                fill = null
                // start with the mask set to allow shapes on the whole draw area
                var accumulatedMask: Shape = drawer.bounds.shape
                for (circle in circles) {
                    mask = accumulatedMask
                    circle(circle)
                    // progressively mask out the area of shapes as they are drawn
                    accumulatedMask = accumulatedMask.difference(circle.shape)
                }
            }

            svg.saveToFile(File("/tmp/maskedCircles.svg"))

            extend {
                drawer.clear(ColorRGBa.WHITE)
                drawer.composition(svg)
            }
        }
    }
}

```

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 1, 2024, 8:16am UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/13 "2024-03-01T08:16:41Z")

</div>

Very nice idea! I also like how the edges of the canvas are not a full rectangle, but only present where it intersects with circles 🙂 Thank you for the contribution!

By the way: this is like drawing circles behind other circles, right? You draw a circle, then that place is taken, so the next circle drawn seems to be behind it. Just an interesting thing because normally last elements are on top of previous elements, but in this case it’s the opposite.

---

<div class="post-metadata">

**Author:** ![bluehut](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/bluehut/32/675_2.png) [@bluehut](https://openrndr.discourse.group/u/bluehut)\
**Post date:** [March 1, 2024, 12:37pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/14 "2024-03-01T12:37:39Z")

</div>

Yes you are correct that you do have to reverse your thinking somewhat! In more complex examples I keep the model and view code separate so that I can build the model the way that’s logical to me and the view code can draw the elements in the necessary order for this technique to work.

---

<div class="post-metadata">

**Author:** ![abe](https://yyz2.discourse-cdn.com/free1/user_avatar/openrndr.discourse.group/abe/32/700_2.png) [@abe](https://openrndr.discourse.group/u/abe)\
**Post date:** [March 1, 2024, 10:58pm UTC](https://openrndr.discourse.group/t/openrndr-pen-plotting-tricks-axidraw-etc/208/15 "2024-03-01T22:58:44Z")

</div>

## 11. Using Signed Distance Fields to create contours with math

The [orx-marching-squares](https://github.com/openrndr/orx/tree/master/orx-marching-squares) extension lets us define mathematical functions to generate contours which we could send to the pen plotter.

Take a look at the simpler examples available in the readme.

In the following example I ported a few functions by [iquilez](https://iquilezles.org/articles/distfunctions2d/) and from the [hg\_sdf library](http://mercury.sexy/hg_sdf/hg_sdf.glsl) to create this design:

 ![Sdf2-2024-03-01-22.48.16](https://global.discourse-cdn.com/free1/uploads/openrndr/original/1X/016aa837bf00b90b30d38b219e0d235eb9d530ad.jpeg)

Basically, I created two sdf shapes (a circle and a rounded box), created “ripples” around them using `cos`, combined the ripples using `fOpDifferenceRound`, then used a circular mask to crop the design using `fOpIntersectionRound`.

```kotlin
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.marchingsquares.findContours
import org.openrndr.math.*
import kotlin.math.*

fun main() = application {
    configure {
        width = 1000
        height = 1000
    }

    program {
        fun Vector2.abs() = Vector2(x.absoluteValue, y.absoluteValue)
        fun Vector2.ndot(other: Vector2) = x * other.x - y * other.y
        fun Vector2.dot2() = this.dot(this)

        // https://iquilezles.org/articles/distfunctions2d/
        fun sdCircle(p: Vector2, r: Double) = p.length - r

        fun sdRoundedBox(p: Vector2, b: Vector2, r: Vector4): Double {
            val r2 = if (p.x > 0.0) r else r.copy(r.z, r.w)
            val r3 = if (p.y > 0.0) r2 else r2.copy(r2.y)
            val q = p.abs() - b + r3.x
            return min(max(q.x, q.y), 0.0) + max(q, Vector2.ZERO).length - r3.x
        }

        // http://mercury.sexy/hg_sdf/
        fun fOpUnionRound(a: Double, b: Double, r: Double): Double {
            val u = max(Vector2(r - a, r - b), Vector2.ZERO)
            return max(r, min(a, b)) - u.length
        }

        fun fOpIntersectionRound(a: Double, b: Double, r: Double): Double {
            val u = max(Vector2(r + a, r + b), Vector2.ZERO)
            return min(-r, max(a, b)) + u.length
        }

        fun fOpDifferenceRound(a: Double, b: Double, r: Double) = fOpIntersectionRound(a, -b, r)

        fun f(v: Vector2): Double {
            val cir = cos(
                sdCircle(v - drawer.bounds.position(0.3, 0.3), 100.0) * 0.1
            ) - 0.7

            val box = cos(
                sdRoundedBox(
                    v - drawer.bounds.position(0.7, 0.7),
                    Vector2(150.0, 100.0),
                    Vector4(10.0, 30.0, 50.0, 70.0)
                ) * 0.15
            ) + 0.7

            val c = fOpDifferenceRound(cir, box, 1.0)
            val mask = sdCircle(v - drawer.bounds.center, 400.0)
            return fOpIntersectionRound(c, mask, 10.0)
        }

        val c = findContours(::f, drawer.bounds, 3.0)

        extend {
            drawer.clear(ColorRGBa.WHITE)
            drawer.stroke = ColorRGBa.BLACK
            drawer.fill = null
            drawer.contours(c)
        }
    }
}

```

Note that there are many more SDF functions to play with available in the two links above.

Sculpting shapes via math can be somewhat unintutive, although this technique is common in shader programs running on the GPU.
