EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / AI / From Model to 6-Meter Steel Cutting Layout and Workshop Cut Sheet
AI

From Model to 6-Meter Steel Cutting Layout and Workshop Cut Sheet

by Ton-Or ·3 Oct 2026·9 min read
From Model to 6-Meter Steel Cutting Layout and Workshop Cut Sheet

A steel cut sheet that a fabricator can actually use needs more than a list of lengths. It also needs to say which pieces come from which stock length, what order to cut them in, and how much offcut remains—all of which can be calculated directly from the model.

I was building a small steel frame for a shelf beside my bed, and once I had finished modeling it on the computer, it was time to tell the shop how many lengths of steel I needed to buy. At first, I added up the lengths and got 4.69 meters, so I thought one stock length would cover it. But when I showed it to the fabricator, he asked which pieces should be cut first and which last, because the wrong order would mean one stock length would not be enough. So I went back and wrote a script to work out the cutting layout, then turn it into a cut sheet I could print on A4 and leave beside the cutting machine.

I have already covered conventions about which members sit on top of which, and which sections are actually available, in Thai Steel Framing Drawing Conventions. This time, I am taking it one stage further: from the finished model to the paper the fabricator holds while cutting.

Takeoff pipelineModelName groups->Cut listGroup duplicates->Cutting layout6000 stock->Cross-section->A4 sheet

Read the section and length from the bounding box, but sort first

Nearly all the steel frame members are straight bars, so their bounding boxes tell me everything I need to know. Just do not assume that width is the width, because these values follow the world axes, not the part itself. An upright post and a horizontal beam can use exactly the same steel section, yet all the dimensions come back in different positions. The only safe approach is to sort the three dimensions first, then read them by position.

# encoding: UTF-8
m  = Sketchup.active_model
st = m.entities.grep(Sketchup::Group).find { |g| g.name =~ /ชั้นข้างเตียง/ }
mv = ->(v) { (v.to_f * 25.4).round(1) }
rows = []   # [ชื่อชิ้น, หน้าตัด, ความยาว, จำนวน]

st.entities.grep(Sketchup::Group).each do |g|
  b  = g.bounds
  dx = mv.(b.max.x - b.min.x)
  dy = mv.(b.max.y - b.min.y)
  dz = mv.(b.max.z - b.min.z)
  d  = [dx, dy, dz].sort          # d[0]=หนา d[1]=กว้าง d[2]=ยาว

  case g.name
  when /^เสาแนบข้าง/   then rows << ["เสาแนบข้าง",   "กล่อง 1 นิ้ว", d[2], 1]
  when /^เสาค้ำบนเพลท/  then rows << ["เสาค้ำบนเพลท",  "กล่อง 1 นิ้ว", d[2], 1]
  when /^คานขวางชั้น/   then rows << ["คานขวางชั้น",   "กล่อง 1 นิ้ว", d[2], 1]
  when /^เพลทคีบ/      then rows << ["เพลทคีบสันเตียง", "แบน 4 มม. x #{d[1].round}", d[2], 1]
  end
end

Notice that for flat bar, I extract two values: d[1] is the face width, and d[2] is the length. That is because flat bar is ordered by width and thickness, then cut to length. It is different from box section, which already has a fixed cross-section. If I lump them together and lay out their cuts in one batch, I end up with a layout that cannot actually be cut.

Consolidate duplicates before laying out the cuts

A single frame has many repeated pieces of the same length. All four posts are the same length, and all three crossmembers are the same length. If I leave them as a twelve-line list, the fabricator has to work through it to find the duplicates. So I consolidate them with a Hash keyed by the combination of name, section, and length, and count the quantities instead.

agg = Hash.new(0)
rows.each { |nm, sec, len, n| agg[[nm, sec, len]] += n }

box_rows  = agg.select { |(nm, sec, _), _| sec =~ /กล่อง/ }
flat_rows = agg.reject { |(nm, sec, _), _| sec =~ /กล่อง/ }
tot_box   = box_rows.sum { |(_, _, len), n| len * n }
12Pieces to cut
4,691Total length, mm
6,000Standard stock, mm
3 mmSaw kerf per cut
1,276Offcut, mm
1Stock lengths to buy

