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.
The Cango canvas drawing library has been designed to take advantage of the property of Bézier curves to hold their shape under rotation and translation and also maintain their shape if the nodes are traversed in reverse order. Cango paths may be defined using the same segment commands as SVG paths.
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.
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 Cgo2D path data (equivalent to SVG but with Y coordinates inverted) for the three sections forming one thread's profile are shown below:
Top profile of a ISO metric thread in Cgo2D format. 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 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.
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 Cgo2D 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 Cgo2D representation of the cosine 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 Cgo2D 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 Cgo2D (SVG) data 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.
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.
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 Cgo2D 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 Cgo2D 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.
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.
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.