diff options
author | Jeff Epler <jepler@dsndata.com> | 2012-05-09 10:48:08 -0500 |
---|---|---|
committer | Jeff Epler <jepler@dsndata.com> | 2012-05-09 13:33:40 -0500 |
commit | 43eda790e13bf3fa29832c984271ae490048931b (patch) | |
tree | 5353b71eb7d70fc568b0e12be005f98aa48d9671 | |
parent | 9d83730d877c713e3b2789d8c917be31f9a1efa6 (diff) | |
download | linuxcnc-43eda790e13bf3fa29832c984271ae490048931b.tar.gz linuxcnc-43eda790e13bf3fa29832c984271ae490048931b.zip |
axis: Add grid lines to preview
-rw-r--r-- | docs/src/config/ini_config.txt | 3 | ||||
-rw-r--r-- | docs/src/gui/axis.txt | 12 | ||||
-rw-r--r-- | lib/python/rs274/glcanon.py | 130 | ||||
-rw-r--r-- | share/axis/tcl/axis.tcl | 20 | ||||
-rwxr-xr-x | src/emc/usr_intf/axis/scripts/axis.py | 42 |
5 files changed, 206 insertions, 1 deletions
diff --git a/docs/src/config/ini_config.txt b/docs/src/config/ini_config.txt index 339532bcd..0c393bfcd 100644 --- a/docs/src/config/ini_config.txt +++ b/docs/src/config/ini_config.txt @@ -219,6 +219,9 @@ The following [DISPLAY] items are for the AXIS interface only. Metric and imperial distances may be mixed: INCREMENTS = 1 inch, 1 mil, 1 cm, 1 mm, 1 um is a valid entry. +* 'GRIDS = 10 mm, 1 in, ...' - Defines the preset values for grid lines. + The value is interpreted the same way as 'INCREMENTS'. + * 'OPEN_FILE = /full/path/to/file.ngc' - The file to show in the preview plot when AXIS starts. Use a blank string "" and no file will be loaded at start up. diff --git a/docs/src/gui/axis.txt b/docs/src/gui/axis.txt index d660e5ebe..217272cd0 100644 --- a/docs/src/gui/axis.txt +++ b/docs/src/gui/axis.txt @@ -433,6 +433,18 @@ Yellow for jogs, faint green for rapid movements, red for straight moves at a feed rate, and magenta for circular moves at a feed rate. +.Grid + +Axis can optionally display a grid when in orthogonal views. Enable +or disable the grid using the 'Grid' menu under 'View'. When +enabled, the grid is shown in the top and rotated top views; when +coordinate system is not rotated, the grid is shown in the front and +side views as well. The presets in the 'Grid' menu are controlled +by the inifile item `[DISPLAY]GRIDS`; if unspecified, the default is +`10mm 20mm 50mm 100mm 1in 2in 5in 10in`. + +Specifying a very small grid may decrease performance. + .Interacting By left-clicking on a portion of the preview plot, diff --git a/lib/python/rs274/glcanon.py b/lib/python/rs274/glcanon.py index 240020199..7ddce4cff 100644 --- a/lib/python/rs274/glcanon.py +++ b/lib/python/rs274/glcanon.py @@ -24,6 +24,9 @@ import linuxcnc import array import gcode +def minmax(*args): + return min(*args), max(*args) + homeicon = array.array('B', [0x2, 0x00, 0x02, 0x00, 0x02, 0x00, 0x0f, 0x80, 0x1e, 0x40, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, @@ -367,6 +370,7 @@ class GlCanonDraw: 'arc_feed_uv': (0.20, 0.20, 1.00), 'arc_feed_alpha_uv': 1/3., 'axis_y': (1.00, 0.20, 0.20), + 'grid': (0.15, 0.15, 0.15), } def __init__(self, s, lp, g=None): self.stat = s @@ -782,6 +786,130 @@ class GlCanonDraw: if self.canon: return self.canon.foam_w return 1.5 + def get_grid(self): + if self.canon and self.canon.grid: return self.canon.grid + return 5./25.4 + + def comp(self, (sx, sy), (cx, cy)): + return -(sx*cx + sy*cy) / (sx*sx + sy*sy) + + def param(self, (x1, y1), (dx1, dy1), (x3, y3), (dx3, dy3)): + den = (dy3)*(dx1) - (dx3)*(dy1) + if den == 0: return 0 + num = (dx3)*(y1-y3) - (dy3)*(x1-x3) + return num * 1. / den + + def draw_grid_lines(self, space, (ox, oy), (dx, dy), lim_min, lim_max, + inverse_permutation): + # draw a series of line segments of the form + # dx(x-ox) + dy(y-oy) + k*space = 0 + # for integers k that intersect the AABB [lim_min, lim_max] + lim_pts = [ + (lim_min[0], lim_min[1]), + (lim_max[0], lim_min[1]), + (lim_min[0], lim_max[1]), + (lim_max[0], lim_max[1])] + od = self.comp((dy, -dx), (ox, oy)) + d0, d1 = minmax(*(self.comp((dy, -dx), i)-od for i in lim_pts)) + k0 = int(math.ceil(d0/space)) + k1 = int(math.floor(d1/space)) + delta = (dx, dy) + for k in range(k0, k1+1): + d = k*space + # Now we're drawing the line dx(x-ox) + dx(y-oy) + d = 0 + p0 = (ox - dy * d, oy + dx * d) + # which is the same as the line p0 + u * delta + + # but we only want the part that's inside the box lim_pts... + if dx and dy: + times = [ + self.param(p0, delta, lim_min[:2], (0, 1)), + self.param(p0, delta, lim_min[:2], (1, 0)), + self.param(p0, delta, lim_max[:2], (0, 1)), + self.param(p0, delta, lim_max[:2], (1, 0))] + times.sort() + t0, t1 = times[1], times[2] # Take the middle two times + elif dx: + times = [ + self.param(p0, delta, lim_min[:2], (0, 1)), + self.param(p0, delta, lim_max[:2], (0, 1))] + times.sort() + t0, t1 = times[0], times[1] # Take the only two times + else: + times = [ + self.param(p0, delta, lim_min[:2], (1, 0)), + self.param(p0, delta, lim_max[:2], (1, 0))] + times.sort() + t0, t1 = times[0], times[1] # Take the only two times + x0, y0 = p0[0] + delta[0]*t0, p0[1] + delta[1]*t0 + x1, y1 = p0[0] + delta[0]*t1, p0[1] + delta[1]*t1 + xm, ym = (x0+x1)/2, (y0+y1)/2 + # The computation of k0 and k1 above should mean that + # the lines are always in the limits, but I observed + # that this wasn't always the case... + #if xm < lim_min[0] or xm > lim_max[0]: continue + #if ym < lim_min[1] or ym > lim_max[1]: continue + glVertex3f(*inverse_permutation((x0, y0, lim_min[2]))) + glVertex3f(*inverse_permutation((x1, y1, lim_min[2]))) + + def draw_grid_permuted(self, rotation, permutation, inverse_permutation): + grid_size=self.get_grid_size() + if not grid_size: return + + glLineWidth(1) + glColor3f(*self.colors['grid']) + lim_min, lim_max = self.soft_limits() + lim_min = permutation(lim_min) + lim_max = permutation(lim_max) + + lim_pts = ( + (lim_min[0], lim_min[1]), + (lim_max[0], lim_min[1]), + (lim_min[0], lim_max[1]), + (lim_max[0], lim_max[1])) + s = self.stat + g5x_offset = permutation(self.to_internal_units(s.g5x_offset)[:3])[:2] + g92_offset = permutation(self.to_internal_units(s.g92_offset)[:3])[:2] + if self.get_show_relative(): + cos_rot = math.cos(rotation) + sin_rot = math.sin(rotation) + offset = ( + g5x_offset[0] + g92_offset[0] * cos_rot + - g92_offset[1] * sin_rot, + g5x_offset[1] + g92_offset[0] * sin_rot + + g92_offset[1] * cos_rot) + else: + offset = 0., 0. + cos_rot = 1. + sin_rot = 0. + glDepthMask(False) + glBegin(GL_LINES) + self.draw_grid_lines(grid_size, offset, (cos_rot, sin_rot), + lim_min, lim_max, inverse_permutation) + self.draw_grid_lines(grid_size, offset, (sin_rot, -cos_rot), + lim_min, lim_max, inverse_permutation) + glEnd() + glDepthMask(True) + + def draw_grid(self): + x,y,z,p = 0,1,2,3 + view = self.get_view() + if view == p: return + rotation = math.radians(self.stat.rotation_xy % 90) + if rotation != 0 and view != z and self.get_show_relative(): return + permutations = [ + lambda (x, y, z): (z, y, x), # YZ X + lambda (x, y, z): (z, x, y), # ZX Y + lambda (x, y, z): (x, y, z), # XY Z + ] + inverse_permutations = [ + lambda (z, y, x): (x, y, z), # YZ X + lambda (z, x, y): (x, y, z), # ZX Y + lambda (x, y, z): (x, y, z), # XY Z + ] + self.draw_grid_permuted(rotation, permutations[view], + inverse_permutations[view]) + def redraw(self): s = self.stat s.poll() @@ -790,7 +918,7 @@ class GlCanonDraw: glDisable(GL_LIGHTING) glMatrixMode(GL_MODELVIEW) - + self.draw_grid() if self.get_show_program(): if self.get_program_alpha(): glDisable(GL_DEPTH_TEST) diff --git a/share/axis/tcl/axis.tcl b/share/axis/tcl/axis.tcl index 3e1630746..064eb1386 100644 --- a/share/axis/tcl/axis.tcl +++ b/share/axis/tcl/axis.tcl @@ -337,6 +337,10 @@ setup_menu_accel .menu.view end [_ "Show too_l"] -command toggle_show_extents setup_menu_accel .menu.view end [_ "Show e_xtents"] +.menu.view add cascade \ + -menu .menu.view.grid +setup_menu_accel .menu.view end [_ "_Grid"] + .menu.view add checkbutton \ -variable show_offsets \ -command toggle_show_offsets @@ -414,6 +418,22 @@ setup_menu_accel .menu.view end [_ "Joint mode"] -accelerator $ \ -command set_joint_mode setup_menu_accel .menu.view end [_ "World mode"] + +menu .menu.view.grid + +.menu.view.grid add radiobutton \ + -value 0 \ + -variable grid_size \ + -command set_grid_size +setup_menu_accel .menu.view.grid end [_ "_Off"] + +.menu.view.grid add radiobutton \ + -value -1 \ + -variable grid_size \ + -command set_grid_size_custom +setup_menu_accel .menu.view.grid end [_ "_Custom"] + + # ---------------------------------------------------------------------- .menu.help add command \ -command { diff --git a/src/emc/usr_intf/axis/scripts/axis.py b/src/emc/usr_intf/axis/scripts/axis.py index 16cfccdc8..f8338f3b3 100755 --- a/src/emc/usr_intf/axis/scripts/axis.py +++ b/src/emc/usr_intf/axis/scripts/axis.py @@ -502,6 +502,7 @@ class MyOpengl(GlCanonDraw, Opengl): def get_show_program(self): return vars.show_program.get() def get_show_offsets(self): return vars.show_offsets.get() def get_show_extents(self): return vars.show_extents.get() + def get_grid_size(self): return vars.grid_size.get() def get_show_metric(self): return vars.metric.get() def get_show_live_plot(self): return vars.show_live_plot.get() def get_show_machine_speed(self): return vars.show_machine_speed.get() @@ -1212,6 +1213,7 @@ widgets = nf.Widgets(root_window, ("spinoverridef", Scale, pane_top + ".spinoverride"), ("menu_view", Menu, ".menu.view"), + ("menu_grid", Menu, ".menu.view.grid"), ("menu_file", Menu, ".menu.file"), ("menu_machine", Menu, ".menu.machine"), ("menu_touchoff", Menu, ".menu.machine.touchoff"), @@ -1423,6 +1425,7 @@ class _prompt_float: t.wm_resizable(0, 0) self.m = m = Message(t, text=text, aspect=500, anchor="w", justify="left") self.v = v = StringVar(t) + self.vv = vv = DoubleVar(t) self.u = u = BooleanVar(t) self.w = w = StringVar(t) l = Tkinter.Message(t, textvariable=w, justify="left", anchor="w", @@ -2184,6 +2187,19 @@ class TclCommands(nf.TclCommands): ap.putpref("show_offsets", vars.show_offsets.get()) o.tkRedraw() + def set_grid_size(*event): + ap.putpref("grid_size", vars.grid_size.get(), type=float) + o.tkRedraw() + + def set_grid_size_custom(*event): + if vars.metric.get(): unit_str = " " + _("mm") + else: unit_str = " " + _("in") + v = prompt_float("Custom Grid", "Enter grid size", + "", unit_str) or 0 + if v <= 0: return + if vars.metric.get(): v /= 25.4 + match_grid_size(v) + def toggle_show_machine_limits(*event): ap.putpref("show_machine_limits", vars.show_machine_limits.get()) o.tkRedraw() @@ -2499,6 +2515,7 @@ vars = nf.Variables(root_window, ("show_tool", IntVar), ("show_extents", IntVar), ("show_offsets", IntVar), + ("grid_size", DoubleVar), ("show_machine_limits", IntVar), ("show_machine_speed", IntVar), ("show_distance_to_go", IntVar), @@ -2540,6 +2557,7 @@ vars.show_live_plot.set(ap.getpref("show_live_plot", True)) vars.show_tool.set(ap.getpref("show_tool", True)) vars.show_extents.set(ap.getpref("show_extents", True)) vars.show_offsets.set(ap.getpref("show_offsets", True)) +vars.grid_size.set(ap.getpref("grid_size", 0.0, type=float)) vars.show_machine_limits.set(ap.getpref("show_machine_limits", True)) vars.show_machine_speed.set(ap.getpref("show_machine_speed", True)) vars.show_distance_to_go.set(ap.getpref("show_distance_to_go", False)) @@ -2976,6 +2994,30 @@ o = MyOpengl(widgets.preview_frame, width=400, height=300, double=1, depth=1) o.last_line = 1 o.pack(fill="both", expand=1) +def match_grid_size(v): + for idx in range(3, widgets.menu_grid.index("end")+1): + gv = widgets.menu_grid.entrycget(idx, "value") + if abs(float(gv)-v) < 1e-5: + vars.grid_size.set(gv) + widgets.menu_grid.entryconfigure(2, value=-1) + break + else: + vars.grid_size.set(v) + widgets.menu_grid.entryconfigure(2, value=v) + commands.set_grid_size() + o.tkRedraw() + +def setup_grid_menu(grids): + for i in grids.split(): + v = to_internal_linear_unit(parse_increment(i)) + widgets.menu_grid.add_radiobutton(value=v, label=i, + variable="grid_size", command="set_grid_size") + match_grid_size(vars.grid_size.get()) + +grids = inifile.find("DISPLAY", "GRIDS") \ + or "10mm 20mm 50mm 100mm 1in 2in 5in 10in" +setup_grid_menu(grids) + # Find font for coordinate readout and get metrics font_cache = {} |