How do I draw multiple horizontal lines at specific spacing?

This is displaying my ignorance of how the placement function works, but I would like to draw a box with a specified number of lines drawn through it at a specific spacing. I tried the code below, but the spacing of the lines seems to increase with time, nor are the lines in the box:

#let lined_box(divisions,filled, total_height: 50pt) = {
  box(fill:aqua, width:100%, height: total_height, stroke: black)
  let division_size = total_height/divisions
  for i in range(1,divisions) {
    line(start:(0%,i*division_size), end:(100%,i* division_size) )
  }
}

Looking at the documentation for line, I am obviously missing something significant. Anyone help me out?

Hey @cntaylor,
Before going into your problem a quick note for future posts in the questions category: please read through the questions guidelines How to post in the Questions category! In short: the title of the post should be posed as a question, which you would ask a friend.

Now to your stuff! Functions such as line or rect insert a content in the shape of the called function. Now what exactly is in that content I’m not sure, but what your code is doing, is inserting lines one after another in the shape of:

line(start: (0%, XXpt), end: (100%, XXpt)
line(start: (0%, YYpt), end: (100%, YYpt)
line(start: (0%, ZZpt), end: (100%, ZZpt)
...

The y-component simply offsets the line inside an invisible box, meaning each line - “box” gets more and more stretched. This gives off the illusion of “the spacing of the lines increasing with time”.

To fix this, you need to use the #place functionality and also include the content inside the box:

#let lined_box(divisions,filled, total_height: 50pt) = {
  box(fill:aqua, width:100%, height: total_height, stroke: black,{
    let division_size = total_height/divisions
    for i in range(1,divisions) {
      place(line(start:(0%,0%), end:(100%,0%)), dy: division_size * i)
    }
  })
}

alternatively you can also write place(line(start:(0%,division_size * i), end:(100%,division_size * i))) instead. Since place inserts the content “on top” of the other, it always starts at relative coordinates (0,0). In our box above this corresponds to this point being in the upper left corner of the box.

2 Likes

And to make it even more compact, which you might not find a use for, you can also replace the box and line with a grid!

#let lined_box(divisions,filled, total_height: 50pt) = {
  grid(fill: filled, stroke: 1pt, columns: 100%, rows: (total_height/divisions,) * divisions)
}

This has the exact same behaviour as the other code!

Thanks for the correction on posting questions. I’ve fixed the title.

This is great. My final piece of code is:

#let h_lined_box(divisions, filled, total_height: 40pt) = {
  let division_size = total_height/divisions
  place(box(fill:aqua, width:100%, height: division_size * filled, stroke: black), dy: division_size * (divisions - filled))
  for i in range(1,divisions) {
    place(line(start:(0%,0%), end:(100%,0%) ), dy: division_size * i)
  }
  box(width: 100%, height: total_height, stroke: 1pt) // This goes last so the next thing shows up in the right spot
}

So the lines are within the box. I have to use this approach as opposed to your grid approach because I am actually trying to only fill in a certain number of the “divisions”, as this code shows.

Thanks for your help!

1 Like