import { ScatterBase } from './ScatterBase' ; import * as markers from './Markers' ; import * as d3 from 'd3' ; const bqSymbol = markers . symbol ; export class Scatter extends ScatterBase { render ( ) { this . dot = bqSymbol ( ) . type ( this . model . get ( 'marker' ) ) . size ( this . model . get ( 'default_size' ) ) . skew ( this . model . get ( 'default_skew' ) ) ; return super . render ( ) ; } create_listeners ( ) { super . create_listeners ( ) ; this . listenTo ( this . model , 'change:colors' , this . update_colors ) ; this . listenTo ( this . model , 'change:stroke' , this . update_stroke ) ; this . listenTo ( this . model , 'change:stroke_width' , this . update_stroke_width ) ; this . listenTo ( this . model , 'change:opacities' , this . update_opacities ) ; this . listenTo ( this . model , 'change:default_skew' , this . update_default_skew ) ; this . listenTo ( this . model , 'change:marker' , this . update_marker ) ; this . listenTo ( this . model , 'change:default_size' , this . update_default_size ) ; this . listenTo ( this . model , 'change:fill' , this . update_fill ) ; this . listenTo ( this . model , 'change:display_names' , this . update_names ) ; this . listenTo ( this . model , 'change:label_display_horizontal_offset change:label_display_vertical_offset' , this . update_names ) ; } update_colors ( model , new_colors ) { if ( ! this . model . dirty ) { const stroke = this . model . get ( 'stroke' ) ; const len = new_colors . length ; this . d3el . selectAll ( '.dot' ) . style ( 'fill' , this . model . get ( 'fill' ) ? this . get_mark_color . bind ( this ) : 'none' ) . style ( 'stroke' , stroke ? stroke : this . get_mark_color . bind ( this ) ) ; if ( this . legend_el ) { this . legend_el . select ( 'path' ) . style ( 'fill' , ( d , i ) => { return new_colors [ i % len ] ; } ) . style ( 'stroke' , stroke ? stroke : ( d , i ) => { return new_colors [ i % len ] ; } ) ; this . legend_el . select ( 'text' ) . style ( 'fill' , this . model . get ( 'fill' ) ? ( d , i ) => { return new_colors [ i % len ] ; } : 'none' ) ; } } this . apply_styles ( ) ; } update_fill ( model , fill ) { const colors = this . model . get ( 'colors' ) ; const len = colors . length ; this . d3el . selectAll ( '.dot' ) . style ( 'fill' , fill ? this . get_mark_color . bind ( this ) : 'none' ) ; if ( this . legend_el ) { this . legend_el . selectAll ( 'path' ) . style ( 'fill' , fill ? ( d , i ) => { return colors [ i % len ] ; } : 'none' ) ; } } update_stroke_width ( ) { const stroke_width = this . model . get ( 'stroke_width' ) ; this . d3el . selectAll ( '.dot' ) . style ( 'stroke-width' , stroke_width ) ; if ( this . legend_el ) { this . legend_el . selectAll ( 'path' ) . style ( 'stroke-width' , stroke_width ) ; } } update_stroke ( model , fill ) { const stroke = this . model . get ( 'stroke' ) ; this . d3el . selectAll ( '.dot' ) . style ( 'stroke' , stroke ? stroke : this . get_mark_color . bind ( this ) ) ; if ( this . legend_el ) { this . legend_el . selectAll ( 'path' ) . style ( 'stroke' , stroke ) ; } } update_opacities ( animate ) { if ( ! this . model . dirty ) { const opacities = this . model . get ( 'opacities' ) ; const colors = this . model . get ( 'colors' ) ; const len = colors . length ; const len_opac = opacities . length ; const animation_duration = animate === true ? this . parent . model . get ( 'animation_duration' ) : 0 ; const that = this ; this . d3el . selectAll ( '.dot' ) . transition ( 'update_opacities' ) . duration ( animation_duration ) . style ( 'opacity' , ( d , i ) => { return that . get_element_opacity ( d , i ) ; } ) ; if ( this . legend_el ) { this . legend_el . select ( 'path' ) . style ( 'opacity' , ( d , i ) => { return opacities [ i % len_opac ] ; } ) . style ( 'fill' , ( d , i ) => { return colors [ i % len ] ; } ) ; } } } update_marker ( model , marker ) { if ( ! this . model . dirty ) { this . d3el . selectAll ( '.dot' ) . transition ( 'update_marker' ) . duration ( this . parent . model . get ( 'animation_duration' ) ) . attr ( 'd' , this . dot . type ( marker ) ) ; if ( this . legend_el ) { this . legend_el . select ( 'path' ) . attr ( 'd' , this . dot . type ( marker ) ) ; } } } update_default_skew ( animate ) { if ( ! this . model . dirty ) { const animation_duration = animate === true ? this . parent . model . get ( 'animation_duration' ) : 0 ; const that = this ; this . d3el . selectAll ( '.dot' ) . transition ( 'update_default_skew' ) . duration ( animation_duration ) . attr ( 'd' , this . dot . skew ( ( d ) => { return that . get_element_skew ( d ) ; } ) ) ; } } update_default_size ( animate ) { this . compute_view_padding ( ) ; if ( ! this . model . dirty ) { const animation_duration = animate === true ? this . parent . model . get ( 'animation_duration' ) : 0 ; const that = this ; this . d3el . selectAll ( '.dot' ) . transition ( 'update_default_size' ) . duration ( animation_duration ) . attr ( 'd' , this . dot . size ( ( d ) => { return that . get_element_size ( d ) ; } ) ) ; this . update_names ( animate ) ; } } update_names ( animate ) { const that = this ; const names = this . model . get ( 'names' ) || [ ] ; const show_names = this . model . get ( 'display_names' ) && names . length !== 0 ; const animation_duration = animate ? this . parent . model . get ( 'animation_duration' ) : 0 ; this . d3el . selectAll ( '.object_grp' ) . select ( 'text' ) . text ( ( d ) => { return d . name ; } ) . transition ( 'update_names' ) . duration ( animation_duration ) . attr ( 'transform' , ( d ) => { const label_display_horizontal_offset = that . model . get ( 'label_display_horizontal_offset' ) ; const label_display_vertical_offset = that . model . get ( 'label_display_vertical_offset' ) ; const text_loc = Math . sqrt ( that . get_element_size ( d ) ) / 2.0 ; return ( 'translate(' + ( label_display_horizontal_offset ? label_display_horizontal_offset : text_loc ) + ',' + ( label_display_vertical_offset ? - label_display_vertical_offset : - text_loc ) + ')' ) ; } ) . attr ( 'display' , ( d ) => { return show_names ? 'inline' : 'none' ; } ) ; } color_scale_updated ( animate ) { const fill = this . model . get ( 'fill' ) , stroke = this . model . get ( 'stroke' ) ; const animation_duration = animate === true ? this . parent . model . get ( 'animation_duration' ) : 0 ; this . d3el . selectAll ( '.object_grp' ) . select ( 'path' ) . transition ( 'color_scale_updated' ) . duration ( animation_duration ) . style ( 'fill' , fill ? this . get_mark_color . bind ( this ) : 'none' ) . style ( 'stroke' , stroke ? stroke : this . get_mark_color . bind ( this ) ) ; } draw_elements ( animate , elements_added ) { const that = this ; const animation_duration = animate === true ? this . parent . model . get ( 'animation_duration' ) : 0 ; const elements = this . d3el . selectAll ( '.object_grp' ) ; elements_added . append ( 'path' ) . attr ( 'class' , 'dot element' ) ; elements_added . append ( 'text' ) . attr ( 'class' , 'dot_text' ) ; elements . select ( 'path' ) . transition ( 'draw_elements' ) . duration ( animation_duration ) . attr ( 'd' , this . dot . size ( ( d ) => { return that . get_element_size ( d ) ; } ) . skew ( ( d ) => { return that . get_element_skew ( d ) ; } ) ) ; this . update_names ( animate ) ; this . apply_styles ( ) ; } draw_legend_elements ( elements_added , rect_dim ) { const colors = this . model . get ( 'colors' ) , len = colors . length , stroke = this . model . get ( 'stroke' ) ; elements_added . append ( 'path' ) . attr ( 'transform' , ( d , i ) => { return 'translate( ' + rect_dim / 2 + ', ' + rect_dim / 2 + ')' ; } ) . attr ( 'd' , this . dot . size ( 64 ) ) . style ( 'fill' , this . model . get ( 'fill' ) ? ( d , i ) => { return colors [ i % len ] ; } : 'none' ) . style ( 'stroke' , stroke ? stroke : ( d , i ) => { return colors [ i % len ] ; } ) ; } set_default_style ( indices ) { if ( ! indices || indices . length === 0 ) { return ; } const elements = this . d3el . selectAll ( '.element' ) . filter ( ( data , index ) => { return indices . indexOf ( index ) !== - 1 ; } ) ; const fill = this . model . get ( 'fill' ) , stroke = this . model . get ( 'stroke' ) , stroke_width = this . model . get ( 'stroke_width' ) , that = this ; elements . style ( 'fill' , fill ? this . get_mark_color . bind ( this ) : 'none' ) . style ( 'stroke' , stroke ? stroke : this . get_mark_color . bind ( this ) ) . style ( 'opacity' , ( d , i ) => { return that . get_element_opacity ( d , i ) ; } ) . style ( 'stroke-width' , stroke_width ) ; } set_drag_style ( d , i , dragged_node ) { d3 . select ( dragged_node ) . select ( 'path' ) . classed ( 'drag_scatter' , true ) . transition ( 'set_drag_style' ) . attr ( 'd' , this . dot . size ( 5 * this . model . get ( 'default_size' ) ) ) ; const drag_color = this . model . get ( 'drag_color' ) ; if ( drag_color ) { d3 . select ( dragged_node ) . select ( 'path' ) . style ( 'fill' , drag_color ) . style ( 'stroke' , drag_color ) ; } } reset_drag_style ( d , i , dragged_node ) { const stroke = this . model . get ( 'stroke' ) , original_color = this . get_mark_color ( d , i ) ; d3 . select ( dragged_node ) . select ( 'path' ) . classed ( 'drag_scatter' , false ) . transition ( 'reset_drag_style' ) . attr ( 'd' , this . dot . size ( this . get_element_size ( d ) ) ) ; if ( this . model . get ( 'drag_color' ) ) { d3 . select ( dragged_node ) . select ( 'path' ) . style ( 'fill' , original_color ) . style ( 'stroke' , stroke ? stroke : original_color ) ; } } dot : any ; }