Consolidating the list also affects the cutting layout, because the next step expands it back into individual pieces. This time, though, I already know how many pieces there are and how long they are, without having to traverse the model again.

Lay out the cuts with first-fit decreasing and allow for saw kerf

Packing pieces into stock lengths while using as few lengths as possible is a problem whose true optimum is hard to find. But workshop work does not need the absolute best solution, just one that is good enough and easy to explain to the fabricator. My approach is to sort the pieces from longest to shortest, then put each into the first stock length with enough room. This is called first-fit decreasing. It takes ten lines to write and gives results close to the optimum almost every time on small jobs.

# ผังตัดจากเส้น 6000 มม. เผื่อร่องใบเลื่อย 3 มม. ต่อหนึ่งรอยตัด
pieces = []
box_rows.each { |(nm, _, len), n| n.times { pieces << [len, nm] } }
pieces.sort_by! { |l, _| -l }                    # ยาวก่อน

bars = []
pieces.each do |len, nm|
  bar = bars.find { |b| b[:left] >= len + 3 }    # เส้นแรกที่ยังพอ
  bar ||= (bars << { :left => 6000.0, :items => [] }).last
  bar[:left] -= (len + 3)
  bar[:items] << "#{nm} #{len.round}"
end

bars.each_with_index do |b, i|
  puts "เส้น #{i + 1}: " + b[:items].join(" + ") + " | เหลือ #{b[:left].round}"
end
Saw kerf removes real steel—remember to add it

A circular saw blade for steel removes about 2 to 3 millimeters per cut. I use 3 to be safe. With dozens of small pieces, leaving out this allowance means finding that the last piece is two or three millimeters short every time I cut. It may not be noticeable on a small job, but thirty cuts in one stock length consume almost ten centimeters, enough to make the last piece unusable.

I always have the script print the offcut remaining from each stock length, not just how many lengths to buy, because the offcut is information the fabricator uses to make decisions. If 1,276 millimeters remain, as on this job, the fabricator can keep it for other work. But if the remainder is scattered across three or four 200-millimeter scraps, the cutting layout is poor, and I should go back and adjust the part lengths in the design to fit better first.

Frame corners: miter both ends at 45 degrees, or notch one continuous length

When I got to the shelf frame, which has an L-shaped plan with six sides, the question was how to make it. The standard method is to cut six pieces, miter both ends of each at 45 degrees, then weld them together at the corners, for a total of twelve angled cuts and six corner welds. I tried another method: use one continuous length of steel, notch it at the corners, then bend it.

Conventional method

Six pieces, 45-degree miters

  • 12 cuts at 45 degrees
  • 6 corner butt welds, with the pieces aligned square
  • Total length calculated from the outer edge of each side
  • Corners can go out of square if not clamped firmly during welding
Single-length method

Notch at 90 degrees, then bend square

  • 6 notches plus 1 end-to-end butt joint
  • Saves 100 millimeters of steel per frame
  • Corners come out square because they are bent from one length
  • Length must be calculated along the tube centerline, not the outer edge

The calculation people often get wrong is the length of this single piece. Adding the outer lengths of the six sides gives a result that is too long, because notching and bending uses the inner wall of the tube as a hinge. The line that neither stretches nor shrinks during bending is the tube centerline. So I always calculate along the centerline, which gives 2,374 millimeters for this frame.

# ผังตัว L: เดินตามจุดมุมของเส้นกึ่งกลางท่อ แล้ววัดระยะทีละช่วง
arm_x0 = -100.0; x_end = 900.0
y_out  = -250.0; y_in  = 0.0; y_wood = -50.0
cl     = 4.0                                   # ครึ่งความหนาผนังท่อ

fo = [[arm_x0, y_out + cl], [x_end - cl, y_out + cl], [x_end - cl, y_in - cl],
      [0, y_in - cl], [0, y_wood], [arm_x0, y_wood]]

