@quartic/bokehjs
Version:
Interactive, novel data visualization
121 lines (97 loc) • 4.08 kB
text/coffeescript
import {XYGlyph, XYGlyphView} from "./xy_glyph"
import * as hittest from "core/hittest"
import * as p from "core/properties"
import {angle_between} from "core/util/math"
export class AnnularWedgeView extends XYGlyphView
_map_data: () ->
if .properties.inner_radius.units == "data"
=
else
=
if .properties.outer_radius.units == "data"
=
else
=
= new Float32Array(.length)
for i in [0....length]
[i] = [i] - [i]
_render: (ctx, indices, {sx, sy, _start_angle, _angle, sinner_radius, souter_radius}) ->
direction = .properties.direction.value()
for i in indices
if isNaN(sx[i]+sy[i]+sinner_radius[i]+souter_radius[i]+_start_angle[i]+_angle[i])
continue
ctx.translate(sx[i], sy[i])
ctx.rotate(_start_angle[i])
ctx.moveTo(souter_radius[i], 0)
ctx.beginPath()
ctx.arc(0, 0, souter_radius[i], 0, _angle[i], direction)
ctx.rotate(_angle[i])
ctx.lineTo(sinner_radius[i], 0)
ctx.arc(0, 0, sinner_radius[i], 0, -_angle[i], not direction)
ctx.closePath()
ctx.rotate(-_angle[i]-_start_angle[i])
ctx.translate(-sx[i], -sy[i])
if .fill.doit
.fill.set_vectorize(ctx, i)
ctx.fill()
if .line.doit
.line.set_vectorize(ctx, i)
ctx.stroke()
_hit_point: (geometry) ->
[vx, vy] = [geometry.vx, geometry.vy]
x = .xmapper.map_from_target(vx, true)
y = .ymapper.map_from_target(vy, true)
# check radius first
if .properties.outer_radius.units == "data"
x0 = x -
x1 = x +
y0 = y -
y1 = y +
else
vx0 = vx -
vx1 = vx +
[x0, x1] = .xmapper.v_map_from_target([vx0, vx1], true)
vy0 = vy -
vy1 = vy +
[y0, y1] = .ymapper.v_map_from_target([vy0, vy1], true)
candidates = []
bbox = hittest.validate_bbox_coords([x0, x1], [y0, y1])
for i in .indices(bbox)
or2 = Math.pow([i], 2)
ir2 = Math.pow([i], 2)
sx0 = .xmapper.map_to_target(x, true)
sx1 = .xmapper.map_to_target([i], true)
sy0 = .ymapper.map_to_target(y, true)
sy1 = .ymapper.map_to_target([i], true)
dist = Math.pow(sx0-sx1, 2) + Math.pow(sy0-sy1, 2)
if dist <= or2 and dist >= ir2
candidates.push([i, dist])
direction = .properties.direction.value()
hits = []
for [i, dist] in candidates
sx = .plot_view.canvas.vx_to_sx(vx)
sy = .plot_view.canvas.vy_to_sy(vy)
# NOTE: minus the angle because JS uses non-mathy convention for angles
angle = Math.atan2(sy-[i], sx-[i])
if angle_between(-angle, -[i], -[i], direction)
hits.push([i, dist])
return hittest.create_1d_hit_test_result(hits)
draw_legend_for_index: (ctx, x0, x1, y0, y1, index) ->
_scxy: (i) ->
r = ([i] + [i])/2
a = ([i] + [i]) /2
return {x: [i] + r*Math.cos(a), y: [i] + r*Math.sin(a)}
scx: (i) -> .x
scy: (i) -> .y
export class AnnularWedge extends XYGlyph
default_view: AnnularWedgeView
type: 'AnnularWedge'
['line', 'fill']
{
direction: [ p.Direction, 'anticlock' ]
inner_radius: [ p.DistanceSpec ]
outer_radius: [ p.DistanceSpec ]
start_angle: [ p.AngleSpec ]
end_angle: [ p.AngleSpec ]
}