diff options
author | Dewey Garrett <dgarrett@panix.com> | 2014-03-02 17:49:17 -0700 |
---|---|---|
committer | Dewey Garrett <dgarrett@panix.com> | 2014-03-03 11:18:43 -0700 |
commit | b64d7497078ea1b9a93f64d971487cb3ef04a2ec (patch) | |
tree | 648c765f7544bb7fd44bb22817e746d7c4c45442 | |
parent | 1cbe86cf1871ab191c57b67d6fe1a1b916f90a5f (diff) | |
download | linuxcnc-b64d7497078ea1b9a93f64d971487cb3ef04a2ec.tar.gz linuxcnc-b64d7497078ea1b9a93f64d971487cb3ef04a2ec.zip |
ngcgui-gcmc programmable involute-gear example
new: involute-gear.gcmc adapted for ngcgui from B. Stultien's example
new: ensure_mode.gcmd include file to resolve potential conflict
with preamble and gcmc code (gcmc wins)
with updates:
1) popup msg error if entry box for gcmc not a number
2) allow comment after tag when tagging a gcmc option
3) handle gcmc_include_path for new custom tab additions
4) incorporate ensure_mode in .gcmc examples
5) update ngcgui.txt for ensure_mode
-rw-r--r-- | configs/sim/axis/ngcgui/ngcgui_gcmc.ini | 2 | ||||
-rw-r--r-- | configs/sim/axis/ngcgui/pyngcgui_gcmc.ini | 3 | ||||
-rw-r--r-- | configs/sim/gscreen/ngcgui/pyngcgui_gscreen_gcmc.ini | 2 | ||||
-rw-r--r-- | configs/sim/touchy/ngcgui/pyngcgui_touchy_gcmc.ini | 2 | ||||
-rw-r--r-- | docs/src/gui/ngcgui.txt | 24 | ||||
-rw-r--r-- | lib/python/pyngcgui.py | 15 | ||||
-rw-r--r-- | nc_files/gcmc_lib/drill.gcmc | 1 | ||||
-rw-r--r-- | nc_files/gcmc_lib/ensure_mode.gcmc | 21 | ||||
-rw-r--r-- | nc_files/gcmc_lib/involute-gear.gcmc | 234 | ||||
-rw-r--r-- | nc_files/gcmc_lib/square.gcmc | 1 | ||||
-rw-r--r-- | nc_files/gcmc_lib/star.gcmc | 2 | ||||
-rw-r--r-- | nc_files/gcmc_lib/wheels.gcmc | 1 | ||||
-rwxr-xr-x | tcl/ngcgui.tcl | 24 |
13 files changed, 327 insertions, 5 deletions
diff --git a/configs/sim/axis/ngcgui/ngcgui_gcmc.ini b/configs/sim/axis/ngcgui/ngcgui_gcmc.ini index 62b159cb6..5d53b30c0 100644 --- a/configs/sim/axis/ngcgui/ngcgui_gcmc.ini +++ b/configs/sim/axis/ngcgui/ngcgui_gcmc.ini @@ -27,6 +27,7 @@ NGCGUI_FONT = Helvetica -12 normal NGCGUI_PREAMBLE = in_std.ngc NGCGUI_SUBFILE = square.gcmc NGCGUI_SUBFILE = drill.gcmc +NGCGUI_SUBFILE = involute-gear.gcmc NGCGUI_SUBFILE = star.gcmc NGCGUI_SUBFILE = wheels.gcmc NGCGUI_SUBFILE = xyz.ngc @@ -44,6 +45,7 @@ NGCGUI_OPTIONS = # example for gcmc includes: separate dirnames with colons: # GCMC_INCLUDE_PATH = dir1:dir2:... +GCMC_INCLUDE_PATH = ../../nc_files/gcmc_lib TTT = truetype-tracer TTT_PREAMBLE = in_std.ngc diff --git a/configs/sim/axis/ngcgui/pyngcgui_gcmc.ini b/configs/sim/axis/ngcgui/pyngcgui_gcmc.ini index ed25b4772..de616cba1 100644 --- a/configs/sim/axis/ngcgui/pyngcgui_gcmc.ini +++ b/configs/sim/axis/ngcgui/pyngcgui_gcmc.ini @@ -28,6 +28,7 @@ NGCGUI_FONT = Helvetica -12 normal NGCGUI_PREAMBLE = in_std.ngc NGCGUI_SUBFILE = square.gcmc NGCGUI_SUBFILE = drill.gcmc +NGCGUI_SUBFILE = involute-gear.gcmc NGCGUI_SUBFILE = star.gcmc NGCGUI_SUBFILE = wheels.gcmc NGCGUI_SUBFILE = simp.ngc @@ -46,6 +47,7 @@ NGCGUI_OPTIONS = # example for gcmc includes: separate dirnames with colons: # GCMC_INCLUDE_PATH = dir1:dir2:... +GCMC_INCLUDE_PATH = ../../nc_files/gcmc_lib DISPLAY = axis CYCLE_TIME = 0.100 @@ -89,6 +91,7 @@ HALFILE = simulated_home.hal HALUI = halui [TRAJ] +NO_FORCE_HOMING = 1 AXES = 3 COORDINATES = X Y Z #NO_FORCE_HOMING = 1 diff --git a/configs/sim/gscreen/ngcgui/pyngcgui_gscreen_gcmc.ini b/configs/sim/gscreen/ngcgui/pyngcgui_gscreen_gcmc.ini index 2f56d4894..0eb376bea 100644 --- a/configs/sim/gscreen/ngcgui/pyngcgui_gscreen_gcmc.ini +++ b/configs/sim/gscreen/ngcgui/pyngcgui_gscreen_gcmc.ini @@ -37,6 +37,7 @@ EMBED_TAB_COMMAND = gladevcp -x {XID} pyngcgui.ui NGCGUI_PREAMBLE = in_std.ngc NGCGUI_SUBFILE = square.gcmc NGCGUI_SUBFILE = drill.gcmc +NGCGUI_SUBFILE = involute-gear.gcmc NGCGUI_SUBFILE = star.gcmc NGCGUI_SUBFILE = wheels.gcmc NGCGUI_SUBFILE = simp.ngc @@ -44,6 +45,7 @@ NGCGUI_SUBFILE = "" # example for gcmc includes: separate dirnames with colons: # GCMC_INCLUDE_PATH = dir1:dir2:... +GCMC_INCLUDE_PATH = ../../nc_files/gcmc_lib # Cycle time, in milliseconds, that display will sleep between polls #was: CYCLE_TIME = 100 diff --git a/configs/sim/touchy/ngcgui/pyngcgui_touchy_gcmc.ini b/configs/sim/touchy/ngcgui/pyngcgui_touchy_gcmc.ini index 47f0021d2..600774b8e 100644 --- a/configs/sim/touchy/ngcgui/pyngcgui_touchy_gcmc.ini +++ b/configs/sim/touchy/ngcgui/pyngcgui_touchy_gcmc.ini @@ -32,12 +32,14 @@ NGCGUI_PREAMBLE = in_std.ngc NGCGUI_SUBFILE = square.gcmc NGCGUI_SUBFILE = drill.gcmc NGCGUI_SUBFILE = star.gcmc +NGCGUI_SUBFILE = involute-gear.gcmc NGCGUI_SUBFILE = wheels.gcmc NGCGUI_SUBFILE = simp.ngc NGCGUI_SUBFILE = "" # example for gcmc includes: separate dirnames with colons: # GCMC_INCLUDE_PATH = dir1:dir2:... +GCMC_INCLUDE_PATH = ../../nc_files/gcmc_lib DISPLAY = touchy diff --git a/docs/src/gui/ngcgui.txt b/docs/src/gui/ngcgui.txt index 344aa8042..43293c155 100644 --- a/docs/src/gui/ngcgui.txt +++ b/docs/src/gui/ngcgui.txt @@ -832,6 +832,30 @@ Options for gcmc are available with the terminal command: gcmc --help ---- +A gcmc program by default uses metric mode. The mode can be +set to inches with the option setting: + +---- +//ngcgui: --imperial +---- + +A preamble file, if used, can set a mode (g20 or g21) that +conflicts with the mode used by a gcmc file. To ensure that +the gcmc program mode is in effect, include the following +statement in the .gcmc file: + +---- +include("ensure_mode.gcmc") +---- + +and provide a proper path for gcmc include_files in the ini file, +for example: + +---- +[DISPLAY] +GCMC_INCLUDE_PATH = ../../nc_files/gcmc_lib +---- + == DB25 Example The following shows the DB25 subroutine. In the first photo you see where you diff --git a/lib/python/pyngcgui.py b/lib/python/pyngcgui.py index a8d9e442a..ddfae7992 100644 --- a/lib/python/pyngcgui.py +++ b/lib/python/pyngcgui.py @@ -1389,7 +1389,11 @@ class SubFile(): ropt = re.search(r'^ *\/\/ *ngcgui *: *(-.*)$' ,l) if ropt: - self.gcmc_opts.append(ropt.group(1)) + gopt = ropt.group(1) + gopt = gopt.split("/")[0] ;# trailing comment + gopt = gopt.split(";")[0] ;# convenience + gopt = gopt.split()[0] ;# leading/trailing spaces + self.gcmc_opts.append(gopt) continue name = None @@ -2185,7 +2189,14 @@ class ControlPanel(): #print 'k=',k,p.sub_data.ndict[k] name,dvalue,comment = p.sub_data.ndict[k] # make all entry box values explicitly floating point - fvalue = str(float(m.efields.pentries[k].getentry())) + try: + fvalue = str(float(m.efields.pentries[k].getentry())) + except ValueError: + user_message(mtype=gtk.MESSAGE_ERROR + ,title='gcmc input ERROR' + ,msg=_('<%s> must be a number' % m.efields.pentries[k].getentry()) + ) + return False ;# fail xcmd.append('--define=' + name + '=' + fvalue) xcmd.append(m.sub_file) diff --git a/nc_files/gcmc_lib/drill.gcmc b/nc_files/gcmc_lib/drill.gcmc index 0b6d254c9..498f26193 100644 --- a/nc_files/gcmc_lib/drill.gcmc +++ b/nc_files/gcmc_lib/drill.gcmc @@ -1,3 +1,4 @@ +include("ensure_mode.gcmc"); //avoid preamble conflict /******************************************************* The following //comment lines identify ngcgui inputs: diff --git a/nc_files/gcmc_lib/ensure_mode.gcmc b/nc_files/gcmc_lib/ensure_mode.gcmc new file mode 100644 index 000000000..028cb50d9 --- /dev/null +++ b/nc_files/gcmc_lib/ensure_mode.gcmc @@ -0,0 +1,21 @@ +// gcmc include file +// ensure mode agrees with g20,g21 at runtime +// in case a preamble conflicts with gcmc program + +if (ismodemm()) { + literal("\ + o1 if [#<_metric> ne 1]\n\ + (debug, !!!ensure_mode.gcmc:)\n\ + (debug, !!!Changed to g21)\n\ + g21\n\ + o1 endif\n\ + "); +} else { + literal("\ + o1 if [#<_imperial> ne 1]\n\ + (debug, !!!ensure_mode.gcmc:)\n\ + (debug, !!!Changed to g20)\n\ + g20\n\ + o1 endif\n\ + "); +} diff --git a/nc_files/gcmc_lib/involute-gear.gcmc b/nc_files/gcmc_lib/involute-gear.gcmc new file mode 100644 index 000000000..420050b4a --- /dev/null +++ b/nc_files/gcmc_lib/involute-gear.gcmc @@ -0,0 +1,234 @@ +/* + * G-code meta compiler + * + * Copyright (C) 2014 B. Stultiens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Gear example + * ------------ + * This example is by no means a complete or correct implementation of gears in + * any generic form. It is primarily for inspiration and to show what gcmc can + * do with relatively little coding. You may use this script as inspiration to + * create a better implementation if you like. + * + * Please note: The below parameters will shift the examples with an XY offset + * as indicated by the options. These are passed on the command-line when the + * examples are generated. + * + * @@@--svg-toolwidth 0.1 --svg-opacity 1 --svg-no-movelayer -x 110 -y 70@@@ + */ + + +/* + * Gear terms: + * N - Number of Teeth + * Pa - Pressure Angle + * D - Pitch Diameter - D = N/P = Do - 2/P (Gear radius at center of the teeth) + * P - Diametral Pitch - P = N/D + * p - Circular Pitch - p = pi() / P + * Db - Base Diameter - Db = D * cos(Pa) (Bottom of teeth insertion) + * Dr - Root Diameter - Dr = D - 2b (Bottom of tooth cutout) + * Do - Outside Diameter - Do = D + 2a + * a - Addendum - a = 1/P + * b - Dedendum - b = ht - a + * ht - Whole Depth (Pa<20) - 2.157/P + * ht - Whole Depth (Pa>=20) - 2.2/P + 0.05mm (Total depth from outer dia to bottom) + * t - Tooth Thickness - t = pi()/(2*P) (Thinckness at Pitch Diameter) + */ + +__ang_step = 2.0deg; /* Trace interval for curves */ + +/* + * Point on involute curve at specified angle, see https://en.wikipedia.org/wiki/Involute + * Cartesian: + * x = a * ( cos(t) + t * sin(t)) + * y = a * ( sin(t) - t * cos(t)) + * Polar: + * r = a * sqrt(1 + t^2) = sqrt(a^2 + (a*t)^2) + * phi = t - atan(t) + * where: + * - a = circle radius + * - t = angle (radians) + * + * For angle from circle radius: t^2 = (r/a)^2 - 1 + */ +function involute_point(angle, radius) +{ + angle = to_rad(angle); /* Multiplication must be in radians */ + return radius * [cos(angle) + to_none(angle) * sin(angle), sin(angle) - to_none(angle) * cos(angle)]; +} + +function involute_angle(radius, outrad) +{ + return to_rad(sqrt(pow(outrad/radius, 2.0) - 1)); +} + +/* + * Make a gear with: + * - nteeth Number of teeth + * - pressure_angle Teeth contact pressure angle + * - diametral_pitch Diametral pitch (teets/length) + * + * Return a vectorlist with outer points of the gear centered at [0,0] + */ +function gear_P(nteeth, pressure_angle, diametral_pitch) +{ + /* The routine gets is serious trouble if you make the pressure angle + * too large or too small. Warn the user if such case occurs. + */ + if(pressure_angle > 24.6deg) { + warning("Pressure angle (", pressure_angle, ") too large, cannot fit teeth inside the set outside diameter"); + } + if(pressure_angle < 12.0deg) { + warning("Pressure angle (", pressure_angle, ") too small, teeth may get stuck at pitch radius"); + } + local i; + local pitch_diameter = nteeth / diametral_pitch; + local base_diameter = pitch_diameter * cos(pressure_angle); + local addendum = 1.0/diametral_pitch; + local ht = 2.157 / diametral_pitch; + local dedendum = ht - addendum; + local outside_diameter = pitch_diameter + 2.0*addendum; + local root_diameter = base_diameter - 2.0*dedendum; + local work_diameter = outside_diameter - 4.0*addendum; + + local tooth = {}; // The curve for one tooth + + /* + * message("nteeth=", nteeth, " pressure_angle=", pressure_angle, " diametral_pitch=", diametral_pitch); + * message("addendum=", addendum, " dedendum=", dedendum, " ht=", ht); + * message("pitch_diameter=", pitch_diameter); + * message("base_diameter=", base_diameter); + * message("outside_diameter=", outside_diameter); + * message("root_diameter=", root_diameter); + * message("work_diameter=", work_diameter); + */ + + /* + * Show the different diameters: + * hole([0, 0], pitch_diameter/2.0); + * hole([0, 0], base_diameter/2.0); + * hole([0, 0], outside_diameter/2.0); + * hole([0, 0], root_diameter/2.0); + * hole([0, 0], work_diameter/2.0); + */ + + // Fillet radius is approx. Will not reach root exactly, but close enough + // Otherwise need to calculate intersection with root-circle + local filletrad = (base_diameter - root_diameter)/8.0; + + // Center of the fillet arc, involute makes a ~240deg angle with fillet arc + // The fillet arc runs from the root to the working depth of the gear + local center = rotate_xy([-filletrad, 0.0mm], 60.0deg) + [work_diameter/2.0, 0]; + + // Trace the fillet arc from ~root-circle to working depth at involute arc starting Y-level + for(i = 180.0deg; i > 60.0deg; i -= __ang_step*2.5) { + tooth += { [cos(i), sin(i)] * filletrad + center }; + } + if(i != 60.0deg) { + // Add the last point if we did not reach the working depth + tooth += { [cos(60.0deg), sin(60.0deg)] * filletrad + center }; + } + + // Calculate the maximum involute angle to intersect at the outside radius + local max_a = involute_angle(base_diameter/2.0, outside_diameter/2.0); + + // Trace the involute arc from the base up to outside radius + for(i = 0.0deg; i < max_a; i += __ang_step) { + tooth += { involute_point(i, base_diameter/2.0)}; + } + if(i != max_a) { + // Add the last point if we did not reach the outside radius + tooth += { involute_point(max_a, base_diameter/2.0)}; + } + + // We now have one side of the tooth. Rotate to be at tooth-symmetry on X-axis + tooth = rotate_xy(tooth, -90.0deg / nteeth); + + // Add the same curve mirrored to make the other side of the tooth + // Coordinates reverse to have them all in one direction only + tooth += reverse(scale(tooth, [1, -1])); + + // Create all teeth of the gear by adding each tooth at correct angle + local gear = {}; + repeat(nteeth; i) { + gear += rotate_xy(tooth, 360.0deg * i / nteeth); + } + + return gear; +} + +/* -------------------- Helper Functions -------------------- */ + +/* + * Trace a path at given offset + */ +function trace(path, offset) +{ + goto(path[-1] + offset); + foreach(path; v) { + move(v + offset); + } +} + +/* + * Make a hole at center point with given radius + */ +function hole(point, radius) +{ + goto(point - [radius]); + circle_cw_r([radius, 0]); +} + +/* -------------------- Main Program -------------------- */ + +//ngcgui: info: Involute-gear example (inputs in mm) + +include("ensure_mode.gcmc"); //avoid preamble conflict + +//ngcgui: D = 100.0; //, Pitch Dia (mm) +//ngcgui: HD = 6.0; //, Hole Dia (mm) +//ngcgui: N = 9; //, Number of teeth +//ngcgui: PA = 20.0; //, Pressure Angle (deg) +//ngcgui: frate = 600; //, Feedrate (mm/sec) +//ngcgui: xoffset = 0; +//ngcgui: yoffset = 0; +//ngcgui: howmany = 1; //, howmany (1 | 2) + +// ngcgui entries are unitless so these additions are used +// to ensure 1) floatingpoint and 2) mm units for this program +frate = frate + 0.0mm; + HD = HD + 0.0mm; + D = D + 0.0mm; + PA = PA + 0.0deg; + +P = N/D; // Diametral pitch + +feedrate(frate); +location = [xoffset,yoffset]; + +if (howmany == 1) { + hole(location, HD/2.0); + trace(gear_P(N, PA, P), location); +} elif (howmany == 2) { + hole(location + [D/2.0, 0.0mm], HD/2.0); + trace(gear_P(N, PA, P), location + [D/2.0, 0.0mm]); + hole(location + [-D/2.0, 0.0mm], HD/2.0); + trace(gear_P(N, PA, P), location + [-D/2, 0.0mm]); +} else { + error("howmany must be 1 or 2"); +} + diff --git a/nc_files/gcmc_lib/square.gcmc b/nc_files/gcmc_lib/square.gcmc index ad4849a53..bd5c2634e 100644 --- a/nc_files/gcmc_lib/square.gcmc +++ b/nc_files/gcmc_lib/square.gcmc @@ -1,3 +1,4 @@ +include("ensure_mode.gcmc"); //avoid preamble conflict //ngcgui: info: SQUARE -- simple gcmc example /*COPY this file to a directory in your subroutine search path that diff --git a/nc_files/gcmc_lib/star.gcmc b/nc_files/gcmc_lib/star.gcmc index 31ce271b0..43a27c976 100644 --- a/nc_files/gcmc_lib/star.gcmc +++ b/nc_files/gcmc_lib/star.gcmc @@ -1,3 +1,5 @@ +include("ensure_mode.gcmc"); //avoid preamble conflict + /* demo for using gcmc file with [py]ngcgui ngcgui tags follow: diff --git a/nc_files/gcmc_lib/wheels.gcmc b/nc_files/gcmc_lib/wheels.gcmc index 8addbc3d9..5e28c27f5 100644 --- a/nc_files/gcmc_lib/wheels.gcmc +++ b/nc_files/gcmc_lib/wheels.gcmc @@ -1,3 +1,4 @@ +include("ensure_mode.gcmc"); //avoid preamble conflict /* demo for using gcmc file with [py]ngcgui //ngcgui: info: WHEELS (gcmc G-Code Meta Compiler) diff --git a/tcl/ngcgui.tcl b/tcl/ngcgui.tcl index f0d6a706e..d217b6ce8 100755 --- a/tcl/ngcgui.tcl +++ b/tcl/ngcgui.tcl @@ -884,6 +884,13 @@ proc ::ngcgui::parse_gcmc {hdl ay_name filename args} { set eopt "^ *\\/\\/ *ngcgui *: *\(-.*\)$" if {[regexp $eopt $theline match opt]} { + # remove a trailing comment: + set idx [string first / $opt] + if {$idx >= 0} { set opt [string replace $opt $idx end] } + set idx [string first \; $opt] + if {$idx >= 0} { set opt [string replace $opt $idx end] } + set opt [string trim $opt] + lappend ::ngc($hdl,gcmc,opts) $opt continue } @@ -2061,8 +2068,8 @@ proc ::ngcgui::savesection_gcmc {hdl} { set cmd $::ngc(any,gcmc,executable) set opts "" - if [info exists ::ngc(input,gcmc_include_path)] { - foreach dir [split $::ngc(input,gcmc_include_path) ":"] { + if [info exists ::ngc(any,gcmc_include_path)] { + foreach dir [split $::ngc(any,gcmc_include_path) ":"] { set opts "$opts --include $dir" } } @@ -2081,7 +2088,14 @@ proc ::ngcgui::savesection_gcmc {hdl} { for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} { set idx [format %02d $i] # make all entry box values explicitly floating point - set floatvalue [expr 1.0 * $::ngc($hdl,arg,value,$idx)] + if [catch {set floatvalue [expr 1.0 * $::ngc($hdl,arg,value,$idx)]} msg] { + set answer [tk_dialog .gcmcerror \ + "gcmc input ERROR" \ + "<$::ngc($hdl,arg,value,$idx)> must be a number" \ + error -1 \ + "OK"] + return 0 ;# fail + } set opts "$opts --define=$::ngc($hdl,arg,name,$idx)=$floatvalue" } } @@ -3387,6 +3401,7 @@ proc ::ngcgui::newpage {creatinghdl} { postamble=$postfile \ font=$::ngc(any,font) \ options=$::ngc(input,options) \ + gcmc_include_path=$::ngc(input,gcmc_include_path) \ ] $::ngc(any,axis,parent) itemconfigure $pageid \ -createcmd "::ngcgui::pagecreate $newhdl"\ @@ -3633,6 +3648,9 @@ proc ::ngcgui::embed_in_axis_tab {f args} { # ex: input,subfile } foreach item $equalitems {set $item $::ngc(input,$item)} + if [info exists ::ngc(input,gcmc_include_path)] { + set ::ngc(any,gcmc_include_path) $::ngc(input,gcmc_include_path) + } set ::ngc($hdl,dir) $::ngc(input,startdir) |