MISCELLANEOUS TECHNICAL ARTICLES BY Dr A R COLLINS

Drawing Metric Screw Threads

Introduction

This paper describes how to draw metric screw threads on the HTML canvas using the Cango graphics library. The 2D representation of a screw thread comprises straight lines, representing the profile of the thread crests and flanks, circular arcs representing the thread root profile and sinusoid curves for 2D projection of the cylindrical helices of the crest and root edges. Path segments are defined using the same commands as SVG path data.

Metric thread profile

The profile of metric threads are defined by ISO standard 68-1. The profile diagram with the relative dimensions is shown in Fig 1. Profiles of different threads scale with the threads pitch value.

Figure 1. ISO Metric external thread profile. Nodes used in the drawing example are shown as red dots.

A metric thread form is fully defined by the diameter measured across the crests \(Dc\) and the thread pitch \(P\), the profile may be drawn as a series of straight line and circular arc segments. The nodes required to draw the profile are shown as red dots in the diagram. The Cartesian coordinates of the profile's nodes are given by:

$$ \begin {aligned} t_1 &= (\tfrac{1}{16}P,\; \frac{Dc}{2}) \\ t_2 &= (\tfrac{3}{8}P, \;\; \frac{Dc}{2}-\tfrac{5}{8}H) \\ t_3 &= (\tfrac{1}{2}P, \;\; \frac{Dc}{2}-\tfrac{17}{24}H) \\ t_4 &= (P - \tfrac{3}{8}P, \;\; \frac{Dc}{2}-\tfrac{5}{8}H) \\ t_5 &= (P - \tfrac{1}{16}P,\;\; \frac{Dc}{2}) \\ t_6 &= (P + \tfrac{1}{16}P,\;\; \frac{Dc}{2}) \end {aligned} $$

The path data, expressed as arrays of SVG commands, for the three sections forming one thread's profile are shown below:

Top profile of a ISO metric thread.
The relative commands have been used so the initial "M" can be dropped when concatenating.

Dc = thread diameter across crests
P  = thread pitch
H  = 0.866025×P

// from t1 down left flank to t2, and the arc to t3 at the thread root
f1 = ["M",P/16,Dc/2,
      "l",5*P/16,-5*H/8, "a",H/6,H/6,0,0,1,P/8,-H/12]
   = ["M",P/16,Dc/2,
      "l",0.3125*P,-0.5413*P, "a",0.1443*P,0.1443*P,0,0,1,0.125*P,-0.0722*P]             (tl)

// right flank starting from t3 with the arc to t4 and up the right flank to t5
f2 = ["M",P/2,Dc/2-17*H/24,
      "a",H/6,H/6,0,0,1,P/8,H/12, "l",5*P/16,5*H/8]
   = ["M",P/2,Dc/2-17*H/24,
      "a",0.1443*P,0.1443*P,0,0,1,0.125*P,0.0722*P,"l",0.3125*P,0.5413*P]                (tr)

// from t5 the right flank crest edge across the crest to t6, the start of next thread
f3 = ["M",15*P/16,Dc/2, "l",P/8,0]
   = ["M",15*P/16,Dc/2, "l",0.125*P,0]                                                   (tc)

The bottom profile of the thread will have the same dimensions as the top but the Y coordinates are inverted. To keep a consistent winding direction when concatenating paths the bottom profile nodes are traversed in reverse order to the top.

Bottom profile of ISO metric thread.

var Dc,  // thread diameter across crests
    P,   // thread pitch
    H;   // 0.866025×P

f1 = ["M",P/2,Dc/2-17*H/24,
      "a",0.1443*P,0.1443*P,0,0,1,-0.125*P,-0.0722*P, "l",-0.3125*P,-0.5413*P]           (bl)

f2 = ["M",15*P/16,Dc/2,
      "l",-0.3125*P,0.5413*P, "a",0.1443*P,0.1443*P,0,0,1,-0.125*P,0.0722*P]             (br)

f3 = ["M",17*P/16,Dc/2, "l",-0.125*P,0]                                                  (bc)

The thread crest edges and thread root trace out cylindrical helices in 3 dimensions. The crest helix is defined by the thread diameter, measured across the crests and the thread pitch. The root helix is defined by the root diameter and the pitch. When projected into 2D the crest and root helices form a sinusoid. Fig. 2 shows one crest edge, drawn in red, as it traces out one cycle of a cosine function.

Figure 2. The outline drawing of a single turn of an ISO Metric thread. The left crest edge is shown in red, forming a single cycle of a cosine waveform.

