summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Epler <jepler@dsndata.com>2012-05-09 10:48:08 -0500
committerJeff Epler <jepler@dsndata.com>2012-05-09 13:33:40 -0500
commit43eda790e13bf3fa29832c984271ae490048931b (patch)
tree5353b71eb7d70fc568b0e12be005f98aa48d9671
parent9d83730d877c713e3b2789d8c917be31f9a1efa6 (diff)
downloadlinuxcnc-43eda790e13bf3fa29832c984271ae490048931b.tar.gz
linuxcnc-43eda790e13bf3fa29832c984271ae490048931b.zip
axis: Add grid lines to preview
-rw-r--r--docs/src/config/ini_config.txt3
-rw-r--r--docs/src/gui/axis.txt12
-rw-r--r--lib/python/rs274/glcanon.py130
-rw-r--r--share/axis/tcl/axis.tcl20
-rwxr-xr-xsrc/emc/usr_intf/axis/scripts/axis.py42
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 = {}