---
title: Dealing with laser cutters
sidebar_position: 25
---

Laser cutters is merely an example of a situation where your user wants not the
complete detailed pattern with all annotations, but just the outlines.
Essentially what we had at the end of part 2 of this tutorial.

Since then, we've added a bunch of embellishments, and perhaps the user does 
not want those.

Well, good news: there is a setting for that too. That setting is `complete`, 
and more annotations will automatically take it into account.

For example, if you put a logo or title on the pattern, it will check the 
`complete` setting and if it is `false` it will do nothing.
When we say we're going to *complete* our pattern, we mean we're going to add
things like a title and a scalebox and so on.

So while in most scenarios you don't have to worry about `complete`, you 
should keep it in mind when you are adding text or paths to the design
that should not be shown on a non-complete pattern.

An example will make this more clear:

## Adding an indicator for bias tape

<Example tutorial previewFirst caption="A nice note about bias tape">
```design/src/bib.mjs
function draftBib({
  Point,
  points,
  Path,
  paths,
  utils,
  measurements,
  options,
  macro,
  Snippet,
  snippets,
  store,
  // highlight-start
  complete,
  // highlight-end
  part,
}) {

  /*
    * Construct the neck opening
    */
  const target = (measurements.head * options.neckRatio) / 4
  let tweak = 1
  let delta
  do {
    points.right = new Point((tweak * measurements.head) / 10, 0)
    points.bottom = new Point(0, (tweak * measurements.head) / 12)

    points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
    points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)

    paths.neck = new Path()
      .move(points.right)
      .curve(points.rightCp1, points.bottomCp2, points.bottom)

    delta = paths.neck.length() - target
    if (delta > 0) tweak = tweak * 0.99
    else tweak = tweak * 1.02
  } while (Math.abs(delta) > 1)

  points.rightCp2 = points.rightCp1.flipY()
  points.bottomCp1 = points.bottomCp2.flipX()

  points.left = points.right.flipX()
  points.leftCp1 = points.rightCp2.flipX()
  points.leftCp2 = points.rightCp1.flipX()

  points.top = points.bottom.flipY()
  points.topCp1 = points.bottomCp2.flipY()
  points.topCp2 = points.bottomCp1.flipY()

  /*
    * Construct the outline
    */
  let width = measurements.head * options.widthRatio
  let length = measurements.head * options.lengthRatio

  points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
  points.topRight = points.topLeft.shift(0, width)
  points.bottomLeft = points.topLeft.shift(-90, length)
  points.bottomRight = points.topRight.shift(-90, length)

  points.edgeLeft = new Point(points.topLeft.x, points.left.y)
  points.edgeRight = new Point(points.topRight.x, points.right.y)
  points.edgeTop = new Point(0, points.topLeft.y)

  points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
  points.edgeRightCp = points.edgeLeftCp.flipX()
  points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
  points.edgeTopRightCp = points.edgeTopLeftCp.flipX()

  /*
    * Round the end of the straps
    */
  let strap = points.edgeTop.dy(points.top)

  points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
  points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
  points.tipRightBottom = new Point(points.tipRight.x, points.top.y)

  /*
    * Macros will return the auto-generated IDs
    */
  const ids1 = {
    tipRightTop: macro('round', {
      id: 'tipRightTop',
      from: points.edgeTop,
      to: points.tipRight,
      via: points.tipRightTop,
    }),
    tipRightBottom: macro('round', {
      id: 'tipRightBottom',
      from: points.tipRight,
      to: points.top,
      via: points.tipRightBottom,
    }),
  }

  /*
    * Create points from them with easy names
    */
  for (const side in ids1) {
    for (const id of ['start', 'cp1', 'cp2', 'end']) {
      points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
    }
  }

  /*
    * Rotate straps so they don't overlap
    */
  let rotateThese = [
    'edgeTopLeftCp',
    'edgeTop',
    'tipRight',
    'tipRightTop',
    'tipRightTopStart',
    'tipRightTopCp1',
    'tipRightTopCp2',
    'tipRightTopEnd',
    'tipRightBottomStart',
    'tipRightBottomCp1',
    'tipRightBottomCp2',
    'tipRightBottomEnd',
    'tipRightBottom',
    'top',
    'topCp2',
  ]

  while (points.tipRightBottomStart.x > -1) {
    for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
  }

  /*
    * Add points to anchor snaps on
    */
  points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)

  /*
    * Mirror points to the other side
    */
  points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
  points.topCp1 = points.topCp2.flipX()
  points.tipLeftTopStart = points.tipRightTopStart.flipX()
  points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
  points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
  points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
  points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
  points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
  points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
  points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
  points.snapRight = points.snapLeft.flipX()

  /*
    * Round the bottom of the bib
    * Radius is fixed, but you could use an option for it)
    *
    * Macros will return the auto-generated IDs
    */
  const ids2 = {
    bottomLeft: macro('round', {
      id: 'bottomLeft',
      from: points.topLeft,
      to: points.bottomRight,
      via: points.bottomLeft,
      radius: points.bottomRight.x / 4,
    }),
    bottomRight: macro('round', {
      id: 'bottomRight',
      from: points.bottomLeft,
      to: points.topRight,
      via: points.bottomRight,
      radius: points.bottomRight.x / 4,
    }),
  }
  /*
    * Create points from them with easy names
    */
  for (const side in ids2) {
    for (const id of ['start', 'cp1', 'cp2', 'end']) {
      points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
    }
  }

  /*
    * Construct the path
    */
  paths.seam = new Path()
    .move(points.edgeLeft)
    .line(points.bottomLeftStart)
    .curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
    .line(points.bottomRightStart)
    .curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
    .line(points.edgeRight)
    .curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
    .curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
    .curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
    .curve(points.topCp1, points.rightCp2, points.right)
    .curve(points.rightCp1, points.bottomCp2, points.bottom)
    .curve(points.bottomCp1, points.leftCp2, points.left)
    .curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
    .curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
    .curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
    .curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
    .close()
    .attr('class', 'fabric')

  /*
    *
    *  Annotations
    *
    */

  /*
    * Cut list
    */
  store.cutlist.addCut({ cut: 1, from: 'fabric' })

  /*
    * Add the snaps
    */
  snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
  snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)

  // highlight-start
  /*
    * Add the bias tape
    */
  if (complete)
    paths.bias = paths.seam
      .offset(-5)
      .addClass('note dashed')
      .addText('tutorial:finishWithBiasTape', 'center fill-note')
  // highlight-end

  /*
    * Add the title
    */
  points.title = points.bottom.shift(-90, 45)
  macro('title', {
    at: points.title,
    nr: 1,
    title: 'bib',
    align: 'center',
    scale: 0.8,
  })

  /*
    * Add the scalebox
    */
  points.scalebox = points.title.shift(-90, 65)
  macro('scalebox', { at: points.scalebox })

  /*
    * Add the logo
    */
  points.logo = new Point(0, 0)
  snippets.logo = new Snippet('logo', points.logo)

  return part
}
```
</Example>