Bézier approximation to the cosine function

To simplify drawing a thread, the sinusoidal paths can be approximated by Bézier curves, resulting in smoother curves with fewer points to manipulate. The approximation can be scaled horizontally to match the thread pitch and vertically to match the thread diameter.

The approximation of the sine function using cubic Bézier curves is described elsewhere. A derivation of the node coordinates is given by Thomas W at MathB.in[1]). Using this approximation the Cartesian coordinates of the seven points defining the two cubic Bézier curve approximation to the half cycle cosine function are given by:

The coordinates of the seven Bézier nodes are[1]: $$ \begin{aligned} P_0 &= \{ x:0,\quad y: 1\} \\ P_1 &= \{ x:\frac{\pi}{2}-1,\quad y:1\} \\ P_2 &= \{ x:\frac{\pi}{2} - \frac{6-(\frac{3}{2}\pi-3)^2}{6},\quad y: \frac{6-(\frac{3}{2}\pi-3)^2}{6}\} \\ P_3 &= \{ x:\pi/2,\quad y: 0\} \\ P_4 &= \{ x:\pi/2+\frac{6-(\frac{3}{2}\pi-3)^2}{6},\quad y:-\frac{6-(\frac{3}{2}\pi-3)^2}{6}\} \\ P_5 &= \{ x:\pi/2+1,\quad y:-1\} \\ P_6 &= \{ x:\pi,\quad y:-1\} \end{aligned} $$

The nodes, P0 .. P6 defining the cubic Bézier curve approximation to the first two quadrants of a cosine are shown as red dots in Fig. 3.

Figure 3. Cubic Bézier approximation to the cosine function. The Bézier control points for the first two quadrants are shown as red dots.

The path data for the half cycle cosine function are shown below:

 m = (6 - (pi*3/2 - 3)2)/6
   = 0.5113
pi = Math.PI

cosine_f = ["M",0,1, "c",pi/2-1,0, pi/2-m,m-1, pi/2,-1, "s", 1,-1, pi/2,-1];

Also useful is the commands representing the cosine and traversing the nodes in reverse order
i.e. the opposite winding direction.

cosine_r = ["M",pi/2,0, "c",-m,m, -1,1, -pi/2,1, "s", m-1,0, m-pi/2,1-m, -pi/2,1];

Scaling the cosine to thread dimensions

When drawing the 2D elevation of a screw thread, the thread crest cosine function must be scaled horizontally to match the thread pitch and vertically the thread crest or root diameter.

If pitch = \(P\) then X coordinate scale factor, \(sp\), must map \(2\pi\) to \(P\), hence \(sp = P/2\pi\). For the crest helix, the Y coordinate scale factor, \(sc\), must map the cosine amplitude, 1, to the crest radius, hence \(sc = Dc/2\). For the root helix, the Y coordinate scale factor, \(sr\), must map 1 to the root radius, hence \(sr = Dc/2 - \frac{17}{24}H\)

The data for these scaled profiles are shown below:

Bézier approximation to half cycle cosine paths of thread crests and valleys

 P = pitch
Dc = diameter across crests
 m = (6 - (pi*3/2 - 3)2)/6
   = 0.5113
pi = Math.PI
sp = P/(pi*2)
   = 0.1592*P           X coordinate scale factor
sc = 0.5*Dc             Crest cosine Y coordinate scale factor
sr = Dc/2-17*H/24
   = 0.5*Dc-0.6134*P    Root cosine Y coordinate scale factor

crest_f = ["M",0,sc,
           "c",0.5708*sp,0,1.0595*sp,-0.4887*sc,1.5708*sp,-sc, "s",sp,-sc,1.5708*sp,-sc];

 root_f = ["M",0,sr,
           "c",0.5708*sp,0,1.0595*sp,-0.4887*sr,1.5708*sp,-sr, "s",sp,-sr,1.5708*sp,-sr];

Traversing in reverse direction

crest_r = ["M",pi*sp,-sy,
           "c",-0.5708*sp,0,-1.0595*sp,0.4887*sy, -1.5708*sp,sy, "s",-sp,sy, -1.5708*sp,sy];

 root_r = ["M",pi*sp,-sr,
           "c",-0.5708*sp,0, -1.0595*sp,0.4887*sr, -1.5708*sp,sr, "s",-sp,sr, -1.5708*sp,sr];

(The initial "M" command can be dropped if pen is in place when segments are concatenated)

Outline path of one turn of a metric thread

