diff --git a/CHANGELOG.md b/CHANGELOG.md index b77079de..cfe435da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased => [0.7.2] +### Add +- Add Piechart drawing ## [0.7.1] ### Add diff --git a/code_pylint.py b/code_pylint.py index e00efcc0..ea842455 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -44,7 +44,7 @@ 'too-many-return-statements': 8, 'raise-missing-from': 6, 'consider-merging-isinstance': 6, - 'abstract-method': 25, + 'abstract-method': 26, 'import-outside-toplevel': 7, 'too-many-instance-attributes': 3, 'consider-iterating-dictionary': 4, diff --git a/cypress/e2e/piechart.cy.ts b/cypress/e2e/piechart.cy.ts new file mode 100644 index 00000000..708a9ff9 --- /dev/null +++ b/cypress/e2e/piechart.cy.ts @@ -0,0 +1,19 @@ +import { parseHTML } from '../support/parseHTML'; +import piechartData from '../data_src/piechart.data.json'; + +const FEATURE_NAME = "piechart" + +before(() => { + parseHTML(FEATURE_NAME, piechartData) +}) + +describe('PIECHART CANVAS', function () { + const describeTitle = this.title + ' -- ' + beforeEach(() => { + cy.visit("cypress/html_files/" + FEATURE_NAME + ".html"); + }) + + it("should draw canvas", function () { + cy.compareSnapshot(describeTitle + this.test.title, 0.05); + }) +}) diff --git a/cypress/snapshots/base/graph2d.cy.ts/GRAPH2D CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/graph2d.cy.ts/GRAPH2D CANVAS -- should draw canvas-base.png index c138ce27..2e0d6289 100644 Binary files a/cypress/snapshots/base/graph2d.cy.ts/GRAPH2D CANVAS -- should draw canvas-base.png and b/cypress/snapshots/base/graph2d.cy.ts/GRAPH2D CANVAS -- should draw canvas-base.png differ diff --git a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should add primitive group container in multiplot-base.png b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should add primitive group container in multiplot-base.png index 16ace35e..d7f918d2 100644 Binary files a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should add primitive group container in multiplot-base.png and b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should add primitive group container in multiplot-base.png differ diff --git a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should draw canvas-base.png index c998980c..0b233d69 100644 Binary files a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should draw canvas-base.png and b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should draw canvas-base.png differ diff --git a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should remove primitive group from container in multiplot-base.png b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should remove primitive group from container in multiplot-base.png index 0e0e3098..dcde7e4b 100644 Binary files a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should remove primitive group from container in multiplot-base.png and b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should remove primitive group from container in multiplot-base.png differ diff --git a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should reorder all plots in canvas-base.png b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should reorder all plots in canvas-base.png index 6f668e92..29a8a57e 100644 Binary files a/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should reorder all plots in canvas-base.png and b/cypress/snapshots/base/multiplot.cy.ts/MULTIPLOT CANVAS -- should reorder all plots in canvas-base.png differ diff --git a/cypress/snapshots/base/piechart.cy.ts/PIECHART CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/piechart.cy.ts/PIECHART CANVAS -- should draw canvas-base.png new file mode 100644 index 00000000..7e348aea Binary files /dev/null and b/cypress/snapshots/base/piechart.cy.ts/PIECHART CANVAS -- should draw canvas-base.png differ diff --git a/cypress/templates/piechart.template.html b/cypress/templates/piechart.template.html new file mode 100644 index 00000000..80fb57da --- /dev/null +++ b/cypress/templates/piechart.template.html @@ -0,0 +1,20 @@ + + + + + +
+ + + +
+ \ No newline at end of file diff --git a/plot_data/core.py b/plot_data/core.py index 189297d8..4406bd74 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -755,6 +755,30 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po PlotDataObject.__init__(self, type_='scatterplot', name=name) +class PieChart(PlotDataObject): + """ + A class for drawing pie plots. + + :param data_samples: [{'name_param_1': any,..., 'name_param_p': any},..., + {'name_param_1': any,..., 'name_param_p': any}] + :type data_samples: List[dict] + :param slicing_variable: variable that you want to use to fill pie parts + :type slicing_variable: str + """ + + def __init__(self, slicing_variable: str, + data_samples: List[Any] = None, + name: str = ''): + + self.slicing_variable = slicing_variable + if not data_samples: + self.data_samples = [] + else: + self.data_samples = data_samples + + PlotDataObject.__init__(self, type_='piechart', name=name) + + class ScatterMatrix(PlotDataObject): def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_style: PointStyle = None, surface_style: SurfaceStyle = None, @@ -1199,6 +1223,8 @@ def plot_canvas(plot_data_object: PlotDataObject, template = templates.histogram_template elif plot_type == "scattermatrix": template = templates.scatter_matrix_template + elif plot_type == "piechart": + template = templates.piechart_template else: raise NotImplementedError('Type {} not implemented'.format(plot_type)) diff --git a/plot_data/templates.py b/plot_data/templates.py index 99dced8a..13671338 100644 --- a/plot_data/templates.py +++ b/plot_data/templates.py @@ -225,3 +225,35 @@ ''') + + +piechart_template = Template(''' + + + + + +
+ + + + + + +
+ +''') diff --git a/script/ci_tests.py b/script/ci_tests.py index 5ae4386e..39fe0673 100644 --- a/script/ci_tests.py +++ b/script/ci_tests.py @@ -24,6 +24,7 @@ "plot_scatter.py", "primitive_group.py", "simple_shapes.py", + "pie_chart.py", "text_scaling.py" ] diff --git a/script/multiplot.py b/script/multiplot.py index 7f95a64c..a06c5dd8 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -37,12 +37,14 @@ """Scatterplots""" scatterplot1 = plot_data.Scatter(x_variable='x', y_variable='y') +piechart1 = plot_data.PieChart(data_samples=elements, + slicing_variable='mass') scatterplot2 = plot_data.Scatter(x_variable='y', y_variable='color', point_style=plot_data.PointStyle(shape='square')) # optional argument that changes # points' appearance -scatterplot3 = plot_data.Scatter(x_variable='x', y_variable='direction') +# scatterplot3 = plot_data.Scatter(x_variable='x', y_variable='direction') """PrimitiveGroupContainers""" contour = plot_data.Contour2D(plot_data_primitives=[plot_data.LineSegment2D([1, 1], [1, 2]), @@ -68,8 +70,7 @@ histogram = plot_data.Histogram(x_variable='x') """Creating the multiplot""" -plots = [parallelplot1, parallelplot2, scatterplot1, - scatterplot2, scatterplot3, graph2d, primitive_group_container, +plots = [parallelplot1, parallelplot2, scatterplot1, scatterplot2, parallelplot1, graph2d, primitive_group_container, histogram] # plots = [scatterplot1, scatterplot2] diff --git a/script/pie_chart.py b/script/pie_chart.py new file mode 100644 index 00000000..a4d32d34 --- /dev/null +++ b/script/pie_chart.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Fri Jun 3 16:18:31 2022 + +@author: tanguy +""" + +import plot_data +import plot_data.colors as colors +import random + + +data_samples = [] +SHAPES = ['round', 'square', 'triangle', 'ellipse'] +COLORS = [colors.RED, colors.BLUE, colors.GREEN, colors.YELLOW, colors.ORANGE, colors.VIOLET] +for i in range(50): + random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] + random_color = COLORS[random.randint(0, len(SHAPES) - 1)] + data_samples.append({'mass': random.uniform(0, 50), + 'length': random.uniform(0, 100), + 'shape': random_shape, + 'color': random_color + }) + + +piechart1 = plot_data.PieChart(data_samples=data_samples, + slicing_variable='mass') + +plot_data_object = plot_data.PieChart(data_samples=data_samples, + slicing_variable='length') + + +plot_data.plot_canvas(plot_data_object=piechart1, debug_mode=True) +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) diff --git a/src/multiplots.ts b/src/multiplots.ts index 387cd26e..f4c05338 100644 --- a/src/multiplots.ts +++ b/src/multiplots.ts @@ -83,7 +83,7 @@ export class MultiplePlots { } else if (object_type_ === 'parallelplot') { this.dataObjects[i]['elements'] = elements; newObject = new ParallelPlot(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true); - } else if (object_type_ === 'primitivegroup') { + } else if (object_type_ === 'primitivegroup') { newObject = new PlotContour(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true); } else if (object_type_ === 'primitivegroupcontainer') { newObject = new PrimitiveGroupContainer(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true); @@ -101,6 +101,7 @@ export class MultiplePlots { } this.initializeObjectContext(newObject); this.objectList.push(newObject); + console.log(newObject) } if (elements) {this.initialize_point_families();} @@ -109,6 +110,7 @@ export class MultiplePlots { this.display_order.push(i); this.to_display_plots.push(i); } + this.mouse_interaction(); if (buttons_ON) { diff --git a/src/plot-data.ts b/src/plot-data.ts index 559f784e..fa4125ba 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -1,5 +1,5 @@ import { heatmap_color, string_to_hex } from "./color_conversion"; -import { Point2D, PrimitiveGroup, Contour2D, Circle2D, Dataset, Graph2D, Scatter, Heatmap, Wire } from "./primitives"; +import { Point2D, PrimitiveGroup, Contour2D, Circle2D, Dataset, Graph2D, Scatter, Heatmap, Wire, PieChart } from "./primitives"; import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv } from "./utils"; import { EdgeStyle } from "./style"; import { Shape, List, MyMath } from "./toolbox"; @@ -103,90 +103,90 @@ export abstract class PlotData { graph_text_spacing_list:number[]=[]; decalage_axis_x = 50; decalage_axis_y = 20; - last_point_list:any[]=[]; - scatter_points:Point2D[]=[]; - scatter_init_points:Point2D[]=[]; - refresh_point_list_bool:boolean=true; - sc_interpolation_ON: boolean=false; - isSelectingppAxis:boolean=false; - zoom_box_x:number=0; - zoom_box_y:number=0; - zoom_box_w:number=0; - zoom_box_h:number=0; - clear_point_button_y:number=0; + last_point_list: any[] = []; + scatter_points: Point2D[] = []; + scatter_init_points: Point2D[] = []; + refresh_point_list_bool: boolean = true; + sc_interpolation_ON: boolean = false; + isSelectingppAxis: boolean = false; + zoom_box_x: number = 0; + zoom_box_y: number = 0; + zoom_box_w: number = 0; + zoom_box_h: number = 0; + clear_point_button_y: number = 0; xlog_button_y: number = 0; ylog_button_y: number = 0; heatmap_button_y: number = 0; - all_attributes:Attribute[]=[]; - attribute_booleans:boolean[]=[]; - axis_list:Attribute[]=[]; - to_display_list:any[]=[]; + all_attributes: Attribute[] = []; + attribute_booleans: boolean[] = []; + axis_list: Attribute[] = []; + to_display_list: any[] = []; axis_y_start = 0 axis_y_end = 0 - y_step:number=0; - axis_x_start:number=0; - axis_x_end:number=0; - x_step:number=0; - move_index:number = -1; - elements:any; - vertical:boolean=false; - disp_x:number = 0; - disp_y:number = 0; - disp_w:number = 0; - disp_h:number = 0; - selected_axis_name:string=''; - inverted_axis_list:boolean[]=[]; - rubber_bands:any[]=[]; - rubber_last_min:number=0; - rubber_last_max:number=0; - edge_style:EdgeStyle; - bandWidth:number=30; - bandColor:string=string_to_hex('lightblue'); - bandOpacity:number=0.5; - axisNameSize:number=12; - gradSize:number=10; - axisNbGrad:number=10; - interpolation_colors:string[]=[]; - rgbs:[number, number, number][]=[]; - hexs:string[]; - pp_selected:any[]=[]; - pp_selected_index:number[]=[]; - click_on_button:boolean=false; - vertical_axis_coords:number[][]=[]; - horizontal_axis_coords:number[][]=[]; - display_list_to_elements_dict:any; - - initial_rect_color_stroke:string=string_to_hex('grey'); - initial_rect_line_width:number=0.2; - initial_rect_dashline:number[]=[]; - manipulation_rect_color_fill:string=string_to_hex('lightblue'); - manipulation_rect_color_stroke:string=string_to_hex('black'); - manipulation_rect_line_width:number=1; - manipulation_rect_opacity:number=0.3; - manipulation_rect_dashline:number[]=[15,15]; - - isSelecting:boolean=false; - selection_coords:[number, number][]=[]; - is_drawing_rubber_band:boolean=false; - rubberbands_dep:[string, [number, number]][]=[]; - - point_families:PointFamily[]=[]; - latest_selected_points:Point2D[]=[]; - latest_selected_points_index:number[]=[]; + y_step: number = 0; + axis_x_start: number = 0; + axis_x_end: number = 0; + x_step: number = 0; + move_index: number = -1; + elements: any; + vertical: boolean = false; + disp_x: number = 0; + disp_y: number = 0; + disp_w: number = 0; + disp_h: number = 0; + selected_axis_name: string = ''; + inverted_axis_list: boolean[] = []; + rubber_bands: any[] = []; + rubber_last_min: number = 0; + rubber_last_max: number = 0; + edge_style: EdgeStyle; + bandWidth: number = 30; + bandColor: string = string_to_hex('lightblue'); + bandOpacity: number = 0.5; + axisNameSize: number = 12; + gradSize: number = 10; + axisNbGrad: number = 10; + interpolation_colors: string[] = []; + rgbs: [number, number, number][] = []; + hexs: string[]; + pp_selected: any[] = []; + pp_selected_index: number[] = []; + click_on_button: boolean = false; + vertical_axis_coords: number[][] = []; + horizontal_axis_coords: number[][] = []; + display_list_to_elements_dict: any; + + initial_rect_color_stroke: string = string_to_hex('grey'); + initial_rect_line_width: number = 0.2; + initial_rect_dashline: number[] = []; + manipulation_rect_color_fill: string = string_to_hex('lightblue'); + manipulation_rect_color_stroke: string = string_to_hex('black'); + manipulation_rect_line_width: number = 1; + manipulation_rect_opacity: number = 0.3; + manipulation_rect_dashline: number[] = [15, 15]; + + isSelecting: boolean = false; + selection_coords: [number, number][] = []; + is_drawing_rubber_band: boolean = false; + rubberbands_dep: [string, [number, number]][] = []; + + point_families: PointFamily[] = []; + latest_selected_points: Point2D[] = []; + latest_selected_points_index: number[] = []; // primitive_group_container's attributes - manipulation_bool:boolean=false; - button_y:number=0; - manip_button_x:number=0; - reset_button_x:number=0; - display_order:number[]=[]; - shown_datas:any[]=[]; - hidden_datas:any[]=[]; - clickedPlotIndex:number=-1; - primitive_dict:any={}; - elements_dict:any={}; - dep_mouse_over:boolean=false; + manipulation_bool: boolean = false; + button_y: number = 0; + manip_button_x: number = 0; + reset_button_x: number = 0; + display_order: number[] = []; + shown_datas: any[] = []; + hidden_datas: any[] = []; + clickedPlotIndex: number = -1; + primitive_dict: any = {}; + elements_dict: any = {}; + dep_mouse_over: boolean = false; // Heatmap heatmap: Heatmap; @@ -195,7 +195,7 @@ export abstract class PlotData { heatmap_table; public constructor( - public data:any, + public data: any, public width: number, public height: number, public buttons_ON: boolean, @@ -203,29 +203,29 @@ export abstract class PlotData { public Y: number, public canvas_id: string, public is_in_multiplot: boolean = false) { - this.initial_width = width; - this.initial_height = height; - this.name = data["name"]; - } + this.initial_width = width; + this.initial_height = height; + this.name = data["name"]; + } abstract draw(); abstract draw_from_context(hidden); - define_canvas(canvas_id: string):void { - var canvas:any = document.getElementById(canvas_id); + define_canvas(canvas_id: string): void { + var canvas: any = document.getElementById(canvas_id); canvas.width = this.width; - canvas.height = this.height; + canvas.height = this.height; this.context_show = canvas.getContext("2d"); - var hiddenCanvas:any = document.createElement("canvas", { is : canvas_id }); + var hiddenCanvas: any = document.createElement("canvas", { is: canvas_id }); hiddenCanvas.id = canvas_id + '_hidden'; - hiddenCanvas.width = this.width; - hiddenCanvas.height = this.height; + hiddenCanvas.width = this.width; + hiddenCanvas.height = this.height; this.context_hidden = hiddenCanvas.getContext("2d"); } - set_canvas_size(width:number, height:number): void { + set_canvas_size(width: number, height: number): void { this.width = width; this.height = height; } @@ -235,11 +235,11 @@ export abstract class PlotData { this.draw(); } - refresh_MinMax(point_list, is_graph2D=false):void { + refresh_MinMax(point_list, is_graph2D = false): void { if (!is_graph2D) { this.minX = Infinity; this.maxX = -Infinity; this.minY = Infinity; this.maxY = -Infinity; } - for (var j=0; j=0) && (x - this.pointLength <= this.width) && (y + this.pointLength >= 0) && (y - this.pointLength <= this.height)); + var is_inside_canvas = ((x + this.pointLength >= 0) && (x - this.pointLength <= this.width) && (y + this.pointLength >= 0) && (y - this.pointLength <= this.height)); if (is_inside_canvas === true) { this.context.beginPath(); d.draw(this.context, this.originX, this.originY, this.scaleX, this.scaleY, this.X, this.Y, this.log_scale_x, this.log_scale_y); @@ -546,8 +546,8 @@ export abstract class PlotData { d.draw_vertical_axis(this.context, mvy, scaleY, this.width, this.height, this.init_scaleY, this.minY, this.maxY, this.scroll_y, this.decalage_axis_x, this.decalage_axis_y, this.X, this.Y, this.plotObject['attribute_names'][1], log_scale_y); - this.x_nb_digits = Math.max(0, 1-Math.floor(Math.log10(d.x_step))); - this.y_nb_digits = Math.max(0, 1-Math.floor(Math.log10(d.y_step))); + this.x_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(d.x_step))); + this.y_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(d.y_step))); } draw_scatterplot_axis(d:Axis, lists, to_display_attributes) { @@ -555,13 +555,13 @@ export abstract class PlotData { this.init_scaleX, this.init_scaleY, lists, to_display_attributes, this.scroll_x, this.scroll_y, this.decalage_axis_x, this.decalage_axis_y, this.X, this.Y, this.width, this.height, this.log_scale_x, this.log_scale_y); - this.x_nb_digits = Math.max(0, 1-Math.floor(Math.log10(d.x_step))); - this.y_nb_digits = Math.max(0, 1-Math.floor(Math.log10(d.y_step))); + this.x_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(d.x_step))); + this.y_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(d.y_step))); this.context.closePath(); this.context.fill(); } - draw_tooltip(d:Tooltip, mvx, mvy, point_list, initial_point_list, elements, mergeON, axes:any[]) { + draw_tooltip(d: Tooltip, mvx, mvy, point_list, initial_point_list, elements, mergeON, axes: any[]) { if (d['type_'] == 'tooltip') { this.tooltip_ON = true; d.manage_tooltip(this.context, mvx, mvy, this.scaleX, this.scaleY, this.width, this.height, this.tooltip_list, @@ -571,25 +571,25 @@ export abstract class PlotData { } find_min_dist(d, mvx, mvy, step) { - var x0 = this.scaleX*d.point_list[0].cx + mvx; - var y0 = this.scaleY*d.point_list[0].cy + mvy; - var x1 = this.scaleX*d.point_list[step].cx + mvx; - var y1 = this.scaleY*d.point_list[step].cy + mvy; - var min_dist = this.distance([x0,y0],[x1,y1]); - for (var i=1; i=rubber_min) && (coord_ax<=rubber_max)) { + if ((coord_ax >= rubber_min) && (coord_ax <= rubber_max)) { return true; } return false; @@ -1145,16 +1145,16 @@ export abstract class PlotData { sort_to_display_list() { if (List.is_name_include(this.selected_axis_name, this.axis_list)) { - for (var i=0; i Click > Heatmap selection > Rubber band selection if (over) { - over_seg_lists.push({seg_list:seg_list, index:i}); + over_seg_lists.push({ seg_list: seg_list, index: i }); } else if (clicked) { - clicked_seg_lists.push({seg_list:seg_list, index:i}); + clicked_seg_lists.push({ seg_list: seg_list, index: i }); } else if (selected_by_heatmap) { - heatmap_seg_lists.push({seg_list:seg_list, index:i}); + heatmap_seg_lists.push({ seg_list: seg_list, index: i }); } else if (selected) { - selected_seg_lists.push({seg_list:seg_list, index:i}); + selected_seg_lists.push({ seg_list: seg_list, index: i }); } else { this.context.beginPath(); this.pp_color_management(i, false, false, false, false); @@ -1318,10 +1318,10 @@ export abstract class PlotData { this.pp_selected = []; this.pp_selected_index = []; if (this.vertical) { var axis_coords = this.vertical_axis_coords; } else { axis_coords = this.horizontal_axis_coords; } - for (let i=0; i= optiNbPermu) { break; } } - if (currentNbPermu; this.latest_selected_points_index = []; - for (let i=0; i=this.X) && (mouse2X<=this.width + this.X) && (mouse2Y>=this.Y) && (mouse2Y<=this.height + this.Y); + var is_inside_canvas = (mouse2X >= this.X) && (mouse2X <= this.width + this.X) && (mouse2Y >= this.Y) && (mouse2Y <= this.height + this.Y); if (!is_inside_canvas) { isDrawing = false; mouse_moving = false; @@ -2173,8 +2192,8 @@ export abstract class PlotData { } mouse_up_interaction(mouse_moving, mouse1X, mouse1Y, mouse2X, mouse2Y) { - var scale_ceil = 400*Math.max(this.init_scaleX, this.init_scaleY); - var scale_floor = Math.min(this.init_scaleX, this.init_scaleY)/3; + var scale_ceil = 400 * Math.max(this.init_scaleX, this.init_scaleY); + var scale_floor = Math.min(this.init_scaleX, this.init_scaleY) / 3; var click_on_plus = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.zoom_rect_y + this.Y, this.button_w, this.button_h); var click_on_minus = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.zoom_rect_y + this.button_h + this.Y, this.button_w, this.button_h); @@ -2190,8 +2209,8 @@ export abstract class PlotData { var click_on_heatmap = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.heatmap_button_y + this.Y, this.button_w, this.button_h); var click_on_csv = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.csv_button_y + this.Y, this.button_w, this.button_h); var text_spacing_sum_i = 0; - for (var i=0; i scale_floor) && (this.scaleY/1.2 > scale_floor)) { - Interactions.zoom_out_button_action(this); - - } else if (click_on_zoom_window === true) { - Interactions.click_on_zoom_window_action(this); - - } else if (click_on_reset === true) { - Interactions.click_on_reset_action(this); - - } else if (click_on_select === true) { - Interactions.click_on_selection_button_action(this); - - } else if (click_on_graph) { - Interactions.graph_button_action(mouse1X, mouse1Y, this); - - } else if (click_on_merge && this.type_ === "scatterplot") { - Interactions.click_on_merge_action(this); - } else if (click_on_perm) { - Interactions.click_on_perm_action(this); - } else if (click_on_clear) { - Interactions.click_on_clear_action(this); - } else if (click_on_xlog) { - Interactions.click_on_xlog_action(this); - } else if (click_on_ylog) { - Interactions.click_on_ylog_action(this); - } else if (click_on_heatmap) { - Interactions.click_on_heatmap_action(this); - } else if (click_on_csv && this.type_ === "graph2d") { - Interactions.click_on_csv_action(this); - } else { - if (this.heatmap_view) { - this.refresh_selected_areas(mouse1X, mouse1Y); - Interactions.refresh_heatmap_selected_points(this); - } + this.selecting_point_action(mouse1X, mouse1Y); + if ((click_on_plus === true) && (this.scaleX * 1.2 < scale_ceil) && (this.scaleY * 1.2 < scale_ceil)) { + Interactions.zoom_in_button_action(this); + + } else if ((click_on_minus === true) && (this.scaleX / 1.2 > scale_floor) && (this.scaleY / 1.2 > scale_floor)) { + Interactions.zoom_out_button_action(this); + + } else if (click_on_zoom_window === true) { + Interactions.click_on_zoom_window_action(this); + + } else if (click_on_reset === true) { + Interactions.click_on_reset_action(this); + + } else if (click_on_select === true) { + Interactions.click_on_selection_button_action(this); + + } else if (click_on_graph) { + Interactions.graph_button_action(mouse1X, mouse1Y, this); + + } else if (click_on_merge) { + Interactions.click_on_merge_action(this); + } else if (click_on_perm) { + Interactions.click_on_perm_action(this); + } else if (click_on_clear) { + Interactions.click_on_clear_action(this); + } else if (click_on_xlog) { + Interactions.click_on_xlog_action(this); + } else if (click_on_ylog) { + Interactions.click_on_ylog_action(this); + } else if (click_on_heatmap) { + Interactions.click_on_heatmap_action(this); + } else { + if (this.heatmap_view) { + this.refresh_selected_areas(mouse1X, mouse1Y); + Interactions.refresh_heatmap_selected_points(this); } } - Interactions.reset_zoom_box(this); - this.draw(); - var isDrawing = false; - mouse_moving = false; - this.isSelecting = false; - this.is_drawing_rubber_band = false; - return [isDrawing, mouse_moving, mouse1X, mouse1Y, mouse2X, mouse2Y]; + } + Interactions.reset_zoom_box(this); + this.draw(); + var isDrawing = false; + mouse_moving = false; + this.isSelecting = false; + this.is_drawing_rubber_band = false; + return [isDrawing, mouse_moving, mouse1X, mouse1Y, mouse2X, mouse2Y]; } wheel_interaction(mouse3X, mouse3Y, e) { @@ -2263,41 +2280,41 @@ export abstract class PlotData { var event = -Math.sign(e.deltaY); mouse3X = e.offsetX; mouse3Y = e.offsetY; - if ((mouse3Y>=this.height - this.decalage_axis_y + this.Y) && (mouse3X>this.decalage_axis_x + this.X) && this.axis_ON) { - if (event>0) { - this.scaleX = this.scaleX*this.fusion_coeff; - this.scroll_x++; - this.originX = this.width/2 + this.fusion_coeff * (this.originX - this.width/2); - } else if (event<0) { - this.scaleX = this.scaleX/this.fusion_coeff; - this.scroll_x--; - this.originX = this.width/2 + 1/this.fusion_coeff * (this.originX - this.width/2); - } - - } else if ((mouse3X<=this.decalage_axis_x + this.X) && (mouse3Y0) { - this.scaleY = this.scaleY*this.fusion_coeff; - this.scroll_y++; - this.originY = this.height/2 + this.fusion_coeff * (this.originY - this.height/2); - } else if (event<0) { - this.scaleY = this.scaleY/this.fusion_coeff; - this.scroll_y--; - this.originY = this.height/2 + 1/this.fusion_coeff * (this.originY - this.height/2); - } + if ((mouse3Y >= this.height - this.decalage_axis_y + this.Y) && (mouse3X > this.decalage_axis_x + this.X) && this.axis_ON) { + if (event > 0) { + this.scaleX = this.scaleX * this.fusion_coeff; + this.scroll_x++; + this.originX = this.width / 2 + this.fusion_coeff * (this.originX - this.width / 2); + } else if (event < 0) { + this.scaleX = this.scaleX / this.fusion_coeff; + this.scroll_x--; + this.originX = this.width / 2 + 1 / this.fusion_coeff * (this.originX - this.width / 2); + } + + } else if ((mouse3X <= this.decalage_axis_x + this.X) && (mouse3Y < this.height - this.decalage_axis_y + this.Y) && this.axis_ON) { + if (event > 0) { + this.scaleY = this.scaleY * this.fusion_coeff; + this.scroll_y++; + this.originY = this.height / 2 + this.fusion_coeff * (this.originY - this.height / 2); + } else if (event < 0) { + this.scaleY = this.scaleY / this.fusion_coeff; + this.scroll_y--; + this.originY = this.height / 2 + 1 / this.fusion_coeff * (this.originY - this.height / 2); + } } else { - if (event>0) var coeff = this.fusion_coeff; else coeff = 1/this.fusion_coeff; - this.scaleX = this.scaleX*coeff; - this.scaleY = this.scaleY*coeff; - this.scroll_x = this.scroll_x + event; - this.scroll_y = this.scroll_y + event; - this.originX = mouse3X - this.X + coeff * (this.originX - mouse3X + this.X); - this.originY = mouse3Y - this.Y + coeff * (this.originY - mouse3Y + this.Y); - } - if (isNaN(this.scroll_x)) this.scroll_x = 0; - if (isNaN(this.scroll_y)) this.scroll_y = 0; - this.draw(); - return [mouse3X, mouse3Y]; + if (event > 0) var coeff = this.fusion_coeff; else coeff = 1 / this.fusion_coeff; + this.scaleX = this.scaleX * coeff; + this.scaleY = this.scaleY * coeff; + this.scroll_x = this.scroll_x + event; + this.scroll_y = this.scroll_y + event; + this.originX = mouse3X - this.X + coeff * (this.originX - mouse3X + this.X); + this.originY = mouse3Y - this.Y + coeff * (this.originY - mouse3Y + this.Y); + } + if (isNaN(this.scroll_x)) this.scroll_x = 0; + if (isNaN(this.scroll_y)) this.scroll_y = 0; + this.draw(); + return [mouse3X, mouse3Y]; } mouse_up_interaction_pp(click_on_axis, selected_axis_index, click_on_name, click_on_band, click_on_border, is_resizing, selected_name_index, mouse_moving, isDrawing, mouse1X, mouse1Y, mouse3X, mouse3Y, e) { @@ -2324,22 +2341,22 @@ export abstract class PlotData { return [mouse3X, mouse3Y, click_on_axis, isDrawing, mouse_moving, is_resizing]; } - mouse_interaction(is_parallelplot:boolean) { + mouse_interaction(is_parallelplot: boolean) { if (this.interaction_ON === true) { var isDrawing = false; var mouse_moving = false; var mouse1X = 0; var mouse1Y = 0; var mouse2X = 0; var mouse2Y = 0; var mouse3X = 0; var mouse3Y = 0; - var click_on_axis:boolean=false; - var selected_axis_index:number = -1; - var click_on_name:boolean = false; - var selected_name_index:number = -1; - var click_on_band:boolean = false; - var click_on_border:boolean = false; - var selected_band_index:number = -1; - var selected_border:number[]=[]; - var is_resizing:boolean=false; - var click_on_selectw_border:boolean = false; - var up:boolean = false; var down:boolean = false; var left:boolean = false; var right:boolean = false; + var click_on_axis: boolean = false; + var selected_axis_index: number = -1; + var click_on_name: boolean = false; + var selected_name_index: number = -1; + var click_on_band: boolean = false; + var click_on_border: boolean = false; + var selected_band_index: number = -1; + var selected_border: number[] = []; + var is_resizing: boolean = false; + var click_on_selectw_border: boolean = false; + var up: boolean = false; var down: boolean = false; var left: boolean = false; var right: boolean = false; var canvas = document.getElementById(this.canvas_id); @@ -2419,10 +2436,10 @@ export abstract class PlotData { var nb_points_in = 0; var index_last_in = -1; - while ((k=0) && (x<=this.width) && (y>=0) && (y<=this.height); + while ((k < list_points.length) && bool) { + var x = this.scaleX * (list_points[k].cx + mvx); + var y = this.scaleY * (list_points[k].cy + mvy); + var is_inside_canvas = (x >= 0) && (x <= this.width) && (y >= 0) && (y <= this.height); if (is_inside_canvas === true) { index_first_in = k; bool = false; @@ -2434,10 +2451,10 @@ export abstract class PlotData { return [index_first_in, nb_points_in, index_last_in]; } - while (k=0) && (x<=this.width) && (y>=0) && (y<=this.height); + while (k < list_points.length) { + var x = this.scaleX * (list_points[k].cx + mvx); + var y = this.scaleY * (list_points[k].cy + mvy); + var is_inside_canvas = (x >= 0) && (x <= this.width) && (y >= 0) && (y <= this.height); if (is_inside_canvas === true) { index_last_in = k; nb_points_in++; @@ -2448,40 +2465,40 @@ export abstract class PlotData { } is_inside_canvas(point, mvx, mvy) { - var x = this.scaleX*(point.cx + mvx); - var y = this.scaleY*(point.cy + mvy); + var x = this.scaleX * (point.cx + mvx); + var y = this.scaleY * (point.cy + mvy); length = point.size; - return (x+length>=0) && (x-length<=this.width) && (y+length>=0) && (y-length<=this.height); + return (x + length >= 0) && (x - length <= this.width) && (y + length >= 0) && (y - length <= this.height); } get_points_inside_canvas(list_points, mvx, mvy) { var new_list_points = []; - for (var i=0; i=size_j) {var max_size_index = i;} else {var max_size_index = j;} - var xj = this.scaleX*point_list_copy[j].cx + mvx; - var yj = this.scaleY*point_list_copy[j].cy + mvy; - if (this.distance([xi,yi], [xj,yj])< 10*(point_list_copy[i].size + point_list_copy[j].size)) { + if (size_i >= size_j) { var max_size_index = i; } else { var max_size_index = j; } + var xj = this.scaleX * point_list_copy[j].cx + mvx; + var yj = this.scaleY * point_list_copy[j].cy + mvy; + if (this.distance([xi, yi], [xj, yj]) < 10 * (point_list_copy[i].size + point_list_copy[j].size)) { var point_i = point_list_copy[i]; var point_j = point_list_copy[j]; - var new_cx = (point_i.cx + point_j.cx)/2; - var new_cy = (point_i.cy + point_j.cy)/2; + var new_cx = (point_i.cx + point_j.cx) / 2; + var new_cy = (point_i.cy + point_j.cy) / 2; var point = new Point2D(new_cx, new_cy, point_list_copy[i].point_style, 'point', ''); point.points_inside = point_list_copy[i].points_inside.concat(point_list_copy[j].points_inside); point.point_families = List.union(point_list_copy[i].point_families, point_list_copy[j].point_families); point.selected = point_list_copy[i].selected || point_list_copy[j].selected; var size_coeff = 1.15; - point.size = point_list_copy[max_size_index].size*size_coeff; + point.size = point_list_copy[max_size_index].size * size_coeff; this.delete_clicked_points([point_i, point_j]); this.delete_tooltip([point_i, point_j]); point_list_copy = List.remove_element(point_list_copy[i], point_list_copy); - point_list_copy = List.remove_element(point_list_copy[j-1], point_list_copy); + point_list_copy = List.remove_element(point_list_copy[j - 1], point_list_copy); point_list_copy.push(point); this.color_to_plot_data[point.hidden_color] = point; bool = true; @@ -2545,7 +2562,7 @@ export abstract class PlotData { delete_clicked_points(point_list) { var i = 0; - while (iq1[1]) || (p0[1]>q0[1] && p1[1] q1[1]) || (p0[1] > q0[1] && p1[1] < q1[1]))) { return true; - } else if (this.vertical===false && ((p0[0]q1[0]) || (p0[0]>q0[0] && p1[0] q1[0]) || (p0[0] > q0[0] && p1[0] < q1[0]))) { return true; } return false; } counting_intersections(segments) { - if (segments.length<2) { + if (segments.length < 2) { return 0; } var nb = 0; - for (let i=1; i=plot_data.X) && (mouse2X<=plot_data.width + plot_data.X) && (mouse2Y>=plot_data.Y) && (mouse2Y<=plot_data.height + plot_data.Y); + var is_inside_canvas = (mouse2X >= plot_data.X) && (mouse2X <= plot_data.width + plot_data.X) && (mouse2Y >= plot_data.Y) && (mouse2Y <= plot_data.height + plot_data.Y); var mouse_move = true; if (!is_inside_canvas) { isDrawing = false; @@ -2917,16 +2934,16 @@ export class Interactions { return [mouse2X, mouse2Y, isDrawing, mouse_move]; } - public static create_rubber_band(mouse1X, mouse1Y, selected_axis_index, e, plot_data:any) { + public static create_rubber_band(mouse1X, mouse1Y, selected_axis_index, e, plot_data: any) { var mouse2X = e.offsetX; var mouse2Y = e.offsetY; plot_data.is_drawing_rubber_band = true; if (plot_data.vertical) { - var min = Math.max(Math.min((mouse1Y - plot_data.axis_y_end)/(plot_data.axis_y_start - plot_data.axis_y_end), (mouse2Y - plot_data.axis_y_end)/(plot_data.axis_y_start - plot_data.axis_y_end)), -0.01); - var max = Math.min(Math.max((mouse1Y - plot_data.axis_y_end)/(plot_data.axis_y_start - plot_data.axis_y_end), (mouse2Y - plot_data.axis_y_end)/(plot_data.axis_y_start - plot_data.axis_y_end)), 1); + var min = Math.max(Math.min((mouse1Y - plot_data.axis_y_end) / (plot_data.axis_y_start - plot_data.axis_y_end), (mouse2Y - plot_data.axis_y_end) / (plot_data.axis_y_start - plot_data.axis_y_end)), -0.01); + var max = Math.min(Math.max((mouse1Y - plot_data.axis_y_end) / (plot_data.axis_y_start - plot_data.axis_y_end), (mouse2Y - plot_data.axis_y_end) / (plot_data.axis_y_start - plot_data.axis_y_end)), 1); } else { - var min = Math.max(Math.min((mouse1X - plot_data.axis_x_start)/(plot_data.axis_x_end - plot_data.axis_x_start), (mouse2X - plot_data.axis_x_start)/(plot_data.axis_x_end - plot_data.axis_x_start)), -0.01); - var max = Math.min(Math.max((mouse1X - plot_data.axis_x_start)/(plot_data.axis_x_end - plot_data.axis_x_start), (mouse2X - plot_data.axis_x_start)/(plot_data.axis_x_end - plot_data.axis_x_start)), 1); + var min = Math.max(Math.min((mouse1X - plot_data.axis_x_start) / (plot_data.axis_x_end - plot_data.axis_x_start), (mouse2X - plot_data.axis_x_start) / (plot_data.axis_x_end - plot_data.axis_x_start)), -0.01); + var max = Math.min(Math.max((mouse1X - plot_data.axis_x_start) / (plot_data.axis_x_end - plot_data.axis_x_start), (mouse2X - plot_data.axis_x_start) / (plot_data.axis_x_end - plot_data.axis_x_start)), 1); } plot_data.rubber_bands[selected_axis_index] = [min, max]; var realCoord_min = plot_data.axis_to_real_coords(min, plot_data.axis_list[selected_axis_index]['type_'], plot_data.axis_list[selected_axis_index]['list'], plot_data.inverted_axis_list[selected_axis_index]); @@ -2939,16 +2956,16 @@ export class Interactions { return [mouse2X, mouse2Y]; } - public static rubber_band_translation(mouse1X, mouse1Y, selected_band_index, e, plot_data:any) { + public static rubber_band_translation(mouse1X, mouse1Y, selected_band_index, e, plot_data: any) { var mouse2X = e.offsetX; var mouse2Y = e.offsetY; plot_data.is_drawing_rubber_band = true; if (plot_data.vertical) { - var deltaY = (mouse2Y - mouse1Y)/(plot_data.axis_y_start - plot_data.axis_y_end); + var deltaY = (mouse2Y - mouse1Y) / (plot_data.axis_y_start - plot_data.axis_y_end); var new_min = Math.max(plot_data.rubber_last_min + deltaY, -0.01); var new_max = Math.min(plot_data.rubber_last_max + deltaY, 1); } else { - var deltaX = (mouse2X - mouse1X)/(plot_data.axis_x_end - plot_data.axis_x_start); + var deltaX = (mouse2X - mouse1X) / (plot_data.axis_x_end - plot_data.axis_x_start); var new_min = Math.max(plot_data.rubber_last_min + deltaX, -0.01); var new_max = Math.min(plot_data.rubber_last_max + deltaX, 1); } @@ -2966,14 +2983,14 @@ export class Interactions { return [mouse2X, mouse2Y]; } - public static rubber_band_resize(mouse1X, mouse1Y, selected_border, e, plot_data:any) { + public static rubber_band_resize(mouse1X, mouse1Y, selected_border, e, plot_data: any) { var mouse2X = e.offsetX; var mouse2Y = e.offsetY; var axis_index = selected_border[0]; var border_number = selected_border[1]; plot_data.is_drawing_rubber_band = true; if (plot_data.vertical) { - var deltaY = (mouse2Y - mouse1Y)/(plot_data.axis_y_start - plot_data.axis_y_end); + var deltaY = (mouse2Y - mouse1Y) / (plot_data.axis_y_start - plot_data.axis_y_end); if (border_number == 0) { var new_min = Math.min(Math.max(plot_data.rubber_last_min + deltaY, -0.01), 1); plot_data.rubber_bands[axis_index][0] = new_min; @@ -2982,7 +2999,7 @@ export class Interactions { plot_data.rubber_bands[axis_index][1] = new_max; } } else { - var deltaX = (mouse2X - mouse1X)/(plot_data.axis_x_end - plot_data.axis_x_start); + var deltaX = (mouse2X - mouse1X) / (plot_data.axis_x_end - plot_data.axis_x_start); if (border_number == 0) { var new_min = Math.min(Math.max(plot_data.rubber_last_min + deltaX, -0.01), 1); plot_data.rubber_bands[axis_index][0] = new_min; @@ -2991,8 +3008,8 @@ export class Interactions { plot_data.rubber_bands[axis_index][1] = new_max; } } - if (plot_data.rubber_bands[axis_index][0]>plot_data.rubber_bands[axis_index][1]) { - [plot_data.rubber_bands[axis_index][0],plot_data.rubber_bands[axis_index][1]] = [plot_data.rubber_bands[axis_index][1],plot_data.rubber_bands[axis_index][0]]; + if (plot_data.rubber_bands[axis_index][0] > plot_data.rubber_bands[axis_index][1]) { + [plot_data.rubber_bands[axis_index][0], plot_data.rubber_bands[axis_index][1]] = [plot_data.rubber_bands[axis_index][1], plot_data.rubber_bands[axis_index][0]]; border_number = 1 - border_number; [plot_data.rubber_last_min, plot_data.rubber_last_max] = [plot_data.rubber_last_max, plot_data.rubber_last_min]; } @@ -3012,7 +3029,7 @@ export class Interactions { return [border_number, mouse2X, mouse2Y, is_resizing]; } - public static select_axis_action(selected_axis_index, click_on_band, click_on_border, plot_data:PlotData) { + public static select_axis_action(selected_axis_index, click_on_band, click_on_border, plot_data: PlotData) { plot_data.isSelectingppAxis = true; if (plot_data.rubber_bands[selected_axis_index].length == 0) { var attribute_name = plot_data.axis_list[selected_axis_index]['name']; @@ -3025,7 +3042,7 @@ export class Interactions { } } else if ((plot_data.rubber_bands[selected_axis_index].length != 0) && !click_on_band && !click_on_border) { plot_data.rubber_bands[selected_axis_index] = []; - for (let i=0; imouse1X) { - var new_index = Math.floor((mouse3X - plot_data.axis_x_start)/plot_data.x_step); + if (mouse3X > mouse1X) { + var new_index = Math.floor((mouse3X - plot_data.axis_x_start) / plot_data.x_step); } else { - var new_index = Math.ceil((mouse3X - plot_data.axis_x_start)/plot_data.x_step); + var new_index = Math.ceil((mouse3X - plot_data.axis_x_start) / plot_data.x_step); } } else { - if (mouse3Y>mouse1Y) { - var new_index = Math.floor((mouse3Y - plot_data.axis_y_start)/plot_data.y_step); + if (mouse3Y > mouse1Y) { + var new_index = Math.floor((mouse3Y - plot_data.axis_y_start) / plot_data.y_step); } else { - var new_index = Math.ceil((mouse3Y - plot_data.axis_y_start)/plot_data.y_step); + var new_index = Math.ceil((mouse3Y - plot_data.axis_y_start) / plot_data.y_step); } } this.move_axis(plot_data.move_index, new_index, plot_data); @@ -3058,7 +3075,7 @@ export class Interactions { return [mouse3X, mouse3Y, click_on_axis]; } - public static select_title_action(selected_name_index, plot_data:any) { + public static select_title_action(selected_name_index, plot_data: any) { plot_data.inverted_axis_list[selected_name_index] = !plot_data.inverted_axis_list[selected_name_index]; if (plot_data.rubber_bands[selected_name_index].length != 0) { plot_data.invert_rubber_bands([selected_name_index]); @@ -3067,7 +3084,7 @@ export class Interactions { plot_data.draw(); } - public static change_disposition_action(plot_data:any) { + public static change_disposition_action(plot_data: any) { plot_data.vertical = !plot_data.vertical; plot_data.refresh_axis_bounds(plot_data.axis_list.length); plot_data.invert_rubber_bands('all'); @@ -3075,8 +3092,8 @@ export class Interactions { plot_data.draw(); } - public static rubber_band_size_check(selected_band_index, plot_data:any) { - if (plot_data.rubber_bands[selected_band_index].length != 0 && Math.abs(plot_data.rubber_bands[selected_band_index][0] - plot_data.rubber_bands[selected_band_index][1])<=0.02) { + public static rubber_band_size_check(selected_band_index, plot_data: any) { + if (plot_data.rubber_bands[selected_band_index].length != 0 && Math.abs(plot_data.rubber_bands[selected_band_index][0] - plot_data.rubber_bands[selected_band_index][1]) <= 0.02) { plot_data.rubber_bands[selected_band_index] = []; } plot_data.draw(); @@ -3084,7 +3101,7 @@ export class Interactions { return is_resizing; } - public static move_axis(old_index, new_index, plot_data:any) { + public static move_axis(old_index, new_index, plot_data: any) { plot_data.axis_list = List.move_elements(old_index, new_index, plot_data.axis_list); plot_data.rubber_bands = List.move_elements(old_index, new_index, plot_data.rubber_bands); plot_data.inverted_axis_list = List.move_elements(old_index, new_index, plot_data.inverted_axis_list); @@ -3094,39 +3111,39 @@ export class Interactions { plot_data.draw(); } - public static initialize_click_on_bands(mouse1X, mouse1Y, plot_data:any) { + public static initialize_click_on_bands(mouse1X, mouse1Y, plot_data: any) { var border_size = 10; - var click_on_band:any = false; - var click_on_border:any = false; - var selected_band_index:any = -1; - var selected_border:any = []; - for (var i=0; i +export class PieChart { + pieParts: Array=[]; + + constructor(public slicingVariable: string, + public dataSamples: DataSample[], + public name: string + ) + { + this.pieParts = this.definePieParts(); + } + + public static deserialize(serialized): PieChart { // TODO : clarify this + return new PieChart(serialized['slicing_variable'], + serialized['data_samples'], + serialized['name']); + } + + private get normedRatio(): number { + let sumSamples: number = 0; + let normedRatio: number = 2*Math.PI; + + this.dataSamples.forEach(sample => { + sumSamples += sample[this.slicingVariable] + }); + normedRatio /= sumSamples; + return normedRatio + } + + definePieParts(): PiePart[] { + const colorRatio: number = 360 / this.dataSamples.length; + let initAngle: number = Math.PI/2; + let nextAngle: number = initAngle; + let colorRadius: number = 0; + let pieParts: PiePart[] = []; + + this.dataSamples.forEach(sample => { + colorRadius += colorRatio; + nextAngle -= sample[this.slicingVariable] * this.normedRatio; + + let newPart = new PiePart(initAngle, nextAngle, 'hsl('+ colorRadius +', 50%, 50%, 90%)'); + pieParts.push(newPart); + + initAngle = nextAngle; + }) + return pieParts + } +} + +export class PiePart { + hidden_color: string = ''; + path: Path2D; + clicked: boolean = false; + selected: boolean = false; + points_inside: PiePart[] = [this]; + readonly center = { x: 0, y : 0 }; + readonly radius = 1; + + /** + * @param initAngle in radian + * @param endAngle in radian + * @param color default color of part + */ + constructor( + public initAngle: number, // Warning : X axis is from top to bottom then trigonometric sense is inverted + public endAngle: number, + public color: string = '') + { + this.hidden_color = genColor(); + this.path = this.buildPath(); + } + + private buildPath(): Path2D { + const path = new Path2D(); + path.moveTo(this.center.x, this.center.y); + path.arc( + this.center.x, + this.center.y, + this.radius, + this.initAngle, + this.endAngle, + true); + return path + } + + draw(context: CanvasRenderingContext2D): void { + /** + * @param context Context in which to draw + */ + context.stroke(this.path); + context.fill(this.path); + } + + assignColor(select_on_mouse: PiePart): string { + let color: string = this.color; + if (this.clicked && select_on_mouse !== this) { + if (select_on_mouse != undefined) { + if (select_on_mouse.clicked) { + this.clicked = false + } else { + color = string_to_hex("red"); + } + } else { + color = string_to_hex("red"); + } + } else if (this.clicked && select_on_mouse === this) { + color = string_to_hex('lightskyblue'); + } else if (!this.clicked && select_on_mouse === this) { + color = string_to_hex('lightskyblue'); + } + return color + } +} + + diff --git a/src/subplots.ts b/src/subplots.ts index c765b098..661df95f 100644 --- a/src/subplots.ts +++ b/src/subplots.ts @@ -1,8 +1,8 @@ import { PlotData, Buttons, Interactions } from "./plot-data"; import { check_package_version, Attribute, Axis, Sort, set_default_values, TypeOf } from "./utils"; -import { Heatmap, PrimitiveGroup } from "./primitives"; +import { Heatmap, PrimitiveGroup, DataSample } from "./primitives"; import { List, Shape, MyObject } from "./toolbox"; -import { Graph2D, Scatter } from "./primitives"; +import { Graph2D, Scatter, PieChart, PiePart } from "./primitives"; import { string_to_hex, string_to_rgb, get_interpolation_colors, rgb_to_string, rgb_to_hex, color_to_string } from "./color_conversion"; import { EdgeStyle, TextStyle, SurfaceStyle } from "./style"; @@ -12,1936 +12,2078 @@ var alert_count = 0; * A class that inherits from PlotData and is specific for drawing PrimitiveGroups. */ export class PlotContour extends PlotData { - plot_datas:any; - selected: boolean = true; - public constructor(public data:any, - public width: number, - public height: number, - public buttons_ON: boolean, - public X: number, - public Y: number, - public canvas_id: string, - public is_in_multiplot = false) { - super(data, width, height, buttons_ON, 0, 0, canvas_id, is_in_multiplot); - if (!is_in_multiplot) { - var requirement = '0.6.0'; - check_package_version(data['package_version'], requirement); - } - this.plot_datas = []; - this.type_ = 'primitivegroup'; - var d = this.data; - if (d['type_'] == 'primitivegroup') { - var a = PrimitiveGroup.deserialize(d); - this.plot_datas.push(a); - let multiple_labels_index = -1; - for (let i=0; i 20) && (this.button_h > 10)) { + if (this.zw_bool || (this.isSelecting && !this.permanent_window)) { + this.draw_zoom_rectangle(); + } + this.context.restore(); - this.refresh_buttons_coords(); - //Drawing the zooming button - Buttons.zoom_button(this.button_x, this.zoom_rect_y, this.button_w, this.button_h, this); + if ((this.buttons_ON) && (this.button_w > 20) && (this.button_h > 10)) { - //Drawing the button for zooming window selection - Buttons.zoom_window_button(this.button_x,this.zw_y,this.button_w,this.button_h, this); + this.refresh_buttons_coords(); + //Drawing the zooming button + Buttons.zoom_button(this.button_x, this.zoom_rect_y, this.button_w, this.button_h, this); - //Drawing the reset button - Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); - } + //Drawing the button for zooming window selection + Buttons.zoom_window_button(this.button_x, this.zw_y, this.button_w, this.button_h, this); + + //Drawing the reset button + Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); } + } } -/** A class that inherits from PlotData and is specific for drawing ScatterPlots and Graph2Ds +/** A class that inherits from PlotData and is specific for drawing ScatterPlots and Graph2Ds */ export class PlotScatter extends PlotData { - public constructor(public data:any, - public width: number, - public height: number, - public buttons_ON: boolean, - public X: number, - public Y: number, - public canvas_id: string, - public is_in_multiplot = false) { - super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); - if (!is_in_multiplot) { - var requirement = '0.6.0'; - check_package_version(data['package_version'], requirement); - } - if (this.buttons_ON) { - this.refresh_buttons_coords(); - } - this.log_scale_x = data['log_scale_x']; - this.log_scale_y = data['log_scale_y']; - if (data['type_'] == 'graph2d') { - this.type_ = 'graph2d'; - this.graph_ON = true; - this.axis_ON = true; - this.plotObject = Graph2D.deserialize(data); - this.plot_datas['value'] = this.plotObject.graphs; - for (let i=0; i 20) && (this.button_h > 10)) { + this.refresh_buttons_coords(); + + //Drawing the zooming button + Buttons.zoom_button(this.button_x, this.zoom_rect_y, this.button_w, this.button_h, this); + + //Drawing the button for zooming window selection + Buttons.zoom_window_button(this.button_x, this.zw_y, this.button_w, this.button_h, this); + + //Drawing the reset button + Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); + + //Drawing the selection button + Buttons.selection_button(this.button_x, this.select_y, this.button_w, this.button_h, this); + + //Drawing the enable/disable graph button + Buttons.graph_buttons(this.graph1_button_y, this.graph1_button_w, this.graph1_button_h, '10px Arial', this); + + if (this.plotObject.type_ == 'scatterplot') { + // TODO To check, 'this' in args is weird + Buttons.merge_button(this.button_x, this.merge_y, this.button_w, this.button_h, '10px Arial', this); } - if ((this.buttons_ON) && (this.button_w > 20) && (this.button_h > 10)) { - this.refresh_buttons_coords(); + //draw permanent window button + Buttons.perm_window_button(this.button_x, this.perm_button_y, this.button_w, this.button_h, '10px Arial', this); - //Drawing the zooming button - Buttons.zoom_button(this.button_x, this.zoom_rect_y, this.button_w, this.button_h, this); + //draw clear point button + Buttons.clear_point_button(this.button_x, this.clear_point_button_y, this.button_w, this.button_h, '10px Arial', this); - //Drawing the button for zooming window selection - Buttons.zoom_window_button(this.button_x,this.zw_y,this.button_w,this.button_h, this); + // Draw log scale buttons + Buttons.log_scale_buttons(this.button_x, this.xlog_button_y, this.ylog_button_y, this.button_w, this.button_h, + "10px Arial", this); - //Drawing the reset button - Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); + // Draw Heatmap button + Buttons.heatmap_button(this.button_x, this.heatmap_button_y, this.button_w, this.button_h, "10px Arial", this); + } + if (this.multiplot_manipulation) { + this.draw_manipulable_rect(); + } + this.context.restore(); + } +} + +/** A class that inherits from PlotData and is specific for drawing ScatterPlots and Graph2Ds + */ +export class PlotPieChart extends PlotData { + public constructor( + public data: DataSample[], + public width: number, + public height: number, + public buttons_ON: boolean, + public X: number, + public Y: number, + public canvas_id: string, + public is_in_multiplot = false) { + super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); + if (!is_in_multiplot) { + let requirement = '0.6.0'; + check_package_version(data['package_version'], requirement); + } + if (this.buttons_ON) { + this.refresh_buttons_coords(); + } + this.log_scale_y = false; + this.mergeON = true; + this.selected_areas = []; + this.refresh_MinMax(); + this.plotObject = PieChart.deserialize(data); + this.color_to_plot_data = this.initHiddenColors() + } - //Drawing the selection button - Buttons.selection_button(this.button_x, this.select_y, this.button_w, this.button_h, this); + private initHiddenColors(): any { + let hiddenColorsList = {}; + this.plotObject.pieParts.forEach( + part => { hiddenColorsList[part.hidden_color] = part; }) + return hiddenColorsList + } - //Drawing the enable/disable graph button - Buttons.graph_buttons(this.graph1_button_y, this.graph1_button_w, this.graph1_button_h, '10px Arial', this); + refresh_MinMax(): void { + this.minX = -this.width / 2; + this.maxX = this.width / 2; + this.minY = -this.height / 2; + this.maxY = this.height / 2; + } - if (this.plotObject.type_ == 'scatterplot') { - Buttons.merge_button(this.button_x, this.merge_y, this.button_w, this.button_h, '10px Arial', this); - // Draw Heatmap button - Buttons.heatmap_button(this.button_x, this.heatmap_button_y, this.button_w, this.button_h, "10px Arial", this); - } else if (this.plotObject.type_ === "graph2d") { - Buttons.csv_button(this.button_x, this.csv_button_y, this.button_w, this.button_h, "12px Arial", this); + private drawPiechart(): void { + // Anything related to mouse handling should be done in a specific class and will be the purpose of a future release + const radiusPieScale = this.height * 0.475; + const matrix = this.context.getTransform(); + + [this.context_show, this.context_hidden].forEach((context, i) => { + context.transform(this.scaleX * radiusPieScale, 0, 0, this.scaleX * radiusPieScale, this.originX + this.X, this.originY + this.Y); + context.lineWidth = 0.5 / this.scaleX / radiusPieScale; + for (let part of this.plotObject.pieParts) { + if (i == 0) { + context.fillStyle = part.assignColor(this.select_on_mouse); + } else { + context.fillStyle = part.hidden_color; } + context.strokeStyle = this.context_show.fillStyle + part.draw(context); + } + context.setTransform(matrix); + }) + } - //draw permanent window button - Buttons.perm_window_button(this.button_x, this.perm_button_y, this.button_w, this.button_h, '10px Arial', this); - - //draw clear point button - Buttons.clear_point_button(this.button_x, this.clear_point_button_y, this.button_w, this.button_h, '10px Arial', this); + draw(): void { + this.drawCanvas() + this.drawPiechart(); + this.drawSiders(); + this.context_hidden.restore(); + this.context_show.restore(); + } - // Draw log scale buttons - Buttons.log_scale_buttons(this.button_x, this.xlog_button_y, this.ylog_button_y, this.button_w, this.button_h, - "10px Arial", this); + private drawCanvas(): void { + for (let context of [this.context_show, this.context_hidden]) { + context.save() + this.draw_empty_canvas(context); + this.context = context; + if (this.settings_on) { + this.draw_settings_rect(); + } else { + this.draw_rect(); + } + context.beginPath(); + context.rect(this.X - 1, this.Y - 1, this.width + 2, this.height + 2); + context.clip(); + context.closePath(); + } + } + private drawSiders(): void { + for (let context of [this.context_hidden, this.context_show]) { + this.context = context; + if (this.permanent_window) { + this.draw_selection_rectangle(); + } + if (this.zw_bool || (this.isSelecting && !this.permanent_window)) { + this.draw_zoom_rectangle(); + } + if ((this.buttons_ON) && (this.button_w > 20) && (this.button_h > 10)) { + this.drawButtons(); } if (this.multiplot_manipulation) { this.draw_manipulable_rect(); } - this.context.restore(); } + } + + private drawButtons(): void { + this.refresh_buttons_coords(); + //Drawing the zooming button + Buttons.zoom_button(this.button_x, this.zoom_rect_y, this.button_w, this.button_h, this); + + //Drawing the button for zooming window selection + Buttons.zoom_window_button(this.button_x, this.zw_y, this.button_w, this.button_h, this); + + //Drawing the reset button + Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); + + //Drawing the selection button + Buttons.selection_button(this.button_x, this.select_y, this.button_w, this.button_h, this); + + //Drawing the enable/disable graph button + Buttons.graph_buttons(this.graph1_button_y, this.graph1_button_w, this.graph1_button_h, '10px Arial', this); + + //draw permanent window button + Buttons.perm_window_button(this.button_x, this.perm_button_y, this.button_w, this.button_h, '10px Arial', this); + + //draw clear point button + Buttons.clear_point_button(this.button_x, this.clear_point_button_y, this.button_w, this.button_h, '10px Arial', this); + + // Draw log scale buttons + Buttons.log_scale_buttons(this.button_x, this.xlog_button_y, this.ylog_button_y, this.button_w, this.button_h, + "10px Arial", this); + + // Draw Heatmap button + Buttons.heatmap_button(this.button_x, this.heatmap_button_y, this.button_w, this.button_h, "10px Arial", this); + } + + draw_from_context(hidden: any) { + return + } } /** A class thtat inherits from PlotData and is specific for drawing ParallelPlots */ export class ParallelPlot extends PlotData { - constructor(public data, public width, public height, public buttons_ON, X, Y, public canvas_id: string, - public is_in_multiplot = false) { - super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); - if (!is_in_multiplot) { - var requirement = '0.6.1'; - check_package_version(data['package_version'], requirement); - } - this.type_ = 'parallelplot'; - if (this.buttons_ON) { - this.disp_x = this.width - 35; - this.disp_y = this.height - 25; - this.disp_w = 30; - this.disp_h = 20; - } - let default_edge_style = {color_stroke:string_to_rgb('black'), dashline:[], line_width:0.5, name:''}; - let default_dict_ = {edge_style:default_edge_style, disposition: 'vertical', rgbs:[[192, 11, 11], [14, 192, 11], [11, 11, 192]]}; - data = set_default_values(data, default_dict_); - this.elements = data['elements']; - this.edge_style = EdgeStyle.deserialize(data['edge_style']); - var attribute_names = data['attribute_names']; - if (data['disposition'] == 'vertical') { - this.vertical = true; - } else if (data['disposition'] == 'horizontal') { - this.vertical = false; - } else { - throw new Error('Axis disposition must be vertical or horizontal'); - } - this.initialize_all_attributes(); - this.initialize_attributes_list(); - this.add_to_axis_list(attribute_names); - this.initialize_data_lists(); - var nb_axis = this.axis_list.length; - if (nb_axis<=1) {throw new Error('At least 2 axis are required')}; - this.refresh_to_display_list(this.elements); - this.refresh_all_attributes(); - this.refresh_attribute_booleans(); - this.refresh_axis_bounds(nb_axis); - this.refresh_axis_coords(); - this.isParallelPlot = true; - this.rgbs = data['rgbs']; - this.interpolation_colors = get_interpolation_colors(this.rgbs, this.to_display_list.length); - this.initialize_hexs(); - this.initialize_display_list_to_elements_dict(); - this.refresh_pp_selected(); - } - - refresh_pp_buttons_coords() { + constructor(public data, public width, public height, public buttons_ON, X, Y, public canvas_id: string, + public is_in_multiplot = false) { + super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); + if (!is_in_multiplot) { + var requirement = '0.6.1'; + check_package_version(data['package_version'], requirement); + } + this.type_ = 'parallelplot'; + if (this.buttons_ON) { this.disp_x = this.width - 35; this.disp_y = this.height - 25; this.disp_w = 30; this.disp_h = 20; } + let default_edge_style = { color_stroke: string_to_rgb('black'), dashline: [], line_width: 0.5, name: '' }; + let default_dict_ = { edge_style: default_edge_style, disposition: 'vertical', rgbs: [[192, 11, 11], [14, 192, 11], [11, 11, 192]] }; + data = set_default_values(data, default_dict_); + this.elements = data['elements']; + this.edge_style = EdgeStyle.deserialize(data['edge_style']); + var attribute_names = data['attribute_names']; + if (data['disposition'] == 'vertical') { + this.vertical = true; + } else if (data['disposition'] == 'horizontal') { + this.vertical = false; + } else { + throw new Error('Axis disposition must be vertical or horizontal'); + } + this.initialize_all_attributes(); + this.initialize_attributes_list(); + this.add_to_axis_list(attribute_names); + this.initialize_data_lists(); + var nb_axis = this.axis_list.length; + if (nb_axis <= 1) { throw new Error('At least 2 axis are required') }; + this.refresh_to_display_list(this.elements); + this.refresh_all_attributes(); + this.refresh_attribute_booleans(); + this.refresh_axis_bounds(nb_axis); + this.refresh_axis_coords(); + this.isParallelPlot = true; + this.rgbs = data['rgbs']; + this.interpolation_colors = get_interpolation_colors(this.rgbs, this.to_display_list.length); + this.initialize_hexs(); + this.initialize_display_list_to_elements_dict(); + this.refresh_pp_selected(); + } - initialize_display_list_to_elements_dict() { - this.display_list_to_elements_dict = {}; - for (let i=0; i max) { + max = elt; + } + } + this.all_attributes[i]['list'] = [min, max]; + } else { //ie string + var list = []; + for (var j = 0; j < this.elements.length; j++) { + if (type_ == 'color') { + var elt: any = rgb_to_string(this.elements[j][attribute_name]); + } else { var elt = this.elements[j][attribute_name]; - if (eltmax) { - max = elt; - } } - this.all_attributes[i]['list'] = [min, max]; - } else { //ie string - var list = []; - for (var j=0; j { - this.hexs.push(rgb_to_hex(rgb)); - }); - } + initialize_hexs() { + this.hexs = []; + this.interpolation_colors.forEach(rgb => { + this.hexs.push(rgb_to_hex(rgb)); + }); + } } /** A class that inherits from PlotData and is specific for drawing PrimitiveGroupContainers. */ export class PrimitiveGroupContainer extends PlotData { - primitive_groups:PlotContour[]=[]; - layout_mode:string='regular'; - layout_axis:Axis; - layout_attributes:Attribute[]=[]; - selected_primitive:number=-1; - custom_sizes:boolean=false; - - constructor(public data:any, - public width: number, - public height: number, - public buttons_ON: boolean, - public X: number, - public Y: number, - public canvas_id: string, - public is_in_multiplot: boolean = false) { - super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); - if (!is_in_multiplot) { - var requirement = '0.6.0'; - check_package_version(data['package_version'], requirement); - } - this.type_ = 'primitivegroupcontainer'; - var serialized = data['primitive_groups']; - var initial_coords = data['coords'] || Array(serialized.length).fill([0,0]); - if (data['sizes']) { - this.custom_sizes = true; - } - var initial_sizes = data['sizes'] || Array(serialized.length).fill([560, 300]); - for (let i=0; i= 1) this.reset_scales(); } } + } - click_on_manipulation_action() { - this.manipulation_bool = !this.manipulation_bool; - for (let i=0; i= 1) this.reset_scales(); - } + for (let i = 0; i < nb_primitives; i++) { + let center_x = this.primitive_groups[i].X + this.primitive_groups[i].width / 2; + let center_y = this.primitive_groups[i].Y + this.primitive_groups[i].height / 2; + if (this.custom_sizes) { + this.primitive_groups[i].width = Math.min(this.data['sizes'][i][0], this.width); + this.primitive_groups[i].height = Math.min(this.data['sizes'][i][1], this.height); + } else { + this.primitive_groups[i].width = primitive_width; + this.primitive_groups[i].height = primitive_height; } + this.primitive_groups[i].X = center_x - this.primitive_groups[i].width / 2; + this.primitive_groups[i].Y = center_y - this.primitive_groups[i].height / 2; } + } - reset_sizes() { - var nb_primitives = this.primitive_groups.length; - if (!this.custom_sizes) { - if (nb_primitives === 1) { - var primitive_width = this.width/3; - var primitive_height = this.height/3; - } else { - primitive_width = this.width/nb_primitives; - primitive_height = this.height/nb_primitives; - } - } - for (let i=0; i= 2) { - this.refresh_MinMax(); - for (let i=0; i<10; i++) { - this.refresh_spacing(); - } - } - else if (this.primitive_groups.length === 1) Interactions.click_on_reset_action(this.primitive_groups[0]); + reset_scales() { + this.reset_sizes(); + if (this.primitive_groups.length >= 2) { this.refresh_MinMax(); - this.translate_inside_canvas(); + for (let i = 0; i < 10; i++) { + this.refresh_spacing(); + } } + else if (this.primitive_groups.length === 1) Interactions.click_on_reset_action(this.primitive_groups[0]); + this.refresh_MinMax(); + this.translate_inside_canvas(); + } - draw_initial() { - for (let i=0; i 100 && this.height > 100) { + this.draw_layout_axis(); + if (this.layout_mode !== 'regular') { + this.draw_coordinate_lines(); } - this.context = this.context_show; - this.context.save(); - this.draw_empty_canvas(this.context); - if (this.settings_on) {this.draw_settings_rect();} else {this.draw_rect();} - this.context.clip(this.context.rect(this.X-1, this.Y-1, this.width+2, this.height+2)); - if (this.width > 100 && this.height > 100) { - this.draw_layout_axis(); - if (this.layout_mode !== 'regular') { - this.draw_coordinate_lines(); - } - for (let index of this.display_order) { - if (this.primitive_groups[index].selected) { - this.primitive_groups[index].draw(); - } + for (let index of this.display_order) { + if (this.primitive_groups[index].selected) { + this.primitive_groups[index].draw(); } } + } - if (this.multiplot_manipulation) { - this.draw_manipulable_rect(); - } else { - this.context.strokeStyle = this.initial_rect_color_stroke; - this.context.lineWidth = this.initial_rect_line_width; - this.context.strokeRect(this.X, this.Y, this.width, this.height); - } - if (this.buttons_ON) { this.draw_buttons(); } - this.context.restore(); + if (this.multiplot_manipulation) { + this.draw_manipulable_rect(); + } else { + this.context.strokeStyle = this.initial_rect_color_stroke; + this.context.lineWidth = this.initial_rect_line_width; + this.context.strokeRect(this.X, this.Y, this.width, this.height); } + if (this.buttons_ON) { this.draw_buttons(); } + this.context.restore(); + } - draw_from_context(hidden) {} + draw_from_context(hidden) { } - reset_selection() { - for (let i=0; i [val[1], val[0]])); - for (let i=0; i [val[1], val[0]])); + for (let i = 0; i < this.primitive_groups.length; i++) { + let index = Number(reverse_primitive_dict[i]); + this.primitive_groups[i].selected = List.is_include(index, this.selected_point_index); } + } - redraw_object() { - this.store_datas(); - this.draw_empty_canvas(this.context); - for (let display_index of this.display_order) { - let obj = this.primitive_groups[display_index]; - if (display_index == this.clickedPlotIndex) { - this.primitive_groups[display_index].draw(); - } else { - this.context_show.putImageData(this.shown_datas[display_index], obj.X, obj.Y); - this.context_hidden.putImageData(this.hidden_datas[display_index], obj.X, obj.Y); - } + redraw_object() { + this.store_datas(); + this.draw_empty_canvas(this.context); + for (let display_index of this.display_order) { + let obj = this.primitive_groups[display_index]; + if (display_index == this.clickedPlotIndex) { + this.primitive_groups[display_index].draw(); + } else { + this.context_show.putImageData(this.shown_datas[display_index], obj.X, obj.Y); + this.context_hidden.putImageData(this.hidden_datas[display_index], obj.X, obj.Y); } - if (this.buttons_ON) { this.draw_buttons(); } } + if (this.buttons_ON) { this.draw_buttons(); } + } - store_datas() { - this.shown_datas = []; this.hidden_datas = []; - for (let i=0; i primitive_index) { - this.primitive_dict[key]--; - } + remove_primitive_group(point_index) { + var primitive_index = this.primitive_dict[point_index.toString()]; + this.primitive_groups = List.remove_at_index(primitive_index, this.primitive_groups); + this.display_order = List.remove_element(primitive_index, this.display_order); + this.primitive_dict = MyObject.removeEntries([point_index.toString()], this.primitive_dict); + this.elements_dict = MyObject.removeEntries([primitive_index.toString()], this.elements_dict); + var keys = Object.keys(this.primitive_dict); + for (let key of keys) { + if (this.primitive_dict[key] > primitive_index) { + this.primitive_dict[key]--; } - for (let i=0; i primitive_index) { - this.display_order[i]--; - } + } + for (let i = 0; i < this.display_order.length; i++) { + if (this.display_order[i] > primitive_index) { + this.display_order[i]--; } - var elements_entries = Object.entries(this.elements_dict); - for (let i=0; i primitive_index) { - elements_entries[i][0] = (Number(elements_entries[i][0]) - 1).toString(); - } + } + var elements_entries = Object.entries(this.elements_dict); + for (let i = 0; i < elements_entries.length; i++) { + if (Number(elements_entries[i][0]) > primitive_index) { + elements_entries[i][0] = (Number(elements_entries[i][0]) - 1).toString(); } - this.elements_dict = Object.fromEntries(elements_entries); - this.reset_action(); - this.draw(); } + this.elements_dict = Object.fromEntries(elements_entries); + this.reset_action(); + this.draw(); + } - setAllInteractionsToOff() { - for (let i=0; imax) { - max = elt; - } - if (elt max) { + max = elt; } - return [min, max]; - } else if (type_ == 'color') { - var list = [] - for (let j=0; j= 1) this.reset_scales(); - this.resetAllObjects(); - this.draw(); + return value; } + } - refresh_two_axis_layout_list(attributes:Attribute[]) { - this.layout_mode = 'two_axis'; - if (!this.is_element_dict_empty()) { - attributes[0].list = this.initialize_list(attributes[0]); - attributes[1].list = this.initialize_list(attributes[1]); - } - this.layout_attributes = attributes; - } + is_element_dict_empty() { + return Object.keys(this.elements_dict).length === 0; + } - multiplot_two_axis_layout(attributes:Attribute[]) { - this.refresh_two_axis_layout_list(attributes); - this.two_axis_layout(); + multiplot_one_axis_layout(attribute: Attribute) { + this.refresh_one_axis_layout_list(attribute); + this.one_axis_layout(); + } + + refresh_one_axis_layout_list(attribute: Attribute) { + this.layout_mode = 'one_axis'; + if (!this.is_element_dict_empty()) { + attribute.list = this.initialize_list(attribute); } + this.layout_attributes = [attribute]; + } - two_axis_layout() { - var graduation_style = new TextStyle(string_to_rgb('grey'), 12, 'sans-serif', 'center', 'alphabetic'); - var axis_style = new EdgeStyle(0.5, string_to_rgb('lightgrey'), [], ''); - var serialized_axis = {graduation_style: graduation_style, axis_style: axis_style, grid_on: false}; - this.layout_axis = Axis.deserialize(serialized_axis); - var nb_primitive_groups = this.primitive_groups.length; - this.scaleX = 1; this.scaleY = 1; - this.originX = 0; this.originY = 0; - for (let i=0; i= 2) this.reset_scales(); - this.resetAllObjects(); - this.draw(); + var center_x = this.scaleX * real_x + this.originX; + this.primitive_groups[i].X = this.X + center_x - this.primitive_groups[i].width / 2; + this.primitive_groups[i].Y = this.Y + this.height / 2 - this.primitive_groups[i].height / 2; + if (type_ !== 'float') this.primitive_groups[i].Y += y_incs[i]; } + if (this.primitive_groups.length >= 1) this.reset_scales(); + this.resetAllObjects(); + this.draw(); + } - translatePrimitive(index, tx, ty) { - this.primitive_groups[index].X = this.primitive_groups[index].X + tx; - this.primitive_groups[index].Y = this.primitive_groups[index].Y + ty; + refresh_two_axis_layout_list(attributes: Attribute[]) { + this.layout_mode = 'two_axis'; + if (!this.is_element_dict_empty()) { + attributes[0].list = this.initialize_list(attributes[0]); + attributes[1].list = this.initialize_list(attributes[1]); } + this.layout_attributes = attributes; + } - translateAllPrimitives(tx, ty) { - for (let i=0; i= 2) this.reset_scales(); + this.resetAllObjects(); + this.draw(); + } - regular_zoom_elements(mouse3X, mouse3Y, event) { - if (event > 0) { - var zoom_coeff = 1.1; - } else { - var zoom_coeff = 1/1.1; - } - for (let i=0; i 0) { - var zoom_coeff = 1.1; - } else { - zoom_coeff = 1/1.1; - } - var container_center_x = this.X + this.width/2; - for (let i=0; i 0) { - var zoom_coeff = 1.1; - } else { - zoom_coeff = 1/1.1; - } - var container_center_y = this.Y + this.height/2; - for (let i=0; i 0) { + var zoom_coeff = 1.1; + } else { + var zoom_coeff = 1 / 1.1; + } + for (let i = 0; i < this.primitive_groups.length; i++) { + this.primitive_groups[i].X = mouse3X + zoom_coeff * (this.primitive_groups[i].X - mouse3X); + this.primitive_groups[i].Y = mouse3Y + zoom_coeff * (this.primitive_groups[i].Y - mouse3Y); + this.primitive_groups[i].width = this.primitive_groups[i].width * zoom_coeff; + this.primitive_groups[i].height = this.primitive_groups[i].height * zoom_coeff; + } + this.resetAllObjects(); + this.scaleX = this.scaleX * zoom_coeff; this.scaleY = this.scaleY * zoom_coeff; + this.originX = mouse3X - this.X + zoom_coeff * (this.originX - mouse3X + this.X); + this.originY = mouse3Y - this.Y + zoom_coeff * (this.originY - mouse3Y + this.Y); + this.draw(); + } + + x_zoom_elements(event) { + if (event > 0) { + var zoom_coeff = 1.1; + } else { + zoom_coeff = 1 / 1.1; + } + var container_center_x = this.X + this.width / 2; + for (let i = 0; i < this.primitive_groups.length; i++) { + let primitive_center_x = this.primitive_groups[i].X + this.primitive_groups[i].width / 2; + primitive_center_x = container_center_x + zoom_coeff * (primitive_center_x - container_center_x); + this.primitive_groups[i].X = primitive_center_x - this.primitive_groups[i].width / 2; + } + this.resetAllObjects(); + this.scaleX = this.scaleX * zoom_coeff; + this.originX = this.width / 2 + zoom_coeff * (this.originX - this.width / 2); + this.draw(); + } + + y_zoom_elements(event) { + if (event > 0) { + var zoom_coeff = 1.1; + } else { + zoom_coeff = 1 / 1.1; + } + var container_center_y = this.Y + this.height / 2; + for (let i = 0; i < this.primitive_groups.length; i++) { + let primitive_center_y = this.primitive_groups[i].Y + this.primitive_groups[i].height / 2; + primitive_center_y = container_center_y + zoom_coeff * (primitive_center_y - container_center_y); + this.primitive_groups[i].Y = primitive_center_y - this.primitive_groups[i].height / 2; + } + this.resetAllObjects(); + this.scaleY = this.scaleY * zoom_coeff; + this.originY = this.height / 2 + zoom_coeff * (this.originY - this.height / 2); + this.draw(); + } + + manage_scroll(mouse3X, mouse3Y, event) { + if ((this.layout_mode !== 'regular') && (Shape.isInRect(mouse3X, mouse3Y, this.X + this.decalage_axis_x, + this.height - this.decalage_axis_y + this.Y, this.width - this.decalage_axis_x, this.height - this.decalage_axis_y))) { + this.scroll_x = this.scroll_x + event; + } else if ((this.layout_mode == 'two_axis') && (Shape.isInRect(mouse3X, mouse3Y, this.X, this.Y, this.decalage_axis_x, + this.height - this.decalage_axis_y))) { + this.scroll_y = this.scroll_y + event; + } else { + this.scroll_x = this.scroll_x + event; + this.scroll_y = this.scroll_y + event; + } + if (isNaN(this.scroll_x)) this.scroll_x = 0; + if (isNaN(this.scroll_y)) this.scroll_y = 0; + } + + delete_unwanted_vertex(vertex_infos) { + var i = 0; + while (i < vertex_infos.length) { + let to_delete = false; + if (this.clickedPlotIndex != vertex_infos[i].index) { + let j = 0; + let cpi_vertex = false; + while (j < vertex_infos.length) { + if ((vertex_infos[j].index == this.clickedPlotIndex)) { + cpi_vertex = true; + break; } - to_delete = !cpi_vertex; - } - if (to_delete) { - vertex_infos = List.remove_at_index(i, vertex_infos); - } else { - i++; + j++; } + to_delete = !cpi_vertex; } - return vertex_infos; - } - - initialize_clickOnVertex(mouse1X, mouse1Y):[boolean, Object] { - var thickness = 15; - var vertex_infos = []; - for (let i=0; i heightSizeLimit) { - this.primitive_groups[vertex_object_index].Y = this.primitive_groups[vertex_object_index].Y + deltaY; - this.primitive_groups[vertex_object_index].height = this.primitive_groups[vertex_object_index].height - deltaY; - } else { - this.primitive_groups[vertex_object_index].height = heightSizeLimit; - } + initialize_clickOnVertex(mouse1X, mouse1Y): [boolean, Object] { + var thickness = 15; + var vertex_infos = []; + for (let i = 0; i < this.primitive_groups.length; i++) { + let obj: PlotData = this.primitive_groups[this.display_order[i]]; + let up = Shape.isInRect(mouse1X, mouse1Y, obj.X - thickness * 1 / 3, obj.Y - thickness * 1 / 3, obj.width + thickness * 2 / 3, thickness); + let down = Shape.isInRect(mouse1X, mouse1Y, obj.X - thickness * 1 / 3, obj.Y + obj.height - thickness * 2 / 3, obj.width + thickness * 2 / 3, thickness); + let left = Shape.isInRect(mouse1X, mouse1Y, obj.X - thickness * 1 / 3, obj.Y - thickness * 1 / 3, thickness, obj.height + thickness * 2 / 3); + let right = Shape.isInRect(mouse1X, mouse1Y, obj.X + obj.width - thickness * 2 / 3, obj.Y - thickness * 1 / 3, thickness, obj.height + thickness * 2 / 3); + var clickOnVertex_i = up || down || left || right; + if (clickOnVertex_i) { + vertex_infos.push({ 'index': this.display_order[i], 'up': up, 'down': down, 'left': left, 'right': right }); + } + } + vertex_infos = this.delete_unwanted_vertex(vertex_infos); + var clickOnVertex = !(vertex_infos.length == 0); + return [clickOnVertex, vertex_infos]; + } + + resizeObject(vertex_infos, deltaX, deltaY): void { + var widthSizeLimit = 100; + var heightSizeLimit = 100; + for (let i = 0; i < vertex_infos.length; i++) { + let vertex_object_index = vertex_infos[i].index; + if (vertex_infos[i].up === true) { + if (this.primitive_groups[vertex_object_index].height - deltaY > heightSizeLimit) { + this.primitive_groups[vertex_object_index].Y = this.primitive_groups[vertex_object_index].Y + deltaY; + this.primitive_groups[vertex_object_index].height = this.primitive_groups[vertex_object_index].height - deltaY; + } else { + this.primitive_groups[vertex_object_index].height = heightSizeLimit; } - if (vertex_infos[i].down === true) { - if (this.primitive_groups[vertex_object_index].height + deltaY > heightSizeLimit) { - this.primitive_groups[vertex_object_index].height = this.primitive_groups[vertex_object_index].height + deltaY; - } else { - this.primitive_groups[vertex_object_index].height = heightSizeLimit; - } + } + if (vertex_infos[i].down === true) { + if (this.primitive_groups[vertex_object_index].height + deltaY > heightSizeLimit) { + this.primitive_groups[vertex_object_index].height = this.primitive_groups[vertex_object_index].height + deltaY; + } else { + this.primitive_groups[vertex_object_index].height = heightSizeLimit; } - if (vertex_infos[i].left === true) { - if (this.primitive_groups[vertex_object_index].width - deltaX > widthSizeLimit) { - this.primitive_groups[vertex_object_index].X = this.primitive_groups[vertex_object_index].X + deltaX; - this.primitive_groups[vertex_object_index].width = this.primitive_groups[vertex_object_index].width - deltaX; - } else { - this.primitive_groups[vertex_object_index].width = widthSizeLimit; - } + } + if (vertex_infos[i].left === true) { + if (this.primitive_groups[vertex_object_index].width - deltaX > widthSizeLimit) { + this.primitive_groups[vertex_object_index].X = this.primitive_groups[vertex_object_index].X + deltaX; + this.primitive_groups[vertex_object_index].width = this.primitive_groups[vertex_object_index].width - deltaX; + } else { + this.primitive_groups[vertex_object_index].width = widthSizeLimit; } - if (vertex_infos[i].right === true) { - if (this.primitive_groups[vertex_object_index].width + deltaX > widthSizeLimit) { - this.primitive_groups[vertex_object_index].width = this.primitive_groups[vertex_object_index].width + deltaX; - } else { - this.primitive_groups[vertex_object_index].width = widthSizeLimit; - } + } + if (vertex_infos[i].right === true) { + if (this.primitive_groups[vertex_object_index].width + deltaX > widthSizeLimit) { + this.primitive_groups[vertex_object_index].width = this.primitive_groups[vertex_object_index].width + deltaX; + } else { + this.primitive_groups[vertex_object_index].width = widthSizeLimit; } } - this.draw(); } + this.draw(); + } - reorder_resize_style(resize_style) { - var resize_dict = ['n', 'ns', 'ne', 'nwse', 'nw', 'e', 'ew', 's', 'se', 'sw', 'w']; - for (let i=0; i { + isDrawing = true; + if (this.interaction_ON) { + mouse1X = e.offsetX; mouse1Y = e.offsetY; + this.click_on_button_check(mouse1X, mouse1Y); + this.clickedPlotIndex = this.get_selected_primitive(mouse1X, mouse1Y); + if (this.manipulation_bool) { + if (this.clickedPlotIndex != -1) { + [clickOnVertex, vertex_infos] = this.initialize_clickOnVertex(mouse1X, mouse1Y); + } else { + clickOnVertex = false; + } + } } - this.setAllInteractionsToOff(); + }); - canvas.addEventListener('mousedown', e => { - isDrawing = true; - if (this.interaction_ON) { - mouse1X = e.offsetX; mouse1Y = e.offsetY; - this.click_on_button_check(mouse1X, mouse1Y); - this.clickedPlotIndex = this.get_selected_primitive(mouse1X, mouse1Y); - if (this.manipulation_bool) { - if (this.clickedPlotIndex != -1) { - [clickOnVertex, vertex_infos] = this.initialize_clickOnVertex(mouse1X, mouse1Y); + canvas.addEventListener('mousemove', e => { + if (this.interaction_ON) { + var old_mouse2X = mouse2X; var old_mouse2Y = mouse2Y; + mouse2X = e.offsetX, mouse2Y = e.offsetY; + this.selected_primitive = this.get_selected_primitive(mouse2X, mouse2Y); + if (this.manipulation_bool) { + if (isDrawing) { + if ((this.clickedPlotIndex == -1) || (this.layout_mode !== 'regular')) { + this.translateAllPrimitives(mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); + this.draw(); } else { - clickOnVertex = false; - } - } - } - }); - - canvas.addEventListener('mousemove', e => { - if (this.interaction_ON) { - var old_mouse2X = mouse2X; var old_mouse2Y = mouse2Y; - mouse2X = e.offsetX, mouse2Y = e.offsetY; - this.selected_primitive = this.get_selected_primitive(mouse2X, mouse2Y); - if (this.manipulation_bool) { - if (isDrawing) { - if ((this.clickedPlotIndex == -1) || (this.layout_mode !== 'regular')) { - this.translateAllPrimitives(mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); - this.draw(); + if (clickOnVertex) { + this.resizeObject(vertex_infos, mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); } else { - if (clickOnVertex) { - this.resizeObject(vertex_infos, mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); - } else { - this.translatePrimitive(this.clickedPlotIndex, mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); - this.draw(); - } - } - } else { - if (this.layout_mode === 'regular') { - this.setCursorStyle(mouse2X, mouse2Y, canvas, this.selected_primitive); + this.translatePrimitive(this.clickedPlotIndex, mouse2X - old_mouse2X, mouse2Y - old_mouse2Y); + this.draw(); } } } else { - if (this.selected_primitive !== last_selected_primitive) { - this.manage_mouse_interactions(this.selected_primitive); + if (this.layout_mode === 'regular') { + this.setCursorStyle(mouse2X, mouse2Y, canvas, this.selected_primitive); } - last_selected_primitive = this.selected_primitive; } } else { - isDrawing = false; + if (this.selected_primitive !== last_selected_primitive) { + this.manage_mouse_interactions(this.selected_primitive); + } + last_selected_primitive = this.selected_primitive; } - }); + } else { + isDrawing = false; + } + }); - canvas.addEventListener('mouseup', e => { - if (this.interaction_ON) { - isDrawing = false; - this.draw(); - } - }); - - canvas.addEventListener('wheel', e => { - if (this.interaction_ON) { - e.preventDefault(); - mouse3X = e.offsetX; mouse3Y = e.offsetY; - if (this.manipulation_bool) { - var event = -e.deltaY/Math.abs(e.deltaY); - this.manage_scroll(mouse3X, mouse3Y, event); - this.zoom_elements(mouse3X, mouse3Y, event); - } + canvas.addEventListener('mouseup', e => { + if (this.interaction_ON) { + isDrawing = false; + this.draw(); + } + }); + + canvas.addEventListener('wheel', e => { + if (this.interaction_ON) { + e.preventDefault(); + mouse3X = e.offsetX; mouse3Y = e.offsetY; + if (this.manipulation_bool) { + var event = -e.deltaY / Math.abs(e.deltaY); + this.manage_scroll(mouse3X, mouse3Y, event); + this.zoom_elements(mouse3X, mouse3Y, event); } - }); + } + }); - canvas.addEventListener('mouseleave', e => { - this.clickedPlotIndex = -1; - this.selected_primitive = -1; - this.setAllInteractionsToOff(); - }) - } + canvas.addEventListener('mouseleave', e => { + this.clickedPlotIndex = -1; + this.selected_primitive = -1; + this.setAllInteractionsToOff(); + }) + } } export class Histogram extends PlotData { - edge_style: EdgeStyle; - surface_style: SurfaceStyle; - x_variable: Attribute; - axis: Axis; - graduation_nb: number; - initial_graduation_nb: number; - infos = {}; - min_abs: number = 0; - max_abs: number = 0; - max_frequency: number = 0; - discrete: boolean = false; - x_rubberband:number[]=[]; - y_rubberband:number[]=[]; - coeff:number=0.88; - y_step: number = 0; - selected_keys = []; - - constructor(public data:any, - public width: number, - public height: number, - public buttons_ON: boolean, - public X: number, - public Y: number, - public canvas_id: string, - public is_in_multiplot: boolean = false) { - super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); - if (!is_in_multiplot) { - var requirement = '0.6.1'; - check_package_version(data['package_version'], requirement); - } - this.type_ = "histogram"; - this.elements = data['elements']; - this.graduation_nb = data['graduation_nb'] || 6; - this.initial_graduation_nb = this.graduation_nb; - let name = data['x_variable']; - let type_ = TypeOf(this.elements[0][name]); - - this.discrete = false; - if (type_ !== 'float') this.discrete = true; - - this.x_variable = new Attribute(name, type_); - const axis = data['axis'] || {grid_on: false}; - this.axis = Axis.deserialize(axis); - let temp_surface_style = data['surface_style'] || {}; - temp_surface_style = set_default_values(temp_surface_style, {color_fill: string_to_rgb('lightblue'), opacity: 1}); - this.surface_style = SurfaceStyle.deserialize(temp_surface_style); - this.edge_style = EdgeStyle.deserialize(data['edge_style']); - if (type_ === 'float') { - for (let element of this.elements) { - this.minX = Math.min(this.minX, element[name]); - this.maxX = Math.max(this.maxX, element[name]); - } - this.x_variable.list = [this.minX, this.maxX]; - } else { - let list = []; - for (let element of this.elements) { - let graduation = element[name]; - if (type_ === 'color') graduation = color_to_string(graduation); - if (!list.includes(graduation)) list.push(graduation); - } - this.minX = 0; - this.maxX = list.length; - this.min_abs = this.minX; - this.max_abs = this.maxX; - this.x_variable.list = list; - } - this.infos = this.get_infos(); - this.refresh_max_frequency(); - if (buttons_ON) this.refresh_buttons_coords(); - } + edge_style: EdgeStyle; + surface_style: SurfaceStyle; + x_variable: Attribute; + axis: Axis; + graduation_nb: number; + initial_graduation_nb: number; + infos = {}; + min_abs: number = 0; + max_abs: number = 0; + max_frequency: number = 0; + discrete: boolean = false; + x_rubberband: number[] = []; + y_rubberband: number[] = []; + coeff: number = 0.88; + y_step: number = 0; + selected_keys = []; + + constructor(public data: any, + public width: number, + public height: number, + public buttons_ON: boolean, + public X: number, + public Y: number, + public canvas_id: string, + public is_in_multiplot: boolean = false) { + super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); + if (!is_in_multiplot) { + var requirement = '0.6.1'; + check_package_version(data['package_version'], requirement); + } + this.type_ = "histogram"; + this.elements = data['elements']; + this.graduation_nb = data['graduation_nb'] || 6; + this.initial_graduation_nb = this.graduation_nb; + let name = data['x_variable']; + let type_ = TypeOf(this.elements[0][name]); + + this.discrete = false; + if (type_ !== 'float') this.discrete = true; + + this.x_variable = new Attribute(name, type_); + const axis = data['axis'] || { grid_on: false }; + this.axis = Axis.deserialize(axis); + let temp_surface_style = data['surface_style'] || {}; + temp_surface_style = set_default_values(temp_surface_style, { color_fill: string_to_rgb('lightblue'), opacity: 1 }); + this.surface_style = SurfaceStyle.deserialize(temp_surface_style); + this.edge_style = EdgeStyle.deserialize(data['edge_style']); + if (type_ === 'float') { + for (let element of this.elements) { + this.minX = Math.min(this.minX, element[name]); + this.maxX = Math.max(this.maxX, element[name]); + } + this.x_variable.list = [this.minX, this.maxX]; + } else { + let list = []; + for (let element of this.elements) { + let graduation = element[name]; + if (type_ === 'color') graduation = color_to_string(graduation); + if (!list.includes(graduation)) list.push(graduation); + } + this.minX = 0; + this.maxX = list.length; + this.min_abs = this.minX; + this.max_abs = this.maxX; + this.x_variable.list = list; + } + this.infos = this.get_infos(); + this.refresh_max_frequency(); + if (buttons_ON) this.refresh_buttons_coords(); + } - draw_initial() { - this.reset_scales(); - this.draw(); - } + draw_initial() { + this.reset_scales(); + this.draw(); + } - draw() { - this.refresh_graduation_nb(); - this.context = this.context_show; - this.context.save(); - this.draw_empty_canvas(this.context); - if (this.settings_on) {this.draw_settings_rect();} else {this.draw_rect();} - this.context.beginPath(); - this.context.rect(this.X-1, this.Y-1, this.width+2, this.height + 2); - this.context.clip(); - this.context.closePath(); + draw() { + this.refresh_graduation_nb(); + this.context = this.context_show; + this.context.save(); + this.draw_empty_canvas(this.context); + if (this.settings_on) { this.draw_settings_rect(); } else { this.draw_rect(); } + this.context.beginPath(); + this.context.rect(this.X - 1, this.Y - 1, this.width + 2, this.height + 2); + this.context.clip(); + this.context.closePath(); + + this.infos = this.get_infos(); + this.draw_histogram(); + this.draw_rubberbands(); + this.draw_axis(); + + if ((this.buttons_ON) && (this.button_w > 20) && (this.button_h > 10)) { + this.refresh_buttons_coords(); + this.draw_buttons(); + } - this.infos = this.get_infos(); - this.draw_histogram(); - this.draw_rubberbands(); - this.draw_axis(); + if (this.multiplot_manipulation) { + this.draw_manipulable_rect(); + } + this.context.restore(); + }; - if ((this.buttons_ON) && (this.button_w > 20) && (this.button_h > 10)) { - this.refresh_buttons_coords(); - this.draw_buttons(); - } - if (this.multiplot_manipulation) { - this.draw_manipulable_rect(); - } - this.context.restore(); - }; + draw_from_context(hidden) { }; - draw_from_context(hidden) {}; + draw_buttons() { + this.reset_rect_y = 10; + Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); + } - draw_buttons() { - this.reset_rect_y = 10; - Buttons.reset_button(this.button_x, this.reset_rect_y, this.button_w, this.button_h, this); + draw_rubberbands() { + this.context.beginPath(); + if (this.x_rubberband.length !== 0) { + this.draw_x_rubberband(); } + if (this.y_rubberband.length !== 0) { + this.draw_y_rubberband(); + } + this.context.closePath(); + } - draw_rubberbands() { - this.context.beginPath(); - if (this.x_rubberband.length !== 0) { - this.draw_x_rubberband(); - } - if (this.y_rubberband.length !== 0) { - this.draw_y_rubberband(); - } - this.context.closePath(); - } + draw_x_rubberband() { + let grad_beg_y = this.height - this.decalage_axis_y; + let h = 20; + let x1d = this.real_to_display(this.x_rubberband[0], 'x'); + let x2d = this.real_to_display(this.x_rubberband[1], 'x'); + let w = x2d - x1d; + Shape.rect(x1d, grad_beg_y - h / 2 + this.Y, w, h, this.context, string_to_hex('lightrose'), + string_to_hex('lightgrey'), 0.5, 0.6); + } + draw_y_rubberband() { + let w = 20; + let y1d = this.real_to_display(this.y_rubberband[0], 'y'); + let y2d = this.real_to_display(this.y_rubberband[1], 'y'); + let h = y2d - y1d; + Shape.rect(this.decalage_axis_x - w / 2 + this.X, y1d, w, h, this.context, string_to_hex('lightrose'), + string_to_hex('lightgrey'), 0.5, 0.6); + } - draw_x_rubberband() { - let grad_beg_y = this.height - this.decalage_axis_y; - let h = 20; - let x1d = this.real_to_display(this.x_rubberband[0], 'x'); - let x2d = this.real_to_display(this.x_rubberband[1], 'x'); - let w = x2d - x1d; - Shape.rect(x1d, grad_beg_y - h/2 + this.Y, w, h, this.context, string_to_hex('lightrose'), - string_to_hex('lightgrey'), 0.5, 0.6); - } - draw_y_rubberband() { - let w = 20; - let y1d = this.real_to_display(this.y_rubberband[0], 'y'); - let y2d = this.real_to_display(this.y_rubberband[1], 'y'); - let h = y2d - y1d; - Shape.rect(this.decalage_axis_x - w/2 + this.X, y1d, w, h, this.context, string_to_hex('lightrose'), - string_to_hex('lightgrey'), 0.5, 0.6); + coordinate_to_string(x1, x2?) { + if (this.discrete) { + return this.x_variable.list[x1]; } + return x1.toString() + '_' + x2.toString(); + } - - coordinate_to_string(x1, x2?) { - if (this.discrete) { - return this.x_variable.list[x1]; - } - return x1.toString() + '_' + x2.toString(); + string_to_coordinate(str) { + if (this.discrete) { + return { x1: List.get_index_of_element(str, this.x_variable.list) }; } + let l = str.split('_'); + return { x1: Number(l[0]), x2: Number(l[1]) }; + } - string_to_coordinate(str) { - if (this.discrete) { - return {x1: List.get_index_of_element(str, this.x_variable.list)}; - } - let l = str.split('_'); - return {x1: Number(l[0]), x2: Number(l[1])}; + + refresh_max_frequency() { + let keys = Object.keys(this.infos); + for (let key of keys) { + this.max_frequency = Math.max(this.infos[key].length, this.max_frequency); } + } - refresh_max_frequency() { - let keys = Object.keys(this.infos); - for (let key of keys) { - this.max_frequency = Math.max(this.infos[key].length, this.max_frequency); - } - } - - - get_infos() { - let infos = {}, temp_infos = []; - if (this.x_variable.type_ === 'float') { - let min = this.x_variable.list[0], max = this.x_variable.list[1]; - let delta = max - min; - let d = Math.pow(10, Math.floor(Math.log10(delta))); - this.min_abs = Math.floor(min / d) * d; - this.max_abs = Math.ceil(max / d) * d; - let step; - if (this.graduation_nb !== 1) { - step = (this.max_abs - this.min_abs) / (this.graduation_nb - 1); - let current_x = this.min_abs; - while (current_x < this.max_abs) { - let next_x = current_x + step; - let selected_elts = []; - for (let i=0;i= current_x && nb < next_x) || (next_x === this.max_abs && nb === next_x)) { - selected_elts.push(i); - } + get_infos() { + let infos = {}, temp_infos = []; + if (this.x_variable.type_ === 'float') { + let min = this.x_variable.list[0], max = this.x_variable.list[1]; + let delta = max - min; + let d = Math.pow(10, Math.floor(Math.log10(delta))); + this.min_abs = Math.floor(min / d) * d; + this.max_abs = Math.ceil(max / d) * d; + let step; + if (this.graduation_nb !== 1) { + step = (this.max_abs - this.min_abs) / (this.graduation_nb - 1); + let current_x = this.min_abs; + while (current_x < this.max_abs) { + let next_x = current_x + step; + let selected_elts = []; + for (let i = 0; i < this.elements.length; i++) { + let nb = this.elements[i][this.x_variable.name]; + if ((nb >= current_x && nb < next_x) || (next_x === this.max_abs && nb === next_x)) { + selected_elts.push(i); } - let key = this.coordinate_to_string(current_x, next_x); - temp_infos.push([key, selected_elts]); - current_x = next_x; } - } else { - step = this.width / 3; - let key = this.coordinate_to_string(this.minX, this.maxX); - temp_infos = [[key, this.elements]]; + let key = this.coordinate_to_string(current_x, next_x); + temp_infos.push([key, selected_elts]); + current_x = next_x; } - infos = Object.fromEntries(temp_infos); } else { - for (let graduation of this.x_variable.list) { - temp_infos.push([graduation, []]); - } - infos = Object.fromEntries(temp_infos); - for (let i=0; i { - if (!this.interaction_ON) return; - isDrawing = true; - mouse_moving = false; - mouse1X = e.offsetX; - mouse1Y = e.offsetY; - click_on_x_axis = Shape.isInRect(mouse1X, mouse2Y, this.decalage_axis_x + this.X, this.height - this.decalage_axis_y - 10 + this.Y, - this.width - this.decalage_axis_x, 20); - click_on_y_axis = Shape.isInRect(mouse1X, mouse1Y, this.decalage_axis_x - 10 + this.X, this.Y, 20, this.height - this.decalage_axis_y); - let click_on_reset = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.reset_rect_y + this.Y, - this.button_w, this.button_h); + mouse_interaction() { + var mouse1X = 0, mouse1Y = 0, mouse2X = 0, mouse2Y = 0; + var isDrawing = false, mouse_moving = false; + var canvas = document.getElementById(this.canvas_id); + var click_on_x_axis = false; + var click_on_y_axis = false; + var click_on_x_band = false; + var click_on_y_band = false; + var up = false, down = false; + var left = false, right = false; + + canvas.addEventListener('mousedown', e => { + if (!this.interaction_ON) return; + isDrawing = true; + mouse_moving = false; + mouse1X = e.offsetX; + mouse1Y = e.offsetY; + click_on_x_axis = Shape.isInRect(mouse1X, mouse2Y, this.decalage_axis_x + this.X, this.height - this.decalage_axis_y - 10 + this.Y, + this.width - this.decalage_axis_x, 20); + click_on_y_axis = Shape.isInRect(mouse1X, mouse1Y, this.decalage_axis_x - 10 + this.X, this.Y, 20, this.height - this.decalage_axis_y); + let click_on_reset = Shape.isInRect(mouse1X, mouse1Y, this.button_x + this.X, this.reset_rect_y + this.Y, + this.button_w, this.button_h); + if (click_on_x_axis) { + [click_on_x_band, left, right] = this.is_in_x_rubberband(mouse1X, mouse1Y); + } + if (click_on_y_axis) { + [click_on_y_band, up, down] = this.is_in_y_rubberband(mouse1X, mouse1Y); + } + if (click_on_reset) this.reset_scales(); + }); + + canvas.addEventListener('mousemove', e => { + if (!this.interaction_ON) return; + let old_mouse2X = mouse2X, old_mouse2Y = mouse2Y; + mouse_moving = true; + mouse2X = e.offsetX; + mouse2Y = e.offsetY; + if (isDrawing) { if (click_on_x_axis) { - [click_on_x_band, left, right] = this.is_in_x_rubberband(mouse1X, mouse1Y); - } - if (click_on_y_axis) { - [click_on_y_band, up, down] = this.is_in_y_rubberband(mouse1X, mouse1Y); - } - if (click_on_reset) this.reset_scales(); - }); - - canvas.addEventListener('mousemove', e => { - if (!this.interaction_ON) return; - let old_mouse2X = mouse2X, old_mouse2Y = mouse2Y; - mouse_moving = true; - mouse2X = e.offsetX; - mouse2Y = e.offsetY; - if (isDrawing) { - if (click_on_x_axis) { - this.is_drawing_rubber_band = true; - if (click_on_x_band) { - let tx = this.display_to_real_length(mouse2X - old_mouse2X, 'x') - if (left || right) { - this.resize_rubberband(tx, false, false, left, right); - } else { - this.translate_rubberband(tx, 'x'); - } + this.is_drawing_rubber_band = true; + if (click_on_x_band) { + let tx = this.display_to_real_length(mouse2X - old_mouse2X, 'x') + if (left || right) { + this.resize_rubberband(tx, false, false, left, right); } else { - this.set_x_rubberband(mouse1X, mouse2X); + this.translate_rubberband(tx, 'x'); } - this.get_selected_keys(); - } else if (click_on_y_axis) { - if (click_on_y_band) { - let ty = this.display_to_real_length(mouse2Y - old_mouse2Y, 'y'); - if (up || down) { - this.resize_rubberband(ty, up, down, false, false); - } else { - this.translate_rubberband(ty, 'y'); - } + } else { + this.set_x_rubberband(mouse1X, mouse2X); + } + this.get_selected_keys(); + } else if (click_on_y_axis) { + if (click_on_y_band) { + let ty = this.display_to_real_length(mouse2Y - old_mouse2Y, 'y'); + if (up || down) { + this.resize_rubberband(ty, up, down, false, false); } else { - this.set_y_rubberband(mouse1Y, mouse2Y); + this.translate_rubberband(ty, 'y'); } - this.get_selected_keys(); } else { - this.originX += mouse2X - old_mouse2X; + this.set_y_rubberband(mouse1Y, mouse2Y); } + this.get_selected_keys(); + } else { + this.originX += mouse2X - old_mouse2X; } - this.draw(); - }); + } + this.draw(); + }); - canvas.addEventListener('mouseup', e => { - if (!this.interaction_ON) return; - if (mouse_moving) { - if (up || down || left || right) { - this.reorder_rubberbands(); - } - } else { - if (click_on_x_axis) { - this.reset_x_rubberband(); - } else if (click_on_y_axis) { - this.y_rubberband = []; - } - this.get_selected_keys(); + canvas.addEventListener('mouseup', e => { + if (!this.interaction_ON) return; + if (mouse_moving) { + if (up || down || left || right) { + this.reorder_rubberbands(); } - reset_parameters(); - this.is_drawing_rubber_band = false; - this.draw(); - }); + } else { + if (click_on_x_axis) { + this.reset_x_rubberband(); + } else if (click_on_y_axis) { + this.y_rubberband = []; + } + this.get_selected_keys(); + } + reset_parameters(); + this.is_drawing_rubber_band = false; + this.draw(); + }); - canvas.addEventListener('wheel', e => { - if (!this.interaction_ON) return; - e.preventDefault(); - let zoom_coeff; - let event = -Math.sign(e.deltaY); - if (event >= 0) zoom_coeff = 1.2; else zoom_coeff = 1/1.2; - this.scale = this.scale * zoom_coeff; - this.originX = mouse2X - this.X + zoom_coeff * (this.originX - mouse2X + this.X); - this.draw(); - }); + canvas.addEventListener('wheel', e => { + if (!this.interaction_ON) return; + e.preventDefault(); + let zoom_coeff; + let event = -Math.sign(e.deltaY); + if (event >= 0) zoom_coeff = 1.2; else zoom_coeff = 1 / 1.2; + this.scale = this.scale * zoom_coeff; + this.originX = mouse2X - this.X + zoom_coeff * (this.originX - mouse2X + this.X); + this.draw(); + }); - function reset_parameters() { - isDrawing = false; - mouse_moving = false; - click_on_x_axis = false; - click_on_y_axis = false; - click_on_x_band = false; - click_on_y_band = false; - up = false; - down = false; - left = false; - right = false; - } + function reset_parameters() { + isDrawing = false; + mouse_moving = false; + click_on_x_axis = false; + click_on_y_axis = false; + click_on_x_band = false; + click_on_y_band = false; + up = false; + down = false; + left = false; + right = false; } + } - set_x_rubberband(mouse1X, mouse2X) { - let x1 = this.display_to_real(mouse1X, 'x'); - let x2 = this.display_to_real(mouse2X, 'x'); - this.x_rubberband = [Math.min(x1, x2), Math.max(x1, x2)]; - } + set_x_rubberband(mouse1X, mouse2X) { + let x1 = this.display_to_real(mouse1X, 'x'); + let x2 = this.display_to_real(mouse2X, 'x'); + this.x_rubberband = [Math.min(x1, x2), Math.max(x1, x2)]; + } - set_y_rubberband(mouse1Y, mouse2Y) { - let y1 = this.display_to_real(mouse1Y, 'y'); - let y2 = this.display_to_real(mouse2Y, 'y'); - this.y_rubberband = [Math.min(y1, y2), Math.max(y1, y2)]; - } + set_y_rubberband(mouse1Y, mouse2Y) { + let y1 = this.display_to_real(mouse1Y, 'y'); + let y2 = this.display_to_real(mouse2Y, 'y'); + this.y_rubberband = [Math.min(y1, y2), Math.max(y1, y2)]; + } - is_in_x_rubberband(x, y) { - let h = 20; - let grad_beg_y = this.height - this.decalage_axis_y + this.Y; - let y1 = grad_beg_y - h/2; - let x1 = this.real_to_display(this.x_rubberband[0], 'x'); - let x2 = this.real_to_display(this.x_rubberband[1], 'x'); - let w = x2 - x1; - let click_on_x_band = Shape.isInRect(x, y, x1, y1, w, h); + is_in_x_rubberband(x, y) { + let h = 20; + let grad_beg_y = this.height - this.decalage_axis_y + this.Y; + let y1 = grad_beg_y - h / 2; + let x1 = this.real_to_display(this.x_rubberband[0], 'x'); + let x2 = this.real_to_display(this.x_rubberband[1], 'x'); + let w = x2 - x1; + let click_on_x_band = Shape.isInRect(x, y, x1, y1, w, h); - let vertex_w = 5; - let left = Shape.isInRect(x, y, x1, y1, vertex_w, h); - let right = Shape.isInRect(x, y, x2, y1, -vertex_w, h); - return [click_on_x_band, left, right]; - } + let vertex_w = 5; + let left = Shape.isInRect(x, y, x1, y1, vertex_w, h); + let right = Shape.isInRect(x, y, x2, y1, -vertex_w, h); + return [click_on_x_band, left, right]; + } - is_in_y_rubberband(x, y) { - let w = 20; - let x1 = this.X + this.decalage_axis_x - w/2; - let y1 = this.real_to_display(this.y_rubberband[0], 'y'); - let y2 = this.real_to_display(this.y_rubberband[1], 'y'); - let h = y2 - y1; - let click_on_y_band = Shape.isInRect(x, y, x1, y1, w, h); + is_in_y_rubberband(x, y) { + let w = 20; + let x1 = this.X + this.decalage_axis_x - w / 2; + let y1 = this.real_to_display(this.y_rubberband[0], 'y'); + let y2 = this.real_to_display(this.y_rubberband[1], 'y'); + let h = y2 - y1; + let click_on_y_band = Shape.isInRect(x, y, x1, y1, w, h); - let vertex_h = 5; - let down = Shape.isInRect(x, y, x1, y1, w, -vertex_h); - let up = Shape.isInRect(x, y, x1, y2, w, vertex_h); - return [click_on_y_band, up, down]; - } + let vertex_h = 5; + let down = Shape.isInRect(x, y, x1, y1, w, -vertex_h); + let up = Shape.isInRect(x, y, x1, y2, w, vertex_h); + return [click_on_y_band, up, down]; + } - translate_rubberband(t, type_) { - if (type_ === 'x') { - this.x_rubberband = [this.x_rubberband[0] + t, this.x_rubberband[1] + t]; - } else if (type_ === 'y') { - this.y_rubberband = [this.y_rubberband[0] + t, this.y_rubberband[1] + t]; - } else { - throw new Error("translate_rubberband(): type_ must be 'x' or 'y'"); - } + translate_rubberband(t, type_) { + if (type_ === 'x') { + this.x_rubberband = [this.x_rubberband[0] + t, this.x_rubberband[1] + t]; + } else if (type_ === 'y') { + this.y_rubberband = [this.y_rubberband[0] + t, this.y_rubberband[1] + t]; + } else { + throw new Error("translate_rubberband(): type_ must be 'x' or 'y'"); } + } - resize_rubberband(t, up, down, left, right) { - if (up) { - this.y_rubberband[1] += t; - } else if (down) { - this.y_rubberband[0] += t; - } else if (left) { - this.x_rubberband[0] += t; - } else if (right) { - this.x_rubberband[1] += t; - } + resize_rubberband(t, up, down, left, right) { + if (up) { + this.y_rubberband[1] += t; + } else if (down) { + this.y_rubberband[0] += t; + } else if (left) { + this.x_rubberband[0] += t; + } else if (right) { + this.x_rubberband[1] += t; } + } - reorder_rubberbands() { - if (this.x_rubberband.length !== 0) { - this.x_rubberband = [Math.min(this.x_rubberband[0], this.x_rubberband[1]), - Math.max(this.x_rubberband[0], this.x_rubberband[1])]; - } - if (this.y_rubberband.length !== 0) { - this.y_rubberband = [Math.min(this.y_rubberband[0], this.y_rubberband[1]), - Math.max(this.y_rubberband[0], this.y_rubberband[1])]; - } + reorder_rubberbands() { + if (this.x_rubberband.length !== 0) { + this.x_rubberband = [Math.min(this.x_rubberband[0], this.x_rubberband[1]), + Math.max(this.x_rubberband[0], this.x_rubberband[1])]; } - - - reset_x_rubberband() { - this.x_rubberband = []; - this.selected_keys = []; - this.selected_point_index = []; + if (this.y_rubberband.length !== 0) { + this.y_rubberband = [Math.min(this.y_rubberband[0], this.y_rubberband[1]), + Math.max(this.y_rubberband[0], this.y_rubberband[1])]; } + } - get_selected_keys() { - this.selected_keys = []; - this.selected_point_index = []; - let keys = Object.keys(this.infos); - if (this.x_rubberband.length === 0 && this.y_rubberband.length === 0) return; - for (let key of keys) { - let {x1, x2} = this.string_to_coordinate(key); - let x_rubberband_0 = Math.min(this.x_rubberband[0], this.x_rubberband[1]); - let x_rubberband_1 = Math.max(this.x_rubberband[0], this.x_rubberband[1]); - let y_rubberband_0 = Math.min(this.y_rubberband[0], this.y_rubberband[1]); - let y_rubberband_1 = Math.max(this.y_rubberband[0], this.y_rubberband[1]); - let f = this.infos[key].length; - let bool = true; - if (this.x_rubberband.length !== 0) { - if (this.discrete) { - let middle = x1 + 1/4; - bool = bool && x_rubberband_0 <= middle && middle <= x_rubberband_1; - // bool = bool && x_rubberband_0 <= x1 && x1 + 1/2 <= x_rubberband_1; - } else { - let middle = (x1 + x2) / 2; - bool = bool && x_rubberband_0 <= middle && middle <= x_rubberband_1; - // bool = bool && x_rubberband_0 <= x1 && x2 <= x_rubberband_1; - } - } - if (this.y_rubberband.length !== 0) { - bool = bool && y_rubberband_0 <= f && f <= y_rubberband_1; - } - if (bool) { - this.selected_keys.push(key); - this.selected_point_index = this.selected_point_index.concat(this.infos[key]); - } - } - let sort = new Sort(); - this.selected_point_index = sort.MergeSort(this.selected_point_index); - } + reset_x_rubberband() { + this.x_rubberband = []; + this.selected_keys = []; + this.selected_point_index = []; + } - real_to_display(real: number, type_) { // type_ = 'x' or 'y' - if (type_ === 'x') { + get_selected_keys() { + this.selected_keys = []; + this.selected_point_index = []; + let keys = Object.keys(this.infos); + if (this.x_rubberband.length === 0 && this.y_rubberband.length === 0) return; + for (let key of keys) { + let { x1, x2 } = this.string_to_coordinate(key); + let x_rubberband_0 = Math.min(this.x_rubberband[0], this.x_rubberband[1]); + let x_rubberband_1 = Math.max(this.x_rubberband[0], this.x_rubberband[1]); + let y_rubberband_0 = Math.min(this.y_rubberband[0], this.y_rubberband[1]); + let y_rubberband_1 = Math.max(this.y_rubberband[0], this.y_rubberband[1]); + let f = this.infos[key].length; + let bool = true; + if (this.x_rubberband.length !== 0) { if (this.discrete) { - let grad_beg_x = this.decalage_axis_x / this.scale; - return this.scale * (grad_beg_x + real) + this.originX + this.X; + let middle = x1 + 1 / 4; + bool = bool && x_rubberband_0 <= middle && middle <= x_rubberband_1; + // bool = bool && x_rubberband_0 <= x1 && x1 + 1/2 <= x_rubberband_1; } else { - return this.scale * real + this.originX + this.X; + let middle = (x1 + x2) / 2; + bool = bool && x_rubberband_0 <= middle && middle <= x_rubberband_1; + // bool = bool && x_rubberband_0 <= x1 && x2 <= x_rubberband_1; } - } else if (type_ === 'y') { - let grad_beg_y = this.height - this.decalage_axis_y; - let scale_y = (this.coeff*this.height - this.decalage_axis_y) / this.max_frequency; - return grad_beg_y - scale_y * real + this.Y; - } else { - throw new Error("real_to_display(): type_ must be 'x' or 'y'"); + } + if (this.y_rubberband.length !== 0) { + bool = bool && y_rubberband_0 <= f && f <= y_rubberband_1; + } + if (bool) { + this.selected_keys.push(key); + this.selected_point_index = this.selected_point_index.concat(this.infos[key]); } } + let sort = new Sort(); + this.selected_point_index = sort.MergeSort(this.selected_point_index); + } - real_to_display_length(real: number, type_) { - if (type_ === 'x') { - return this.scale * real; - } else if (type_ === 'y') { - let scale_y = (this.coeff*this.height - this.decalage_axis_y) / this.max_frequency; - return -scale_y * real; + real_to_display(real: number, type_) { // type_ = 'x' or 'y' + if (type_ === 'x') { + if (this.discrete) { + let grad_beg_x = this.decalage_axis_x / this.scale; + return this.scale * (grad_beg_x + real) + this.originX + this.X; } else { - throw new Error("real_to_display_length(): type_ must be 'x' or 'y'"); + return this.scale * real + this.originX + this.X; } + } else if (type_ === 'y') { + let grad_beg_y = this.height - this.decalage_axis_y; + let scale_y = (this.coeff * this.height - this.decalage_axis_y) / this.max_frequency; + return grad_beg_y - scale_y * real + this.Y; + } else { + throw new Error("real_to_display(): type_ must be 'x' or 'y'"); } + } - display_to_real(display: number, type_) { - if (type_ === 'x') { - if (this.discrete) { - let grad_beg_x = this.decalage_axis_x / this.scale; - return (display - this.originX - this.X) / this.scale - grad_beg_x; - } else { - return (display - this.originX - this.X) / this.scale; - } - } else if (type_ === 'y') { - let grad_beg_y = this.height - this.decalage_axis_y; - let scale_y = (this.coeff*this.height - this.decalage_axis_y) / this.max_frequency; - return (grad_beg_y + this.Y - display) / scale_y; - } else { - throw new Error("display_to_real(): type_ must be 'x' or 'y'"); - } + real_to_display_length(real: number, type_) { + if (type_ === 'x') { + return this.scale * real; + } else if (type_ === 'y') { + let scale_y = (this.coeff * this.height - this.decalage_axis_y) / this.max_frequency; + return -scale_y * real; + } else { + throw new Error("real_to_display_length(): type_ must be 'x' or 'y'"); } + } - display_to_real_length(display: number, type_) { - if (type_ === 'x') { - return display / this.scale; - } else if (type_ === 'y') { - let scale_y = (this.coeff*this.height - this.decalage_axis_y) / this.max_frequency; - return -display / scale_y; + display_to_real(display: number, type_) { + if (type_ === 'x') { + if (this.discrete) { + let grad_beg_x = this.decalage_axis_x / this.scale; + return (display - this.originX - this.X) / this.scale - grad_beg_x; } else { - throw new Error("display_to_real_length(): type_ must be 'x' or 'y'") + return (display - this.originX - this.X) / this.scale; } + } else if (type_ === 'y') { + let grad_beg_y = this.height - this.decalage_axis_y; + let scale_y = (this.coeff * this.height - this.decalage_axis_y) / this.max_frequency; + return (grad_beg_y + this.Y - display) / scale_y; + } else { + throw new Error("display_to_real(): type_ must be 'x' or 'y'"); + } + } + + + display_to_real_length(display: number, type_) { + if (type_ === 'x') { + return display / this.scale; + } else if (type_ === 'y') { + let scale_y = (this.coeff * this.height - this.decalage_axis_y) / this.max_frequency; + return -display / scale_y; + } else { + throw new Error("display_to_real_length(): type_ must be 'x' or 'y'") } + } } @@ -1949,7 +2091,7 @@ export class Histogram extends PlotData { export class ScatterMatrix extends PlotData { plots = []; - constructor(public data:any, + constructor(public data: any, public width: number, public height: number, public buttons_ON: boolean, @@ -1957,45 +2099,49 @@ export class ScatterMatrix extends PlotData { public Y: number, public canvas_id: string, public is_in_multiplot = false) { - super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); - if (data["elements"][0]["type_"] == "sample") { - this.elements = [] - for (var element of data["elements"]) { - this.elements.push(element.values) - } - } else { - this.elements = data["elements"]; - } - let axes = data["axes"] || Object.getOwnPropertyNames(this.elements[0]); - let x_step = width / axes.length; - let y_step = height / axes.length; - for (let i=0; i