And here is the exact same thing, but withe `complete` setting set to `false`:

<Example tutorial previewFirst settings="complete: false" caption="Same code, but with complete disabled">
```design/src/bib.mjs
function draftBib({
  Point,
  points,
  Path,
  paths,
  utils,
  measurements,
  options,
  macro,
  Snippet,
  snippets,
  store,
  // highlight-start
  complete,
  // highlight-end
  part,
}) {

  /*
    * Construct the neck opening
    */
  const target = (measurements.head * options.neckRatio) / 4
  let tweak = 1
  let delta
  do {
    points.right = new Point((tweak * measurements.head) / 10, 0)
    points.bottom = new Point(0, (tweak * measurements.head) / 12)

    points.rightCp1 = points.right.shift(90, points.bottom.dy(points.right) / 2)
    points.bottomCp2 = points.bottom.shift(0, points.bottom.dx(points.right) / 2)

    paths.neck = new Path()
      .move(points.right)
      .curve(points.rightCp1, points.bottomCp2, points.bottom)

    delta = paths.neck.length() - target
    if (delta > 0) tweak = tweak * 0.99
    else tweak = tweak * 1.02
  } while (Math.abs(delta) > 1)

  points.rightCp2 = points.rightCp1.flipY()
  points.bottomCp1 = points.bottomCp2.flipX()

  points.left = points.right.flipX()
  points.leftCp1 = points.rightCp2.flipX()
  points.leftCp2 = points.rightCp1.flipX()

  points.top = points.bottom.flipY()
  points.topCp1 = points.bottomCp2.flipY()
  points.topCp2 = points.bottomCp1.flipY()

  /*
    * Construct the outline
    */
  let width = measurements.head * options.widthRatio
  let length = measurements.head * options.lengthRatio

  points.topLeft = new Point(width / -2, points.top.y - (width / 2 - points.right.x))
  points.topRight = points.topLeft.shift(0, width)
  points.bottomLeft = points.topLeft.shift(-90, length)
  points.bottomRight = points.topRight.shift(-90, length)

  points.edgeLeft = new Point(points.topLeft.x, points.left.y)
  points.edgeRight = new Point(points.topRight.x, points.right.y)
  points.edgeTop = new Point(0, points.topLeft.y)

  points.edgeLeftCp = points.edgeLeft.shiftFractionTowards(points.topLeft, 0.5)
  points.edgeRightCp = points.edgeLeftCp.flipX()
  points.edgeTopLeftCp = points.edgeTop.shiftFractionTowards(points.topLeft, 0.5)
  points.edgeTopRightCp = points.edgeTopLeftCp.flipX()

  /*
    * Round the end of the straps
    */
  let strap = points.edgeTop.dy(points.top)

  points.tipRight = points.edgeTop.translate(strap / 2, strap / 2)
  points.tipRightTop = new Point(points.tipRight.x, points.edgeTop.y)
  points.tipRightBottom = new Point(points.tipRight.x, points.top.y)

  /*
    * Macros will return the auto-generated IDs
    */
  const ids1 = {
    tipRightTop: macro('round', {
      id: 'tipRightTop',
      from: points.edgeTop,
      to: points.tipRight,
      via: points.tipRightTop,
    }),
    tipRightBottom: macro('round', {
      id: 'tipRightBottom',
      from: points.tipRight,
      to: points.top,
      via: points.tipRightBottom,
    }),
  }

  /*
    * Create points from them with easy names
    */
  for (const side in ids1) {
    for (const id of ['start', 'cp1', 'cp2', 'end']) {
      points[`${side}${utils.capitalize(id)}`] = points[ids1[side].points[id]].copy()
    }
  }

  /*
    * Rotate straps so they don't overlap
    */
  let rotateThese = [
    'edgeTopLeftCp',
    'edgeTop',
    'tipRight',
    'tipRightTop',
    'tipRightTopStart',
    'tipRightTopCp1',
    'tipRightTopCp2',
    'tipRightTopEnd',
    'tipRightBottomStart',
    'tipRightBottomCp1',
    'tipRightBottomCp2',
    'tipRightBottomEnd',
    'tipRightBottom',
    'top',
    'topCp2',
  ]

  while (points.tipRightBottomStart.x > -1) {
    for (let p of rotateThese) points[p] = points[p].rotate(1, points.edgeLeft)
  }

  /*
    * Add points to anchor snaps on
    */
  points.snapLeft = points.top.shiftFractionTowards(points.edgeTop, 0.5)

  /*
    * Mirror points to the other side
    */
  points.edgeTopRightCp = points.edgeTopLeftCp.flipX()
  points.topCp1 = points.topCp2.flipX()
  points.tipLeftTopStart = points.tipRightTopStart.flipX()
  points.tipLeftTopCp1 = points.tipRightTopCp1.flipX()
  points.tipLeftTopCp2 = points.tipRightTopCp2.flipX()
  points.tipLeftTopEnd = points.tipRightTopEnd.flipX()
  points.tipLeftBottomStart = points.tipRightBottomStart.flipX()
  points.tipLeftBottomCp1 = points.tipRightBottomCp1.flipX()
  points.tipLeftBottomCp2 = points.tipRightBottomCp2.flipX()
  points.tipLeftBottomEnd = points.tipRightBottomEnd.flipX()
  points.snapRight = points.snapLeft.flipX()

  /*
    * Round the bottom of the bib
    * Radius is fixed, but you could use an option for it)
    *
    * Macros will return the auto-generated IDs
    */
  const ids2 = {
    bottomLeft: macro('round', {
      id: 'bottomLeft',
      from: points.topLeft,
      to: points.bottomRight,
      via: points.bottomLeft,
      radius: points.bottomRight.x / 4,
    }),
    bottomRight: macro('round', {
      id: 'bottomRight',
      from: points.bottomLeft,
      to: points.topRight,
      via: points.bottomRight,
      radius: points.bottomRight.x / 4,
    }),
  }
  /*
    * Create points from them with easy names
    */
  for (const side in ids2) {
    for (const id of ['start', 'cp1', 'cp2', 'end']) {
      points[`${side}${utils.capitalize(id)}`] = points[ids2[side].points[id]].copy()
    }
  }

  /*
    * Construct the path
    */
  paths.seam = new Path()
    .move(points.edgeLeft)
    .line(points.bottomLeftStart)
    .curve(points.bottomLeftCp1, points.bottomLeftCp2, points.bottomLeftEnd)
    .line(points.bottomRightStart)
    .curve(points.bottomRightCp1, points.bottomRightCp2, points.bottomRightEnd)
    .line(points.edgeRight)
    .curve(points.edgeRightCp, points.edgeTopRightCp, points.tipLeftTopStart)
    .curve(points.tipLeftTopCp1, points.tipLeftTopCp2, points.tipLeftTopEnd)
    .curve(points.tipLeftBottomCp1, points.tipLeftBottomCp2, points.tipLeftBottomEnd)
    .curve(points.topCp1, points.rightCp2, points.right)
    .curve(points.rightCp1, points.bottomCp2, points.bottom)
    .curve(points.bottomCp1, points.leftCp2, points.left)
    .curve(points.leftCp1, points.topCp2, points.tipRightBottomEnd)
    .curve(points.tipRightBottomCp2, points.tipRightBottomCp1, points.tipRightBottomStart)
    .curve(points.tipRightTopCp2, points.tipRightTopCp1, points.tipRightTopStart)
    .curve(points.edgeTopLeftCp, points.edgeLeftCp, points.edgeLeft)
    .close()
    .attr('class', 'fabric')

  /*
    *
    *  Annotations
    *
    */

  /*
    * Cut list
    */
  store.cutlist.addCut({ cut: 1, from: 'fabric' })

  /*
    * Add the snaps
    */
  snippets.snapStud = new Snippet('snap-stud', points.snapLeft)
  snippets.snapSocket = new Snippet('snap-socket', points.snapRight).attr('opacity', 0.5)

  // highlight-start
  /*
    * Add the bias tape
    */
  if (complete)
    paths.bias = paths.seam
      .offset(-5)
      .addClass('note dashed')
      .addText('tutorial:finishWithBiasTape', 'center fill-note')
  // highlight-end

  /*
    * Add the title
    */
  points.title = points.bottom.shift(-90, 45)
  macro('title', {
    at: points.title,
    nr: 1,
    title: 'bib',
    align: 'center',
    scale: 0.8,
  })

  /*
    * Add the scalebox
    */
  points.scalebox = points.title.shift(-90, 65)
  macro('scalebox', { at: points.scalebox })

  /*
    * Add the logo
    */
  points.logo = new Point(0, 0)
  snippets.logo = new Snippet('logo', points.logo)

  return part
}
```
</Example>