The preceding sections have provided the components needed to assemble the path data to draw one turn of a metric thread. A JavaScript function generate the SVG commands array is shown below. For this example drawing just the thread outline the segment paths have been concatenated into a single array. The scale factors for pitch and thread diameter have been left as variables for brevity.

Note: To assist in drawing colour filled threads, the thread path data has been generated in three distinct sections, left flank, right flank and crest. Each section would need an addition segment to close the shape outline (as shown in the code comments). The three shapes would each have a different gradient fill with corresponding sections on each thread turn coloured alike. The nodes of each section have been traversed in a consistent (clockwise) direction for correct colour filling when rendered to a canvas.

function genThreadOutline(pitch, diameter)
{
  'use strict'
  var pi = Math.PI,
    P = pitch,
    H = 0.86603*pitch,
    Dc = diameter,                // diameter across thread crests
    m = 0.51128733,
    sp = 0.15915*P,
    sc = 0.5*Dc,               // crest profile Y coordinate scale factor
    sr = 0.5*Dc - 0.6134*P,    // root profile Y coordinate scale factor
    topL    = ["l",0.3125*P,-0.5413*P, "a",0.1443*P,0.1443*P,0,0,1,0.125*P,-0.0722*P],   //from (tl)
    topR    = ["a",0.1443*P,0.1443*P,0,0,1,0.125*P,0.0722*P, "l",0.3125*P,0.5413*P],     //from (tr)
    topC    = ["l",0.125*P,0],                                                           //from (tc)
    bottomL = ["a",0.1443*P,0.1443*P,0,0,1,-0.125*P,-0.0722*P, "l",-0.3125*P,-0.5413*P], //from (bl)
    bottomR = ["l",-0.3125*P,0.5413*P, "a",0.1443*P,0.1443*P,0,0,1,-0.125*P,0.0722*P],   //from (br)
    bottomC = ["l",-0.125*P,0],                                                          //from (bc)
    crestR  = ["c",0.5708*sp,0, 1.0595*sp,-0.4887*sc, 1.5708*sp,-sc,
                "s",sp,-sc, 1.5708*sp,-sc],                                              //from (cf)
    crestL  = ["c",-0.5708*sp,0, -1.0595*sp,0.4887*sc, -1.5708*sp,sc,
                "s",-sp,sc, -1.5708*sp,sc],                                              //from (cr)
    rootL   = ["c",0.5708*sp,0, 1.0595*sp,-0.4887*sr, 1.5708*sp,-sr,
                "s",sp,-sr, 1.5708*sp,-sr],                                              //from (rf)
    rootR   = ["c",-0.5708*sp,0, -1.0595*sp,0.4887*sr, -1.5708*sp,sr,
                "s",-sp,sr, -1.5708*sp,sr],                                              //from (rr)
    startL, startR, startC,
    flankL, flankR, crest;

  startL = ["M",0.0625*P,0.5*Dc];
  flankL = startL.concat(topL).concat(rootL).concat(bottomL);
    // for closed shape: flankL.concat(crestL);

  startR = ["M",0.5*P, sr];
  flankR = startR.concat(topR).concat(crestR).concat(bottomR);
    // for closed shape: flankR.concat(rootR);

  startC = ["M",0.9375*P,0.5*Dc];
  crest = startC.concat(topC).concat(crestR).concat(bottomC);
    // for closed shape: crest.concat(crestL);

  return flankL.concat(flankR).concat(crest);
}

Here is an example of drawing an "M6" thread. The M6 thread has diameter of 6mm across the thread crests, the pitch for an M6 coarse thread is 1mm [2].

Fig 4 shows the three paths; flankL, flankR and crest drawn onto the canvas by the Cango library.

Figure 4. A single turn of external thread of an M6 ISO Metric thread.

Adding a chamfered end

Metric bolts usually have their end chamfered down to the root diameter of the thread. Fig 5. shows the outline of this type of thread end. The top right flank will start as normal at the thread root but only go up to e1 (e1 is 3/5 of the distance from t4 to t5 in Fig 1). The chamfer face is at 45° down to the root diameter at e2. e3 is the bottom point of the end just e2 flipped about the Y axis. The bottom chamfer then returns at 45° to the axis, meeting the tip of the bottom root arc at e4. The end is completed by two quadrants of a cosine back to e1.

Figure 5. Outline a typical Metric screw thread end.