fo.each_with_index do |p, i|
  q   = fo[(i + 1) % fo.size]
  len = Math.sqrt((q[0] - p[0])**2 + (q[1] - p[1])**2).round(1)
  puts "ช่วงที่ #{i + 1} ยาว #{len}"
end

A 90-degree notch is made with two 45-degree cuts that meet exactly at the inner wall, cutting through three sides of the tube and leaving the inner wall as a hinge. I then bend it on a flat table, keeping it in the same plane, and finish by welding the notch closed. The notch opening at the outer surface is exactly twice the tube height. For one-inch tubing, that is 50.8 millimeters.

Measure notch positions from the middle of a straight section, not a corner

This is not about mathematics, but about practical fabrication. If I start measuring from a corner, the end-to-end butt joint that closes the loop lands right on that corner, which is the weakest part of the frame and the hardest place to weld. So I start measuring from the midpoint of the longest side, putting the end-to-end butt joint in the middle of a straight section instead.

One length, 6 notches, measured on tube centerline, total 2,374 mm123456485.3216.6870.646.0100.0170.6485.3Start: long-side midpointEnd meets startNotches 1, 2, 3, 5, 6: bend in (outer corners)Notch 4: bend out (inner corner), notch opposite side
One steel length shown to scale. The figures above and below the line give segment lengths in millimeters; the numbered labels identify the notches in the table below.
Notch number Distance from start Next segment Bend direction
1 485.3 216.6 Bend inward, outer corner
2 701.9 870.6 Bend inward, outer corner
3 1,572.5 46.0 Bend inward, outer corner
4 1,618.5 100.0 Bend outward, inner corner, notch opposite side
5 1,718.5 170.6 Bend inward, outer corner
6 1,889.1 485.3 Bend inward, then butt the end against the start
Put an alternative in the drawing in case fabrication is difficult

Notches 3 and 4 are only 46 millimeters apart and bend in opposite directions, making that section difficult to clamp. So I explicitly note on the sheet that if the fabricator finds it too short, this 46 segment can be cut as a separate piece and joined with two butt welds instead. Giving an alternative is better than leaving the fabricator to decide on the spot without me knowing how the finished piece turns out.

A cross-section with coordinates taken from the actual model

With only length figures on a cut sheet, the fabricator still has to guess which pieces sit at which heights. So I always include one cross-section, drawn not by hand but by having the script traverse the model, find every piece intersected by the section plane, and return the rectangle coordinates for each one.

# ดึงหน้าตัดจริงที่ระนาบ x = xc คืนสี่เหลี่ยม (y0,z0,y1,z1) ของทุกชิ้นที่โดนตัด
m  = Sketchup.active_model
xc = 300.0
mv = ->(v) { (v.to_f * 25.4).round(1) }
rows = []

walk = lambda do |ents, prefix|
  ents.grep(Sketchup::Group).each do |g|
    kids = g.entities.grep(Sketchup::Group)
    if kids.empty?                                   # ชิ้นจริง ไม่ใช่กลุ่มห่อ
      b = g.bounds
      next unless mv.(b.min.x) <= xc && mv.(b.max.x) >= xc
      rows << [prefix + g.name, mv.(b.min.y), mv.(b.min.z), mv.(b.max.y), mv.(b.max.z)]
    else
      walk.(g.entities, prefix)                      # ลงลึกต่อ
    end
  end
end
walk.(m.entities, "")
rows.sort_by! { |r| [r[2], r[1]] }                   # เรียงจากล่างขึ้นบน

The returned values can be drawn directly as SVG on the sheet, because they are already rectangles with coordinates. The advantage is that the section always matches the model. Change the model and rerun the script, and the drawing changes with it; they never get out of sync. The key levels I label in the drawing are the underside of the frame, the top surface of the frame, the top of the existing edge, and the bottom of each set of posts, because these four values are what the fabricator needs to mark on the actual work before welding.

