UNPKG

@quartic/bokehjs

Version:

Interactive, novel data visualization

174 lines (135 loc) 5.23 kB
import canvas_template from "./canvas_template" import {LayoutCanvas} from "core/layout/layout_canvas" import {BokehView} from "core/bokeh_view" import {GE, EQ} from "core/layout/solver" import {logger} from "core/logging" import * as p from "core/properties" import {isEqual} from "core/util/eq" import {fixup_image_smoothing, fixup_line_dash, fixup_line_dash_offset, fixup_measure_text, get_scale_ratio, fixup_ellipse} from "core/util/canvas" export class CanvasView extends BokehView className: "bk-canvas-wrapper" template: canvas_template initialize: (options) -> super(options) html = @template({ map: @model.map }) @el.appendChild(html) # create the canvas context that gets passed around for drawing @ctx = @get_ctx() # work around canvas incompatibilities fixup_line_dash(@ctx) fixup_line_dash_offset(@ctx) fixup_image_smoothing(@ctx) fixup_measure_text(@ctx) fixup_ellipse(@ctx) # fixes up a problem with some versions of IE11 # ref: http://stackoverflow.com/questions/22062313/imagedata-set-in-internetexplorer if window.CanvasPixelArray? CanvasPixelArray.prototype.set = (arr) -> for i in [0...@length] @[i] = arr[i] # map plots reference this attribute @map_div = @el.querySelector('div.bk-canvas-map') @set_dims([@model.initial_width, @model.initial_height]) logger.debug("CanvasView initialized") get_canvas_element: () -> return @el.querySelector('canvas.bk-canvas') get_ctx: () -> return @get_canvas_element().getContext('2d') prepare_canvas: (force=false) -> # Ensure canvas has the correct size, taking HIDPI into account width = @model._width._value height = @model._height._value dpr = window.devicePixelRatio # only resize the canvas when the canvas dimensions change unless force==true if not isEqual(@last_dims, [width, height, dpr]) or force @el.style.width = "#{width}px" @el.style.height = "#{height}px" # Scale the canvas (this resets the context's state) @pixel_ratio = ratio = get_scale_ratio(@ctx, @model.use_hidpi) canvas_el = @get_canvas_element() canvas_el.style.width = "#{width}px" canvas_el.style.height = "#{height}px" canvas_el.setAttribute('width', width*ratio) canvas_el.setAttribute('height', height*ratio) logger.debug("Rendering CanvasView [force=#{force}] with width: #{width}, height: #{height}, ratio: #{ratio}") @model.pixel_ratio = @pixel_ratio @last_dims = [width, height, dpr] set_dims: (dims, trigger=true) -> @requested_width = dims[0] @requested_height = dims[1] @update_constraints(trigger) return update_constraints: (trigger=true) -> requested_width = @requested_width requested_height = @requested_height if not requested_width? or not requested_height? return MIN_SIZE = 50 if requested_width < MIN_SIZE or requested_height < MIN_SIZE return if isEqual(@last_requested_dims, [requested_width, requested_height]) return s = @model.document.solver() if @_width_constraint? s.remove_constraint(@_width_constraint, true) @_width_constraint = EQ(@model._width, -requested_width) s.add_constraint(@_width_constraint) if @_height_constraint? s.remove_constraint(@_height_constraint, true) @_height_constraint = EQ(@model._height, -requested_height) s.add_constraint(@_height_constraint) @last_requested_dims = [requested_width, requested_height] s.update_variables(trigger) export class Canvas extends LayoutCanvas type: 'Canvas' default_view: CanvasView @internal { map: [ p.Boolean, false ] initial_width: [ p.Number ] initial_height: [ p.Number ] use_hidpi: [ p.Boolean, true ] pixel_ratio: [ p.Number ] } initialize: (attrs, options) -> super(attrs, options) @panel = @ # transform view coordinates to underlying screen coordinates vx_to_sx: (x) -> x vy_to_sy: (y) -> # Note: +1 to account for 1px canvas dilation return @_height._value - (y + 1) # vectorized versions of vx_to_sx/vy_to_sy v_vx_to_sx: (xx) -> return new Float64Array(xx) v_vy_to_sy: (yy) -> _yy = new Float64Array(yy.length) height = @_height._value # Note: +1 to account for 1px canvas dilation for y, idx in yy _yy[idx] = height - (y + 1) return _yy sx_to_vx: (x) -> x sy_to_vy: (y) -> # Note: +1 to account for 1px canvas dilation return @_height._value - (y + 1) # vectorized versions of sx_to_vx/sy_to_vy v_sx_to_vx: (xx) -> return new Float64Array(xx) v_sy_to_vy: (yy) -> _yy = new Float64Array(yy.length) height = @_height._value # Note: +1 to account for 1px canvas dilation for y, idx in yy _yy[idx] = height - (y + 1) return _yy get_constraints: () -> constraints = super() constraints.push(GE(@_top)) constraints.push(GE(@_bottom)) constraints.push(GE(@_left)) constraints.push(GE(@_right)) constraints.push(GE(@_width)) constraints.push(GE(@_height)) constraints.push(EQ(@_width, [-1, @_right])) constraints.push(EQ(@_height, [-1, @_top])) return constraints