$$ \begin {aligned} e_1 &= (\tfrac{13}{16}P, \; \frac{Dc}{2}-\tfrac{1}{4}H) \\ e_2 &= (\tfrac{9}{8}P + \tfrac{1}{12}H, \;\; \frac{Dc}{2}-\tfrac{17}{24}H) \\ e_3 &= (\tfrac{9}{8}P + \tfrac{1}{12}H, \;\; -\frac{Dc}{2}+\tfrac{17}{24}H) \\ e_4 &= (\tfrac{9}{8}P, \;\; -\frac{Dc}{2}+\tfrac{5}{8}H) \\ \end {aligned} $$

The end helix runs from Y = \(\frac{Dc}{2}-\tfrac{1}{4}H\) to \(-\frac{Dc}{2}+\tfrac{5}{8}H\), so diameter = \(Dc-\frac{21}{24}H\) and the Y axis scale factor for the cosine, \(se = \frac{Dc}{2}-\frac{21}{48}H\).

The end helix runs from X = \(\tfrac{13}{16}P\) to \(\tfrac{9}{8}P\) (half turn) so pitch = \(\frac{5}{8}P\) and the X axis scale factor, \(sx = {\frac{5}{8}P}/{2\pi} \)

Metric thread chamfered end

sp = 0.15915*P          // X scale factor for cosine 0..pi to 0.. thread pitch
sx = 0.625*P/(2*pi)     // X scale factor mapping cosine 0..pi to 0.. end crest pitch
sc = 0.5*Dc             // Y crest profile Y coordinate scale factor
sr = 0.5*Dc-0.6134*P    // Y root profile Y coordinate scale factor
se = 0.5*Dc-0.3789*P    // Y crest profile Y coordinate for shortened end crest

The data for the end chamfers using relative coordinates is:
endCap = ["M",0,0, "l", 5*P/16+H/12,-11*H/24, 0,-Dc+17*H/12, -H/12,-H/12];
       = ["M",0,0, "l", 0.3847*P,-0.3969*P, 0,1.2269*P-Dc, -0.0722*P,-0.0722*P];

End crest outline (CW) used for the right flank:
end crestR = ["M",0,0, "c",0.5708*sx,0,1.0595*sx,-0.4887*se, 1.5708*sx,-se,
                        "s",sx,-se, 1.5708*sx,-se];

Traversing in reverse direction (CCW) used for chamfered:
end crestL = ["M",0,0, "c",-0.5708*sx,0, -1.0595*sx,0.4887*se, -1.5708*sx,se,
                            "c",-0.5113*sx,0.5113*se, -sx,se, -1.5708*sx,se];

The JavaScript function to generate the data for the outline path of the chamfered last turn is shown below. It has been written to allow easy modification to generate closed shaped for colour filled drawings.

function genEndOutline(pitch, diameter)
{
  'use strict'
  var pi = Math.PI,
      P = pitch,
      H = 0.86603*pitch,
      Dc = diameter,             // diameter across thread crests
      m = 0.51128733,
      sp = 0.15915*P,
      sc = 0.5*Dc,               // crest profile Y coordinate scale factor
      sr = 0.5*Dc - 0.6134*P,    // root profile Y coordinate scale factor
      sx = 0.625*P/(2*pi),       // X scale factor for cosine 0..pi to 0..end crest pitch
      se = 0.5*Dc - 0.3789*P,    // Y scale cosine amplitude 1 to end crest diameter/2
      topL    = ["l",0.3125*P,-0.5413*P, "a",0.1443*P,0.1443*P,0,0,0,0.125*P,-0.0722*P],
      rootL   = ["c",0.5708*sp,0, 1.0595*sp,-0.4887*sr, 1.5708*sp,-sr,
                 "c",0.5113*sp,-0.5113*sr, sp,-sr, 1.5708*sp,-sr],
      bottomL = ["a",0.1443*P,0.1443*P,0,0,1,-0.125*P,-0.0722*P, "l",-0.3125*P,-0.5413*P],
      crestL  = ["c",-0.5708*sp,0, -1.0595*sp,0.4887*sc, -1.5708*sp,sc,
                 "s",-sp,sc, -1.5708*sp,sc],
      endTopR    = ["m",0,0, "a",0.1443*P,0.1443*P,0,0,1,0.125*P,0.0722*P, "l",0.1875*P,0.3248*P],
      endCrestR = ["m",0,0, "c",0.5708*sx,0, 1.0595*sx,-0.4887*se, 1.5708*sx,-se,
                   "s",sx,-se, 1.5708*sx,-se],
      endBottomR = ["m",0,0, "a",0.1443*P,0.1443*P,0,0,1,-0.125*P,0.0722*P],
      rootR   = ["c",-0.5708*sp,0, -1.0595*sp,0.4887*sr, -1.5708*sp,sr,"s",-sp,sr, -1.5708*sp,sr],
      endCap  = ["m",0,0, "l", 0.3847*P,-0.3969*P, 0,1.2269*P-Dc, -0.0722*P,-0.0722*P],
      endCrestL = ["m",0,0, "c",-0.5708*sx,0, -1.0595*sx,0.4887*se, -1.5708*sx,se,
                   "s",-sx,se, -1.5708*sx,se],
      startL, startR, startE,
      flankL, endFlankR, end;

  startL = ["M",P/16,Dc/2];
  flankL = startL.concat(topL).concat(rootL).concat(bottomL);
  // for closed shape: flankL.concat(crestL);

  startR = ["M",P/2, sr];
  endFlankR  = startR.concat(endTopR).concat(endCrestR).concat(endBottomR);
  // for closed shape: endFlankR.concat(rootR);

  startE = ["M",13*P/16, Dc/2-H/4];
  end = startE.concat(endCap)
  // for closed shape: end.concat(endCrestL);

  return flankL.concat(endFlankR).concat(end);
}

