@quartic/bokehjs
Version:
Interactive, novel data visualization
155 lines (128 loc) • 4.36 kB
text/coffeescript
import {GuideRenderer} from "../renderers/guide_renderer"
import {RendererView} from "../renderers/renderer"
import * as p from "core/properties"
import {isArray} from "core/util/types"
export class GridView extends RendererView
initialize: (attrs, options) ->
super(attrs, options)
= .x_range_name
= .y_range_name
render: () ->
if .visible == false
return
ctx = .canvas_view.ctx
ctx.save()
ctx.restore()
bind_bokeh_events: () ->
_draw_regions: (ctx) ->
if not .band_fill.doit
return
[xs, ys] = .grid_coords('major', false)
.band_fill.set_value(ctx)
for i in [0...xs.length-1]
if i % 2 == 1
[sx0, sy0] = .map_to_screen(xs[i], ys[i], , )
[sx1, sy1] = .map_to_screen(xs[i+1], ys[i+1], , )
ctx.fillRect(sx0[0], sy0[0], sx1[1]-sx0[0], sy1[1]-sy0[0])
ctx.fill()
return
_draw_grids: (ctx) ->
if not .grid_line.doit
return
[xs, ys] = .grid_coords('major')
_draw_minor_grids: (ctx) ->
if not .minor_grid_line.doit
return
[xs, ys] = .grid_coords('minor')
_draw_grid_helper: (ctx, props, xs, ys) ->
props.set_value(ctx)
for i in [0...xs.length]
[sx, sy] = .map_to_screen(xs[i], ys[i], , )
ctx.beginPath()
ctx.moveTo(Math.round(sx[0]), Math.round(sy[0]))
for i in [1...sx.length]
ctx.lineTo(Math.round(sx[i]), Math.round(sy[i]))
ctx.stroke()
return
export class Grid extends GuideRenderer
default_view: GridView
type: 'Grid'
['line:grid_', 'line:minor_grid_', 'fill:band_']
{
bounds: [ p.Any, 'auto' ] # TODO (bev)
dimension: [ p.Number, 0 ]
ticker: [ p.Instance ]
x_range_name: [ p.String, 'default' ]
y_range_name: [ p.String, 'default' ]
}
{
level: "underlay"
band_fill_color: null
band_fill_alpha: 0
grid_line_color: '#e5e5e5'
minor_grid_line_color: null
}
ranges: () ->
i =
j = (i + 1) % 2
frame = .plot_canvas.frame
ranges = [
frame.x_ranges[],
frame.y_ranges[]
]
return [ranges[i], ranges[j]]
computed_bounds: () ->
[range, cross_range] =
user_bounds =
range_bounds = [range.min, range.max]
if isArray(user_bounds)
start = Math.min(user_bounds[0], user_bounds[1])
end = Math.max(user_bounds[0], user_bounds[1])
if start < range_bounds[0]
start = range_bounds[0]
else if start > range_bounds[1]
start = null
if end > range_bounds[1]
end = range_bounds[1]
else if end < range_bounds[0]
end = null
else
[start, end] = range_bounds
return [start, end]
grid_coords: (location, exclude_ends=true) ->
i =
j = (i + 1) % 2
[range, cross_range] =
[start, end] =
tmp = Math.min(start, end)
end = Math.max(start, end)
start = tmp
# TODO: (bev) using cross_range.min for cross_loc is a bit of a cheat. Since we
# currently only support "straight line" grids, this should be OK for now. If
# we ever want to support "curved" grids, e.g. for some projections, we may
# have to communicate more than just a single cross location.
ticks = .get_ticks(start, end, range, cross_range.min, {})[location]
min = range.min
max = range.max
cmin = cross_range.min
cmax = cross_range.max
coords = [[], []]
for ii in [0...ticks.length]
if (ticks[ii] == min or ticks[ii] == max) and exclude_ends
continue
dim_i = []
dim_j = []
N = 2
for n in [0...N]
loc = cmin + (cmax-cmin)/(N-1) * n
dim_i.push(ticks[ii])
dim_j.push(loc)
coords[i].push(dim_i)
coords[j].push(dim_j)
return coords