Allow for setbacks based on facing thickness, not opening size

I ran into this while making a timber frame faced with plywood, but the same principle applies to steelwork that needs sheet cladding. Suppose a panel consists of 4 millimeters of plywood, a 22-millimeter frame, and another 4 millimeters of plywood, totaling exactly 30 millimeters. If the top panel's frame runs to the outer face of the side panel, it ends at the plywood surface, not the frame itself. Drive a nail into it and there is nothing for it to grip.

# ฝาหนึ่งแผ่น = ไม้อัด 4 + โครง 22 + ไม้อัด 4 = 30 มม.
# ฝาข้างซ้ายวางที่ x = 4 ถึง 34 -> ผิวเนื้อโครงอยู่ที่ x = 26
# ฝาข้างขวาวางที่ x = 1150 ถึง 1180 -> ผิวเนื้อโครงอยู่ที่ x = 1154
#
# ความกว้างช่องระหว่างผิวไม้อัดด้านใน = 1120
# แต่โครงฝาบน/ล่าง/หลัง ต้องยาว 1128 เพื่อวิ่งเข้าไปชนเนื้อโครงฝาข้าง
frame_len_wrong = 1120     # หยุดที่ผิวไม้อัด ไม่มีเนื้อไม้ให้ยึด
frame_len_right = 1128     # ชนเนื้อโครงกับเนื้อโครง

My short reminder to myself is frame members must meet directly; the facing comes afterward. The allowance is not the opening size on the drawing, but the facing thickness on each side. For two sides, add it twice. When taking off the cut list, the number to print on the sheet is 1128, not 1120. This is where visual takeoffs most often go wrong, because everything looks the same on screen.

An A4 cut sheet that prints correctly

I make my cut sheet as a single HTML page and print it through the browser. It is easier to edit than drawing it in a drafting program, and I can embed the SVG from the script directly. The key is three print CSS rules: set the page size to A4, remove the browser margins and set them inside the container instead, and prevent page breaks within content sections.

@media print {
  @page { size: A4; margin: 0; }
  body  { background: #fff; padding: 0; }
  .sheet {
    border: none; margin: 0;
    width: auto; min-height: auto;
    padding: 12mm;                 /* ตั้งขอบกระดาษเอง ให้คุมได้จริง */
    page-break-after: always;
  }
  .sheet:last-child { page-break-after: auto; }
  section { break-inside: avoid; }  /* ห้ามตัดตารางกลางหน้า */
}

The sheet layout I use for every job has seven sections, arranged in the order the fabricator actually works, not the order the drafter thinks.

  1. Steelwork summary Four large figures: total length, number of lengths, number of notches, and number of square-cut pieces
  2. Frame plan viewed from above With circled numbers marking notch positions and developed lengths shown along the lines
  3. Cut list table Code, part name, length, quantity, total, forming method
  4. Notch position table Distance from start, next segment, and bend direction
  5. Cross-section At a plane through the key pieces, with level labels
  6. Cutting layout on 6-meter stock Ordered longest to shortest, ending with the offcut
  7. Drilling and fastener table Hole sizes, quantities, distances, and welding assembly sequence
Print at 100 percent only

If someone opens the print dialog and leaves it set to fit to page, the scale changes across the whole sheet while the printed numbers remain correct. A fabricator measuring it with a ruler will get distances that disagree with the figures and assume the figures are wrong. So I put this note at the bottom of every sheet: All dimensions are in millimeters. Use the stated dimensions; do not measure from the paper. There is no need to put a scale on the cut sheet at all, because a cut sheet is not a construction drawing.

Personally, I think taking the work all the way to a cut sheet is well worth it, because it moves the thinking from the workshop to the desk. The fabricator does not have to calculate anything while standing at the cutting machine, just follow the printed sequence. And if the design changes, I only need to rerun the script and print it again, without manually correcting the numbers one table cell at a time. Feel free to adapt the sheet layout and saw kerf allowance to your own tools. If I have left anything out of the explanation or made a typo, I will come back and correct it later.

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first