Drawing a hexagonal bolt head

Metric bolts come with several head styles, for the purpose of this page a standard hex head has been constructed to complete the drawing example. Here is the code to draw a standard hex head for the M6 screw.

Figure 6. Outline a hex head Metric bolt.

function genHexHeadOutline(diameter, length)
{
  'use strict'
  function r3p(x1,y1, x2,y2, x3,y3) // radius of circle through 3 given points
  {
    var num = Math.sqrt(((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))*((x2-x3)*(x2-x3)+
                        (y2-y3)*(y2-y3))*((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1))),
        den = 2*Math.abs(x1*y2+x2*y3+x3*y1-x1*y3-x2*y1-x3*y2);

    return num/den;
  }

  var P = prefPitch[diameter],
      Dc = diameter,
      t = 2*Dc/3,                                              // thickness of head
      daf = (Dc>10)? Math.round(1.5*Dc): Math.round(1.625*Dc), // across flats (valid M5..M33)
      dap = 2*daf/Math.sqrt(3),                                // diameter across the points
      dx = Math.tan(30*Math.PI/180)*(dap-daf)/2,   // 30deg chamfer cuts dx off hex points
      f = dap/2,                      // width of flats
      r1 = r3p(dx,-f/2,0,0,dx,f/2),   // radius of flat top edge
      pr1 = r3p(dx,-f/4,0,0,dx,f/4),  // projected (turn by 60deg) r1
      fEdge = t - dx,                 // length of flat axial edges
      turns = Math.ceil((2*Dc+6)/P),  // Thread length is (2*Dc+6)mm
      slen = length - (turns+1)*P,    // shank length from start of thread to base of head
      sp = 0.15915*P,
      sc = 0.5*Dc,                    // crest profile Y coordinate scale factor
      crestR, shankData, headData, arcsData, topData;

  crestR  = ["M", P/16,Dc/2, "c",0.5708*sp,0, 1.0595*sp,-0.4887*sc, 1.5708*sp,-sc,
                             "s",sp,-sc, 1.5708*sp,-sc],
  shankData = ["M", 9*P/16,-Dc/2, "L", -slen, -Dc/2, "M", P/16,Dc/2, "L", -slen, Dc/2];
  headData = ["M", -slen-fEdge,-dap/2, "L",-slen,-dap/2, -slen,dap/2, -slen-fEdge,dap/2,
              "M", -slen,f/2, "L",-slen-fEdge,f/2, "M", -slen,-f/2, "L",-slen-fEdge,-f/2];
  arcsData = ["M", -slen-fEdge,-dap/2, "A", pr1,pr1,0,0,0,-slen-fEdge,-f/2,
              "A", r1,r1,0,0,0,-slen-fEdge,f/2, "A", pr1,pr1,0,0,0,-slen-fEdge,dap/2];
  topData = ["M", -slen-t,-3*f/4, "L", -slen-t,3*f/4];

  return crestR.concat(shankData).concat(headData).concat(arcsData).concat(topData);
}

Putting all the outline drawings together gives us a schematic drawing of various size metric bolts shown in Fig 7.

Figure 7. ISO Metric bolts drawn with the Cango graphics library. Use the drop down list to select the thread size and the length. Only preferred threads (M5 to M30) and preferred lengths (up to 120mm) for each thread are shown.

References:
1. Thomas W, "Approximating a sine curve with cubic Bézier splines" http://mathb.in/1447.
2. "ISO general purpose metric screw threads" ISO 262 1998.