From f948bb876a35d2c854b6a1ec649d5fb63955393a Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 2 Jun 2023 15:03:29 +0200 Subject: [PATCH 01/54] fix(dataset_step): allow to draw all points --- CHANGELOG.md | 4 ++++ plot_data/core.py | 3 ++- src/plot-data.ts | 15 ++++++++++----- src/primitives.ts | 5 ++++- src/subplots.ts | 2 ++ 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b383e51..053ff9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. 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). +## [0.13.1] +### Add +- Allow to draw all points of a Dataset in Graph objects + ## [0.13.0] ### Add - Doc Typescript diff --git a/plot_data/core.py b/plot_data/core.py index ee5e4589..265b2bea 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -735,7 +735,7 @@ class Dataset(PlotDataObject): def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, tooltip: Tooltip = None, - point_style: PointStyle = None, + point_style: PointStyle = None, partial_points: bool = None, display_step: int = 1, name: str = ''): self.edge_style = edge_style @@ -755,6 +755,7 @@ def __init__(self, elements: List[Sample] = None, else: raise ValueError(f"Element of type {type(element)} cannot be used as a Dataset data element.") self.elements = sampled_elements + self.partial_points = partial_points self.display_step = display_step PlotDataObject.__init__(self, type_='dataset', name=name) diff --git a/src/plot-data.ts b/src/plot-data.ts index 6eeecbde..8d7e8513 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -600,11 +600,16 @@ export abstract class PlotData { this.context.setLineDash([]); [this.index_first_in, this.nb_points_in, this.index_last_in] = this.get_nb_points_inside_canvas(d.point_list, mvx, mvy); - var step = d.display_step; - var min_dist = this.find_min_dist(d,mvx,mvy,step); - while ((min_dist<20) && (step Date: Fri, 2 Jun 2023 16:02:09 +0200 Subject: [PATCH 02/54] fix(dataset_step): remove numpy from plot_data and regenerate pictures that depended on numpy --- .../GRAPH2D CANVAS -- should draw canvas-base.png | 4 ++-- ...d add primitive group container in multiplot-base.png | 4 ++-- .../MULTIPLOT CANVAS -- should draw canvas-base.png | 4 ++-- ... primitive group from container in multiplot-base.png | 4 ++-- ...CANVAS -- should reorder all plots in canvas-base.png | 4 ++-- plot_data/core.py | 4 ---- script/test_objects/graph_test.py | 9 ++++----- script/test_objects/primitive_group_test.py | 5 ++--- setup.py | 2 +- 9 files changed, 17 insertions(+), 23 deletions(-) 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 c8c762d5..62d04419 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f7be4a1f6fe693ae6647b0e3ed0c12d6f2b9fa0f5308234b539ff2a00f8cdb2 -size 71788 +oid sha256:246617c9dd215026cbef48e0d0d3bcc7e17bb9d40f6ba507cff724c79aef89a8 +size 73391 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 bba3c3e1..b6e448e1 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e543a7e392fcdb7b5709561bbde807f89bd52455eede9aef308a8d62db75941b -size 225326 +oid sha256:92c3ccb0110db8932eb2f9a500f4f4358244b52a401cd8a499bc0be129b950bf +size 225589 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 3155e66d..eeca6cd9 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427fc339306e4a32a1818129f9e489a517da5403af191dcf83c1bee3868a2d06 -size 323150 +oid sha256:410927fdd516ad62b977e61439485d3490efbd19f4fdb7afb18a8259dc359ab2 +size 323282 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 eedf8244..416e69fa 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1037793f0fd81d10ca246c8b24d13fe27acd91e5c7a2cff254ba7357cb07f2d -size 315067 +oid sha256:25f557a019625d725d3038964f39fa7ed0e7d9d97af465cff207de0c9fe73764 +size 315188 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 1e04112f..cc84e2a9 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4339a7704dba389f214959223063c60635b00dd7f8edc28a306973f9e11ef65a -size 312577 +oid sha256:abbbc5af62cc48855491c03845efc7404b7c4745160964f7011742e221bcf7e9 +size 312357 diff --git a/plot_data/core.py b/plot_data/core.py index 265b2bea..8a9a356e 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -15,7 +15,6 @@ import matplotlib.pyplot as plt from matplotlib.patches import Polygon -import numpy as npy try: # dessia_common >= 0.12.0 @@ -32,9 +31,6 @@ import plot_data.colors from plot_data import templates -npy.seterr(divide='raise') - - def delete_none_from_dict(dict1): """ Delete input dictionary's keys where value is None. """ dict2 = {} diff --git a/script/test_objects/graph_test.py b/script/test_objects/graph_test.py index 66ce9bc2..8c18596e 100644 --- a/script/test_objects/graph_test.py +++ b/script/test_objects/graph_test.py @@ -1,14 +1,13 @@ # An example of Graph2D instantiation. It draws one or several datasets on # one canvas and is useful for displaying numerical functions -import numpy as np - +import math import plot_data from plot_data.colors import BLACK, BLUE, RED k = 0 -T1 = np.linspace(1, 20, 20) +T1 = list(range(1, 21, 1)) I1 = [t ** 2 for t in T1] elements1 = [] for k in range(len(T1)): @@ -27,8 +26,8 @@ # Now let's create another dataset for the purpose of this exercice -T2 = np.linspace(1, 20, 100) -I2 = [100*(2+np.cos(t)) for t in T2] +T2 = list(map(lambda x: x / 10, range(1, 201, 1))) +I2 = [100*(2+math.cos(t)) for t in T2] elements2 = [] for k in range(1, len(T2)): elements2.append({'time': T2[k], 'electric current': I2[k]}) diff --git a/script/test_objects/primitive_group_test.py b/script/test_objects/primitive_group_test.py index 9b8709e6..58f42ba6 100644 --- a/script/test_objects/primitive_group_test.py +++ b/script/test_objects/primitive_group_test.py @@ -2,8 +2,7 @@ # primitive_group: an object that contains multiple primitives. A primitive # is either a Circle2D, a LineSegment, a Contour2D, an Arc2D or a Text -import numpy as npy - +import math import plot_data import plot_data.colors as colors @@ -22,7 +21,7 @@ point1 = plot_data.Point2D(0.1, 0.2) # arc -arc = plot_data.Arc2D(cx=8, cy=0, r=2, start_angle=0, end_angle=npy.pi / 2) +arc = plot_data.Arc2D(cx=8, cy=0, r=2, start_angle=0, end_angle=math.pi / 2) # square contour rectangle_size = 2 diff --git a/setup.py b/setup.py index 449d08f6..b3ce06d5 100644 --- a/setup.py +++ b/setup.py @@ -116,5 +116,5 @@ def get_version(): packages=['plot_data'], package_dir={}, include_package_data=True, - install_requires=['matplotlib', 'dessia_common', 'numpy<=1.24.0'], + install_requires=['matplotlib', 'dessia_common'], classifiers=['Topic :: Scientific/Engineering :: Visualization', 'Development Status :: 3 - Alpha']) From ec6ed582da8f417b4e6f5b1175ef3c78fc2077ae Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 2 Jun 2023 17:33:45 +0200 Subject: [PATCH 03/54] fix(dataset_step): remove console log --- src/primitives.ts | 1 - src/subplots.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/primitives.ts b/src/primitives.ts index 13f44c7c..f5a0b655 100644 --- a/src/primitives.ts +++ b/src/primitives.ts @@ -259,7 +259,6 @@ export class Dataset { initialize_segments() { this.segments = []; - console.log(this.point_list) for (let i=0; i Date: Mon, 5 Jun 2023 17:56:15 +0200 Subject: [PATCH 04/54] fix(parallel_names): first fix of bug --- CHANGELOG.md | 6 ++++ script/parallel_plot.py | 8 ++--- src/plot-data.ts | 56 +++++++++++++++++++++++++---------- src/utils.ts | 65 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 112 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b383e51..4dcd1493 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. 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). +## [0.14.0] +### Add + +### Fix +- Smart writing of axes' names in ParallelPlot + ## [0.13.0] ### Add - Doc Typescript diff --git a/script/parallel_plot.py b/script/parallel_plot.py index 638f29c2..007e2fbf 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'mass': random.uniform(0, 0.05), - 'length': random.uniform(0, 100), + elements.append({'masshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge ua': random.uniform(0, 0.05), + 'length buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKW': random.uniform(0, 100), 'shape': random_shape, - 'color': random_color + 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['mass', 'length', 'shape', 'color']) + axes=['masshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge ua', 'length buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKW', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/plot-data.ts b/src/plot-data.ts index 6eeecbde..1110ec42 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -1,6 +1,6 @@ import { heatmap_color, string_to_hex } from "./color_conversion"; import { Point2D, PrimitiveGroup, Contour2D, Circle2D, Dataset, Graph2D, Scatter, Heatmap, Wire } from "./primitives"; -import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, Vertex } from "./utils"; +import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, newText, Vertex } from "./utils"; import { EdgeStyle } from "./style"; import { Shape, List, MyMath } from "./toolbox"; import { rgb_to_hex, tint_rgb, hex_to_rgb, rgb_to_string, get_interpolation_colors, rgb_strToVector } from "./color_conversion"; @@ -841,18 +841,43 @@ export abstract class PlotData { this.context.beginPath(); this.context.lineWidth = 2; Shape.drawLine(this.context, [[current_x, this.axis_y_start], [current_x, this.axis_y_end]]); - var attribute_name = this.axis_list[i]['name']; - this.context.font = this.axisNameSize.toString() + 'px sans-serif'; - this.context.textAlign = 'center'; - if (attribute_name == this.selected_axis_name) { + + let origin = new Vertex(current_x, this.axis_y_end - 20); + let size = this.x_step - 40; + let align = "center" + if (i == 0) { + origin.x = this.axis_x_start + this.x_step / 2; + size = this.x_step / 2 + 40; + align = "right"; + } else if (i == nb_axis - 1) { + origin.x = current_x - this.x_step / 2; + size = this.x_step / 2 + 40; + align = "left"; + } + let axisTitle = new newText(this.axis_list[i]['name'], origin, size, 12, "sans-serif", align, "alphabetic", '', 0, true) + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { - this.context.strokeStyle = 'lightgrey'; + this.context.strokeStyle = 'black'; } - var attribute_alias = this.axis_list[i]['alias']; this.context.fillStyle = 'black'; - this.context.fillText(attribute_alias, current_x, this.axis_y_end - 20); - this.context.stroke(); + axisTitle.draw(this.context); + + this.context.textBaseline = "alphabetic"; + + // var attribute_name = this.axis_list[i]['name']; + // this.context.font = this.axisNameSize.toString() + 'px sans-serif'; + // this.context.textAlign = 'center'; + // if (attribute_name == this.selected_axis_name) { + // this.context.strokeStyle = 'blue'; + // } else { + // this.context.strokeStyle = 'lightgrey'; + // } + // var attribute_alias = this.axis_list[i]['alias']; + // this.context.fillStyle = 'black'; + // this.context.fillText(attribute_alias, current_x, this.axis_y_end - 20); + // this.context.stroke(); + var attribute_type = this.axis_list[i]['type_']; var list = this.axis_list[i]['list']; this.context.font = this.gradSize.toString() + 'px sans-serif'; @@ -928,18 +953,17 @@ export abstract class PlotData { this.context.beginPath(); this.context.lineWidth = 2; Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]); - var attribute_name = this.axis_list[i]['name']; - this.context.font = this.axisNameSize.toString() + 'px sans-serif'; - this.context.textAlign = 'center'; - if (attribute_name == this.selected_axis_name) { + + let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start, current_y + 15), this.axis_x_start * 1.8, 12, "sans-serif", "center", "hanging", '', 0, true) + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { this.context.strokeStyle = 'black'; } this.context.fillStyle = 'black'; - var attribute_alias = this.axis_list[i]['alias']; - this.context.fillText(attribute_alias, this.axis_x_start, current_y + 15); - this.context.stroke(); + axisTitle.draw(this.context); + + this.context.textBaseline = "alphabetic"; var attribute_type = this.axis_list[i]['type_']; var list = this.axis_list[i]['list']; this.context.font = this.gradSize.toString() + 'px sans-serif'; diff --git a/src/utils.ts b/src/utils.ts index 2d85c72f..6c2e8469 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1673,7 +1673,8 @@ export class newText extends newShape { public justify: string = 'left', public baseline: string = 'alphabetic', protected _style: string = '', - protected _orientation: number = 0 + protected _orientation: number = 0, + protected multiLine: boolean = false ) { super(); this.path = this.buildPath(); @@ -1711,9 +1712,10 @@ export class newText extends newShape { this.width = context.measureText(this.text).width; } else if (this.width !== null && this.fontsize === null) { this.fontsize = this.automaticFontSize(context); - } else if (this.width !== null && this.fontsize !== null) { + } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { let width = context.measureText(this.text).width; if (width > this.width) {this.fontsize = this.automaticFontSize(context)} + } else if (this.width !== null && this.fontsize !== null && this.multiLine) { } else { throw new Error('Cannot write text with no size'); } @@ -1723,10 +1725,67 @@ export class newText extends newShape { this.path = this.buildPath(); context.translate(this.origin.x, this.origin.y); context.rotate(Math.PI/180 * this.orientation); - context.fillText(this.text, 0, 0); + this.write(context, 1); context.rotate(-Math.PI/180 * this.orientation); context.translate(-this.origin.x, -this.origin.y); } + + private write(context: CanvasRenderingContext2D, scaleX: number) { + if (this.width) { + if (this.multiLine) { + var cut_texts = this.cutting_text(context, scaleX * this.width); + var height_offset: number = cut_texts.length / 2 - 0.5; + if (this.baseline == "hanging" || this.baseline == "top") { + height_offset = 0.5; + } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { + height_offset -= 0.2; + } + for (let i=0; i= maxWidth) { + if (line_text !== '') cut_texts.push(line_text); + line_length = 0; + line_text = ''; + cut_texts.push(word); + i++; + } else { + if (line_length + word_length <= maxWidth) { + if (line_length !== 0) { + line_length = line_length + space_length; + line_text = line_text + ' '; + } + line_text = line_text + word; + line_length = line_length + word_length; + i++; + } else { + cut_texts.push(line_text); + line_length = 0; + line_text = ''; + } + } + } + if (line_text !== '') cut_texts.push(line_text); + return cut_texts; + } } const CIRCLES = ['o', 'circle', 'round']; From 0f4c38e72e1030d2299031983695076194ff610d Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 5 Jun 2023 18:07:58 +0200 Subject: [PATCH 05/54] fix(parallel_names): add rules for border names, still needs some rules for these --- script/parallel_plot.py | 8 ++++---- src/plot-data.ts | 19 ++----------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/script/parallel_plot.py b/script/parallel_plot.py index 007e2fbf..126cc217 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'masshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge ua': random.uniform(0, 0.05), - 'length buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKW': random.uniform(0, 100), + elements.append({'mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass ': random.uniform(0, 0.05), + 'length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length ': random.uniform(0, 100), 'shape': random_shape, - 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ': random_color + 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['masshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge uamasshzeuifg eyg efyefg uafyfg euyafg euyfg afuyge ua', 'length buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKWlength buizf geui qge OGUEI GUfi eg GFE Gf gFOGUE OG YFGYUK QGUEKW', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) + axes=['mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass ', 'length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length ', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/plot-data.ts b/src/plot-data.ts index 1110ec42..682534ac 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -846,7 +846,7 @@ export abstract class PlotData { let size = this.x_step - 40; let align = "center" if (i == 0) { - origin.x = this.axis_x_start + this.x_step / 2; + origin.x = current_x + this.x_step / 2; size = this.x_step / 2 + 40; align = "right"; } else if (i == nb_axis - 1) { @@ -862,22 +862,7 @@ export abstract class PlotData { } this.context.fillStyle = 'black'; axisTitle.draw(this.context); - this.context.textBaseline = "alphabetic"; - - // var attribute_name = this.axis_list[i]['name']; - // this.context.font = this.axisNameSize.toString() + 'px sans-serif'; - // this.context.textAlign = 'center'; - // if (attribute_name == this.selected_axis_name) { - // this.context.strokeStyle = 'blue'; - // } else { - // this.context.strokeStyle = 'lightgrey'; - // } - // var attribute_alias = this.axis_list[i]['alias']; - // this.context.fillStyle = 'black'; - // this.context.fillText(attribute_alias, current_x, this.axis_y_end - 20); - // this.context.stroke(); - var attribute_type = this.axis_list[i]['type_']; var list = this.axis_list[i]['list']; this.context.font = this.gradSize.toString() + 'px sans-serif'; @@ -954,7 +939,7 @@ export abstract class PlotData { this.context.lineWidth = 2; Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]); - let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start, current_y + 15), this.axis_x_start * 1.8, 12, "sans-serif", "center", "hanging", '', 0, true) + let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), 100, 12, "sans-serif", "left", "hanging", '', 0, true) if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { From 8e9a763964db584599518a5d01806a238c3a8d16 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Tue, 6 Jun 2023 17:51:17 +0200 Subject: [PATCH 06/54] fix(parallel_names): begin generic implementation of text sizes --- src/plot-data.ts | 9 +- src/utils.ts | 2334 +++++++++++++++++++++++++--------------------- 2 files changed, 1282 insertions(+), 1061 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index 682534ac..b15bee87 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -1,6 +1,6 @@ import { heatmap_color, string_to_hex } from "./color_conversion"; import { Point2D, PrimitiveGroup, Contour2D, Circle2D, Dataset, Graph2D, Scatter, Heatmap, Wire } from "./primitives"; -import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, newText, Vertex } from "./utils"; +import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, newText, textParams, Vertex } from "./utils"; import { EdgeStyle } from "./style"; import { Shape, List, MyMath } from "./toolbox"; import { rgb_to_hex, tint_rgb, hex_to_rgb, rgb_to_string, get_interpolation_colors, rgb_strToVector } from "./color_conversion"; @@ -854,7 +854,9 @@ export abstract class PlotData { size = this.x_step / 2 + 40; align = "left"; } - let axisTitle = new newText(this.axis_list[i]['name'], origin, size, 12, "sans-serif", align, "alphabetic", '', 0, true) + + const textParams: textParams = {width: size, align: align, baseline: "alphabetic", multiLine: false}; + let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { @@ -939,7 +941,8 @@ export abstract class PlotData { this.context.lineWidth = 2; Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]); - let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), 100, 12, "sans-serif", "left", "hanging", '', 0, true) + const textParams: textParams = {width: 100, align: "left", baseline: "hanging", multiLine: true}; + let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), textParams); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { diff --git a/src/utils.ts b/src/utils.ts index 6c2e8469..d387cfea 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -3,501 +3,503 @@ import { string_to_rgb, rgb_to_hex, color_to_string, isHex, isRGB, string_to_hex import { Shape, MyMath, List } from "./toolbox"; export class Axis { - color_stroke:any; - x_step:number; - y_step:number; - - constructor(public nb_points_x:number=10, - public nb_points_y:number=10, - public graduation_style:TextStyle, - public axis_style:EdgeStyle, - public arrow_on:boolean=false, - public grid_on:boolean=true, - public type_:string='axis', - public name='') { - } - - public static deserialize(serialized) { - let default_axis_style = {line_width:0.5, color_stroke:string_to_rgb('grey'), dashline:[], name:''}; - let default_graduation_style = {text_color:string_to_rgb('grey'), font_size:12, font_style:'sans-serif', name:''}; - let default_dict_ = {nb_points_x:10, nb_points_y:10, graduation_style:default_graduation_style, - axis_style:default_axis_style, arrow_on:false, grid_on:true, name:''}; - serialized = set_default_values(serialized, default_dict_); - var graduation_style = TextStyle.deserialize(serialized['graduation_style']); - var axis_style = EdgeStyle.deserialize(serialized['axis_style']); - return new Axis(serialized['nb_points_x'], - serialized['nb_points_y'], - graduation_style, - axis_style, - serialized['arrow_on'], - serialized['grid_on'], - serialized['type_'], - serialized['name']); - } - + color_stroke: any; + x_step: number; + y_step: number; + + constructor(public nb_points_x: number = 10, + public nb_points_y: number = 10, + public graduation_style: TextStyle, + public axis_style: EdgeStyle, + public arrow_on: boolean = false, + public grid_on: boolean = true, + public type_: string = 'axis', + public name = '') { + } + + public static deserialize(serialized) { + let default_axis_style = { line_width: 0.5, color_stroke: string_to_rgb('grey'), dashline: [], name: '' }; + let default_graduation_style = { text_color: string_to_rgb('grey'), font_size: 12, font_style: 'sans-serif', name: '' }; + let default_dict_ = { + nb_points_x: 10, nb_points_y: 10, graduation_style: default_graduation_style, + axis_style: default_axis_style, arrow_on: false, grid_on: true, name: '' + }; + serialized = set_default_values(serialized, default_dict_); + var graduation_style = TextStyle.deserialize(serialized['graduation_style']); + var axis_style = EdgeStyle.deserialize(serialized['axis_style']); + return new Axis(serialized['nb_points_x'], + serialized['nb_points_y'], + graduation_style, + axis_style, + serialized['arrow_on'], + serialized['grid_on'], + serialized['type_'], + serialized['name']); + } - draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, x_step, font_size, X, canvas_width) { - var i=0; - context.textAlign = 'center'; - var x_nb_digits = Math.max(0, 1-Math.floor(Math.log10(x_step))); - var grad_beg_x = Math.floor(1/this.x_step * ((axis_x_start - mvx - X)/scaleX)) * this.x_step; - var grad_end_x = Math.ceil(1/this.x_step * ((canvas_width - mvx)/scaleX)) * this.x_step; - while(grad_beg_x + i*x_step < grad_end_x) { - if (this.grid_on === true) { - context.strokeStyle = 'lightgrey'; - Shape.drawLine(context, [[scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_start], [scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end + 3]]); - } else { - Shape.drawLine(context, [[scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end - 3], [scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end + 3]]); - } - context.fillText(MyMath.round(grad_beg_x + i*x_step, x_nb_digits), scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end + font_size); - i++ + draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, x_step, font_size, X, canvas_width) { + var i = 0; + context.textAlign = 'center'; + var x_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(x_step))); + var grad_beg_x = Math.floor(1 / this.x_step * ((axis_x_start - mvx - X) / scaleX)) * this.x_step; + var grad_end_x = Math.ceil(1 / this.x_step * ((canvas_width - mvx) / scaleX)) * this.x_step; + + while (grad_beg_x + i * x_step < grad_end_x) { + if (this.grid_on === true) { + context.strokeStyle = 'lightgrey'; + Shape.drawLine(context, [[scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_start], [scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end + 3]]); + } else { + Shape.drawLine(context, [[scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end - 3], [scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end + 3]]); } - context.stroke(); - // return [scaleX*(grad_beg_x) + mvx + X, scaleX*(grad_beg_x + (i-1)*x_step) + mvx + X] + context.fillText(MyMath.round(grad_beg_x + i * x_step, x_nb_digits), scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end + font_size); + i++ } + context.stroke(); + // return [scaleX*(grad_beg_x) + mvx + X, scaleX*(grad_beg_x + (i-1)*x_step) + mvx + X] + } - draw_vertical_graduations(context, mvy, scaleY, axis_x_start, axis_x_end, axis_y_end, y_step, Y) { - var i=0; - var grad_beg_y = Math.ceil(-1/this.y_step*((axis_y_end - mvy - Y)/scaleY)) * this.y_step; - var grad_end_y = Math.floor(mvy/(this.y_step * scaleY)) * this.y_step; - context.textAlign = 'end'; - context.textBaseline = 'middle'; - var y_nb_digits = Math.max(0, 1-Math.floor(Math.log10(y_step))); - while (grad_beg_y + (i-1)*y_step < grad_end_y) { - if (this.grid_on === true) { - context.strokeStyle = 'lightgrey'; - Shape.drawLine(context, [[axis_x_start - 3, -scaleY*(grad_beg_y + i*y_step) + mvy + Y], [axis_x_end, -scaleY*(grad_beg_y + i*y_step) + mvy + Y]]); - } else { - Shape.drawLine(context, [[axis_x_start - 3, -scaleY*(grad_beg_y + i*y_step) + mvy + Y], [axis_x_start + 3, -scaleY*(grad_beg_y + i*y_step) + mvy + Y]]); - } - context.fillText(MyMath.round(grad_beg_y + i*y_step, y_nb_digits), axis_x_start - 5, -scaleY*(grad_beg_y + i*y_step) + mvy + Y); - i++; + draw_vertical_graduations(context, mvy, scaleY, axis_x_start, axis_x_end, axis_y_end, y_step, Y) { + var i = 0; + var grad_beg_y = Math.ceil(-1 / this.y_step * ((axis_y_end - mvy - Y) / scaleY)) * this.y_step; + var grad_end_y = Math.floor(mvy / (this.y_step * scaleY)) * this.y_step; + context.textAlign = 'end'; + context.textBaseline = 'middle'; + var y_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(y_step))); + while (grad_beg_y + (i - 1) * y_step < grad_end_y) { + if (this.grid_on === true) { + context.strokeStyle = 'lightgrey'; + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * (grad_beg_y + i * y_step) + mvy + Y], [axis_x_end, -scaleY * (grad_beg_y + i * y_step) + mvy + Y]]); + } else { + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * (grad_beg_y + i * y_step) + mvy + Y], [axis_x_start + 3, -scaleY * (grad_beg_y + i * y_step) + mvy + Y]]); } - context.stroke(); + context.fillText(MyMath.round(grad_beg_y + i * y_step, y_nb_digits), axis_x_start - 5, -scaleY * (grad_beg_y + i * y_step) + mvy + Y); + i++; } - - draw_horizontal_log_graduations(context, mvx, scaleX, minX, maxX, axis_y_start, axis_y_end, font_size, X, canvas_width) { - context.textAlign = 'center'; - let delta = scaleX; - let numbers = [1]; - if (delta >= canvas_width/3 && delta <= canvas_width/2) { - numbers = [1, 5]; - } else if (delta > canvas_width/2 && delta <= 3/4*canvas_width) { - numbers = [1, 2, 5]; - } else if (delta > 3/4*canvas_width) { - numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - } - let start_pow = Math.floor(minX); - let end_pow = Math.ceil(maxX); - for (let power=start_pow; power <= end_pow; power++) { - for (let num of numbers) { - let x_coord = num * Math.pow(10, power); - if (this.grid_on === true) { - context.strokeStyle = 'lightgrey'; - Shape.drawLine(context, [[scaleX * Math.log10(x_coord) + mvx + X, axis_y_start], [scaleX * Math.log10(x_coord) + mvx + X, axis_y_end + 3]]); - } else { - Shape.drawLine(context, [[scaleX * Math.log10(x_coord) + mvx + X, axis_y_end - 3], [scaleX*Math.log10(x_coord) + mvx + X, axis_y_end + 3]]); - } - context.fillText(x_coord, scaleX*Math.log10(x_coord) + mvx + X, axis_y_end + font_size); - } - } - context.stroke(); + context.stroke(); + } + + draw_horizontal_log_graduations(context, mvx, scaleX, minX, maxX, axis_y_start, axis_y_end, font_size, X, canvas_width) { + context.textAlign = 'center'; + let delta = scaleX; + let numbers = [1]; + if (delta >= canvas_width / 3 && delta <= canvas_width / 2) { + numbers = [1, 5]; + } else if (delta > canvas_width / 2 && delta <= 3 / 4 * canvas_width) { + numbers = [1, 2, 5]; + } else if (delta > 3 / 4 * canvas_width) { + numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; } - - - draw_vertical_log_graduations(context, mvy, scaleY, minY, maxY, axis_x_start, axis_x_end, axis_y_end, canvas_height, Y) { - context.textAlign = 'end'; - context.textBaseline = 'middle'; - - let delta = scaleY; - let numbers = [1]; - if (delta >= canvas_height/3 && delta <= canvas_height/2) { - numbers = [1, 5]; - } else if (delta > canvas_height/2 && delta <= 3/4*canvas_height) { - numbers = [1, 2, 5]; - } else if (delta > 3/4*canvas_height) { - numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - } - - let start_pow = Math.floor(-minY); - let end_pow = Math.ceil(-maxY); - - for (let power=start_pow; power<=end_pow; power++) { - for (let num of numbers) { - let y_coord = num * Math.pow(10, power); - if (this.grid_on === true) { - context.strokeStyle = 'lightgrey'; - Shape.drawLine(context, [[axis_x_start - 3, -scaleY * Math.log10(y_coord) + mvy + Y], [axis_x_end, -scaleY* Math.log10(y_coord) + mvy + Y]]); - } else { - Shape.drawLine(context, [[axis_x_start - 3, -scaleY * Math.log10(y_coord) + mvy + Y], [axis_x_start + 3, -scaleY * Math.log10(y_coord) + mvy + Y]]); - } - context.fillText(y_coord, axis_x_start - 5, -scaleY * Math.log10(y_coord) + mvy + Y); + let start_pow = Math.floor(minX); + let end_pow = Math.ceil(maxX); + for (let power = start_pow; power <= end_pow; power++) { + for (let num of numbers) { + let x_coord = num * Math.pow(10, power); + if (this.grid_on === true) { + context.strokeStyle = 'lightgrey'; + Shape.drawLine(context, [[scaleX * Math.log10(x_coord) + mvx + X, axis_y_start], [scaleX * Math.log10(x_coord) + mvx + X, axis_y_end + 3]]); + } else { + Shape.drawLine(context, [[scaleX * Math.log10(x_coord) + mvx + X, axis_y_end - 3], [scaleX * Math.log10(x_coord) + mvx + X, axis_y_end + 3]]); } + context.fillText(x_coord, scaleX * Math.log10(x_coord) + mvx + X, axis_y_end + font_size); } - context.stroke(); } + context.stroke(); + } - draw_histogram_vertical_graduations(context, height, decalage_axis_y, max_frequency, axis_x_start, y_step, Y, coeff=0.88) { - let scale = (coeff*height - decalage_axis_y) / max_frequency; - let grad_beg_y = height - decalage_axis_y; - let i = 0; - context.textAlign = 'end'; - context.textBaseline = 'middle'; - while (i * y_step < max_frequency + y_step) { - Shape.drawLine(context, [[axis_x_start - 3, grad_beg_y - scale * (i * y_step) + Y], - [axis_x_start + 3, grad_beg_y - scale * (i * y_step) + Y]]); - context.fillText(i * y_step, axis_x_start - 5, grad_beg_y - scale * (i * y_step) + Y); - i++; - } - context.stroke(); - } - + draw_vertical_log_graduations(context, mvy, scaleY, minY, maxY, axis_x_start, axis_x_end, axis_y_end, canvas_height, Y) { + context.textAlign = 'end'; + context.textBaseline = 'middle'; - draw_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, minX, maxX, scroll_x, - decalage_axis_x, decalage_axis_y, X, Y, to_disp_attribute_name, log_scale_x, x_step?) { - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - //Arrow - if (this.arrow_on) { - Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); - Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); - } - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); - //Graduations - if (scroll_x % 5 == 0) { - let kx = 1.1*scaleX/init_scaleX; - let num = Math.max(maxX - minX, 1); - this.x_step = x_step || Math.min(num/(kx*(this.nb_points_x-1)), width/(scaleX*(this.nb_points_x - 1))); - } - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.textAlign = 'end'; - context.fillStyle = this.graduation_style.text_color; - context.fillText(to_disp_attribute_name, axis_x_end - 5, axis_y_end - 10); - // draw_horizontal_graduations - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - if (log_scale_x) { - this.draw_horizontal_log_graduations(context, mvx, scaleX, minX, maxX, axis_y_start, axis_y_end, - this.graduation_style.font_size, X, width); - } else { - // [this.xStart, this.xEnd] = this.draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, - // this.x_step, this.graduation_style.font_size, X, width); - this.draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, - this.x_step, this.graduation_style.font_size, X, width); - } - context.closePath(); - // return [axis_x_start, axis_x_end] - } - - - draw_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, minY, maxY, - scroll_y, decalage_axis_x, decalage_axis_y, X, Y, to_disp_attribute_name, log_scale_y, y_step?) { - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - //Arrows - if (this.arrow_on === true) { - Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); - } - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); - // Graduations - if (scroll_y % 5 == 0) { - let ky = 1.1*scaleY/init_scaleY; - let num = Math.max(maxY - minY, 1); - this.y_step = y_step || Math.min(num/(ky*(this.nb_points_y-1)), height/(scaleY*(this.nb_points_y - 1))); - } - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.textAlign = 'start'; - context.fillStyle = this.graduation_style.text_color; - context.fillText(to_disp_attribute_name, axis_x_start + 5, axis_y_start + 20); - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - if (log_scale_y) { - this.draw_vertical_log_graduations(context, mvy, scaleY, minY, maxY, axis_x_start, axis_x_end, axis_y_end, height, Y) - } else { - this.draw_vertical_graduations(context, mvy, scaleY, axis_x_start, axis_x_end, axis_y_end, this.y_step, Y); - } - context.closePath(); + let delta = scaleY; + let numbers = [1]; + if (delta >= canvas_height / 3 && delta <= canvas_height / 2) { + numbers = [1, 5]; + } else if (delta > canvas_height / 2 && delta <= 3 / 4 * canvas_height) { + numbers = [1, 2, 5]; + } else if (delta > 3 / 4 * canvas_height) { + numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; } + let start_pow = Math.floor(-minY); + let end_pow = Math.ceil(-maxY); - draw_histogram_x_axis(context, scaleX, init_scaleX, mvx, width, height, graduations, decalage_axis_x, - decalage_axis_y, scroll_x, X, Y, to_disp_attribute_name, x_step?) { - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - //Arrow - if (this.arrow_on) { - Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); - Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); - } - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); - - //Graduations - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.textAlign = 'end'; - context.fillStyle = this.graduation_style.text_color; - context.fillText(to_disp_attribute_name, axis_x_end - 5, axis_y_end - 10); - - // draw_horizontal_graduations - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - var i=0; - context.textAlign = 'center'; - var grad_beg_x = decalage_axis_x/scaleX + 1/4; - while(i < graduations.length) { + for (let power = start_pow; power <= end_pow; power++) { + for (let num of numbers) { + let y_coord = num * Math.pow(10, power); if (this.grid_on === true) { context.strokeStyle = 'lightgrey'; - Shape.drawLine(context, [[scaleX*(grad_beg_x + i) + mvx + X, axis_y_start], [scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end + 3]]); + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * Math.log10(y_coord) + mvy + Y], [axis_x_end, -scaleY * Math.log10(y_coord) + mvy + Y]]); } else { - Shape.drawLine(context, [[scaleX*(grad_beg_x + i) + mvx + X, axis_y_end - 3], [scaleX*(grad_beg_x + i*x_step) + mvx + X, axis_y_end + 3]]); + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * Math.log10(y_coord) + mvy + Y], [axis_x_start + 3, -scaleY * Math.log10(y_coord) + mvy + Y]]); } - context.fillText(graduations[i], scaleX*(grad_beg_x + i) + mvx + X, axis_y_end + this.graduation_style.font_size); - i++; + context.fillText(y_coord, axis_x_start - 5, -scaleY * Math.log10(y_coord) + mvy + Y); } - context.stroke(); - context.closePath(); - // this.xStart = scaleX*(grad_beg_x) + mvx + X; - // this.xEnd = scaleX*(grad_beg_x + i - 1) + mvx + X; - // return [axis_x_start, axis_x_end] } + context.stroke(); + } - draw_histogram_y_axis(context, width, height, max_frequency, decalage_axis_x, - decalage_axis_y, X, Y, to_disp_attribute_name, y_step, coeff?) { - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - //Arrows - if (this.arrow_on === true) { - Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); - } - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); - // context.stroke(); - // Graduations - this.y_step = y_step; - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.textAlign = 'start'; - context.fillStyle = this.graduation_style.text_color; - context.fillText(to_disp_attribute_name, axis_x_start + 5, axis_y_start + 20); - - //draw vertical graduations - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - this.draw_histogram_vertical_graduations(context, height, decalage_axis_y, max_frequency, axis_x_start, y_step, Y, coeff); - context.closePath(); - return [axis_y_end, axis_y_start] + draw_histogram_vertical_graduations(context, height, decalage_axis_y, max_frequency, axis_x_start, y_step, Y, coeff = 0.88) { + let scale = (coeff * height - decalage_axis_y) / max_frequency; + let grad_beg_y = height - decalage_axis_y; + let i = 0; + context.textAlign = 'end'; + context.textBaseline = 'middle'; + while (i * y_step < max_frequency + y_step) { + Shape.drawLine(context, [[axis_x_start - 3, grad_beg_y - scale * (i * y_step) + Y], + [axis_x_start + 3, grad_beg_y - scale * (i * y_step) + Y]]); + context.fillText(i * y_step, axis_x_start - 5, grad_beg_y - scale * (i * y_step) + Y); + i++; } - - draw_scatter_axis(context, mvx, mvy, scaleX, scaleY, width, height, init_scaleX, init_scaleY, lists, - to_display_attributes, scroll_x, scroll_y, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, canvas_height, - log_scale_x, log_scale_y) { - - this.draw_sc_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, lists[0], to_display_attributes[0], - scroll_x, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, log_scale_x); - this.draw_sc_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, lists[1], to_display_attributes[1], - scroll_y, decalage_axis_x, decalage_axis_y, X, Y, canvas_height, log_scale_y); + context.stroke(); + } + + + draw_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, minX, maxX, scroll_x, + decalage_axis_x, decalage_axis_y, X, Y, to_disp_attribute_name, log_scale_x, x_step?) { + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + //Arrow + if (this.arrow_on) { + Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); + Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); } - - draw_sc_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, list, to_display_attribute:Attribute, - scroll_x, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, log_scale_x=false) { - // Drawing the coordinate system - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - - //Arrows - if (this.arrow_on) { - Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); - Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); - } - - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); - context.fillStyle = this.graduation_style.text_color; - context.strokeStyle = this.axis_style.color_stroke; - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.textAlign = 'end'; - context.textBaseline = "alphabetic"; - context.fillText(to_display_attribute['name'], axis_x_end - 5, axis_y_end - 10); - context.stroke(); - //Graduations - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - if (log_scale_x) { - if (TypeOf(list[0]) === 'string') { - throw new Error("Cannot use log scale on a non float axis"); - } - this.draw_horizontal_log_graduations(context, mvx, scaleX, Math.log10(list[0]), Math.log10(list[1]), axis_y_start, - axis_y_end, this.graduation_style.font_size, X, width); + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); + //Graduations + if (scroll_x % 5 == 0) { + let kx = 1.1 * scaleX / init_scaleX; + let num = Math.max(maxX - minX, 1); + this.x_step = x_step || Math.min(num / (kx * (this.nb_points_x - 1)), width / (scaleX * (this.nb_points_x - 1))); + } + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.textAlign = 'end'; + context.fillStyle = this.graduation_style.text_color; + context.fillText(to_disp_attribute_name, axis_x_end - 5, axis_y_end - 10); + // draw_horizontal_graduations + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + if (log_scale_x) { + this.draw_horizontal_log_graduations(context, mvx, scaleX, minX, maxX, axis_y_start, axis_y_end, + this.graduation_style.font_size, X, width); + } else { + // [this.xStart, this.xEnd] = this.draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, + // this.x_step, this.graduation_style.font_size, X, width); + this.draw_horizontal_graduations(context, mvx, scaleX, axis_x_start, axis_y_start, axis_y_end, + this.x_step, this.graduation_style.font_size, X, width); + } + context.closePath(); + // return [axis_x_start, axis_x_end] + } + + + draw_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, minY, maxY, + scroll_y, decalage_axis_x, decalage_axis_y, X, Y, to_disp_attribute_name, log_scale_y, y_step?) { + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + //Arrows + if (this.arrow_on === true) { + Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); + } + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); + // Graduations + if (scroll_y % 5 == 0) { + let ky = 1.1 * scaleY / init_scaleY; + let num = Math.max(maxY - minY, 1); + this.y_step = y_step || Math.min(num / (ky * (this.nb_points_y - 1)), height / (scaleY * (this.nb_points_y - 1))); + } + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.textAlign = 'start'; + context.fillStyle = this.graduation_style.text_color; + context.fillText(to_disp_attribute_name, axis_x_start + 5, axis_y_start + 20); + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + if (log_scale_y) { + this.draw_vertical_log_graduations(context, mvy, scaleY, minY, maxY, axis_x_start, axis_x_end, axis_y_end, height, Y) + } else { + this.draw_vertical_graduations(context, mvy, scaleY, axis_x_start, axis_x_end, axis_y_end, this.y_step, Y); + } + context.closePath(); + } + + + draw_histogram_x_axis(context, scaleX, init_scaleX, mvx, width, height, graduations, decalage_axis_x, + decalage_axis_y, scroll_x, X, Y, to_disp_attribute_name, x_step?) { + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + //Arrow + if (this.arrow_on) { + Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); + Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); + } + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); + + //Graduations + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.textAlign = 'end'; + context.fillStyle = this.graduation_style.text_color; + context.fillText(to_disp_attribute_name, axis_x_end - 5, axis_y_end - 10); + + // draw_horizontal_graduations + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + var i = 0; + context.textAlign = 'center'; + var grad_beg_x = decalage_axis_x / scaleX + 1 / 4; + while (i < graduations.length) { + if (this.grid_on === true) { + context.strokeStyle = 'lightgrey'; + Shape.drawLine(context, [[scaleX * (grad_beg_x + i) + mvx + X, axis_y_start], [scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end + 3]]); } else { - this.draw_sc_horizontal_graduations(context, mvx, scaleX, init_scaleX, axis_x_start, axis_x_end, - axis_y_start, axis_y_end, list, to_display_attribute, scroll_x, X, canvas_width); + Shape.drawLine(context, [[scaleX * (grad_beg_x + i) + mvx + X, axis_y_end - 3], [scaleX * (grad_beg_x + i * x_step) + mvx + X, axis_y_end + 3]]); } - context.stroke(); - context.closePath(); + context.fillText(graduations[i], scaleX * (grad_beg_x + i) + mvx + X, axis_y_end + this.graduation_style.font_size); + i++; + } + context.stroke(); + context.closePath(); + // this.xStart = scaleX*(grad_beg_x) + mvx + X; + // this.xEnd = scaleX*(grad_beg_x + i - 1) + mvx + X; + // return [axis_x_start, axis_x_end] + } + + + draw_histogram_y_axis(context, width, height, max_frequency, decalage_axis_x, + decalage_axis_y, X, Y, to_disp_attribute_name, y_step, coeff?) { + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + //Arrows + if (this.arrow_on === true) { + Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); + } + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); + // context.stroke(); + // Graduations + this.y_step = y_step; + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.textAlign = 'start'; + context.fillStyle = this.graduation_style.text_color; + context.fillText(to_disp_attribute_name, axis_x_start + 5, axis_y_start + 20); + + //draw vertical graduations + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + this.draw_histogram_vertical_graduations(context, height, decalage_axis_y, max_frequency, axis_x_start, y_step, Y, coeff); + context.closePath(); + return [axis_y_end, axis_y_start] + } + + draw_scatter_axis(context, mvx, mvy, scaleX, scaleY, width, height, init_scaleX, init_scaleY, lists, + to_display_attributes, scroll_x, scroll_y, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, canvas_height, + log_scale_x, log_scale_y) { + + this.draw_sc_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, lists[0], to_display_attributes[0], + scroll_x, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, log_scale_x); + this.draw_sc_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, lists[1], to_display_attributes[1], + scroll_y, decalage_axis_x, decalage_axis_y, X, Y, canvas_height, log_scale_y); + } + + draw_sc_horizontal_axis(context, mvx, scaleX, width, height, init_scaleX, list, to_display_attribute: Attribute, + scroll_x, decalage_axis_x, decalage_axis_y, X, Y, canvas_width, log_scale_x = false) { + // Drawing the coordinate system + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + + //Arrows + if (this.arrow_on) { + Shape.drawLine(context, [[axis_x_end - 20, axis_y_end - 10], [axis_x_end, axis_y_end]]); + Shape.drawLine(context, [[axis_x_end, axis_y_end], [axis_x_end - 20, axis_y_end + 10]]); } - draw_sc_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, list, to_display_attribute, scroll_y, - decalage_axis_x, decalage_axis_y, X, Y, canvas_height, log_scale_y=false) { - // Drawing the coordinate system - context.beginPath(); - context.strokeStyle = this.axis_style.color_stroke; - context.lineWidth = this.axis_style.line_width; - var axis_x_start = decalage_axis_x + X; - var axis_x_end = width + X; - var axis_y_start = Y; - var axis_y_end = height - decalage_axis_y + Y; - - if (this.arrow_on === true) { - Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_end], [axis_x_end, axis_y_end]]); + context.fillStyle = this.graduation_style.text_color; + context.strokeStyle = this.axis_style.color_stroke; + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.textAlign = 'end'; + context.textBaseline = "alphabetic"; + context.fillText(to_display_attribute['name'], axis_x_end - 5, axis_y_end - 10); + context.stroke(); + //Graduations + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + if (log_scale_x) { + if (TypeOf(list[0]) === 'string') { + throw new Error("Cannot use log scale on a non float axis"); } - //Axis - Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); - - context.fillStyle = this.graduation_style.text_color; - let font_size = Math.max(15, Math.ceil(0.01*(height + width))); - context.font = 'bold ' + font_size + 'px Arial'; - context.strokeStyle = this.axis_style.color_stroke; - context.textAlign = 'start'; - context.textBaseline = "alphabetic"; - context.fillText(to_display_attribute['name'], axis_x_start + 5, axis_y_start + 20); - context.stroke(); - - //Graduations - context.font = this.graduation_style.font_size.toString() + 'px Arial'; - if (log_scale_y) { - if (TypeOf(list[0]) === 'string') { - throw new Error("Cannot use log scale on a non float axis.") - } - this.draw_vertical_log_graduations(context, mvy, scaleY, -Math.log10(list[0]), -Math.log10(list[1]), - axis_x_start, axis_x_end, axis_y_end, canvas_height, Y); - } else { - this.draw_sc_vertical_graduations(context, mvy, scaleY, init_scaleY, axis_x_start, axis_x_end, axis_y_start, - axis_y_end, list, to_display_attribute, scroll_y, Y, canvas_height); + this.draw_horizontal_log_graduations(context, mvx, scaleX, Math.log10(list[0]), Math.log10(list[1]), axis_y_start, + axis_y_end, this.graduation_style.font_size, X, width); + } else { + this.draw_sc_horizontal_graduations(context, mvx, scaleX, init_scaleX, axis_x_start, axis_x_end, + axis_y_start, axis_y_end, list, to_display_attribute, scroll_x, X, canvas_width); + } + context.stroke(); + context.closePath(); + } + + draw_sc_vertical_axis(context, mvy, scaleY, width, height, init_scaleY, list, to_display_attribute, scroll_y, + decalage_axis_x, decalage_axis_y, X, Y, canvas_height, log_scale_y = false) { + // Drawing the coordinate system + context.beginPath(); + context.strokeStyle = this.axis_style.color_stroke; + context.lineWidth = this.axis_style.line_width; + var axis_x_start = decalage_axis_x + X; + var axis_x_end = width + X; + var axis_y_start = Y; + var axis_y_end = height - decalage_axis_y + Y; + + if (this.arrow_on === true) { + Shape.drawLine(context, [[axis_x_start - 10, axis_y_start + 20], [axis_x_start, axis_y_start]]); + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start + 10, axis_y_start + 20]]); + } + //Axis + Shape.drawLine(context, [[axis_x_start, axis_y_start], [axis_x_start, axis_y_end]]); + + context.fillStyle = this.graduation_style.text_color; + let font_size = Math.max(15, Math.ceil(0.01 * (height + width))); + context.font = 'bold ' + font_size + 'px Arial'; + context.strokeStyle = this.axis_style.color_stroke; + context.textAlign = 'start'; + context.textBaseline = "alphabetic"; + context.fillText(to_display_attribute['name'], axis_x_start + 5, axis_y_start + 20); + context.stroke(); + + //Graduations + context.font = this.graduation_style.font_size.toString() + 'px Arial'; + if (log_scale_y) { + if (TypeOf(list[0]) === 'string') { + throw new Error("Cannot use log scale on a non float axis.") } - context.stroke(); - context.closePath(); + this.draw_vertical_log_graduations(context, mvy, scaleY, -Math.log10(list[0]), -Math.log10(list[1]), + axis_x_start, axis_x_end, axis_y_end, canvas_height, Y); + } else { + this.draw_sc_vertical_graduations(context, mvy, scaleY, init_scaleY, axis_x_start, axis_x_end, axis_y_start, + axis_y_end, list, to_display_attribute, scroll_y, Y, canvas_height); } + context.stroke(); + context.closePath(); + } - draw_sc_horizontal_graduations(context, mvx, scaleX, init_scaleX, axis_x_start, axis_x_end, axis_y_start, axis_y_end, list, attribute, scroll_x, X, canvas_width) { - context.textAlign = 'center'; - if (attribute['type_'] == 'float') { - // var minX = list[0]; - // var maxX = list[1]; - if (scroll_x % 5 == 0) { - // let kx = 1.1*scaleX/init_scaleX; - // let num = Math.max(maxX - minX, 1); - // this.x_step = Math.min(num/(kx*(this.nb_points_x-1)), canvas_width/(scaleX*(this.nb_points_x - 1))); - this.x_step = canvas_width/(scaleX*(this.nb_points_x - 1)); + draw_sc_horizontal_graduations(context, mvx, scaleX, init_scaleX, axis_x_start, axis_x_end, axis_y_start, axis_y_end, list, attribute, scroll_x, X, canvas_width) { + context.textAlign = 'center'; + if (attribute['type_'] == 'float') { + // var minX = list[0]; + // var maxX = list[1]; + if (scroll_x % 5 == 0) { + // let kx = 1.1*scaleX/init_scaleX; + // let num = Math.max(maxX - minX, 1); + // this.x_step = Math.min(num/(kx*(this.nb_points_x-1)), canvas_width/(scaleX*(this.nb_points_x - 1))); + this.x_step = canvas_width / (scaleX * (this.nb_points_x - 1)); + } + var i = 0; + var x_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(this.x_step))); + var grad_beg_x = Math.ceil(1 / this.x_step * (axis_x_start - mvx - X) / scaleX) * this.x_step; + var grad_end_x = Math.ceil(1 / this.x_step * ((canvas_width - mvx) / scaleX)) * this.x_step; + while (grad_beg_x + i * this.x_step < grad_end_x) { + if (this.grid_on === true) { + Shape.drawLine(context, [[scaleX * (grad_beg_x + i * this.x_step) + mvx + X, axis_y_start], [scaleX * (grad_beg_x + i * this.x_step) + mvx + X, axis_y_end + 3]]); + } else { + Shape.drawLine(context, [[scaleX * (grad_beg_x + i * this.x_step) + mvx + X, axis_y_end - 3], [scaleX * (grad_beg_x + i * this.x_step) + mvx + X, axis_y_end + 3]]); } - var i=0; - var x_nb_digits = Math.max(0, 1-Math.floor(Math.log10(this.x_step))); - var grad_beg_x = Math.ceil(1/this.x_step * (axis_x_start - mvx - X)/scaleX) * this.x_step; - var grad_end_x = Math.ceil(1/this.x_step * ((canvas_width - mvx)/scaleX)) * this.x_step; - while(grad_beg_x + i*this.x_step < grad_end_x) { + context.fillText(MyMath.round(grad_beg_x + i * this.x_step, x_nb_digits), scaleX * (grad_beg_x + i * this.x_step) + mvx + X, axis_y_end + this.graduation_style.font_size); i++; + } + } else { + for (let i = 0; i < list.length; i++) { + if ((scaleX * i + mvx + X > axis_x_start) && (scaleX * i + mvx + X < axis_x_end)) { if (this.grid_on === true) { - Shape.drawLine(context, [[scaleX*(grad_beg_x + i*this.x_step) + mvx + X, axis_y_start], [scaleX*(grad_beg_x + i*this.x_step) + mvx + X, axis_y_end + 3]]); + Shape.drawLine(context, [[scaleX * i + mvx + X, axis_y_start], [scaleX * i + mvx + X, axis_y_end + 3]]); } else { - Shape.drawLine(context, [[scaleX*(grad_beg_x + i*this.x_step) + mvx + X, axis_y_end - 3], [scaleX*(grad_beg_x + i*this.x_step) + mvx + X, axis_y_end + 3]]); - } - context.fillText(MyMath.round(grad_beg_x + i*this.x_step, x_nb_digits), scaleX*(grad_beg_x + i*this.x_step) + mvx + X, axis_y_end + this.graduation_style.font_size); i++; - } - } else { - for (let i=0; i axis_x_start) && (scaleX*i + mvx + X < axis_x_end)) { - if (this.grid_on === true) { - Shape.drawLine(context, [[scaleX*i + mvx + X, axis_y_start], [scaleX*i + mvx + X, axis_y_end + 3]]); - } else { - Shape.drawLine(context, [[scaleX*i + mvx + X, axis_y_end - 3], [scaleX*i + mvx + X, axis_y_end + 3]]); - } - context.fillText(list[i], scaleX*i + mvx + X, axis_y_end + this.graduation_style.font_size); + Shape.drawLine(context, [[scaleX * i + mvx + X, axis_y_end - 3], [scaleX * i + mvx + X, axis_y_end + 3]]); } + context.fillText(list[i], scaleX * i + mvx + X, axis_y_end + this.graduation_style.font_size); } } } + } - draw_sc_vertical_graduations(context, mvy, scaleY, init_scaleY, axis_x_start, axis_x_end, axis_y_start, axis_y_end, list, attribute, scroll_y, Y, canvas_height) { - context.textAlign = 'end'; - context.textBaseline = 'middle'; - if (attribute['type_'] == 'float') { - var minY = list[0]; - var maxY = list[1]; - if (scroll_y % 5 == 0) { - // let ky = 1.1*scaleY/init_scaleY; - // let num = Math.max(maxY - minY, 1); - // this.y_step = Math.min(num/(ky*(this.nb_points_y-1)), canvas_height/(scaleY*(this.nb_points_y - 1))); - this.y_step = canvas_height/(scaleY*(this.nb_points_y - 1)); + draw_sc_vertical_graduations(context, mvy, scaleY, init_scaleY, axis_x_start, axis_x_end, axis_y_start, axis_y_end, list, attribute, scroll_y, Y, canvas_height) { + context.textAlign = 'end'; + context.textBaseline = 'middle'; + if (attribute['type_'] == 'float') { + var minY = list[0]; + var maxY = list[1]; + if (scroll_y % 5 == 0) { + // let ky = 1.1*scaleY/init_scaleY; + // let num = Math.max(maxY - minY, 1); + // this.y_step = Math.min(num/(ky*(this.nb_points_y-1)), canvas_height/(scaleY*(this.nb_points_y - 1))); + this.y_step = canvas_height / (scaleY * (this.nb_points_y - 1)); + } + var i = 0; + var grad_beg_y = Math.ceil(-1 / this.y_step * ((axis_y_end - mvy - Y) / scaleY)) * this.y_step; + var grad_end_y = Math.floor(mvy / (this.y_step * scaleY)) * this.y_step; + var y_nb_digits = Math.max(0, 1 - Math.floor(Math.log10(this.y_step))); + while (grad_beg_y + (i - 1) * this.y_step < grad_end_y) { + if (this.grid_on === true) { + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * (grad_beg_y + i * this.y_step) + mvy + Y], [axis_x_end, -scaleY * (grad_beg_y + i * this.y_step) + mvy + Y]]); + } else { + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * (grad_beg_y + i * this.y_step) + mvy + Y], [axis_x_start + 3, -scaleY * (grad_beg_y + i * this.y_step) + mvy + Y]]); } - var i=0; - var grad_beg_y = Math.ceil(-1/this.y_step*((axis_y_end - mvy - Y)/scaleY)) * this.y_step; - var grad_end_y = Math.floor(mvy/(this.y_step * scaleY)) * this.y_step; - var y_nb_digits = Math.max(0, 1-Math.floor(Math.log10(this.y_step))); - while (grad_beg_y + (i-1)*this.y_step < grad_end_y) { + context.fillText(MyMath.round(grad_beg_y + i * this.y_step, y_nb_digits), axis_x_start - 5, -scaleY * (grad_beg_y + i * this.y_step) + mvy + Y); + i++; + } + } else { + for (let i = 0; i < list.length; i++) { + if ((-scaleY * i + mvy + Y > axis_y_start + 5) && (-scaleY * i + mvy + Y < axis_y_end)) { if (this.grid_on === true) { - Shape.drawLine(context,[[axis_x_start - 3, -scaleY*(grad_beg_y + i*this.y_step) + mvy + Y], [axis_x_end, -scaleY*(grad_beg_y + i*this.y_step) + mvy + Y]]); + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * i + mvy + Y], [axis_x_end, -scaleY * i + mvy + Y]]); } else { - Shape.drawLine(context, [[axis_x_start - 3, -scaleY*(grad_beg_y + i*this.y_step) + mvy + Y], [axis_x_start + 3, -scaleY*(grad_beg_y + i*this.y_step) + mvy + Y]]); - } - context.fillText(MyMath.round(grad_beg_y + i*this.y_step, y_nb_digits), axis_x_start - 5, -scaleY*(grad_beg_y + i*this.y_step) + mvy + Y); - i++; - } - } else { - for (let i=0; i axis_y_start + 5) && (-scaleY*i + mvy + Y < axis_y_end)) { - if (this.grid_on === true) { - Shape.drawLine(context,[[axis_x_start - 3, -scaleY*i + mvy + Y], [axis_x_end, -scaleY*i + mvy + Y]]); - } else { - Shape.drawLine(context, [[axis_x_start - 3, -scaleY*i + mvy + Y], [axis_x_start + 3, -scaleY*i + mvy + Y]]); - } - context.fillText(list[i], axis_x_start - 5, -scaleY*i + mvy + Y); + Shape.drawLine(context, [[axis_x_start - 3, -scaleY * i + mvy + Y], [axis_x_start + 3, -scaleY * i + mvy + Y]]); } + context.fillText(list[i], axis_x_start - 5, -scaleY * i + mvy + Y); } } } + } } export class PointFamily { - constructor (public color: string, - public point_index: number[], - public name:string) {} - - public static deserialize(serialized) { - return new PointFamily(rgb_to_hex(serialized['color']), - serialized['point_index'], - serialized['name']); - } + constructor(public color: string, + public point_index: number[], + public name: string) { } + + public static deserialize(serialized) { + return new PointFamily(rgb_to_hex(serialized['color']), + serialized['point_index'], + serialized['name']); + } } @@ -505,433 +507,435 @@ export class PointFamily { * A class for sorting lists. */ export class Sort { - nbPermutations:number = 0; - permutations: number[] = []; - constructor(){}; + nbPermutations: number = 0; + permutations: number[] = []; + constructor() { }; - MergeSort(items: number[]): number[] { - return this.divide(items); - } + MergeSort(items: number[]): number[] { + return this.divide(items); + } - divide(items: number[]): number[] { - var halfLength = Math.ceil(items.length / 2); - var low = items.slice(0, halfLength); - var high = items.slice(halfLength); - if (halfLength > 1) { - low = this.divide(low); - high = this.divide(high); - } - return this.combine(low, high); + divide(items: number[]): number[] { + var halfLength = Math.ceil(items.length / 2); + var low = items.slice(0, halfLength); + var high = items.slice(halfLength); + if (halfLength > 1) { + low = this.divide(low); + high = this.divide(high); } - - combine(low: number[], high: number[]): number[] { - var indexLow = 0; - var indexHigh = 0; - var lengthLow = low.length; - var lengthHigh = high.length; - var combined = []; - while (indexLow < lengthLow || indexHigh < lengthHigh) { - var lowItem = low[indexLow]; - var highItem = high[indexHigh]; - if (lowItem !== undefined) { - if (highItem === undefined) { - combined.push(lowItem); - indexLow++; - } else { - if (lowItem <= highItem) { - combined.push(lowItem); - indexLow++; - } else { - combined.push(highItem); - this.nbPermutations = this.nbPermutations + lengthLow - indexLow; - indexHigh++; - } - } + return this.combine(low, high); + } + + combine(low: number[], high: number[]): number[] { + var indexLow = 0; + var indexHigh = 0; + var lengthLow = low.length; + var lengthHigh = high.length; + var combined = []; + while (indexLow < lengthLow || indexHigh < lengthHigh) { + var lowItem = low[indexLow]; + var highItem = high[indexHigh]; + if (lowItem !== undefined) { + if (highItem === undefined) { + combined.push(lowItem); + indexLow++; + } else { + if (lowItem <= highItem) { + combined.push(lowItem); + indexLow++; } else { - if (highItem !== undefined) { - combined.push(highItem); - indexHigh++; - } + combined.push(highItem); + this.nbPermutations = this.nbPermutations + lengthLow - indexLow; + indexHigh++; } + } + } else { + if (highItem !== undefined) { + combined.push(highItem); + indexHigh++; + } } - return combined; } + return combined; + } - sortObjsByAttribute(list:any[], attribute_name:string): any[] { - if (!List.is_include(attribute_name, Object.getOwnPropertyNames(list[0]))) { - throw new Error('sortObjsByAttribute : ' + attribute_name + ' is not a property of the object') - } - var attribute_type = TypeOf(list[0][attribute_name]); - var list_copy = Array.from(list); - - this.permutations = []; - for (let i=0; i= 0) && (x - length <= canvasWidth) && (y + length >= 0) && (y - length <= canvasHeight); + } + + refresh_nb_digits(x_nb_digits, y_nb_digits): [number, number] { + var new_x_digits = x_nb_digits; + var new_y_digit = y_nb_digits; + if (isNaN(new_x_digits)) { + new_x_digits = 3; } - - isTooltipInsideCanvas(cx, cy, size, mvx, mvy, scaleX, scaleY, canvasWidth, canvasHeight) { - var x = scaleX*cx + mvx; - var y = scaleY*cy + mvy; - var length = 100*size; - return (x+length>=0) && (x-length<=canvasWidth) && (y+length>=0) && (y-length<=canvasHeight); + if (isNaN(new_y_digit)) { + new_y_digit = 3; } + return [new_x_digits, new_y_digit]; + } - refresh_nb_digits(x_nb_digits, y_nb_digits): [number, number] { - var new_x_digits = x_nb_digits; - var new_y_digit = y_nb_digits; - if (isNaN(new_x_digits)) { - new_x_digits = 3; + initialize_text_mergeOFF(context, x_nb_digits, y_nb_digits, elt): [string[], number] { + var textfills = ['Information']; + var text_max_length = context.measureText('Information').width; + for (let i = 0; i < this.attribute_names.length; i++) { + let attribute_name = this.attribute_names[i]; + let attribute_type = TypeOf(elt[attribute_name]); + if (attribute_type == 'float') { + var text = attribute_name + ' : ' + MyMath.round(elt[attribute_name], Math.max(x_nb_digits, y_nb_digits, 2)); //x_nb_digit évidemment pas définie lorsque l'axe des x est un string... + } else if (attribute_type == 'color') { + text = attribute_name + ' : ' + color_to_string(elt[attribute_name]); + } else { + text = attribute_name + ' : ' + elt[attribute_name]; } - if (isNaN(new_y_digit)) { - new_y_digit = 3; + var text_w = context.measureText(text).width; + textfills.push(text); + if (text_w > text_max_length) { + text_max_length = text_w; } - return [new_x_digits, new_y_digit]; } + return [textfills, text_max_length]; + } - initialize_text_mergeOFF(context, x_nb_digits, y_nb_digits, elt): [string[], number] { - var textfills = ['Information']; - var text_max_length = context.measureText('Information').width; - for (let i=0; i text_max_length) { - text_max_length = text_w; - } } - return [textfills, text_max_length]; + if (text_w > text_max_length) { + text_max_length = text_w; + } } - - initialize_text_mergeON(context, x_nb_digits, y_nb_digits, point, initial_point_list, elements, axes): [string[], number] { - var textfills = ['Information']; - var text_max_length = context.measureText('Information').width; - for (let i=0; i text_max_length) { - text_max_length = text_w; - } + return [textfills, text_max_length]; + } + + draw(context, point, mvx, mvy, scaleX, scaleY, canvas_width, canvas_height, + X, Y, x_nb_digits, y_nb_digits, point_list, initial_point_list, elements, + mergeON, axes, log_scale_x, log_scale_y) { + var textfills = []; + var text_max_length = 0; + context.font = this.text_style.font; + [x_nb_digits, y_nb_digits] = this.refresh_nb_digits(x_nb_digits, y_nb_digits); + if (point.isPointInList(point_list)) { + var index = point.getPointIndex(point_list); + var elt = elements[index]; + if (mergeON === true) { + [textfills, text_max_length] = this.initialize_text_mergeON(context, x_nb_digits, y_nb_digits, point, initial_point_list, elements, axes); + } else { + [textfills, text_max_length] = this.initialize_text_mergeOFF(context, x_nb_digits, y_nb_digits, elt); } - return [textfills, text_max_length]; } - draw(context, point, mvx, mvy, scaleX, scaleY, canvas_width, canvas_height, - X, Y, x_nb_digits, y_nb_digits, point_list, initial_point_list, elements, - mergeON, axes, log_scale_x, log_scale_y) { - var textfills = []; - var text_max_length = 0; - context.font = this.text_style.font; - [x_nb_digits, y_nb_digits] = this.refresh_nb_digits(x_nb_digits, y_nb_digits); - if (point.isPointInList(point_list)) { - var index = point.getPointIndex(point_list); - var elt = elements[index]; - if (mergeON === true) { - [textfills, text_max_length] = this.initialize_text_mergeON(context, x_nb_digits, y_nb_digits, point, initial_point_list, elements, axes); - } else { - [textfills, text_max_length] = this.initialize_text_mergeOFF(context, x_nb_digits, y_nb_digits, elt); - } - } + if (textfills.length > 0) { + var tp_height = textfills.length * this.text_style.font_size * 1.25; + var cx = point.cx, cy = point.cy; + if (log_scale_x) cx = Math.log10(cx); + if (log_scale_y) cy = -Math.log10(-cy); + var point_size = point.point_style.size; + var decalage = 2.5 * point_size + 15; + var tp_x = scaleX * cx + mvx + decalage + X; + var tp_y = scaleY * cy + mvy - 1 / 2 * tp_height + Y; + var tp_width = text_max_length * 1.3; - if (textfills.length > 0) { - var tp_height = textfills.length*this.text_style.font_size*1.25; - var cx = point.cx, cy = point.cy; - if (log_scale_x) cx = Math.log10(cx); - if (log_scale_y) cy = -Math.log10(-cy); - var point_size = point.point_style.size; - var decalage = 2.5*point_size + 15; - var tp_x = scaleX*cx + mvx + decalage + X; - var tp_y = scaleY*cy + mvy - 1/2*tp_height + Y; - var tp_width = text_max_length*1.3; - - // Bec - var point1 = [tp_x - decalage/2, scaleY*cy + mvy + Y]; - var point2 = [tp_x, scaleY*cy + mvy + Y + 5]; - var point3 = [tp_x, scaleY*cy + mvy + Y - 5]; - - if (tp_x + tp_width > canvas_width + X) { - tp_x = scaleX*cx + mvx - decalage - tp_width + X; - point1 = [tp_x + tp_width, scaleY*cy + mvy + Y + 5]; - point2 = [tp_x + tp_width, scaleY*cy + mvy + Y - 5]; - point3 = [tp_x + tp_width + decalage/2, scaleY*cy + mvy + Y]; - } - if (tp_y < Y) { - tp_y = scaleY*cy + mvy + Y - 7*point_size; - } - if (tp_y + tp_height > canvas_height + Y) { - tp_y = scaleY*cy + mvy - tp_height + Y + 7*point_size; - } - context.beginPath(); - Shape.drawLine(context, [point1, point2, point3]); - context.stroke(); - context.fill(); + // Bec + var point1 = [tp_x - decalage / 2, scaleY * cy + mvy + Y]; + var point2 = [tp_x, scaleY * cy + mvy + Y + 5]; + var point3 = [tp_x, scaleY * cy + mvy + Y - 5]; + + if (tp_x + tp_width > canvas_width + X) { + tp_x = scaleX * cx + mvx - decalage - tp_width + X; + point1 = [tp_x + tp_width, scaleY * cy + mvy + Y + 5]; + point2 = [tp_x + tp_width, scaleY * cy + mvy + Y - 5]; + point3 = [tp_x + tp_width + decalage / 2, scaleY * cy + mvy + Y]; + } + if (tp_y < Y) { + tp_y = scaleY * cy + mvy + Y - 7 * point_size; + } + if (tp_y + tp_height > canvas_height + Y) { + tp_y = scaleY * cy + mvy - tp_height + Y + 7 * point_size; + } + context.beginPath(); + Shape.drawLine(context, [point1, point2, point3]); + context.stroke(); + context.fill(); - Shape.roundRect(tp_x, tp_y, tp_width, tp_height, this.tooltip_radius, context, this.surface_style.color_fill, string_to_hex('black'), 0.5, + Shape.roundRect(tp_x, tp_y, tp_width, tp_height, this.tooltip_radius, context, this.surface_style.color_fill, string_to_hex('black'), 0.5, this.surface_style.opacity, []); - context.fillStyle = this.text_style.text_color; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - - // var x_start = tp_x + 1/10*tp_width; - - var current_y = tp_y + 0.75*this.text_style.font_size; - for (var i=0; i canvas_width + X) { - tp_x = x - tp_width; - // point1 = [tp_x + tp_width, y + 15]; - // point2 = [tp_x + tp_width, y - 15]; - // point3 = [tp_x + tp_width, y]; - } - if (tp_y < Y) { - tp_y = y; - } - if (tp_y + tp_height > canvas_height + Y) { - tp_y = y - tp_height; - } + // Bec + // var point1 = [tp_x + 5, y]; + // var point2 = [tp_x, y + 15]; + // var point3 = [tp_x, y - 15]; + + if (tp_x + tp_width > canvas_width + X) { + tp_x = x - tp_width; + // point1 = [tp_x + tp_width, y + 15]; + // point2 = [tp_x + tp_width, y - 15]; + // point3 = [tp_x + tp_width, y]; + } + if (tp_y < Y) { + tp_y = y; + } + if (tp_y + tp_height > canvas_height + Y) { + tp_y = y - tp_height; + } - // context.beginPath(); - // Shape.drawLine(context, [point1, point2, point3]); - // context.stroke(); - // context.fill(); + // context.beginPath(); + // Shape.drawLine(context, [point1, point2, point3]); + // context.stroke(); + // context.fill(); - Shape.roundRect(tp_x, tp_y, tp_width, tp_height, this.tooltip_radius, context, this.surface_style.color_fill, string_to_hex('black'), 0.5, + Shape.roundRect(tp_x, tp_y, tp_width, tp_height, this.tooltip_radius, context, this.surface_style.color_fill, string_to_hex('black'), 0.5, this.surface_style.opacity, []); - context.fillStyle = this.text_style.text_color; - context.textAlign = 'center'; - context.textBaseline = 'middle'; - context.fillText(this.message, tp_x + tp_width/2, tp_y + tp_height/2); - } + context.fillStyle = this.text_style.text_color; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText(this.message, tp_x + tp_width / 2, tp_y + tp_height / 2); + } } export class Attribute { - list:any[]; - alias:string; - constructor(public name:string, - public type_:string) {} - - public static deserialize(serialized) { - return new Attribute(serialized['name'], - serialized['type_']); - } + list: any[]; + alias: string; + constructor(public name: string, + public type_: string) { } - copy() { - var attribute_copy = new Attribute(this.name, this.type_); - attribute_copy['list'] = this.list; - return attribute_copy; - } + public static deserialize(serialized) { + return new Attribute(serialized['name'], + serialized['type_']); + } + + copy() { + var attribute_copy = new Attribute(this.name, this.type_); + attribute_copy['list'] = this.list; + return attribute_copy; + } } export class Window { - constructor(public height:number,public width:number, public name?:string){ - } + constructor(public height: number, public width: number, public name?: string) { + } - public static deserialize(serialized) { - return new Window(serialized['height'], - serialized['width'], - serialized['name']); - } + public static deserialize(serialized) { + return new Window(serialized['height'], + serialized['width'], + serialized['name']); + } } -export function check_package_version(package_version:string, requirement:string) { - var version_array = package_version.split('.'); - var requirement_array = requirement.split('.'); - var package_version_num = Number(version_array[0])*Math.pow(10, 4) + Number(version_array[1])*Math.pow(10,2) + - Number(version_array[2]); - var requirement_num = Number(requirement_array[0])*Math.pow(10, 4) + Number(requirement_array[1])*Math.pow(10,2) + - Number(requirement_array[2]); - if (package_version_num < requirement_num) { - alert("plot_data's version must be updated. Current version: " + package_version + ", minimum requirement: " + requirement); - } +export function check_package_version(package_version: string, requirement: string) { + var version_array = package_version.split('.'); + var requirement_array = requirement.split('.'); + var package_version_num = Number(version_array[0]) * Math.pow(10, 4) + Number(version_array[1]) * Math.pow(10, 2) + + Number(version_array[2]); + var requirement_num = Number(requirement_array[0]) * Math.pow(10, 4) + Number(requirement_array[1]) * Math.pow(10, 2) + + Number(requirement_array[2]); + if (package_version_num < requirement_num) { + alert("plot_data's version must be updated. Current version: " + package_version + ", minimum requirement: " + requirement); + } } /** * A generic equals function that compares values and not references. */ - export function equals(a, b) { - if (a === b) return true; - - if (a && b && typeof a == 'object' && typeof b == 'object') { - if (a.constructor !== b.constructor) return false; - - var length, i, keys; - if (Array.isArray(a)) { - length = a.length; - if (length != b.length) return false; - for (i = length; i-- !== 0;) - if (!equals(a[i], b[i])) return false; - return true; - } +export function equals(a, b) { + if (a === b) return true; - if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; - if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); - if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); - - keys = Object.keys(a); - length = keys.length; - if (length !== Object.keys(b).length) return false; + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; for (i = length; i-- !== 0;) - if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + if (!equals(a[i], b[i])) return false; + return true; + } - for (i = length; i-- !== 0;) { - var key = keys[i]; + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); - if (!equals(a[key], b[key])) return false; - } + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; - return true; + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (!equals(a[key], b[key])) return false; } - // true if both NaN, false otherwise - return a!==a && b!==b; + return true; + } + + // true if both NaN, false otherwise + return a !== a && b !== b; } var nextCol = 1; -export function genColor(){ +export function genColor() { var ret = []; // via http://stackoverflow.com/a/15804183 - if(nextCol < 16777215){ + if (nextCol < 16777215) { ret.push(nextCol & 0xff); // R ret.push((nextCol & 0xff00) >> 8); // G ret.push((nextCol & 0xff0000) >> 16); // B @@ -945,75 +949,75 @@ export function genColor(){ export function getCurvePoints(pts, tension, isClosed, numOfSegments) { - // use input value if provided, or use a default value - tension = (typeof tension != 'undefined') ? tension : 0.5; - isClosed = isClosed ? isClosed : false; - numOfSegments = numOfSegments ? numOfSegments : 16; - - var _pts = [], res = [], // clone array - x, y, // our x,y coords - t1x, t2x, t1y, t2y, // tension vectors - c1, c2, c3, c4, // cardinal points - st, t, i; // steps based on num. of segments - - // clone array so we don't change the original - // - _pts = pts.slice(0); - - // The algorithm require a previous and next point to the current point array. - // Check if we will draw closed or open curve. - // If closed, copy end points to beginning and first points to end - // If open, duplicate first points to befinning, end points to end - if (isClosed) { - _pts.unshift(pts[pts.length - 1]); - _pts.unshift(pts[pts.length - 2]); - _pts.unshift(pts[pts.length - 1]); - _pts.unshift(pts[pts.length - 2]); - _pts.push(pts[0]); - _pts.push(pts[1]); - } - else { - _pts.unshift(pts[1]); //copy 1. point and insert at beginning - _pts.unshift(pts[0]); - _pts.push(pts[pts.length - 2]); //copy last point and append - _pts.push(pts[pts.length - 1]); - } - - // ok, lets start.. - - // 1. loop goes through point array - // 2. loop goes through each segment between the 2 pts + 1e point before and after - for (i=2; i < (_pts.length - 4); i+=2) { - for (t=0; t <= numOfSegments; t++) { - - // calc tension vectors - t1x = (_pts[i+2] - _pts[i-2]) * tension; - t2x = (_pts[i+4] - _pts[i]) * tension; - - t1y = (_pts[i+3] - _pts[i-1]) * tension; - t2y = (_pts[i+5] - _pts[i+1]) * tension; - - // calc step - st = t / numOfSegments; - - // calc cardinals - c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1; - c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); - c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st; - c4 = Math.pow(st, 3) - Math.pow(st, 2); - - // calc x and y cords with common control vectors - x = c1 * _pts[i] + c2 * _pts[i+2] + c3 * t1x + c4 * t2x; - y = c1 * _pts[i+1] + c2 * _pts[i+3] + c3 * t1y + c4 * t2y; + // use input value if provided, or use a default value + tension = (typeof tension != 'undefined') ? tension : 0.5; + isClosed = isClosed ? isClosed : false; + numOfSegments = numOfSegments ? numOfSegments : 16; + + var _pts = [], res = [], // clone array + x, y, // our x,y coords + t1x, t2x, t1y, t2y, // tension vectors + c1, c2, c3, c4, // cardinal points + st, t, i; // steps based on num. of segments + + // clone array so we don't change the original + // + _pts = pts.slice(0); + + // The algorithm require a previous and next point to the current point array. + // Check if we will draw closed or open curve. + // If closed, copy end points to beginning and first points to end + // If open, duplicate first points to befinning, end points to end + if (isClosed) { + _pts.unshift(pts[pts.length - 1]); + _pts.unshift(pts[pts.length - 2]); + _pts.unshift(pts[pts.length - 1]); + _pts.unshift(pts[pts.length - 2]); + _pts.push(pts[0]); + _pts.push(pts[1]); + } + else { + _pts.unshift(pts[1]); //copy 1. point and insert at beginning + _pts.unshift(pts[0]); + _pts.push(pts[pts.length - 2]); //copy last point and append + _pts.push(pts[pts.length - 1]); + } + + // ok, lets start.. + + // 1. loop goes through point array + // 2. loop goes through each segment between the 2 pts + 1e point before and after + for (i = 2; i < (_pts.length - 4); i += 2) { + for (t = 0; t <= numOfSegments; t++) { + + // calc tension vectors + t1x = (_pts[i + 2] - _pts[i - 2]) * tension; + t2x = (_pts[i + 4] - _pts[i]) * tension; + + t1y = (_pts[i + 3] - _pts[i - 1]) * tension; + t2y = (_pts[i + 5] - _pts[i + 1]) * tension; + + // calc step + st = t / numOfSegments; + + // calc cardinals + c1 = 2 * Math.pow(st, 3) - 3 * Math.pow(st, 2) + 1; + c2 = -(2 * Math.pow(st, 3)) + 3 * Math.pow(st, 2); + c3 = Math.pow(st, 3) - 2 * Math.pow(st, 2) + st; + c4 = Math.pow(st, 3) - Math.pow(st, 2); + + // calc x and y cords with common control vectors + x = c1 * _pts[i] + c2 * _pts[i + 2] + c3 * t1x + c4 * t2x; + y = c1 * _pts[i + 1] + c2 * _pts[i + 3] + c3 * t1y + c4 * t2y; + + //store points in array + res.push(x); + res.push(y); - //store points in array - res.push(x); - res.push(y); - - } } + } - return res; + return res; } @@ -1026,24 +1030,24 @@ export function merge_alert() { export function permutator(inputArr) { - var results = []; + var results = []; - function permute(arr, memo) { - var cur, memo = memo || []; + function permute(arr, memo) { + var cur, memo = memo || []; - for (var i = 0; i < arr.length; i++) { - cur = arr.splice(i, 1); - if (arr.length === 0) { - results.push(memo.concat(cur)); - } - permute(arr.slice(), memo.concat(cur)); - arr.splice(i, 0, cur[0]); + for (var i = 0; i < arr.length; i++) { + cur = arr.splice(i, 1); + if (arr.length === 0) { + results.push(memo.concat(cur)); } - - return results; + permute(arr.slice(), memo.concat(cur)); + arr.splice(i, 0, cur[0]); } - return permute(inputArr, undefined); + return results; + } + + return permute(inputArr, undefined); } @@ -1057,37 +1061,37 @@ export function permutator(inputArr) { * @returns the input dictionaries with potential new values */ export function set_default_values(dict_, default_dict_) { - if (dict_ === undefined) { - dict_ = {}; - } - var properties_names = Object.getOwnPropertyNames(default_dict_); - var entries = Object.entries(dict_); - for (let i=0; i e.join(",")).join("\n"); var encodedUri = encodeURI(csvContent); @@ -1134,23 +1138,23 @@ export class RubberBand { readonly MIN_LENGTH = 5; readonly BORDER = 5; constructor(public attributeName: string, - private _minValue: number, - private _maxValue: number, - public isVertical: boolean) {} + private _minValue: number, + private _maxValue: number, + public isVertical: boolean) { } - public get canvasLength() {return Math.abs(this.realMax - this.realMin)} + public get canvasLength() { return Math.abs(this.realMax - this.realMin) } - public get length() {return Math.abs(this.maxValue - this.minValue)} + public get length() { return Math.abs(this.maxValue - this.minValue) } - public get normedLength() {return Math.abs(this.axisMax - this.axisMin)} + public get normedLength() { return Math.abs(this.axisMax - this.axisMin) } - public set minValue(value: number) {this._minValue = value} + public set minValue(value: number) { this._minValue = value } - public get minValue() {return this._minValue} + public get minValue() { return this._minValue } - public set maxValue(value: number) {this._maxValue = value} + public set maxValue(value: number) { this._maxValue = value } - public get maxValue() {return this._maxValue} + public get maxValue() { return this._maxValue } public draw(origin: number, context: CanvasRenderingContext2D, colorFill: string, colorStroke: string, lineWidth: number, alpha: number) { if (this.isVertical) { @@ -1182,12 +1186,12 @@ export class RubberBand { if (axis.type_ == 'float') { let real_min = axis.list[0]; let real_max = axis.list[1]; - if ((this.isVertical && !inverted) || (!this.isVertical && inverted)) {return (1 - axisValue) * real_max + axisValue * real_min} + if ((this.isVertical && !inverted) || (!this.isVertical && inverted)) { return (1 - axisValue) * real_max + axisValue * real_min } return axisValue * real_max + (1 - axisValue) * real_min } let nb_values = axis.list.length; - if ((this.isVertical && !inverted) || (!this.isVertical && inverted)) {return (1 - axisValue) * (nb_values - 1)} + if ((this.isVertical && !inverted) || (!this.isVertical && inverted)) { return (1 - axisValue) * (nb_values - 1) } return axisValue * (nb_values - 1) } @@ -1199,44 +1203,44 @@ export class RubberBand { this.maxValue = otherRubberBand.maxValue; const diffSenseSameDir = axisInverted != otherAxisInverted && this.isVertical == otherRubberBand.isVertical; const sameSenseDiffDir = axisInverted == otherAxisInverted && this.isVertical != otherRubberBand.isVertical; - if (diffSenseSameDir || sameSenseDiffDir) {this.normedInvert()}; + if (diffSenseSameDir || sameSenseDiffDir) { this.normedInvert() }; this.axisToReal(axisOrigin, axisEnd); } public axisChangeUpdate(origin: number[], end: number[], wasVertical: boolean, newOrigin: number[], newEnd: number[], isVertical: boolean) { - const bounds = [origin[0] + end[0], origin[1] + end[1]]; - const newBounds = [newOrigin[0] + newEnd[0], newOrigin[1] + newEnd[1]]; - const lengths = [Math.abs(origin[0] - end[0]), Math.abs(origin[1] - end[1])]; - const newLengths = [Math.abs(newOrigin[0] - newEnd[0]), Math.abs(newOrigin[1] - newEnd[1])]; + const bounds = [origin[0] + end[0], origin[1] + end[1]]; + const newBounds = [newOrigin[0] + newEnd[0], newOrigin[1] + newEnd[1]]; + const lengths = [Math.abs(origin[0] - end[0]), Math.abs(origin[1] - end[1])]; + const newLengths = [Math.abs(newOrigin[0] - newEnd[0]), Math.abs(newOrigin[1] - newEnd[1])]; - let index = 0; - let newIndex = 0; - if (wasVertical) {index = 1; this.invert(bounds);} - else {newIndex = 1} + let index = 0; + let newIndex = 0; + if (wasVertical) { index = 1; this.invert(bounds); } + else { newIndex = 1 } - const start = Math.min(origin[index], end[index]); - const newStart = Math.min(newOrigin[newIndex], newEnd[newIndex]); - this.switchOrientation(start, newStart, [lengths[index], newLengths[newIndex]]); + const start = Math.min(origin[index], end[index]); + const newStart = Math.min(newOrigin[newIndex], newEnd[newIndex]); + this.switchOrientation(start, newStart, [lengths[index], newLengths[newIndex]]); - if (!wasVertical) {this.invert(newBounds)} + if (!wasVertical) { this.invert(newBounds) } } public updateFromMouse(mouse1: [number, number], mouse2: [number, number], axis: Attribute, axisOrigin: [number, number], axisEnd: [number, number], inverted: boolean): void { - var mouseIdx = this.isVertical ? 1 : 0; - this.newBoundsUpdate( - mouse1[mouseIdx], mouse2[mouseIdx], - [axisOrigin[mouseIdx], axisEnd[mouseIdx]], - axis, inverted); + var mouseIdx = this.isVertical ? 1 : 0; + this.newBoundsUpdate( + mouse1[mouseIdx], mouse2[mouseIdx], + [axisOrigin[mouseIdx], axisEnd[mouseIdx]], + axis, inverted); } public newBoundsUpdate(newMin: number, newMax: number, axisBounds: number[], axis: Attribute, inverted: boolean): void { - this.realMin = Math.max(Math.min(newMin, newMax), axisBounds[1]); - this.realMax = Math.min(Math.max(newMin, newMax), axisBounds[0]); - this.realToAxis(axisBounds[0], axisBounds[1]) - this.updateMinMax(axis, inverted); + this.realMin = Math.max(Math.min(newMin, newMax), axisBounds[1]); + this.realMax = Math.min(Math.max(newMin, newMax), axisBounds[0]); + this.realToAxis(axisBounds[0], axisBounds[1]) + this.updateMinMax(axis, inverted); } public updateMinMax(axis: Attribute, inverted: boolean): void { @@ -1268,7 +1272,7 @@ export class RubberBand { this.isVertical = !this.isVertical; } - public flipValues(): void{ + public flipValues(): void { [this.axisMin, this.axisMax] = [this.axisMax, this.axisMin]; [this.minValue, this.maxValue] = [this.minValue, this.maxValue]; [this.realMin, this.realMax] = [this.realMax, this.realMin]; @@ -1320,15 +1324,15 @@ export class RubberBand { public mouseDown(mouseAxis: number) { this.isClicked = true; - if (Math.abs(mouseAxis - this.realMin) <= this.BORDER_SIZE) {this.minUpdate = true} - else if (Math.abs(mouseAxis - this.realMax) <= this.BORDER_SIZE) {this.maxUpdate = true} - else {this.lastValues = new Vertex(this.minValue, this.maxValue)} + if (Math.abs(mouseAxis - this.realMin) <= this.BORDER_SIZE) { this.minUpdate = true } + else if (Math.abs(mouseAxis - this.realMax) <= this.BORDER_SIZE) { this.maxUpdate = true } + else { this.lastValues = new Vertex(this.minValue, this.maxValue) } } public mouseMove(downValue: number, currentValue: number) { if (this.isClicked) { - if (this.minUpdate) {this.minValue = currentValue} - else if (this.maxUpdate) {this.maxValue = currentValue} + if (this.minUpdate) { this.minValue = currentValue } + else if (this.maxUpdate) { this.maxValue = currentValue } else { const translation = currentValue - downValue; this.minValue = this.lastValues.x + translation; @@ -1353,9 +1357,9 @@ export class Vertex { this.y = y; }; - get coordinates(): Vertex {return new Vertex(this.x, this.y)} + get coordinates(): Vertex { return new Vertex(this.x, this.y) } - public copy(): Vertex {return Object.create(this)} + public copy(): Vertex { return Object.create(this) } public add(other: Vertex): Vertex { let copy = this.copy(); @@ -1378,9 +1382,9 @@ export class Vertex { return copy } - public get normL1(): number {return Math.abs(this.x) + Math.abs(this.y)} + public get normL1(): number { return Math.abs(this.x) + Math.abs(this.y) } - public get norm(): number {return (this.x ** 2 + this.y ** 2) ** 0.5} + public get norm(): number { return (this.x ** 2 + this.y ** 2) ** 0.5 } public scale(scale: Vertex): Vertex { let copy = this.copy(); @@ -1420,7 +1424,7 @@ export class newShape { public isHovered: boolean = false; public isClicked: boolean = false; public isSelected: boolean = false; - constructor() {}; + constructor() { }; public draw(context: CanvasRenderingContext2D) { const scaledPath = new Path2D(); @@ -1436,11 +1440,11 @@ export class newShape { context.restore(); } - public mouseDown(canvasMouse: Vertex, frameMouse: Vertex) {} + public mouseDown(canvasMouse: Vertex, frameMouse: Vertex) { } - public mouseMove(canvasMouse: Vertex, frameMouse: Vertex) {return false} + public mouseMove(canvasMouse: Vertex, frameMouse: Vertex) { return false } - public mouseUp() {} + public mouseUp() { } } export class newCircle extends newShape { @@ -1655,46 +1659,67 @@ export class Triangle extends AbstractTriangle { } public buildPath(): Path2D { - if (this.orientation == 'up') {return new UpTriangle(this.center, this.size).path} - if (this.orientation == 'down') {return new DownTriangle(this.center, this.size).path} - if (this.orientation == 'right') {return new RightTriangle(this.center, this.size).path} - if (this.orientation == 'left') {return new LeftTriangle(this.center, this.size).path} + if (this.orientation == 'up') { return new UpTriangle(this.center, this.size).path } + if (this.orientation == 'down') { return new DownTriangle(this.center, this.size).path } + if (this.orientation == 'right') { return new RightTriangle(this.center, this.size).path } + if (this.orientation == 'left') { return new LeftTriangle(this.center, this.size).path } } } +export interface textParams { + width?: number, height?: number, fontsize?: number, multiLine?: boolean, font?: string, align?: string, + baseline?: string, style?: string, orientation?: number +} + +const DEFAULT_FONTSIZE = 12; export class newText extends newShape { public scale: number = 1; + public width: number; + public height: number; + public fontsize: number; + public font: string; + public align: string; + public baseline: string; + public style: string; + public orientation: number; + public multiLine: boolean; constructor( public text: string, - public origin: Vertex = new Vertex(0, 0), - public width: number = null, - public fontsize: number = 12, - public font: string = 'sans-serif', - public justify: string = 'left', - public baseline: string = 'alphabetic', - protected _style: string = '', - protected _orientation: number = 0, - protected multiLine: boolean = false - ) { - super(); - this.path = this.buildPath(); - } - - get orientation(): number {return this._orientation}; - - set orientation(value: number) {this._orientation = value}; - - get style(): string {return this._style}; + public origin: Vertex, + { width = null, + height = null, + fontsize = null, + multiLine = false, + font = 'sans-serif', + align = 'left', + baseline = 'alphabetic', + style = '', + orientation = 0 + }: textParams = {}) { + super(); + this.width = width; + this.height = height; + this.fontsize = fontsize; + this.multiLine = multiLine; + this.font = font; + this.align = align; + this.baseline = baseline; + this.style = style; + this.orientation = orientation; + this.path = this.buildPath(); + } - set style(value: string) {this._style = value}; + private static buildFont(style: string, fontsize: number, font: string): string { + return `${style} ${fontsize}px ${font}` + } - get fullFont() {return `${this.style} ${this.fontsize}px ${this.font}`} + get fullFont() { return newText.buildFont(this.style, this.fontsize, this.font) } private automaticFontSize(context: CanvasRenderingContext2D): number { let tmp_context: CanvasRenderingContext2D = context let pxMaxWidth: number = this.width * this.scale; tmp_context.font = '1px ' + this.font; - return pxMaxWidth / (tmp_context.measureText(this.text).width) + return Math.min(pxMaxWidth / (tmp_context.measureText(this.text).width), DEFAULT_FONTSIZE) } public buildPath(): Path2D { @@ -1703,54 +1728,116 @@ export class newText extends newShape { return path } - public static capitalize(value: string): string {return value.charAt(0).toUpperCase() + value.slice(1)} + public static capitalize(value: string): string { return value.charAt(0).toUpperCase() + value.slice(1) } - public capitalizeSelf(): void {this.text = newText.capitalize(this.text)} + public capitalizeSelf(): void { this.text = newText.capitalize(this.text) } public draw(context: CanvasRenderingContext2D) { - if (this.width === null && this.fontsize !== null) { - this.width = context.measureText(this.text).width; - } else if (this.width !== null && this.fontsize === null) { - this.fontsize = this.automaticFontSize(context); - } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { - let width = context.measureText(this.text).width; - if (width > this.width) {this.fontsize = this.automaticFontSize(context)} - } else if (this.width !== null && this.fontsize !== null && this.multiLine) { - } else { - throw new Error('Cannot write text with no size'); - } + const writtenText = this.format(context); + // if (this.width === null && this.fontsize !== null) { + // this.width = context.measureText(this.text).width; + // } else if (this.width !== null && this.fontsize === null) { + // this.fontsize = this.automaticFontSize(context); + // } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { + // let width = context.measureText(this.text).width; + // if (width > this.width) { this.fontsize = this.automaticFontSize(context) } + // } else if (this.width !== null && this.fontsize !== null && this.multiLine) { + // } else { + // throw new Error('Cannot write text with no size'); + // } context.font = this.fullFont; - context.textAlign = this.justify as CanvasTextAlign; + context.textAlign = this.align as CanvasTextAlign; context.textBaseline = this.baseline as CanvasTextBaseline; this.path = this.buildPath(); context.translate(this.origin.x, this.origin.y); - context.rotate(Math.PI/180 * this.orientation); - this.write(context, 1); - context.rotate(-Math.PI/180 * this.orientation); + context.rotate(Math.PI / 180 * this.orientation); + this.write(writtenText, context); + context.rotate(-Math.PI / 180 * this.orientation); context.translate(-this.origin.x, -this.origin.y); } - private write(context: CanvasRenderingContext2D, scaleX: number) { + private format(context: CanvasRenderingContext2D): string[] { + let fontsize = this.fontsize? this.fontsize : DEFAULT_FONTSIZE; + let writtenText = [this.text]; + context.font = newText.buildFont(this.style, fontsize, this.font); + console.log(context.font, this.text) if (this.width) { - if (this.multiLine) { - var cut_texts = this.cutting_text(context, scaleX * this.width); - var height_offset: number = cut_texts.length / 2 - 0.5; - if (this.baseline == "hanging" || this.baseline == "top") { - height_offset = 0.5; - } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { - height_offset -= 0.2; - } - for (let i=0; i this.width) { fontsize = this.automaticFontSize(context) ; console.log(fontsize)} } - } else { - context.fillText(this.text, 0, 0); } + } + const measureText = context.measureText(this.text); + this.fontsize = fontsize; + this.width = measureText.width; + this.height = measureText.fontBoundingBoxAscent; + + // if (!this.width && !this.height) { + // if (!this.fontsize) { this.fontsize = DEFAULT_FONTSIZE }; + // context.font = this.fullFont; + // const measureText = context.measureText(this.text); + // this.width = measureText.width; + // this.height = measureText.fontBoundingBoxAscent; + // } else if (this.width && !this.height) { + // if (!this.fontsize) { + // if (this.multiLine) { + // this.fontsize = DEFAULT_FONTSIZE; + // context.font = this.fullFont; + // this.text = this.cutting_text(context, this.width).join("\n"); + // } else { this.fontsize = this.automaticFontSize(context) }; + // } + // context.font = this.fullFont; + // this.height = context.measureText(this.text).fontBoundingBoxAscent; + // } else if (!this.width && this.height) { + // if (this.fontsize) { + // context.font = this.fullFont; + // const measureText = context.measureText(this.text); + // this.width = measureText.width; + // this.height = measureText.fontBoundingBoxAscent; + // } + // } else if (this.width && this.height) { + // if (!this.fontsize) { + // this.fontsize = DEFAULT_FONTSIZE; + // } + // } + return writtenText + } + + private write(writtenText: string[], context: CanvasRenderingContext2D) { + if (writtenText.length != 1) { + var height_offset: number = writtenText.length / 2 - 0.5; + writtenText.forEach((row, index) => { context.fillText(row, 0, 0 + (index - height_offset) * this.fontsize) }); } else { - context.fillText(this.text, 0, 0); + context.fillText(writtenText[0], 0, 0); } } + // private write(context: CanvasRenderingContext2D) { + // if (this.width) { + // if (this.multiLine) { + // var cut_texts = this.cutting_text(context, this.scale * this.width); + // var height_offset: number = cut_texts.length / 2 - 0.5; + // if (this.baseline == "hanging" || this.baseline == "top") { + // height_offset = 0.5; + // } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { + // height_offset -= 0.2; + // } + // for (let i = 0; i < cut_texts.length; i++) { + // context.fillText(cut_texts[i], 0, 0 + (i - height_offset) * this.fontsize); + // } + // } else { + // context.fillText(this.text, 0, 0); + // } + // } else { + // context.fillText(this.text, 0, 0); + // } + // } + private cutting_text(context: CanvasRenderingContext2D, maxWidth: number) { var words = this.text.split(' '); var space_length = context.measureText(' ').width; @@ -1758,7 +1845,7 @@ export class newText extends newShape { var i = 0; var line_length = 0; var line_text = ''; - while (i= maxWidth) { @@ -1788,6 +1875,132 @@ export class newText extends newShape { } } +// export class newText extends newShape { +// public scale: number = 1; +// constructor( +// public text: string, +// public origin: Vertex, +// public width: number = null, +// public fontsize: number = 12, +// public font: string = 'sans-serif', +// public justify: string = 'left', +// public baseline: string = 'alphabetic', +// protected _style: string = '', +// protected _orientation: number = 0, +// protected multiLine: boolean = false +// ) { +// super(); +// this.path = this.buildPath(); +// } + +// get orientation(): number {return this._orientation}; + +// set orientation(value: number) {this._orientation = value}; + +// get style(): string {return this._style}; + +// set style(value: string) {this._style = value}; + +// get fullFont() {return `${this.style} ${this.fontsize}px ${this.font}`} + +// private automaticFontSize(context: CanvasRenderingContext2D): number { +// let tmp_context: CanvasRenderingContext2D = context +// let pxMaxWidth: number = this.width * this.scale; +// tmp_context.font = '1px ' + this.font; +// return pxMaxWidth / (tmp_context.measureText(this.text).width) +// } + +// public buildPath(): Path2D { +// const path = this.path; +// path.rect(this.origin.x, this.origin.y, this.width, this.fontsize); +// return path +// } + +// public static capitalize(value: string): string {return value.charAt(0).toUpperCase() + value.slice(1)} + +// public capitalizeSelf(): void {this.text = newText.capitalize(this.text)} + +// public draw(context: CanvasRenderingContext2D) { +// if (this.width === null && this.fontsize !== null) { +// this.width = context.measureText(this.text).width; +// } else if (this.width !== null && this.fontsize === null) { +// this.fontsize = this.automaticFontSize(context); +// } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { +// let width = context.measureText(this.text).width; +// if (width > this.width) {this.fontsize = this.automaticFontSize(context)} +// } else if (this.width !== null && this.fontsize !== null && this.multiLine) { +// } else { +// throw new Error('Cannot write text with no size'); +// } +// context.font = this.fullFont; +// context.textAlign = this.justify as CanvasTextAlign; +// context.textBaseline = this.baseline as CanvasTextBaseline; +// this.path = this.buildPath(); +// context.translate(this.origin.x, this.origin.y); +// context.rotate(Math.PI/180 * this.orientation); +// this.write(context); +// context.rotate(-Math.PI/180 * this.orientation); +// context.translate(-this.origin.x, -this.origin.y); +// } + +// private write(context: CanvasRenderingContext2D) { +// if (this.width) { +// if (this.multiLine) { +// var cut_texts = this.cutting_text(context, this.scale * this.width); +// var height_offset: number = cut_texts.length / 2 - 0.5; +// if (this.baseline == "hanging" || this.baseline == "top") { +// height_offset = 0.5; +// } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { +// height_offset -= 0.2; +// } +// for (let i=0; i= maxWidth) { +// if (line_text !== '') cut_texts.push(line_text); +// line_length = 0; +// line_text = ''; +// cut_texts.push(word); +// i++; +// } else { +// if (line_length + word_length <= maxWidth) { +// if (line_length !== 0) { +// line_length = line_length + space_length; +// line_text = line_text + ' '; +// } +// line_text = line_text + word; +// line_length = line_length + word_length; +// i++; +// } else { +// cut_texts.push(line_text); +// line_length = 0; +// line_text = ''; +// } +// } +// } +// if (line_text !== '') cut_texts.push(line_text); +// return cut_texts; +// } +// } + const CIRCLES = ['o', 'circle', 'round']; const MARKERS = ['+', 'crux', 'mark']; const CROSSES = ['x', 'cross', 'oblique']; @@ -1807,44 +2020,44 @@ export class newPoint2D extends Vertex { private _size: number = 2, private _marker: string = 'circle', private _markerOrientation: string = 'up' - ) { - super(x, y); - this.path = this.buildPath(); - }; + ) { + super(x, y); + this.path = this.buildPath(); + }; get drawnShape() { let marker = new newShape(); - if (CIRCLES.indexOf(this.marker) > -1) {marker = new newCircle(this.coordinates, this.size)} - if (MARKERS.indexOf(this.marker) > -1) {marker = new Mark(this.coordinates, this.size)}; - if (CROSSES.indexOf(this.marker) > -1) {marker = new Cross(this.coordinates, this.size)}; + if (CIRCLES.indexOf(this.marker) > -1) { marker = new newCircle(this.coordinates, this.size) } + if (MARKERS.indexOf(this.marker) > -1) { marker = new Mark(this.coordinates, this.size) }; + if (CROSSES.indexOf(this.marker) > -1) { marker = new Cross(this.coordinates, this.size) }; if (SQUARES.indexOf(this.marker) > -1) { const halfSize = this.size * 0.5; const origin = new Vertex(this.coordinates.x - halfSize, this.coordinates.y - halfSize) marker = new newRect(origin, new Vertex(this.size, this.size)) }; - if (TRIANGLES.indexOf(this.marker) > -1) {marker = new Triangle(this.coordinates, this.size, this.markerOrientation)}; - if (this.marker == 'halfLine') {marker = new HalfLine(this.coordinates, this.size, this.markerOrientation)}; + if (TRIANGLES.indexOf(this.marker) > -1) { marker = new Triangle(this.coordinates, this.size, this.markerOrientation) }; + if (this.marker == 'halfLine') { marker = new HalfLine(this.coordinates, this.size, this.markerOrientation) }; marker.lineWidth = this.lineWidth; return marker } - get lineWidth(): number {return this._lineWidth}; + get lineWidth(): number { return this._lineWidth }; - set lineWidth(value: number) {this._lineWidth = value}; + set lineWidth(value: number) { this._lineWidth = value }; - get markerOrientation(): string {return this._markerOrientation}; + get markerOrientation(): string { return this._markerOrientation }; - set markerOrientation(value: string) {this._markerOrientation = value}; + set markerOrientation(value: string) { this._markerOrientation = value }; - get size(): number {return this._size}; + get size(): number { return this._size }; - set size(value: number) {this._size = value}; + set size(value: number) { this._size = value }; - get marker(): string {return this._marker}; + get marker(): string { return this._marker }; - set marker(value: string) {this._marker = value}; + set marker(value: string) { this._marker = value }; - private buildPath(): Path2D {return this.drawnShape.path}; + private buildPath(): Path2D { return this.drawnShape.path }; public draw(context: CanvasRenderingContext2D) { this.path = this.buildPath(); @@ -1870,7 +2083,7 @@ export class Bar extends newRect { super(origin, size); } - get length(): number {return this.values.length} + get length(): number { return this.values.length } public setGeometry(origin: Vertex, size: Vertex) { this.origin = origin; @@ -1878,7 +2091,7 @@ export class Bar extends newRect { } public draw(context: CanvasRenderingContext2D) { - if (this.size.x != 0 && this.size.y != 0) {super.draw(context)} + if (this.size.x != 0 && this.size.y != 0) { super.draw(context) } } } @@ -1923,62 +2136,62 @@ export class newAxis { public end: Vertex, public name: string = '', private _nTicks: number = 10) { - this.isDiscrete = typeof vector[0] == 'string'; - if (this.isDiscrete) {this.labels = newAxis.uniqueValues(vector)}; - const [minValue, maxValue] = this.computeMinMax(vector); - [this._previousMin, this._previousMax] = [this.minValue, this.maxValue] = this.marginedBounds(minValue, maxValue); - this.ticks = this.computeTicks(); - if (!this.isDiscrete) {this.labels = this.numericLabels()}; - this.drawPath = this.buildDrawPath(); - this.path = this.buildPath(); - this.rubberBand = new RubberBand(this.name, 0, 0, this.isVertical); - }; + this.isDiscrete = typeof vector[0] == 'string'; + if (this.isDiscrete) { this.labels = newAxis.uniqueValues(vector) }; + const [minValue, maxValue] = this.computeMinMax(vector); + [this._previousMin, this._previousMax] = [this.minValue, this.maxValue] = this.marginedBounds(minValue, maxValue); + this.ticks = this.computeTicks(); + if (!this.isDiscrete) { this.labels = this.numericLabels() }; + this.drawPath = this.buildDrawPath(); + this.path = this.buildPath(); + this.rubberBand = new RubberBand(this.name, 0, 0, this.isVertical); + }; public get drawLength(): number { - if (this.isVertical) {return Math.abs(this.origin.y - this.end.y)}; + if (this.isVertical) { return Math.abs(this.origin.y - this.end.y) }; return Math.abs(this.origin.x - this.end.x); } - get interval(): number {return Math.abs(this.maxValue - this.minValue)}; + get interval(): number { return Math.abs(this.maxValue - this.minValue) }; - get isVertical(): boolean {return this.origin.x == this.end.x}; + get isVertical(): boolean { return this.origin.x == this.end.x }; - get isDiscrete(): boolean {return this._isDiscrete}; + get isDiscrete(): boolean { return this._isDiscrete }; - set isDiscrete(value: boolean) {this._isDiscrete = value}; + set isDiscrete(value: boolean) { this._isDiscrete = value }; - set marginRatio(value: number) {this._marginRatio = value}; + set marginRatio(value: number) { this._marginRatio = value }; - get marginRatio(): number {return this._marginRatio}; + get marginRatio(): number { return this._marginRatio }; - set maxValue(value: number) {this._maxValue = value}; + set maxValue(value: number) { this._maxValue = value }; - get maxValue(): number {return this._maxValue}; + get maxValue(): number { return this._maxValue }; - set minValue(value: number) {this._minValue = value}; + set minValue(value: number) { this._minValue = value }; - get minValue(): number {return this._minValue}; + get minValue(): number { return this._minValue }; - set nTicks(value: number) {this._nTicks = value}; + set nTicks(value: number) { this._nTicks = value }; get nTicks(): number { if (this.isDiscrete) return this.labels.length + 1 return this._nTicks } - get ticks() {return this._ticks} + get ticks() { return this._ticks } - set ticks(value: number[]) {this._ticks = value} + set ticks(value: number[]) { this._ticks = value } - get tickPrecision(): number {return this._tickPrecision}; + get tickPrecision(): number { return this._tickPrecision }; - get title(): string {return newText.capitalize(this.name)} + get title(): string { return newText.capitalize(this.name) } - get transformMatrix(): DOMMatrix {return this.getValueToDrawMatrix()}; + get transformMatrix(): DOMMatrix { return this.getValueToDrawMatrix() }; - private horizontalPickIdx() {return Math.sign(1 - Math.sign(this.transformMatrix.f))} + private horizontalPickIdx() { return Math.sign(1 - Math.sign(this.transformMatrix.f)) } - private verticalPickIdx() {return Math.sign(1 - Math.sign(this.transformMatrix.e))} + private verticalPickIdx() { return Math.sign(1 - Math.sign(this.transformMatrix.e)) } public transform(newOrigin: Vertex, newEnd: Vertex) { this.origin = newOrigin; @@ -1987,7 +2200,7 @@ export class newAxis { this.path = this.buildPath(); } - public reset(): void {this.rubberBand.reset()} + public reset(): void { this.rubberBand.reset() } private static nearestFive(value: number): number { const tenPower = Math.floor(Math.log10(Math.abs(value))); @@ -2001,7 +2214,7 @@ export class newAxis { } private buildDrawPath(): Path2D { - const verticalIdx = Number(this.isVertical) ; const horizontalIdx = Number(!this.isVertical); + const verticalIdx = Number(this.isVertical); const horizontalIdx = Number(!this.isVertical); const path = new Path2D(); const endArrow = new newPoint2D(this.end.x + this.SIZE_END / 2 * horizontalIdx, this.end.y + this.SIZE_END / 2 * verticalIdx, this.SIZE_END, 'triangle', ['right', 'up'][verticalIdx]); path.moveTo(this.origin.x - this.DRAW_START_OFFSET * horizontalIdx, this.origin.y - this.DRAW_START_OFFSET * verticalIdx); @@ -2032,7 +2245,7 @@ export class newAxis { } private computeMinMax(vector: any[]): number[] { - if (this.isDiscrete) {return [0, this.labels.length]}; + if (this.isDiscrete) { return [0, this.labels.length] }; return [Math.min(...vector), Math.max(...vector)] } @@ -2040,16 +2253,16 @@ export class newAxis { const increment = newAxis.nearestFive((this.maxValue - this.minValue) / this.nTicks); const remainder = this.minValue % increment; let ticks = [this.minValue - remainder]; - while (ticks.slice(-1)[0] <= this.maxValue) {ticks.push(ticks.slice(-1)[0] + increment)}; - if (ticks.slice(0)[0] < this.minValue) {ticks.splice(0, 1)}; - if (ticks.slice(-1)[0] > this.maxValue) {ticks.splice(-1, 1)}; + while (ticks.slice(-1)[0] <= this.maxValue) { ticks.push(ticks.slice(-1)[0] + increment) }; + if (ticks.slice(0)[0] < this.minValue) { ticks.splice(0, 1) }; + if (ticks.slice(-1)[0] > this.maxValue) { ticks.splice(-1, 1) }; return ticks } public draw(context: CanvasRenderingContext2D) { context.lineWidth = this.lineWidth; let color = this.strokeStyle; - if (this.mouseStyleON) {color = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.strokeStyle}; + if (this.mouseStyleON) { color = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.strokeStyle }; context.strokeStyle = color; context.fillStyle = color; context.stroke(this.drawPath); @@ -2069,13 +2282,17 @@ export class newAxis { let baseline = ['hanging', 'alphabetic'][this.horizontalPickIdx()] let nameCoords = this.end.add(this.origin).divide(2); if (this.isVertical) { - nameCoords.x += this.OFFSET_NAME.x ; + nameCoords.x += this.OFFSET_NAME.x; baseline = ['alphabetic', 'hanging'][this.verticalPickIdx()]; } - else {nameCoords.y += this.OFFSET_NAME.y} + else { nameCoords.y += this.OFFSET_NAME.y } nameCoords.transformSelf(canvasHTMatrix); const orientation = this.isVertical ? -90 : 0; - const textName = new newText(this.title, nameCoords, this.drawLength, this.FONT_SIZE, this.FONT, 'center', baseline, 'bold', orientation); + const textParams: textParams = { + width: this.drawLength, fontsize: this.FONT_SIZE, font: this.FONT, align: 'center', + baseline: baseline, style: 'bold', orientation: orientation + }; + const textName = new newText(this.title, nameCoords, textParams); textName.draw(context); } @@ -2088,8 +2305,8 @@ export class newAxis { ticksCoords.push(point); let text = this.labels[idx] if (this.isDiscrete) { - if (count == tick && this.labels[count]) {text = this.labels[count] ; count++} - else {text = ''} + if (count == tick && this.labels[count]) { text = this.labels[count]; count++ } + else { text = '' } } this.drawTickText(context, text, point, pointHTMatrix); } @@ -2113,7 +2330,8 @@ export class newAxis { if (textAlign == 'right') textWidth = textOrigin.x - 5; if (textAlign == 'center') textWidth = (this.drawLength) / (this.nTicks * 1.5); context.resetTransform() - const tickText = new newText(newText.capitalize(text), textOrigin, textWidth, this.FONT_SIZE, this.FONT, textAlign, baseline); + const textParams: textParams = {width: textWidth, fontsize: this.FONT_SIZE, font: this.FONT, align: textAlign, baseline: baseline}; + const tickText = new newText(newText.capitalize(text), textOrigin, textParams); tickText.draw(context) context.setTransform(HTMatrix) } @@ -2127,9 +2345,9 @@ export class newAxis { } private marginedBounds(minValue: number, maxValue: number): [number, number] { - if (this.isDiscrete) {return [minValue - 1, maxValue + 1]}; + if (this.isDiscrete) { return [minValue - 1, maxValue + 1] }; return [minValue * (1 - Math.sign(minValue) * this.marginRatio), - maxValue * (1 + Math.sign(maxValue) * this.marginRatio)]; + maxValue * (1 + Math.sign(maxValue) * this.marginRatio)]; } public drawRubberBand(context: CanvasRenderingContext2D) { @@ -2149,7 +2367,7 @@ export class newAxis { if (!this.rubberBand.isClicked) { this.rubberBand.minValue = Math.min(downValue, currentValue); this.rubberBand.maxValue = Math.max(downValue, currentValue); - } else {this.rubberBand.mouseMove(downValue, currentValue)} + } else { this.rubberBand.mouseMove(downValue, currentValue) } return true } @@ -2160,7 +2378,7 @@ export class newAxis { if (!this.isInRubberBand(this.absoluteToRelative(mouseUniCoord))) { this.rubberBand.reset(); isReset = true; - } else {this.rubberBand.mouseDown(mouseUniCoord);} + } else { this.rubberBand.mouseDown(mouseUniCoord); } return isReset } @@ -2211,7 +2429,7 @@ export class newAxis { let center = (viewPoint.x - HTMatrix.e) / HTMatrix.a; let offset = translation.x; let scale = scaling.x; - if (this.isVertical) {center = (viewPoint.y - HTMatrix.f) / HTMatrix.d ; offset = translation.y ; scale = scaling.y}; + if (this.isVertical) { center = (viewPoint.y - HTMatrix.f) / HTMatrix.d; offset = translation.y; scale = scaling.y }; this.minValue = (this._previousMin - center) / scale + center + offset / HTMatrix.a; this.maxValue = (this._previousMax - center) / scale + center + offset / HTMatrix.a; this.updateTicks(); @@ -2221,13 +2439,13 @@ export class newAxis { const minTick = Math.min(...this.ticks); const maxTick = Math.max(...this.ticks); const ratio = Number(maxTick.toPrecision(this.tickPrecision)) / Number(minTick.toPrecision(this.tickPrecision)); - if (ratio == 1) {this._tickPrecision++}; - if (Number(maxTick.toPrecision(this.tickPrecision - 1)) != Number(minTick.toPrecision(this.tickPrecision - 1)) && this.tickPrecision > 4) {this._tickPrecision--}; + if (ratio == 1) { this._tickPrecision++ }; + if (Number(maxTick.toPrecision(this.tickPrecision - 1)) != Number(minTick.toPrecision(this.tickPrecision - 1)) && this.tickPrecision > 4) { this._tickPrecision-- }; return }; private updateTicks(): void { this.ticks = this.computeTicks(); - if (!this.isDiscrete) {this.labels = this.numericLabels()}; + if (!this.isDiscrete) { this.labels = this.numericLabels() }; } } From 79464f26b548fa4dff3dff76c2b38fb17a272c8f Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 7 Jun 2023 12:29:54 +0200 Subject: [PATCH 07/54] fix(parallel_names): add width and origin rules --- script/parallel_plot.py | 6 +++--- src/plot-data.ts | 23 ++++++++++++----------- src/utils.ts | 7 +++---- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/script/parallel_plot.py b/script/parallel_plot.py index 126cc217..c43129ce 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass ': random.uniform(0, 0.05), - 'length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length ': random.uniform(0, 100), + elements.append({'mass': random.uniform(0, 0.05), + 'length': random.uniform(0, 100), 'shape': random_shape, 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass mass ', 'length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length ', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) + axes=['mass', 'length', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/plot-data.ts b/src/plot-data.ts index b15bee87..c9c05be4 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -843,20 +843,21 @@ export abstract class PlotData { Shape.drawLine(this.context, [[current_x, this.axis_y_start], [current_x, this.axis_y_end]]); let origin = new Vertex(current_x, this.axis_y_end - 20); - let size = this.x_step - 40; + let width = this.x_step - 40; let align = "center" - if (i == 0) { - origin.x = current_x + this.x_step / 2; - size = this.x_step / 2 + 40; - align = "right"; - } else if (i == nb_axis - 1) { - origin.x = current_x - this.x_step / 2; - size = this.x_step / 2 + 40; - align = "left"; + const textParams: textParams = { width: width, align: align, baseline: "alphabetic", multiLine: true }; + let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); + axisTitle.format(this.context); + if (i == 0 && axisTitle.width > this.axis_x_start * 2) { + axisTitle.origin.x = current_x + this.x_step / 2; + axisTitle.width = this.x_step / 2 + 40; + axisTitle.align = "right"; + } else if (i == nb_axis - 1 && axisTitle.width > this.axis_x_start * 2) { + axisTitle.origin.x = current_x - this.x_step / 2; + axisTitle.width = this.x_step / 2 + 40; + axisTitle.align = "left"; } - const textParams: textParams = {width: size, align: align, baseline: "alphabetic", multiLine: false}; - let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { diff --git a/src/utils.ts b/src/utils.ts index d387cfea..c0674e98 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1756,11 +1756,10 @@ export class newText extends newShape { context.translate(-this.origin.x, -this.origin.y); } - private format(context: CanvasRenderingContext2D): string[] { + public format(context: CanvasRenderingContext2D): string[] { let fontsize = this.fontsize? this.fontsize : DEFAULT_FONTSIZE; let writtenText = [this.text]; context.font = newText.buildFont(this.style, fontsize, this.font); - console.log(context.font, this.text) if (this.width) { if (this.multiLine) { writtenText = this.cutting_text(context, this.width) } else { @@ -1768,12 +1767,12 @@ export class newText extends newShape { fontsize = this.automaticFontSize(context); context.font = newText.buildFont(this.style, fontsize, this.font); } else { - if (context.measureText(this.text).width > this.width) { fontsize = this.automaticFontSize(context) ; console.log(fontsize)} + if (context.measureText(this.text).width > this.width) { fontsize = this.automaticFontSize(context) } } } } - const measureText = context.measureText(this.text); this.fontsize = fontsize; + const measureText = context.measureText(writtenText[0]); this.width = measureText.width; this.height = measureText.fontBoundingBoxAscent; From 0b5758a69af88daab35ef23279919785fc9f9650 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 7 Jun 2023 16:45:48 +0200 Subject: [PATCH 08/54] fix(parralel_names): fix on vertical axes, with generic newText rules --- script/parallel_plot.py | 8 ++++---- src/plot-data.ts | 28 ++++++++++++++++------------ src/subplots.ts | 2 ++ src/utils.ts | 24 ++++++++---------------- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/script/parallel_plot.py b/script/parallel_plot.py index c43129ce..e17ba8ee 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'mass': random.uniform(0, 0.05), - 'length': random.uniform(0, 100), + elements.append({'TTT mass mass mass RRR': random.uniform(0, 0.05), + 'TTTlength length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length RRR': random.uniform(0, 100), 'shape': random_shape, - 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ': random_color + 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['mass', 'length', 'shape', 'color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color ']) + axes=['TTT mass mass mass RRR', 'TTTlength length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length RRR', 'shape', 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/plot-data.ts b/src/plot-data.ts index c9c05be4..f868934d 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -842,29 +842,33 @@ export abstract class PlotData { this.context.lineWidth = 2; Shape.drawLine(this.context, [[current_x, this.axis_y_start], [current_x, this.axis_y_end]]); - let origin = new Vertex(current_x, this.axis_y_end - 20); + let origin = new Vertex(current_x, this.axis_y_end - 10); let width = this.x_step - 40; - let align = "center" - const textParams: textParams = { width: width, align: align, baseline: "alphabetic", multiLine: true }; + let align = "center"; + const textParams: textParams = { width: width, height: origin.y - 2, align: align, baseline: "bottom", multiLine: true }; let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); + axisTitle.format(this.context); + if (i == 0 && axisTitle.width > this.axis_x_start * 2) { - axisTitle.origin.x = current_x + this.x_step / 2; - axisTitle.width = this.x_step / 2 + 40; + const newWidth = Math.min(axisTitle.width, this.x_step / 2); + axisTitle.origin.x = current_x + newWidth; + axisTitle.width = newWidth + 40; axisTitle.align = "right"; } else if (i == nb_axis - 1 && axisTitle.width > this.axis_x_start * 2) { - axisTitle.origin.x = current_x - this.x_step / 2; - axisTitle.width = this.x_step / 2 + 40; + const newWidth = Math.min(axisTitle.width, this.x_step / 2); + axisTitle.origin.x = current_x - newWidth; + axisTitle.width = newWidth + 40; axisTitle.align = "left"; } + axisTitle.format(this.context); + + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } + else { this.context.strokeStyle = 'black' }; - if (axisTitle.text == this.selected_axis_name) { - this.context.strokeStyle = 'blue'; - } else { - this.context.strokeStyle = 'black'; - } this.context.fillStyle = 'black'; axisTitle.draw(this.context); + this.context.textBaseline = "alphabetic"; var attribute_type = this.axis_list[i]['type_']; var list = this.axis_list[i]['list']; diff --git a/src/subplots.ts b/src/subplots.ts index 08d34437..a7d0d55d 100644 --- a/src/subplots.ts +++ b/src/subplots.ts @@ -472,6 +472,8 @@ export class ParallelPlot extends PlotData { [click_on_name, selected_name_index] = Interactions.initialize_click_on_name(this.axis_list.length, mouse1X, mouse1Y, this); [click_on_band, click_on_border, selected_band_index, selected_border] = Interactions.initialize_click_on_bands(mouse1X, mouse1Y, this); } + console.log(mouse1X, mouse1Y) + }); canvas.addEventListener('mousemove', e => { diff --git a/src/utils.ts b/src/utils.ts index c0674e98..cc3a5dca 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1734,17 +1734,6 @@ export class newText extends newShape { public draw(context: CanvasRenderingContext2D) { const writtenText = this.format(context); - // if (this.width === null && this.fontsize !== null) { - // this.width = context.measureText(this.text).width; - // } else if (this.width !== null && this.fontsize === null) { - // this.fontsize = this.automaticFontSize(context); - // } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { - // let width = context.measureText(this.text).width; - // if (width > this.width) { this.fontsize = this.automaticFontSize(context) } - // } else if (this.width !== null && this.fontsize !== null && this.multiLine) { - // } else { - // throw new Error('Cannot write text with no size'); - // } context.font = this.fullFont; context.textAlign = this.align as CanvasTextAlign; context.textBaseline = this.baseline as CanvasTextBaseline; @@ -1772,9 +1761,12 @@ export class newText extends newShape { } } this.fontsize = fontsize; - const measureText = context.measureText(writtenText[0]); - this.width = measureText.width; - this.height = measureText.fontBoundingBoxAscent; + this.width = context.measureText(writtenText[0]).width; + const tempHeight = this.fontsize * writtenText.length; + if (!this.height) { this.height = tempHeight }; + if (tempHeight > this.height) { this.fontsize = this.height / tempHeight * this.fontsize }; + + // console.log(measureText) // if (!this.width && !this.height) { // if (!this.fontsize) { this.fontsize = DEFAULT_FONTSIZE }; @@ -1809,8 +1801,8 @@ export class newText extends newShape { private write(writtenText: string[], context: CanvasRenderingContext2D) { if (writtenText.length != 1) { - var height_offset: number = writtenText.length / 2 - 0.5; - writtenText.forEach((row, index) => { context.fillText(row, 0, 0 + (index - height_offset) * this.fontsize) }); + var offset: number = writtenText.length - 1; + writtenText.forEach((row, index) => { context.fillText(row, 0, (index - offset) * this.fontsize) }); } else { context.fillText(writtenText[0], 0, 0); } From dd87394473f6c319424b851220d35b48dd861635 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 7 Jun 2023 17:55:13 +0200 Subject: [PATCH 09/54] fix(parallel_names): fix horizontal names, need to check in multiplot --- script/multiplot.py | 10 ++++---- script/parallel_plot.py | 6 ++--- src/plot-data.ts | 17 +++++++++++-- src/utils.ts | 54 ++--------------------------------------- 4 files changed, 25 insertions(+), 62 deletions(-) diff --git a/script/multiplot.py b/script/multiplot.py index 09e2d2dc..64a7fdfb 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -26,17 +26,17 @@ random_direction = directions[random.randint(0, len(directions) - 1)] elements.append({'x': random.uniform(0, 200), 'y': random.uniform(0, 100), - 'color': random_color, + 'color t r e z r t f d s'*10: random_color, 'direction': random_direction}) # ParallelPlot -parallelplot1 = plot_data.ParallelPlot(axes=['x', 'y', 'color', 'direction']) -parallelplot2 = plot_data.ParallelPlot(axes=['y', 'color']) +parallelplot1 = plot_data.ParallelPlot(axes=['x', 'y', 'color t r e z r t f d s'*10, 'direction']) +parallelplot2 = plot_data.ParallelPlot(axes=['y', 'color t r e z r t f d s'*10]) # Scatterplots scatterplot1 = plot_data.Scatter(x_variable='x', y_variable='y') -scatterplot2 = plot_data.Scatter(x_variable='y', y_variable='color', +scatterplot2 = plot_data.Scatter(x_variable='y', y_variable='color t r e z r t f d s'*10, point_style=plot_data.PointStyle(shape='square')) # optional argument that changes # points' appearance @@ -65,7 +65,7 @@ histogram = plot_data.Histogram(x_variable='x') histogram2 = plot_data.Histogram(x_variable='direction') -histogram3 = plot_data.Histogram(x_variable='color') +histogram3 = plot_data.Histogram(x_variable='color t r e z r t f d s'*10) # Creating the multiplot plots = [parallelplot1, scatterplot1] diff --git a/script/parallel_plot.py b/script/parallel_plot.py index e17ba8ee..1439a062 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'TTT mass mass mass RRR': random.uniform(0, 0.05), - 'TTTlength length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length RRR': random.uniform(0, 100), + elements.append({'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR': random.uniform(0, 0.05), + 'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRRTTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR': random.uniform(0, 100), 'shape': random_shape, 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['TTT mass mass mass RRR', 'TTTlength length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length length RRR', 'shape', 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR']) + axes=['TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR', 'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRRTTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR', 'shape', 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/plot-data.ts b/src/plot-data.ts index f868934d..cd61d869 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -946,8 +946,21 @@ export abstract class PlotData { this.context.lineWidth = 2; Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]); - const textParams: textParams = {width: 100, align: "left", baseline: "hanging", multiLine: true}; - let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), textParams); + let origin = new Vertex(this.axis_x_start - 30, current_y + 15); + const textParams: textParams = { width: 150, height: this.y_step, align: "left", baseline: "hanging", multiLine: true }; + let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); + + axisTitle.format(this.context); + + if (i != nb_axis - 1) { + origin.y += (axisTitle.nRows - 1) * axisTitle.fontsize; + } else { + axisTitle.width = this.width; + } + axisTitle.format(this.context); + + // const textParams: textParams = {width: 100, align: "left", baseline: "hanging", multiLine: true}; + // let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), textParams); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { diff --git a/src/utils.ts b/src/utils.ts index cc3a5dca..84b6e843 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1683,6 +1683,7 @@ export class newText extends newShape { public style: string; public orientation: number; public multiLine: boolean; + public nRows: number; constructor( public text: string, public origin: Vertex, @@ -1765,37 +1766,7 @@ export class newText extends newShape { const tempHeight = this.fontsize * writtenText.length; if (!this.height) { this.height = tempHeight }; if (tempHeight > this.height) { this.fontsize = this.height / tempHeight * this.fontsize }; - - // console.log(measureText) - - // if (!this.width && !this.height) { - // if (!this.fontsize) { this.fontsize = DEFAULT_FONTSIZE }; - // context.font = this.fullFont; - // const measureText = context.measureText(this.text); - // this.width = measureText.width; - // this.height = measureText.fontBoundingBoxAscent; - // } else if (this.width && !this.height) { - // if (!this.fontsize) { - // if (this.multiLine) { - // this.fontsize = DEFAULT_FONTSIZE; - // context.font = this.fullFont; - // this.text = this.cutting_text(context, this.width).join("\n"); - // } else { this.fontsize = this.automaticFontSize(context) }; - // } - // context.font = this.fullFont; - // this.height = context.measureText(this.text).fontBoundingBoxAscent; - // } else if (!this.width && this.height) { - // if (this.fontsize) { - // context.font = this.fullFont; - // const measureText = context.measureText(this.text); - // this.width = measureText.width; - // this.height = measureText.fontBoundingBoxAscent; - // } - // } else if (this.width && this.height) { - // if (!this.fontsize) { - // this.fontsize = DEFAULT_FONTSIZE; - // } - // } + this.nRows = writtenText.length; return writtenText } @@ -1808,27 +1779,6 @@ export class newText extends newShape { } } - // private write(context: CanvasRenderingContext2D) { - // if (this.width) { - // if (this.multiLine) { - // var cut_texts = this.cutting_text(context, this.scale * this.width); - // var height_offset: number = cut_texts.length / 2 - 0.5; - // if (this.baseline == "hanging" || this.baseline == "top") { - // height_offset = 0.5; - // } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { - // height_offset -= 0.2; - // } - // for (let i = 0; i < cut_texts.length; i++) { - // context.fillText(cut_texts[i], 0, 0 + (i - height_offset) * this.fontsize); - // } - // } else { - // context.fillText(this.text, 0, 0); - // } - // } else { - // context.fillText(this.text, 0, 0); - // } - // } - private cutting_text(context: CanvasRenderingContext2D, maxWidth: number) { var words = this.text.split(' '); var space_length = context.measureText(' ').width; From 3b4d0d5975d4f5088d9f1d6e8075bc635c24e99a Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 7 Jun 2023 17:58:30 +0200 Subject: [PATCH 10/54] fix(parallel_names): fix height in mp) --- src/plot-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index cd61d869..6cb63a78 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -845,7 +845,7 @@ export abstract class PlotData { let origin = new Vertex(current_x, this.axis_y_end - 10); let width = this.x_step - 40; let align = "center"; - const textParams: textParams = { width: width, height: origin.y - 2, align: align, baseline: "bottom", multiLine: true }; + const textParams: textParams = { width: width, height: origin.y - 2 - this.Y, align: align, baseline: "bottom", multiLine: true }; let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); From d9f6a79064d51728519a95952b7a9dde603dfa2b Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 8 Jun 2023 12:53:12 +0200 Subject: [PATCH 11/54] fix(parallel_names): new rules that work in mp --- script/multiplot.py | 28 ++++++++++++++-------------- src/plot-data.ts | 22 +++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/script/multiplot.py b/script/multiplot.py index 64a7fdfb..4aed4462 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -24,23 +24,23 @@ for i in range(nb_elements): random_color = available_colors[random.randint(0, len(available_colors) - 1)] random_direction = directions[random.randint(0, len(directions) - 1)] - elements.append({'x': random.uniform(0, 200), - 'y': random.uniform(0, 100), - 'color t r e z r t f d s'*10: random_color, + elements.append({'x '*100: random.uniform(0, 200), + 'y '*100: random.uniform(0, 100), + 'color t r e z r t f d s'*3: random_color, 'direction': random_direction}) # ParallelPlot -parallelplot1 = plot_data.ParallelPlot(axes=['x', 'y', 'color t r e z r t f d s'*10, 'direction']) -parallelplot2 = plot_data.ParallelPlot(axes=['y', 'color t r e z r t f d s'*10]) +parallelplot1 = plot_data.ParallelPlot(axes=['x '*100, 'y '*100, 'color t r e z r t f d s'*3, 'direction']) +parallelplot2 = plot_data.ParallelPlot(axes=['y '*100, 'color t r e z r t f d s'*3]) # Scatterplots -scatterplot1 = plot_data.Scatter(x_variable='x', y_variable='y') +scatterplot1 = plot_data.Scatter(x_variable='x '*100, y_variable='y') -scatterplot2 = plot_data.Scatter(x_variable='y', y_variable='color t r e z r t f d s'*10, +scatterplot2 = plot_data.Scatter(x_variable='y '*100, y_variable='color t r e z r t f d s'*3, 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 '*100, y_variable='direction') # PrimitiveGroupContainers contour = plot_data.Contour2D(plot_data_primitives=[plot_data.LineSegment2D([1, 1], [1, 2]), @@ -61,17 +61,17 @@ primitive_group_container = plot_data.PrimitiveGroupsContainer(primitive_groups=primitive_groups, associated_elements=[1, 2, 3, 4], - x_variable='x', y_variable='y') + x_variable='x '*100, y_variable='y '*100) -histogram = plot_data.Histogram(x_variable='x') +histogram = plot_data.Histogram(x_variable='x '*100) histogram2 = plot_data.Histogram(x_variable='direction') -histogram3 = plot_data.Histogram(x_variable='color t r e z r t f d s'*10) +histogram3 = plot_data.Histogram(x_variable='color t r e z r t f d s'*3) # Creating the multiplot plots = [parallelplot1, scatterplot1] -plots2 = [parallelplot1, parallelplot2, scatterplot1, scatterplot2, scatterplot3, graph2d, primitive_group_container, - histogram] - +# plots2 = [parallelplot1, parallelplot2, scatterplot1, scatterplot2, scatterplot3, graph2d, primitive_group_container, +# histogram] +plots2 = [parallelplot1, parallelplot2,parallelplot1, parallelplot2, parallelplot1, parallelplot2] # multiplot = plot_data.MultiplePlots(plots=plots, elements=elements, initial_view_on=True) plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) diff --git a/src/plot-data.ts b/src/plot-data.ts index 6cb63a78..bf046450 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -832,7 +832,7 @@ export abstract class PlotData { } draw_vertical_parallel_axis(nb_axis:number, mvx:number) { - for (var i=0; i this.axis_x_start * 2) { - const newWidth = Math.min(axisTitle.width, this.x_step / 2); - axisTitle.origin.x = current_x + newWidth; - axisTitle.width = newWidth + 40; + if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { + console.log(axisTitle) + const offset = Math.min(axisTitle.width, this.x_step / 2); + axisTitle.origin.x = current_x + offset * 0.95; + axisTitle.width = offset * 0.95 + (this.axis_x_start - this.X) * 0.9; axisTitle.align = "right"; - } else if (i == nb_axis - 1 && axisTitle.width > this.axis_x_start * 2) { - const newWidth = Math.min(axisTitle.width, this.x_step / 2); - axisTitle.origin.x = current_x - newWidth; - axisTitle.width = newWidth + 40; + } else if (i == nb_axis - 1 && axisTitle.width > (this.width - this.X - this.axis_x_end) * 2) { + const offset = Math.min(axisTitle.width, this.x_step / 2); + axisTitle.origin.x = current_x - offset * 0.95; + axisTitle.width = offset * 0.95 + (this.width - this.axis_x_end + this.X) * 0.9; axisTitle.align = "left"; } axisTitle.format(this.context); From 9de863817e735617c23bd2212d0cb7cdcaaa4960 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 8 Jun 2023 15:26:02 +0200 Subject: [PATCH 12/54] fix(parallel_names): fix for vertical version --- src/plot-data.ts | 24 +++++---- src/utils.ts | 126 ----------------------------------------------- 2 files changed, 15 insertions(+), 135 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index bf046450..2e9f3b54 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -850,7 +850,6 @@ export abstract class PlotData { axisTitle.format(this.context); if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { - console.log(axisTitle) const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x + offset * 0.95; axisTitle.width = offset * 0.95 + (this.axis_x_start - this.X) * 0.9; @@ -946,21 +945,28 @@ export abstract class PlotData { this.context.lineWidth = 2; Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]); - let origin = new Vertex(this.axis_x_start - 30, current_y + 15); - const textParams: textParams = { width: 150, height: this.y_step, align: "left", baseline: "hanging", multiLine: true }; + let origin = new Vertex(this.axis_x_start, current_y + 10); + const textParams: textParams = { width: this.width * 0.25, height: this.y_step * 0.98, align: "center", baseline: "hanging", multiLine: true }; let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); - if (i != nb_axis - 1) { - origin.y += (axisTitle.nRows - 1) * axisTitle.fontsize; - } else { - axisTitle.width = this.width; + let control = false; + if (axisTitle.width > (this.axis_x_start - this.X) * 2) { + axisTitle.align = "left"; + axisTitle.origin.x -= (this.axis_x_start - this.X) * 0.8; + if (i != nb_axis - 1) { + control = true; + } else if (i == nb_axis - 1) { + axisTitle.width = this.width; + axisTitle.height = null; + } } + axisTitle.format(this.context); - // const textParams: textParams = {width: 100, align: "left", baseline: "hanging", multiLine: true}; - // let axisTitle = new newText(this.axis_list[i]['name'], new Vertex(this.axis_x_start - 30, current_y + 15), textParams); + if (control) { axisTitle.origin.y += axisTitle.fontsize * (axisTitle.nRows - 1) }; + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { diff --git a/src/utils.ts b/src/utils.ts index 84b6e843..2194dcee 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1816,132 +1816,6 @@ export class newText extends newShape { } } -// export class newText extends newShape { -// public scale: number = 1; -// constructor( -// public text: string, -// public origin: Vertex, -// public width: number = null, -// public fontsize: number = 12, -// public font: string = 'sans-serif', -// public justify: string = 'left', -// public baseline: string = 'alphabetic', -// protected _style: string = '', -// protected _orientation: number = 0, -// protected multiLine: boolean = false -// ) { -// super(); -// this.path = this.buildPath(); -// } - -// get orientation(): number {return this._orientation}; - -// set orientation(value: number) {this._orientation = value}; - -// get style(): string {return this._style}; - -// set style(value: string) {this._style = value}; - -// get fullFont() {return `${this.style} ${this.fontsize}px ${this.font}`} - -// private automaticFontSize(context: CanvasRenderingContext2D): number { -// let tmp_context: CanvasRenderingContext2D = context -// let pxMaxWidth: number = this.width * this.scale; -// tmp_context.font = '1px ' + this.font; -// return pxMaxWidth / (tmp_context.measureText(this.text).width) -// } - -// public buildPath(): Path2D { -// const path = this.path; -// path.rect(this.origin.x, this.origin.y, this.width, this.fontsize); -// return path -// } - -// public static capitalize(value: string): string {return value.charAt(0).toUpperCase() + value.slice(1)} - -// public capitalizeSelf(): void {this.text = newText.capitalize(this.text)} - -// public draw(context: CanvasRenderingContext2D) { -// if (this.width === null && this.fontsize !== null) { -// this.width = context.measureText(this.text).width; -// } else if (this.width !== null && this.fontsize === null) { -// this.fontsize = this.automaticFontSize(context); -// } else if (this.width !== null && this.fontsize !== null && !this.multiLine) { -// let width = context.measureText(this.text).width; -// if (width > this.width) {this.fontsize = this.automaticFontSize(context)} -// } else if (this.width !== null && this.fontsize !== null && this.multiLine) { -// } else { -// throw new Error('Cannot write text with no size'); -// } -// context.font = this.fullFont; -// context.textAlign = this.justify as CanvasTextAlign; -// context.textBaseline = this.baseline as CanvasTextBaseline; -// this.path = this.buildPath(); -// context.translate(this.origin.x, this.origin.y); -// context.rotate(Math.PI/180 * this.orientation); -// this.write(context); -// context.rotate(-Math.PI/180 * this.orientation); -// context.translate(-this.origin.x, -this.origin.y); -// } - -// private write(context: CanvasRenderingContext2D) { -// if (this.width) { -// if (this.multiLine) { -// var cut_texts = this.cutting_text(context, this.scale * this.width); -// var height_offset: number = cut_texts.length / 2 - 0.5; -// if (this.baseline == "hanging" || this.baseline == "top") { -// height_offset = 0.5; -// } else if (this.baseline == "alphabetic" || this.baseline == "bottom") { -// height_offset -= 0.2; -// } -// for (let i=0; i= maxWidth) { -// if (line_text !== '') cut_texts.push(line_text); -// line_length = 0; -// line_text = ''; -// cut_texts.push(word); -// i++; -// } else { -// if (line_length + word_length <= maxWidth) { -// if (line_length !== 0) { -// line_length = line_length + space_length; -// line_text = line_text + ' '; -// } -// line_text = line_text + word; -// line_length = line_length + word_length; -// i++; -// } else { -// cut_texts.push(line_text); -// line_length = 0; -// line_text = ''; -// } -// } -// } -// if (line_text !== '') cut_texts.push(line_text); -// return cut_texts; -// } -// } - const CIRCLES = ['o', 'circle', 'round']; const MARKERS = ['+', 'crux', 'mark']; const CROSSES = ['x', 'cross', 'oblique']; From 1216ba24ab019ade2b0c8f27f1be4ba637af192b Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 8 Jun 2023 16:37:14 +0200 Subject: [PATCH 13/54] fix(parallel_names): fix axis name click for vertical --- src/plot-data.ts | 51 ++++++++++++++++++++++++++++++++++++------------ src/subplots.ts | 2 -- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index 2e9f3b54..2748eff0 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -1,6 +1,6 @@ import { heatmap_color, string_to_hex } from "./color_conversion"; import { Point2D, PrimitiveGroup, Contour2D, Circle2D, Dataset, Graph2D, Scatter, Heatmap, Wire } from "./primitives"; -import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, newText, textParams, Vertex } from "./utils"; +import { Attribute, PointFamily, Axis, Tooltip, Sort, permutator, export_to_csv, RubberBand, newText, textParams, Vertex, newRect } from "./utils"; import { EdgeStyle } from "./style"; import { Shape, List, MyMath } from "./toolbox"; import { rgb_to_hex, tint_rgb, hex_to_rgb, rgb_to_string, get_interpolation_colors, rgb_strToVector } from "./color_conversion"; @@ -192,6 +192,9 @@ export abstract class PlotData { selected_areas: number[][]; heatmap_table; + // HOTFIXES + axisNamesBoxes: newRect[]; + public constructor( public data:any, public width: number, @@ -832,6 +835,7 @@ export abstract class PlotData { } draw_vertical_parallel_axis(nb_axis:number, mvx:number) { + this.axisNamesBoxes = []; for (var i=0; i < nb_axis; i++) { if (i == this.move_index) { var current_x = Math.min(Math.max(this.axis_x_start + i*this.x_step + mvx, this.X), this.X + this.width); @@ -849,18 +853,22 @@ export abstract class PlotData { let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); + let boxOrigin = new Vertex(origin.x - axisTitle.width / 2, this.Y + 10) if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x + offset * 0.95; axisTitle.width = offset * 0.95 + (this.axis_x_start - this.X) * 0.9; axisTitle.align = "right"; + boxOrigin = new Vertex(axisTitle.origin.x - axisTitle.width, this.Y + 10); } else if (i == nb_axis - 1 && axisTitle.width > (this.width - this.X - this.axis_x_end) * 2) { const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x - offset * 0.95; axisTitle.width = offset * 0.95 + (this.width - this.axis_x_end + this.X) * 0.9; axisTitle.align = "left"; + boxOrigin = new Vertex(axisTitle.origin.x, this.Y + 10); } axisTitle.format(this.context); + this.axisNamesBoxes.push(new newRect(boxOrigin, new Vertex(axisTitle.width, axisTitle.height))); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } else { this.context.strokeStyle = 'black' }; @@ -935,6 +943,7 @@ export abstract class PlotData { } draw_horizontal_parallel_axis(nb_axis:number, mvy:number) { + this.axisNamesBoxes = []; for (var i=0; i { From 7f35e4c01e8bad1a95e11f5dac4e5705efc2bd8e Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 11:33:24 +0200 Subject: [PATCH 14/54] fix(parallel_names): totally fixed bug, with robust behavior --- src/plot-data.ts | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index 2748eff0..2f1b99b6 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -853,22 +853,36 @@ export abstract class PlotData { let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); - let boxOrigin = new Vertex(origin.x - axisTitle.width / 2, this.Y + 10) + let boxOriginX = 0; + let axisLocation = 0; if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { + axisLocation = -1; const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x + offset * 0.95; axisTitle.width = offset * 0.95 + (this.axis_x_start - this.X) * 0.9; axisTitle.align = "right"; - boxOrigin = new Vertex(axisTitle.origin.x - axisTitle.width, this.Y + 10); } else if (i == nb_axis - 1 && axisTitle.width > (this.width - this.X - this.axis_x_end) * 2) { + axisLocation = 1; const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x - offset * 0.95; axisTitle.width = offset * 0.95 + (this.width - this.axis_x_end + this.X) * 0.9; axisTitle.align = "left"; - boxOrigin = new Vertex(axisTitle.origin.x, this.Y + 10); } axisTitle.format(this.context); - this.axisNamesBoxes.push(new newRect(boxOrigin, new Vertex(axisTitle.width, axisTitle.height))); + + // Weird but vowed to disappear + if (axisLocation == 0) { + boxOriginX = origin.x - axisTitle.width / 2; + axisTitle.format(this.context); + } else if (axisLocation == -1) { + boxOriginX = axisTitle.origin.x - axisTitle.width; + } else if (axisLocation == 1) { + boxOriginX = axisTitle.origin.x; + axisTitle.format(this.context); + } + + let boxOrigin = new Vertex(boxOriginX, origin.y - axisTitle.nRows * axisTitle.fontsize); + this.axisNamesBoxes.push(new newRect(boxOrigin, new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize))); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } else { this.context.strokeStyle = 'black' }; @@ -961,23 +975,27 @@ export abstract class PlotData { axisTitle.format(this.context); let control = false; + let standard = true; if (axisTitle.width > (this.axis_x_start - this.X) * 2) { + standard = false; axisTitle.align = "left"; axisTitle.origin.x -= (this.axis_x_start - this.X) * 0.8; - if (i != nb_axis - 1) { - control = true; - } else if (i == nb_axis - 1) { - axisTitle.width = this.width; - axisTitle.height = null; + control = true; + if (i == nb_axis - 1) { + axisTitle.width = this.width - axisTitle.origin.x + this.X; + axisTitle.height = this.height - origin.y + this.Y; + axisTitle.fontsize = null; } } axisTitle.format(this.context); + let boxOrigin = new Vertex(origin.x, axisTitle.origin.y); - if (control) { axisTitle.origin.y += axisTitle.fontsize * (axisTitle.nRows - 1) }; + if (control) { axisTitle.origin.y += axisTitle.fontsize * (axisTitle.nRows - 1) } + if (standard) { boxOrigin.x -= axisTitle.width / 2 } - let boxSize = new Vertex(axisTitle.width, axisTitle.height); - this.axisNamesBoxes.push(new newRect(axisTitle.origin.subtract(boxSize), boxSize)); + let boxSize = new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize); + this.axisNamesBoxes.push(new newRect(boxOrigin, boxSize)); if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; From 230eaf3652716215f5bb8e6b7b96b4ef167f99db Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 11:41:42 +0200 Subject: [PATCH 15/54] fix(parallel_names): standard test come back --- script/multiplot.py | 27 +++++++++++++-------------- script/parallel_plot.py | 8 ++++---- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/script/multiplot.py b/script/multiplot.py index 4aed4462..47913b23 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -24,23 +24,23 @@ for i in range(nb_elements): random_color = available_colors[random.randint(0, len(available_colors) - 1)] random_direction = directions[random.randint(0, len(directions) - 1)] - elements.append({'x '*100: random.uniform(0, 200), - 'y '*100: random.uniform(0, 100), - 'color t r e z r t f d s'*3: random_color, + elements.append({'x': random.uniform(0, 200), + 'y': random.uniform(0, 100), + 'color': random_color, 'direction': random_direction}) # ParallelPlot -parallelplot1 = plot_data.ParallelPlot(axes=['x '*100, 'y '*100, 'color t r e z r t f d s'*3, 'direction']) -parallelplot2 = plot_data.ParallelPlot(axes=['y '*100, 'color t r e z r t f d s'*3]) +parallelplot1 = plot_data.ParallelPlot(axes=['x', 'y', 'color', 'direction']) +parallelplot2 = plot_data.ParallelPlot(axes=['y', 'color']) # Scatterplots -scatterplot1 = plot_data.Scatter(x_variable='x '*100, y_variable='y') +scatterplot1 = plot_data.Scatter(x_variable='x', y_variable='y') -scatterplot2 = plot_data.Scatter(x_variable='y '*100, y_variable='color t r e z r t f d s'*3, +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 '*100, 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]), @@ -61,17 +61,16 @@ primitive_group_container = plot_data.PrimitiveGroupsContainer(primitive_groups=primitive_groups, associated_elements=[1, 2, 3, 4], - x_variable='x '*100, y_variable='y '*100) + x_variable='x', y_variable='y') -histogram = plot_data.Histogram(x_variable='x '*100) +histogram = plot_data.Histogram(x_variable='x') histogram2 = plot_data.Histogram(x_variable='direction') -histogram3 = plot_data.Histogram(x_variable='color t r e z r t f d s'*3) +histogram3 = plot_data.Histogram(x_variable='color') # Creating the multiplot plots = [parallelplot1, scatterplot1] -# plots2 = [parallelplot1, parallelplot2, scatterplot1, scatterplot2, scatterplot3, graph2d, primitive_group_container, -# histogram] -plots2 = [parallelplot1, parallelplot2,parallelplot1, parallelplot2, parallelplot1, parallelplot2] +plots2 = [parallelplot1, parallelplot2, scatterplot1, scatterplot2, scatterplot3, graph2d, primitive_group_container, + histogram] # multiplot = plot_data.MultiplePlots(plots=plots, elements=elements, initial_view_on=True) plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) diff --git a/script/parallel_plot.py b/script/parallel_plot.py index 1439a062..638f29c2 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,14 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR': random.uniform(0, 0.05), - 'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRRTTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR': random.uniform(0, 100), + elements.append({'mass': random.uniform(0, 0.05), + 'length': random.uniform(0, 100), 'shape': random_shape, - 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR': random_color + 'color': random_color }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR', 'TTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRRTTT a d a a a a a a a a a a a a d f e e fzsf v r e f z z f fr g h ht r f dz s a d f r g b t g g v e d z s d v g r g y h u j t r e e RRR', 'shape', 'TTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRRTTTcolor color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color color RRR']) + axes=['mass', 'length', 'shape', 'color']) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. From eabe3389d9bd156056580ae2f3ade55aa306b268 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 12:41:04 +0200 Subject: [PATCH 16/54] fix(parallel_names): improve tests and add one for horizontal pplot with long names, and some changes on pics --- cypress/e2e/parallelplot.cy.ts | 9 +++++++++ ...add primitive group container in multiplot-base.png | 4 ++-- .../MULTIPLOT CANVAS -- should draw canvas-base.png | 4 ++-- ...rimitive group from container in multiplot-base.png | 4 ++-- ...NVAS -- should reorder all plots in canvas-base.png | 4 ++-- ...PARALLEL PLOT CANVAS -- should draw canvas-base.png | 4 ++-- script/parallel_plot.py | 10 +++++++--- src/subplots.ts | 1 + 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/parallelplot.cy.ts b/cypress/e2e/parallelplot.cy.ts index 22b44a2f..2269417e 100644 --- a/cypress/e2e/parallelplot.cy.ts +++ b/cypress/e2e/parallelplot.cy.ts @@ -1,5 +1,6 @@ import { parseHTML } from '../support/parseHTML'; import parallelPlotData from '../data_src/parallelplot.data.json'; +import { Interactions } from '../../src/plot-data'; const FEATURE_NAME = "parallelplot" @@ -16,4 +17,12 @@ describe('PARALLEL PLOT CANVAS', function () { it("should draw canvas", function () { cy.compareSnapshot(describeTitle + this.test.title, 0.05); }) + + it("should draw a nice horizontal parallel plot", function () { + cy.window().then((win) => { + let parallelPlot = win.eval('plot_data') + Interactions.change_disposition_action(parallelPlot); + cy.compareSnapshot(describeTitle + this.test.title, 0.05); + }) + }) }) 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 bba3c3e1..1bee4944 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e543a7e392fcdb7b5709561bbde807f89bd52455eede9aef308a8d62db75941b -size 225326 +oid sha256:009ec0d97c445c4c5acdca063fc8796d7aa66e485e58f15dbfd369df21e8eeff +size 225498 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 3155e66d..40c83d00 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427fc339306e4a32a1818129f9e489a517da5403af191dcf83c1bee3868a2d06 -size 323150 +oid sha256:2fd185fc0f087d7ff8fe53f25d064790da72c3e48e9c515378c314b895615cea +size 323518 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 eedf8244..153265d3 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e1037793f0fd81d10ca246c8b24d13fe27acd91e5c7a2cff254ba7357cb07f2d -size 315067 +oid sha256:6da4ce729f840823175c6e794a215a2a805a189004d50dc99af935372eb3b5fb +size 315614 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 1e04112f..10d5bb81 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4339a7704dba389f214959223063c60635b00dd7f8edc28a306973f9e11ef65a -size 312577 +oid sha256:1f7703b046ae3c209812865a20655ce88ef09d45fffb7f42756bf6367e75424f +size 313063 diff --git a/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw canvas-base.png index 91ec70df..09b17553 100644 --- a/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw canvas-base.png +++ b/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw canvas-base.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:51aeba6c46d1e7ef2853c24400b6fd3518b4d6a13917d7be65561c4e20ff0604 -size 433752 +oid sha256:617d50f253574d13a63ae7f68236ffa16393c84959d31c06760d364ad87ea4d1 +size 601347 diff --git a/script/parallel_plot.py b/script/parallel_plot.py index 638f29c2..26ec80dc 100644 --- a/script/parallel_plot.py +++ b/script/parallel_plot.py @@ -11,14 +11,18 @@ for i in range(50): random_shape = SHAPES[random.randint(0, len(SHAPES) - 1)] random_color = COLORS[random.randint(0, len(SHAPES) - 1)] - elements.append({'mass': random.uniform(0, 0.05), + elements.append({'long left attribute name ' * 10: random.uniform(0, 20), + 'mass': random.uniform(0, 0.05), 'length': random.uniform(0, 100), 'shape': random_shape, - 'color': random_color + 'color': random_color, + 'long middle attribute name ' * 10: random.uniform(-1000, -20), + 'long right attribute name ' * 7: random.uniform(0, 35) }) plot_data_object = plot_data.ParallelPlot(elements=elements, - axes=['mass', 'length', 'shape', 'color']) + axes=['long left attribute name ' * 10, 'mass', 'length', 'shape', 'color', + 'long middle attribute name ' * 10, 'long right attribute name ' * 7]) # The line above shows the minimum requirements for creating a # parallel plot. However, many options are available for further customization. diff --git a/src/subplots.ts b/src/subplots.ts index 08d34437..e5be1101 100644 --- a/src/subplots.ts +++ b/src/subplots.ts @@ -249,6 +249,7 @@ export class ParallelPlot extends PlotData { super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot); if (!is_in_multiplot) { var requirement = '0.6.1'; + console.log(data, data.package_version) check_package_version(data['package_version'], requirement); } this.type_ = 'parallelplot'; From 338aea6e9070ef13cc90e3f591a9a0722835a654 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 12:41:45 +0200 Subject: [PATCH 17/54] fix(parallel_names): add missing pic --- ...VAS -- should draw a nice horizontal parallel plot-base.png | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw a nice horizontal parallel plot-base.png diff --git a/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw a nice horizontal parallel plot-base.png b/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw a nice horizontal parallel plot-base.png new file mode 100644 index 00000000..8383f4bb --- /dev/null +++ b/cypress/snapshots/base/parallelplot.cy.ts/PARALLEL PLOT CANVAS -- should draw a nice horizontal parallel plot-base.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:822889e43cbe5103716ddd124683293c2ef013aeb346edd46b80ce3d8a2f4065 +size 695070 From 873670ece4444ce77cb253b48d0e454b7daebb81 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 15:01:35 +0200 Subject: [PATCH 18/54] feat(update_plot_canvas): add html_plot function in python --- plot_data/core.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plot_data/core.py b/plot_data/core.py index ee5e4589..679df754 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1302,6 +1302,15 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem PlotDataObject.__init__(self, type_='multiplot', name=name) +def html_plot(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', + force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): + plot_canvas(plot_data_object=plot_data_object, debug_mode=debug_mode, canvas_id=canvas_id, + force_version=force_version, width=width, height=height, page_name=page_name, display=False) + return + + + + def plot_canvas(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, From 1cd0840a355a6a4b312d5a47db227b8a4294b2fc Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 15:56:15 +0200 Subject: [PATCH 19/54] change python version to 3.9 --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index e77aa3eb..ea34bc4b 100644 --- a/.drone.yml +++ b/.drone.yml @@ -42,7 +42,7 @@ steps: - npx typedoc - name: install, run scripts and build doc - image: python:3.8 + image: python:3.9 commands: - git fetch --tags - python setup.py install @@ -97,7 +97,7 @@ steps: email: root@dessia.tech - name: check changelog update - image: python:3.8 + image: python:3.9 when: event: pull_request branch: @@ -180,7 +180,7 @@ steps: - bash code_pep8.sh - name: sdist - image: python:3.8 + image: python:3.9 commands: - python setup.py sdist From 6071ec3477115745484eca0f064ca38b204a66ec Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:01:05 +0200 Subject: [PATCH 20/54] fix(python_version): remove numpy version and write changelog --- CHANGELOG.md | 4 ++++ setup.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b383e51..30d60368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. 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). +## [0.14.0] +### Fix +- Python 3.9 in drone + ## [0.13.0] ### Add - Doc Typescript diff --git a/setup.py b/setup.py index 449d08f6..9fa557e6 100644 --- a/setup.py +++ b/setup.py @@ -116,5 +116,5 @@ def get_version(): packages=['plot_data'], package_dir={}, include_package_data=True, - install_requires=['matplotlib', 'dessia_common', 'numpy<=1.24.0'], + install_requires=['matplotlib', 'dessia_common', 'numpy'], classifiers=['Topic :: Scientific/Engineering :: Visualization', 'Development Status :: 3 - Alpha']) From 3285aaeca9ba8bb69992737f867cd9a71c1600a9 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:10:41 +0200 Subject: [PATCH 21/54] feat(update_plot_canvas): change name of plot_html --- plot_data/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plot_data/core.py b/plot_data/core.py index 679df754..48500dfd 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1302,7 +1302,7 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem PlotDataObject.__init__(self, type_='multiplot', name=name) -def html_plot(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', +def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): plot_canvas(plot_data_object=plot_data_object, debug_mode=debug_mode, canvas_id=canvas_id, force_version=force_version, width=width, height=height, page_name=page_name, display=False) From 4d0531e0ad56f3732d630757878c72a5639edad8 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:21:31 +0200 Subject: [PATCH 22/54] feat(plot_canvas): copy paste plot_canvas --- plot_data/core.py | 55 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 48500dfd..8e4eb1fa 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1304,8 +1304,55 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): - plot_canvas(plot_data_object=plot_data_object, debug_mode=debug_mode, canvas_id=canvas_id, - force_version=force_version, width=width, height=height, page_name=page_name, display=False) + first_letter = canvas_id[0] + if not isinstance(first_letter, str): + raise ValueError('canvas_id argument must not start with a number') + data = plot_data_object.to_dict() + plot_type = data['type_'] + if plot_type == 'primitivegroup': + template = templates.contour_template + elif plot_type in ('scatterplot', 'graph2d'): + template = templates.scatter_template + elif plot_type == 'parallelplot': + template = templates.parallelplot_template + elif plot_type == 'multiplot': + template = templates.multiplot_template + elif plot_type == 'primitivegroupcontainer': + template = templates.primitive_group_container_template + elif plot_type == 'histogram': + template = templates.histogram_template + elif plot_type == "scattermatrix": + template = templates.scatter_matrix_template + else: + raise NotImplementedError('Type {} not implemented'.format(plot_type)) + + if force_version is not None: + version, folder, filename = get_current_link(version=force_version) + else: + version, folder, filename = get_current_link() + cdn_url = 'https://cdn.dessia.tech/js/plot-data/{}/{}' + lib_path = cdn_url.format(version, filename) + if debug_mode: + core_path = os.sep.join(os.getcwd().split(os.sep)[:-1] + [folder, filename]) + + if not os.path.isfile(core_path): + msg = 'Local compiled {} not found, fall back to CDN' + print(msg.format(core_path)) + else: + lib_path = core_path.replace(" ", "%20") + + s = template.substitute(data=json.dumps(data), core_path=lib_path, + canvas_id=canvas_id, width=width, height=height) + if page_name is None: + temp_file = tempfile.mkstemp(suffix='.html')[1] + + with open(temp_file, 'wb') as file: + file.write(s.encode('utf-8')) + print('file://' + temp_file) + else: + with open(page_name + '.html', 'wb') as file: + file.write(s.encode('utf-8')) + print(page_name + '.html') return @@ -1334,9 +1381,7 @@ def plot_canvas(plot_data_object: PlotDataObject, :param page_name: set the created html file's name :type page_name: str """ - first_letter = canvas_id[0] - if not isinstance(first_letter, str): - raise ValueError('canvas_id argument must not start with a number') + data = plot_data_object.to_dict() plot_type = data['type_'] if plot_type == 'primitivegroup': From 6348f7b6186adec99413c41203aefa4c262a955a Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:25:59 +0200 Subject: [PATCH 23/54] pep8 --- plot_data/core.py | 1 + script/networkx_graph.py | 9 +++++---- script/plot_scatter.py | 4 ++-- script/text_scaling.py | 16 ++++++++-------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 8a9a356e..a8f6d7b6 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -31,6 +31,7 @@ import plot_data.colors from plot_data import templates + def delete_none_from_dict(dict1): """ Delete input dictionary's keys where value is None. """ dict2 = {} diff --git a/script/networkx_graph.py b/script/networkx_graph.py index 650e9669..b25fe650 100644 --- a/script/networkx_graph.py +++ b/script/networkx_graph.py @@ -13,9 +13,10 @@ graph.add_edge('a', 3) graph.add_edge(3, 1) -plotdata_nx_graph = plot_data.PrimitiveGroup(plot_data.graph.NetworkxGraph(graph)._to_primitives(text_style = plot_data.TextStyle(text_color='rgb(0, 0, 0)', - font_size=3, - text_align_x='center', - text_align_y='middle'))) +plotdata_nx_graph = plot_data.PrimitiveGroup(plot_data.graph.NetworkxGraph(graph)._to_primitives( + text_style=plot_data.TextStyle(text_color='rgb(0, 0, 0)', + font_size=3, + text_align_x='center', + text_align_y='middle'))) plot_data.plot_canvas(plotdata_nx_graph, debug_mode=True) diff --git a/script/plot_scatter.py b/script/plot_scatter.py index efced4e3..9d603873 100644 --- a/script/plot_scatter.py +++ b/script/plot_scatter.py @@ -20,7 +20,7 @@ plot_data_object = plot_data.Scatter(elements=elements, - x_variable='mass', y_variable='length') + x_variable='mass', y_variable='length') # The previous scripts shows the simplest way of creating a scatterplot. # However, many options are available for further customization @@ -63,7 +63,7 @@ tooltip = plot_data.Tooltip(attributes=['mass', 'length', 'shape', 'color']) # Heatmap settings -heatmap = plot_data.Heatmap([4,2], colors=[YELLOW, ORANGE, RED]) +heatmap = plot_data.Heatmap([4, 2], colors=[YELLOW, ORANGE, RED]) # Now, here is the new scatterplot customized_scatterplot = plot_data.Scatter(x_variable='mass', y_variable='shape', diff --git a/script/text_scaling.py b/script/text_scaling.py index c841e5f3..a00dd56c 100644 --- a/script/text_scaling.py +++ b/script/text_scaling.py @@ -13,15 +13,15 @@ class Box: def __init__(self, length: float, height: float, font_size: float, - name: str): + name: str): self.length = length self.height = height self.font_size = font_size self.name = name def contour(self, pos_y=0): - edge1 = LineSegment2D([-self.length/2, -self.height/2 + pos_y], - [self.length/2, -self.height/2 + pos_y]) + edge1 = LineSegment2D([-self.length / 2, -self.height / 2 + pos_y], + [self.length / 2, -self.height / 2 + pos_y]) edge2 = LineSegment2D([self.length / 2, -self.height / 2 + pos_y], [self.length / 2, self.height / 2 + pos_y]) edge3 = LineSegment2D([self.length / 2, self.height / 2 + pos_y], @@ -34,9 +34,9 @@ def plot_data(self, pos_y=0.): plot_data_contour = self.contour(pos_y) text_style = plot_data.TextStyle(text_color='rgb(0, 0, 0)', - font_size=self.font_size, - text_align_x='center', - text_align_y='middle') + font_size=self.font_size, + text_align_x='center', + text_align_y='middle') text = plot_data.Text(comment=self.name, position_x=0., position_y=pos_y, text_style=text_style, text_scaling=True, max_width=self.length, @@ -54,14 +54,14 @@ def plot_data(self, pos_y=0.): "lesquels il a bien fallu choisir un compromis, arbitraire :)") for i in range(nb_boxes): primitives.extend(Box(length=length, height=height, - font_size=font_size, name=name).plot_data(i*1.5*height)[0].primitives) + font_size=font_size, name=name).plot_data(i * 1.5 * height)[0].primitives) text_style = plot_data.TextStyle(text_color='rgb(100, 100, 100)', font_size=20) text = plot_data.Text(comment="Ce texte doit s'afficher dans le canvas, pas nécessairement centré sur le canvas", position_x=50., position_y=100, text_style=text_style, multi_lines=True, text_scaling=False, max_width=20) -ff=[text,text] +ff = [text, text] plot_data_object = plot_data.PrimitiveGroup(primitives=primitives + ff) plot_data.plot_canvas(plot_data_object, debug_mode=True) plot_data.plot_canvas(plot_data.PrimitiveGroup(primitives=ff), debug_mode=True) From 494ae414244c48df8987142e681978a05c8af669 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:27:50 +0200 Subject: [PATCH 24/54] pep8 and pre-commit --- src/plot-data.ts | 24 ++++++++++++------------ src/utils.ts | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index 2f1b99b6..49467087 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -852,7 +852,7 @@ export abstract class PlotData { const textParams: textParams = { width: width, height: origin.y - 2 - this.Y, align: align, baseline: "bottom", multiLine: true }; let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); - + let boxOriginX = 0; let axisLocation = 0; if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { @@ -871,25 +871,25 @@ export abstract class PlotData { axisTitle.format(this.context); // Weird but vowed to disappear - if (axisLocation == 0) { - boxOriginX = origin.x - axisTitle.width / 2; - axisTitle.format(this.context); + if (axisLocation == 0) { + boxOriginX = origin.x - axisTitle.width / 2; + axisTitle.format(this.context); } else if (axisLocation == -1) { boxOriginX = axisTitle.origin.x - axisTitle.width; - } else if (axisLocation == 1) { - boxOriginX = axisTitle.origin.x; - axisTitle.format(this.context); + } else if (axisLocation == 1) { + boxOriginX = axisTitle.origin.x; + axisTitle.format(this.context); } let boxOrigin = new Vertex(boxOriginX, origin.y - axisTitle.nRows * axisTitle.fontsize); this.axisNamesBoxes.push(new newRect(boxOrigin, new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize))); - if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } else { this.context.strokeStyle = 'black' }; this.context.fillStyle = 'black'; axisTitle.draw(this.context); - + this.context.textBaseline = "alphabetic"; var attribute_type = this.axis_list[i]['type_']; var list = this.axis_list[i]['list']; @@ -973,7 +973,7 @@ export abstract class PlotData { let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams); axisTitle.format(this.context); - + let control = false; let standard = true; if (axisTitle.width > (this.axis_x_start - this.X) * 2) { @@ -987,7 +987,7 @@ export abstract class PlotData { axisTitle.fontsize = null; } } - + axisTitle.format(this.context); let boxOrigin = new Vertex(origin.x, axisTitle.origin.y); @@ -996,7 +996,7 @@ export abstract class PlotData { let boxSize = new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize); this.axisNamesBoxes.push(new newRect(boxOrigin, boxSize)); - + if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue'; } else { diff --git a/src/utils.ts b/src/utils.ts index 2194dcee..16afe3d9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1667,7 +1667,7 @@ export class Triangle extends AbstractTriangle { } export interface textParams { - width?: number, height?: number, fontsize?: number, multiLine?: boolean, font?: string, align?: string, + width?: number, height?: number, fontsize?: number, multiLine?: boolean, font?: string, align?: string, baseline?: string, style?: string, orientation?: number } @@ -1752,7 +1752,7 @@ export class newText extends newShape { context.font = newText.buildFont(this.style, fontsize, this.font); if (this.width) { if (this.multiLine) { writtenText = this.cutting_text(context, this.width) } - else { + else { if (!this.fontsize) { fontsize = this.automaticFontSize(context); context.font = newText.buildFont(this.style, fontsize, this.font); @@ -2104,7 +2104,7 @@ export class newAxis { nameCoords.transformSelf(canvasHTMatrix); const orientation = this.isVertical ? -90 : 0; const textParams: textParams = { - width: this.drawLength, fontsize: this.FONT_SIZE, font: this.FONT, align: 'center', + width: this.drawLength, fontsize: this.FONT_SIZE, font: this.FONT, align: 'center', baseline: baseline, style: 'bold', orientation: orientation }; const textName = new newText(this.title, nameCoords, textParams); From 683bf460eab77fe1399811d6c4991457bf1d1423 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:28:50 +0200 Subject: [PATCH 25/54] pep8 --- plot_data/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 8e4eb1fa..dc061064 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1356,8 +1356,6 @@ def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas return - - def plot_canvas(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, From a9cc7608f5c8ace7acd6f91a4b9b85914534ef3e Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 16:50:16 +0200 Subject: [PATCH 26/54] feat(plot_canvas): remove if and add empty template --- plot_data/core.py | 51 ++++++++++++++++++++++-------------------- plot_data/templates.py | 2 ++ script/multiplot.py | 2 +- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index dc061064..39e58344 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -50,6 +50,8 @@ def delete_none_from_dict(dict1): class PlotDataObject(DessiaObject): """ Abstract interface for DessiaObject implementation in module. """ + _template_name = "empty_template" + def __init__(self, type_: str, name: str = '', **kwargs): self.type_ = type_ DessiaObject.__init__(self, name=name, **kwargs) @@ -88,6 +90,10 @@ def mpl_plot(self, ax=None): warnings.warn(f'class {self.__class__.__name__} does not implement mpl_plot, not plotting.') return ax + @property + def template(self): + return getattr(templates, self._template_name) + class Sample(PlotDataObject): """ @@ -779,6 +785,8 @@ class Graph2D(PlotDataObject): :type log_scale_y: bool """ + _template_name = "scatter_template" + def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis: Axis = None, log_scale_x: bool = None, log_scale_y: bool = None, name: str = ''): @@ -846,6 +854,8 @@ class Scatter(PlotDataObject): If set to False, you'd still be able to enable it using the button. """ + _template_name = "scatter_template" + def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, point_style: PointStyle = None, elements: List[Sample] = None, axis: Axis = None, log_scale_x: bool = None, log_scale_y: bool = None, heatmap: Heatmap = None, heatmap_view: bool = None, name: str = ''): @@ -878,6 +888,9 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po class ScatterMatrix(PlotDataObject): + + _template_name = "scatter_matrix_template" + def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_style: PointStyle = None, surface_style: SurfaceStyle = None, name: str = ""): @@ -1080,6 +1093,8 @@ class PrimitiveGroup(PlotDataObject): Circle2D, Line2D, MultipleLabels, Wire, Point2D]] """ + _template_name = "contour_template" + def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, Circle2D, Line2D, MultipleLabels, Wire, Point2D]], name: str = ''): @@ -1144,6 +1159,8 @@ class PrimitiveGroupsContainer(PlotDataObject): :type y_variable: str """ + _template_name = "primitive_group_container_template" + def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[float, float]] = None, coords: List[Tuple[float, float]] = None, @@ -1184,6 +1201,8 @@ class ParallelPlot(PlotDataObject): Color interpolation is enabled when clicking on an axis. """ + _template_name = "parallelplot_template" + def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, disposition: str = None, axes: List[str] = None, rgbs: List[Tuple[int, int, int]] = None, name: str = ''): if elements is None: @@ -1254,6 +1273,8 @@ class Histogram(PlotDataObject): :type surface_style: SurfaceStyle """ + _template_name = "histogram_template" + def __init__(self, x_variable: str, elements=None, axis: Axis = None, graduation_nb: float = None, edge_style: EdgeStyle = None, surface_style: SurfaceStyle = None, name: str = ''): self.x_variable = x_variable @@ -1277,6 +1298,8 @@ class MultiplePlots(PlotDataObject): :param initial_view_on: True for enabling initial layout, False otherwise """ + _template_name = "multiplot_template" + def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elements: List[Sample] = None, coords: List[Tuple[float, float]] = None, point_families: List[PointFamily] = None, initial_view_on: bool = None, name: str = ''): @@ -1304,27 +1327,8 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): - first_letter = canvas_id[0] - if not isinstance(first_letter, str): - raise ValueError('canvas_id argument must not start with a number') - data = plot_data_object.to_dict() - plot_type = data['type_'] - if plot_type == 'primitivegroup': - template = templates.contour_template - elif plot_type in ('scatterplot', 'graph2d'): - template = templates.scatter_template - elif plot_type == 'parallelplot': - template = templates.parallelplot_template - elif plot_type == 'multiplot': - template = templates.multiplot_template - elif plot_type == 'primitivegroupcontainer': - template = templates.primitive_group_container_template - elif plot_type == 'histogram': - template = templates.histogram_template - elif plot_type == "scattermatrix": - template = templates.scatter_matrix_template - else: - raise NotImplementedError('Type {} not implemented'.format(plot_type)) + + template = plot_data_object.template if force_version is not None: version, folder, filename = get_current_link(version=force_version) @@ -1341,11 +1345,10 @@ def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas else: lib_path = core_path.replace(" ", "%20") - s = template.substitute(data=json.dumps(data), core_path=lib_path, + s = template.substitute(data=json.dumps(plot_data_object.to_dict()), core_path=lib_path, canvas_id=canvas_id, width=width, height=height) - if page_name is None: + if not page_name: temp_file = tempfile.mkstemp(suffix='.html')[1] - with open(temp_file, 'wb') as file: file.write(s.encode('utf-8')) print('file://' + temp_file) diff --git a/plot_data/templates.py b/plot_data/templates.py index 74cccb49..d0976a38 100644 --- a/plot_data/templates.py +++ b/plot_data/templates.py @@ -1,5 +1,7 @@ from string import Template +empty_template = Template('''''') + contour_template = Template(''' diff --git a/script/multiplot.py b/script/multiplot.py index 09e2d2dc..5865a5a3 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -76,5 +76,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) +plot_data.plot_html(plot_data_object=plot_data_object, debug_mode=True) # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) From a6c1a6675ecb2c4ef521bb8550dba05105f671ad Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 17:03:40 +0200 Subject: [PATCH 27/54] feat(plot_canvas): split plot_canvas in two methods that allow to create file without plotting it in browser --- plot_data/core.py | 70 ++++++++++----------------------------------- script/multiplot.py | 2 +- 2 files changed, 16 insertions(+), 56 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 39e58344..8ce53865 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1325,8 +1325,8 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem PlotDataObject.__init__(self, type_='multiplot', name=name) -def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', - force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): +def to_html_file(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', + force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): template = plot_data_object.template @@ -1351,12 +1351,14 @@ def plot_html(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas temp_file = tempfile.mkstemp(suffix='.html')[1] with open(temp_file, 'wb') as file: file.write(s.encode('utf-8')) - print('file://' + temp_file) + file_name = temp_file else: with open(page_name + '.html', 'wb') as file: file.write(s.encode('utf-8')) - print(page_name + '.html') - return + file_name = page_name + '.html' + + return file_name + def plot_canvas(plot_data_object: PlotDataObject, @@ -1383,57 +1385,15 @@ def plot_canvas(plot_data_object: PlotDataObject, :type page_name: str """ - data = plot_data_object.to_dict() - plot_type = data['type_'] - if plot_type == 'primitivegroup': - template = templates.contour_template - elif plot_type in ('scatterplot', 'graph2d'): - template = templates.scatter_template - elif plot_type == 'parallelplot': - template = templates.parallelplot_template - elif plot_type == 'multiplot': - template = templates.multiplot_template - elif plot_type == 'primitivegroupcontainer': - template = templates.primitive_group_container_template - elif plot_type == 'histogram': - template = templates.histogram_template - elif plot_type == "scattermatrix": - template = templates.scatter_matrix_template - else: - raise NotImplementedError('Type {} not implemented'.format(plot_type)) - - if force_version is not None: - version, folder, filename = get_current_link(version=force_version) - else: - version, folder, filename = get_current_link() - cdn_url = 'https://cdn.dessia.tech/js/plot-data/{}/{}' - lib_path = cdn_url.format(version, filename) - if debug_mode: - core_path = os.sep.join(os.getcwd().split(os.sep)[:-1] + [folder, filename]) - - if not os.path.isfile(core_path): - msg = 'Local compiled {} not found, fall back to CDN' - print(msg.format(core_path)) + html_file = to_html_file(plot_data_object=plot_data_object, debug_mode=debug_mode, canvas_id=canvas_id, + force_version=force_version, width=width, height=height, page_name=page_name) + if display: + if page_name is None: + webbrowser.open('file://' + html_file) + print('file://' + html_file) else: - lib_path = core_path.replace(" ", "%20") - - s = template.substitute(data=json.dumps(data), core_path=lib_path, - canvas_id=canvas_id, width=width, height=height) - if page_name is None: - temp_file = tempfile.mkstemp(suffix='.html')[1] - - with open(temp_file, 'wb') as file: - file.write(s.encode('utf-8')) - - if display: - webbrowser.open('file://' + temp_file) - print('file://' + temp_file) - else: - with open(page_name + '.html', 'wb') as file: - file.write(s.encode('utf-8')) - if display: - webbrowser.open('file://' + os.path.realpath(page_name + '.html')) - print(page_name + '.html') + webbrowser.open('file://' + os.path.realpath(html_file)) + print(html_file) def write_json_for_tests(plot_data_object: PlotDataObject, json_path: str): diff --git a/script/multiplot.py b/script/multiplot.py index 5865a5a3..09e2d2dc 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -76,5 +76,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_html(plot_data_object=plot_data_object, debug_mode=True) +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) From 8a021dfc795248c117665ea76fcf4f23eecc7d93 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 17:05:22 +0200 Subject: [PATCH 28/54] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30d60368..7705bbf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ 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). ## [0.14.0] +### Add +- to_html_file method in Python so that it is possible to create a plot data file without opening it in web browser + ### Fix - Python 3.9 in drone From 17db3398b48624163b3d15b8ecc2c4e419fae022 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 17:06:15 +0200 Subject: [PATCH 29/54] feat(plot_canvas): precommit and pep8 --- plot_data/core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plot_data/core.py b/plot_data/core.py index 8ce53865..794a556a 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1360,7 +1360,6 @@ def to_html_file(plot_data_object: PlotDataObject, debug_mode: bool = False, can return file_name - def plot_canvas(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, From 67b22361a7e4c6da30560a8c31f1802919e46d50 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Fri, 9 Jun 2023 17:19:03 +0200 Subject: [PATCH 30/54] feat(plot_canvas): pylint --- code_pylint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index 1b3d3dc0..3e0600ab 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -24,7 +24,7 @@ 'invalid-name': 13, 'consider-using-f-string': 2, 'no-else-return': 17, - 'arguments-differ': 13, + 'arguments-differ': 2, 'no-member': 1, 'too-many-locals': 3, 'wrong-import-order': 1, @@ -37,7 +37,7 @@ 'trailing-whitespace': 11, 'empty-docstring': 7, 'missing-module-docstring': 4, - 'too-many-arguments': 15, + 'too-many-arguments': 16, 'too-few-public-methods': 7, 'unnecessary-comprehension': 5, 'no-value-for-parameter': 2, From 254705466ef7b3d7a51689f2d7aef8642efb1879 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 12:52:37 +0200 Subject: [PATCH 31/54] feat(update_plot_canvas): add to_html, _to_html and to_html_stream methods in plotdataobject and simplify plot_canvas method --- CHANGELOG.md | 2 +- plot_data/core.py | 89 +++++++++++++++++++++++------------------------ 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7705bbf8..735f1699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.14.0] ### Add -- to_html_file method in Python so that it is possible to create a plot data file without opening it in web browser +- to_html method in Python so that it is possible to create a plot data file without opening it in web browser ### Fix - Python 3.9 in drone diff --git a/plot_data/core.py b/plot_data/core.py index 794a556a..799c3636 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -25,6 +25,7 @@ from dessia_common.utils.serialization import serialize from dessia_common.core import DessiaObject +from dessia_common.exports import ExportFormat from dessia_common.typings import JsonSerializable from matplotlib import patches @@ -94,6 +95,33 @@ def mpl_plot(self, ax=None): def template(self): return getattr(templates, self._template_name) + def _export_formats(self) -> List[ExportFormat]: + """ Return a list of objects describing how to call generic exports (.json, .xlsx). """ + formats = DessiaObject._export_formats(self) + formats.append(ExportFormat(selector="html", extension="html", method_name="to_html_stream", text=False)) + return formats + + def _to_html(self, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, width: int = 750, + height: int = 400): + lib_path = plot_data_path(debug_mode=debug_mode, version=version) + return self.template.substitute(data=json.dumps(self.to_dict()), core_path=lib_path, canvas_id=canvas_id, + width=width, height=height) + + def to_html_stream(self, stream, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, + width: int = 750, height: int = 400): + html = self._to_html(debug_mode=debug_mode, canvas_id=canvas_id, version=version, width=width, height=height) + stream.write(html.encode('utf-8')) + + def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, + width: int = 750, height: int = 400): + """ Export current PlotDataObject to an HTML file given by the filepath. """ + if not filepath.endswith('.html'): + filepath += '.html' + print(f'Changing name to {filepath}') + with open(filepath, 'wb') as file: + self.to_html_stream(file, debug_mode=debug_mode, canvas_id=canvas_id, version=version, width=width, + height=height) + class Sample(PlotDataObject): """ @@ -1325,48 +1353,21 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem PlotDataObject.__init__(self, type_='multiplot', name=name) -def to_html_file(plot_data_object: PlotDataObject, debug_mode: bool = False, canvas_id: str = 'canvas', - force_version: str = None, width: int = 750, height: int = 400, page_name: str = None): - - template = plot_data_object.template - - if force_version is not None: - version, folder, filename = get_current_link(version=force_version) - else: - version, folder, filename = get_current_link() - cdn_url = 'https://cdn.dessia.tech/js/plot-data/{}/{}' - lib_path = cdn_url.format(version, filename) +def plot_data_path(debug_mode: bool = False, version: str = None): + version, folder, filename = get_current_link(version=version) if debug_mode: core_path = os.sep.join(os.getcwd().split(os.sep)[:-1] + [folder, filename]) + if os.path.isfile(core_path): + return core_path.replace(" ", "%20") + print(f'Local compiled {core_path} not found, fall back to CDN') + return f'https://cdn.dessia.tech/js/plot-data/{version}/{filename}' - if not os.path.isfile(core_path): - msg = 'Local compiled {} not found, fall back to CDN' - print(msg.format(core_path)) - else: - lib_path = core_path.replace(" ", "%20") - - s = template.substitute(data=json.dumps(plot_data_object.to_dict()), core_path=lib_path, - canvas_id=canvas_id, width=width, height=height) - if not page_name: - temp_file = tempfile.mkstemp(suffix='.html')[1] - with open(temp_file, 'wb') as file: - file.write(s.encode('utf-8')) - file_name = temp_file - else: - with open(page_name + '.html', 'wb') as file: - file.write(s.encode('utf-8')) - file_name = page_name + '.html' - - return file_name - - -def plot_canvas(plot_data_object: PlotDataObject, - debug_mode: bool = False, canvas_id: str = 'canvas', - force_version: str = None, - width: int = 750, height: int = 400, page_name: str = None, + +def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mode: bool = False, + canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, display: bool = True): """ - Creates a html file and plots input data in web browser + Creates a html file and plots input data in web browser. :param plot_data_object: a PlotDataObject(ie Scatter, ParallelPlot,\ MultiplePlots, Graph2D, PrimitiveGroup or PrimitiveGroupContainer) @@ -1383,16 +1384,14 @@ def plot_canvas(plot_data_object: PlotDataObject, :param page_name: set the created html file's name :type page_name: str """ + if not filepath: + filepath = tempfile.mkstemp(suffix='.html')[1] - html_file = to_html_file(plot_data_object=plot_data_object, debug_mode=debug_mode, canvas_id=canvas_id, - force_version=force_version, width=width, height=height, page_name=page_name) + plot_data_object.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version, + width=width, height=height) if display: - if page_name is None: - webbrowser.open('file://' + html_file) - print('file://' + html_file) - else: - webbrowser.open('file://' + os.path.realpath(html_file)) - print(html_file) + webbrowser.open('file://' + os.path.realpath(filepath)) + print('file://' + filepath) def write_json_for_tests(plot_data_object: PlotDataObject, json_path: str): From bb8c7ac580a1438cc4a5747893a1da4fe37ad560 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 13:01:06 +0200 Subject: [PATCH 32/54] feat(update_plot_canvas): code cosmetics --- plot_data/core.py | 105 ++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 70 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 799c3636..1c56e5ca 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -124,9 +124,7 @@ def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str class Sample(PlotDataObject): - """ - Graph Point. - """ + """ Graph Point. """ def __init__(self, values, reference_path: str = "#", name: str = ""): self.values = values @@ -238,9 +236,8 @@ class PointStyle(DessiaObject): :type shape: str """ - def __init__(self, color_fill: str = None, color_stroke: str = None, - stroke_width: float = None, - size: float = None, shape: str = None, name: str = ''): + def __init__(self, color_fill: str = None, color_stroke: str = None, stroke_width: float = None, size: float = None, + shape: str = None, name: str = ''): self.color_fill = color_fill self.color_stroke = color_stroke self.stroke_width = stroke_width @@ -292,11 +289,8 @@ class TextStyle(DessiaObject): :type angle: float """ - def __init__(self, text_color: plot_data.colors.Color = None, - font_size: float = None, - font_style: str = None, - text_align_x: str = None, text_align_y: str = None, - bold: bool = None, italic: bool = None, + def __init__(self, text_color: plot_data.colors.Color = None, font_size: float = None, font_style: str = None, + text_align_x: str = None, text_align_y: str = None, bold: bool = None, italic: bool = None, angle: float = None, name: str = ''): self.text_color = text_color self.font_size = font_size @@ -328,8 +322,7 @@ class SurfaceStyle(DessiaObject): :type hatching: HatchingSet """ - def __init__(self, color_fill: str = None, opacity: float = 1., - hatching: HatchingSet = None, name: str = ''): + def __init__(self, color_fill: str = None, opacity: float = 1., hatching: HatchingSet = None, name: str = ''): # TODO: migrate from str to Color object self.color_fill = color_fill self.opacity = opacity @@ -386,9 +379,8 @@ class Text(PlotDataObject): :type multi_lines: bool """ - def __init__(self, comment: str, position_x: float, position_y: float, - text_style: TextStyle = None, text_scaling: bool = None, - max_width: float = None, multi_lines: bool = True, name: str = ''): + def __init__(self, comment: str, position_x: float, position_y: float, text_style: TextStyle = None, + text_scaling: bool = None, max_width: float = None, multi_lines: bool = True, name: str = ''): self.comment = comment self.text_style = text_style self.position_x = position_x @@ -425,8 +417,7 @@ class Line2D(PlotDataObject): :type edge_style: EdgeStyle """ - def __init__(self, point1: List[float], point2: List[float], - edge_style: EdgeStyle = None, name: str = ''): + def __init__(self, point1: List[float], point2: List[float], edge_style: EdgeStyle = None, name: str = ''): self.data = point1 + point2 self.edge_style = edge_style PlotDataObject.__init__(self, type_='line2d', name=name) @@ -465,9 +456,7 @@ class LineSegment2D(PlotDataObject): :type edge_style: EdgeStyle """ - def __init__(self, point1: List[float], point2: List[float], - edge_style: EdgeStyle = None, - name: str = ''): + def __init__(self, point1: List[float], point2: List[float], edge_style: EdgeStyle = None, name: str = ''): # Data is used in typescript self.data = point1 + point2 self.point1 = point1 @@ -509,14 +498,12 @@ def mpl_plot(self, ax=None, edge_style=None): else: edge_style = DEFAULT_EDGESTYLE - ax.plot([self.point1[0], self.point2[0]], [self.point1[1], self.point2[1]], - **edge_style.mpl_arguments()) + ax.plot([self.point1[0], self.point2[0]], [self.point1[1], self.point2[1]], **edge_style.mpl_arguments()) return ax class LineSegment(LineSegment2D): - def __init__(self, data: List[float], edge_style: EdgeStyle = None, - name: str = ''): + def __init__(self, data: List[float], edge_style: EdgeStyle = None, name: str = ''): # When to remove support? warnings.warn("LineSegment is deprecated, use LineSegment2D instead", DeprecationWarning) @@ -542,8 +529,8 @@ class Wire(PlotDataObject): :type tooltip: str """ - def __init__(self, lines: List[Tuple[float, float]], edge_style: EdgeStyle = None, - tooltip: str = None, name: str = ""): + def __init__(self, lines: List[Tuple[float, float]], edge_style: EdgeStyle = None, tooltip: str = None, + name: str = ""): self.lines = lines self.edge_style = edge_style self.tooltip = tooltip @@ -580,11 +567,8 @@ class Circle2D(PlotDataObject): :type tooltip: str """ - def __init__(self, cx: float, cy: float, r: float, - edge_style: EdgeStyle = None, - surface_style: SurfaceStyle = None, - tooltip: str = None, - name: str = ''): + def __init__(self, cx: float, cy: float, r: float, edge_style: EdgeStyle = None, + surface_style: SurfaceStyle = None, tooltip: str = None, name: str = ''): self.edge_style = edge_style self.surface_style = surface_style self.r = r @@ -637,9 +621,7 @@ class Point2D(PlotDataObject): :type point_style: PointStyle """ - def __init__(self, cx: float, cy: float, - point_style: PointStyle = None, - name: str = ''): + def __init__(self, cx: float, cy: float, point_style: PointStyle = None, name: str = ''): self.cx = cx self.cy = cy self.point_style = point_style @@ -686,10 +668,8 @@ class Axis(PlotDataObject): :type grid_on: bool """ - def __init__(self, nb_points_x: int = 10, nb_points_y: int = 10, - graduation_style: TextStyle = None, - axis_style: EdgeStyle = None, arrow_on: bool = False, - grid_on: bool = True, name: str = ''): + def __init__(self, nb_points_x: int = 10, nb_points_y: int = 10, graduation_style: TextStyle = None, + axis_style: EdgeStyle = None, arrow_on: bool = False, grid_on: bool = True, name: str = ''): self.nb_points_x = nb_points_x self.nb_points_y = nb_points_y self.graduation_style = graduation_style @@ -722,11 +702,8 @@ class Tooltip(PlotDataObject): :type tooltip_radius: float """ - def __init__(self, attributes: List[str] = None, - text: str = None, - surface_style: SurfaceStyle = None, - text_style: TextStyle = None, tooltip_radius: float = None, - name: str = ''): + def __init__(self, attributes: List[str] = None, text: str = None, surface_style: SurfaceStyle = None, + text_style: TextStyle = None, tooltip_radius: float = None, name: str = ''): self.attributes = attributes self.text = text self.surface_style = surface_style @@ -767,10 +744,8 @@ class Dataset(PlotDataObject): """ attribute_names = None - def __init__(self, elements: List[Sample] = None, - edge_style: EdgeStyle = None, tooltip: Tooltip = None, - point_style: PointStyle = None, - display_step: int = 1, name: str = ''): + def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, tooltip: Tooltip = None, + point_style: PointStyle = None, display_step: int = 1, name: str = ''): self.edge_style = edge_style self.tooltip = tooltip @@ -815,9 +790,8 @@ class Graph2D(PlotDataObject): _template_name = "scatter_template" - def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, - axis: Axis = None, log_scale_x: bool = None, - log_scale_y: bool = None, name: str = ''): + def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis: Axis = None, + log_scale_x: bool = None, log_scale_y: bool = None, name: str = ''): self.graphs = graphs self.attribute_names = [x_variable, y_variable] if axis is None: @@ -919,9 +893,8 @@ class ScatterMatrix(PlotDataObject): _template_name = "scatter_matrix_template" - def __init__(self, elements: List[Sample] = None, axes: List[str] = None, - point_style: PointStyle = None, surface_style: SurfaceStyle = None, - name: str = ""): + def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_style: PointStyle = None, + surface_style: SurfaceStyle = None, name: str = ""): if elements is None: elements = [] sampled_elements = [] @@ -968,10 +941,8 @@ class Arc2D(PlotDataObject): :type edge_style: EdgeStyle """ - def __init__(self, cx: float, cy: float, r: float, start_angle: float, - end_angle: float, data=None, anticlockwise: bool = None, - edge_style: EdgeStyle = None, - name: str = ''): + def __init__(self, cx: float, cy: float, r: float, start_angle: float, end_angle: float, data=None, + anticlockwise: bool = None, edge_style: EdgeStyle = None, name: str = ''): self.cx = cx self.cy = cy self.r = r @@ -1025,8 +996,7 @@ class Contour2D(PlotDataObject): :type tooltip: str """ - def __init__(self, plot_data_primitives: List[Union[Arc2D, LineSegment2D]], - edge_style: EdgeStyle = None, + def __init__(self, plot_data_primitives: List[Union[Arc2D, LineSegment2D]], edge_style: EdgeStyle = None, surface_style: SurfaceStyle = None, tooltip: str = None, name: str = ''): self.plot_data_primitives = plot_data_primitives self.edge_style = edge_style @@ -1087,8 +1057,7 @@ class Label(PlotDataObject): :type rectangle_edge_style: EdgeStyle """ - def __init__(self, title: str, text_style: TextStyle = None, - rectangle_surface_style: SurfaceStyle = None, + def __init__(self, title: str, text_style: TextStyle = None, rectangle_surface_style: SurfaceStyle = None, rectangle_edge_style: EdgeStyle = None, name: str = ''): self.title = title self.text_style = text_style @@ -1189,12 +1158,9 @@ class PrimitiveGroupsContainer(PlotDataObject): _template_name = "primitive_group_container_template" - def __init__(self, primitive_groups: List[PrimitiveGroup], - sizes: List[Tuple[float, float]] = None, - coords: List[Tuple[float, float]] = None, - associated_elements: List[int] = None, - x_variable: str = None, y_variable: str = None, - name: str = ''): + def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[float, float]] = None, + coords: List[Tuple[float, float]] = None, associated_elements: List[int] = None, + x_variable: str = None, y_variable: str = None, name: str = ''): for i, value in enumerate(primitive_groups): if not isinstance(value, PrimitiveGroup): primitive_groups[i] = PrimitiveGroup(primitives=value) @@ -1274,8 +1240,7 @@ class PointFamily(PlotDataObject): :param point_index: a list containing the point's index from MultiplePlots.elements """ - def __init__(self, point_color: str, point_index: List[int], - name: str = ''): + def __init__(self, point_color: str, point_index: List[int], name: str = ''): self.color = point_color self.point_index = point_index PlotDataObject.__init__(self, type_=None, name=name) From 563b558c71882c3652e2693f2d2281cf741a6452 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 13:10:03 +0200 Subject: [PATCH 33/54] feat(update_plot_canvas): add figure object and separate objects that can be htmlified and others --- plot_data/core.py | 55 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 1c56e5ca..42f3ba44 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -58,7 +58,7 @@ def __init__(self, type_: str, name: str = '', **kwargs): DessiaObject.__init__(self, name=name, **kwargs) def to_dict(self, *args, **kwargs) -> JsonSerializable: - """ Redefines DessiaObject's to_dict() in order to not use pointers and remove keys where value is None. """ + """ Redefines DessiaObject's to_dict() in order not to use pointers and remove keys where value is None. """ dict_ = DessiaObject.to_dict(self, use_pointers=False) del dict_['object_class'] package_name = self.__module__.split('.', maxsplit=1)[0] @@ -91,6 +91,15 @@ def mpl_plot(self, ax=None): warnings.warn(f'class {self.__class__.__name__} does not implement mpl_plot, not plotting.') return ax + +class Figure(PlotDataObject): + """ Abstract interface for handling html exportable objects in module. """ + + def __init__(self, type_: str, width: int = 750, height: int = 400, name: str = '', **kwargs): + self.width = width + self.height = height + PlotDataObject.__init__(self, type_=type_, name=name, **kwargs) + @property def template(self): return getattr(templates, self._template_name) @@ -356,8 +365,7 @@ def mpl_arguments(self): class Text(PlotDataObject): """ - A class for displaying texts on canvas. Text is a primitive and can be - instantiated by PrimitiveGroup. + A class for displaying texts on canvas. Text is a primitive and can be instantiated by PrimitiveGroup. :param comment: the comment you want to display :type comment: str @@ -367,15 +375,13 @@ class Text(PlotDataObject): :type position_y: float :param text_style: for customization (optional) :type text_style: TextStyle - :param text_scaling: True if you want the text the be rescaled \ - when zooming and False otherwise. + :param text_scaling: True if you want the text the be rescaled when zooming and False otherwise. :type text_scaling: bool - :param max_width: Set a maximum length for the text. If the text \ - is longer than max_width, it is split into several lines. + :param max_width: Set a maximum length for the text. If the text is longer than max_width, it is split into + several lines. :type max_width: float - :param multi_lines: This parameter is only useful when max_width parameter is set \ - In that case, you can choose between squishing the text in one line or writing on \ - multiple lines. + :param multi_lines: This parameter is only useful when max_width parameter is set. In that case, you can choose + between squishing the text in one line or writing on multiple lines. :type multi_lines: bool """ @@ -391,23 +397,16 @@ def __init__(self, comment: str, position_x: float, position_y: float, text_styl PlotDataObject.__init__(self, type_='text', name=name) def mpl_plot(self, ax=None, color='k', alpha=1.): - """ - Plots using Matplotlib. - """ + """ Plots using Matplotlib. """ if not ax: _, ax = plt.subplots() - ax.text(self.position_x, self.position_y, - self.comment, - color=color, - alpha=alpha) - + ax.text(self.position_x, self.position_y, self.comment, color=color, alpha=alpha) return ax class Line2D(PlotDataObject): """ - An infinite line. Line2D is a primitive and can be instantiated by \ - PrimitiveGroups. + An infinite line. Line2D is a primitive and can be instantiated by PrimitiveGroups. :param point1: first endpoint of the line segment [x1, y1]. :type point1: List[float] @@ -768,7 +767,7 @@ def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, PlotDataObject.__init__(self, type_='dataset', name=name) -class Graph2D(PlotDataObject): +class Graph2D(Figure): """ Takes one or several Datasets as input and displays them all in \ one canvas. @@ -839,7 +838,7 @@ def __init__(self, size: Tuple[int, int] = None, colors: List[plot_data.colors.C DessiaObject.__init__(self, name=name) -class Scatter(PlotDataObject): +class Scatter(Figure): """ A class for drawing scatter plots. @@ -889,7 +888,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po PlotDataObject.__init__(self, type_='scatterplot', name=name) -class ScatterMatrix(PlotDataObject): +class ScatterMatrix(Figure): _template_name = "scatter_matrix_template" @@ -1080,7 +1079,7 @@ def __init__(self, labels: List[Label], name: str = ''): PlotDataObject.__init__(self, type_='multiplelabels', name=name) -class PrimitiveGroup(PlotDataObject): +class PrimitiveGroup(Figure): """ A class for drawing multiple primitives and contours inside a canvas. @@ -1134,7 +1133,7 @@ def bounding_box(self): return xmin, xmax, ymin, ymax -class PrimitiveGroupsContainer(PlotDataObject): +class PrimitiveGroupsContainer(Figure): """ A class for drawing several PrimitiveGroups in one canvas. @@ -1182,7 +1181,7 @@ def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[flo name=name) -class ParallelPlot(PlotDataObject): +class ParallelPlot(Figure): """ Draws a parallel coordinates plot. @@ -1246,7 +1245,7 @@ def __init__(self, point_color: str, point_index: List[int], name: str = ''): PlotDataObject.__init__(self, type_=None, name=name) -class Histogram(PlotDataObject): +class Histogram(Figure): """ The Histogram object. This class can be instantiated in Multiplot. @@ -1279,7 +1278,7 @@ def __init__(self, x_variable: str, elements=None, axis: Axis = None, graduation PlotDataObject.__init__(self, type_='histogram', name=name) -class MultiplePlots(PlotDataObject): +class MultiplePlots(Figure): """ A class for drawing multiple PlotDataObjects (except MultiplePlots) in one canvas. From 6c0c04e4e791c55bf17324aba1238b1dad831ebb Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 13:15:05 +0200 Subject: [PATCH 34/54] feat(update_plot): simplify width and height in Figure --- plot_data/core.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 42f3ba44..5b797cf0 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -110,26 +110,22 @@ def _export_formats(self) -> List[ExportFormat]: formats.append(ExportFormat(selector="html", extension="html", method_name="to_html_stream", text=False)) return formats - def _to_html(self, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, width: int = 750, - height: int = 400): + def _to_html(self, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None): lib_path = plot_data_path(debug_mode=debug_mode, version=version) return self.template.substitute(data=json.dumps(self.to_dict()), core_path=lib_path, canvas_id=canvas_id, - width=width, height=height) + width=self.width, height=self.height) - def to_html_stream(self, stream, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, - width: int = 750, height: int = 400): - html = self._to_html(debug_mode=debug_mode, canvas_id=canvas_id, version=version, width=width, height=height) + def to_html_stream(self, stream, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None): + html = self._to_html(debug_mode=debug_mode, canvas_id=canvas_id, version=version) stream.write(html.encode('utf-8')) - def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None, - width: int = 750, height: int = 400): + def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None): """ Export current PlotDataObject to an HTML file given by the filepath. """ if not filepath.endswith('.html'): filepath += '.html' print(f'Changing name to {filepath}') with open(filepath, 'wb') as file: - self.to_html_stream(file, debug_mode=debug_mode, canvas_id=canvas_id, version=version, width=width, - height=height) + self.to_html_stream(file, debug_mode=debug_mode, canvas_id=canvas_id, version=version) class Sample(PlotDataObject): @@ -1351,8 +1347,13 @@ def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mo if not filepath: filepath = tempfile.mkstemp(suffix='.html')[1] - plot_data_object.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version, - width=width, height=height) + drawn_plot = plot_data_object.copy() + if width: + drawn_plot.witdh = width + if height: + drawn_plot.height = height + + drawn_plot.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) if display: webbrowser.open('file://' + os.path.realpath(filepath)) print('file://' + filepath) From 2df5d4242880cf4da2b4c17c41e130100154eb86 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 14:49:14 +0200 Subject: [PATCH 35/54] feat(update_plot_canvas): fix bugs --- plot_data/core.py | 48 +++++++++++++++++++++------------------- plot_data/templates.py | 34 +++++++--------------------- script/multiplot.py | 2 +- script/scatter_matrix.py | 2 +- 4 files changed, 35 insertions(+), 51 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 5b797cf0..4e4045ef 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -786,7 +786,8 @@ class Graph2D(Figure): _template_name = "scatter_template" def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis: Axis = None, - log_scale_x: bool = None, log_scale_y: bool = None, name: str = ''): + log_scale_x: bool = None, log_scale_y: bool = None, width: int = 750, height: int = 400, + name: str = ''): self.graphs = graphs self.attribute_names = [x_variable, y_variable] if axis is None: @@ -795,7 +796,7 @@ def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis self.axis = axis self.log_scale_x = log_scale_x self.log_scale_y = log_scale_y - PlotDataObject.__init__(self, type_='graph2d', name=name) + Figure.__init__(self, width=width, height=height, type_='graph2d', name=name) def mpl_plot(self): # axs = plt.subplots(len(self.graphs)) @@ -855,7 +856,8 @@ class Scatter(Figure): def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, point_style: PointStyle = None, elements: List[Sample] = None, axis: Axis = None, log_scale_x: bool = None, log_scale_y: bool = None, - heatmap: Heatmap = None, heatmap_view: bool = None, name: str = ''): + heatmap: Heatmap = None, heatmap_view: bool = None, width: int = 750, height: int = 400, + name: str = ''): self.tooltip = tooltip self.attribute_names = [x_variable, y_variable] self.point_style = point_style @@ -881,7 +883,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po self.log_scale_y = log_scale_y self.heatmap = heatmap self.heatmap_view = heatmap_view - PlotDataObject.__init__(self, type_='scatterplot', name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='scatterplot', name=name) class ScatterMatrix(Figure): @@ -889,7 +891,7 @@ class ScatterMatrix(Figure): _template_name = "scatter_matrix_template" def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_style: PointStyle = None, - surface_style: SurfaceStyle = None, name: str = ""): + surface_style: SurfaceStyle = None, width: int = 750, height: int = 400, name: str = ""): if elements is None: elements = [] sampled_elements = [] @@ -907,7 +909,7 @@ def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_ self.axes = axes self.point_style = point_style self.surface_style = surface_style - PlotDataObject.__init__(self, type_="scattermatrix", name=name) + PlotDataObject.__init__(self, width=width, height=height, type_="scattermatrix", name=name) class Arc2D(PlotDataObject): @@ -1087,11 +1089,11 @@ class PrimitiveGroup(Figure): _template_name = "contour_template" - def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, - Circle2D, Line2D, MultipleLabels, Wire, Point2D]], - name: str = ''): + def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, Circle2D, + Line2D, MultipleLabels, Wire, Point2D]], width: int = 750, + height: int = 400, name: str = ''): self.primitives = primitives - PlotDataObject.__init__(self, type_='primitivegroup', name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='primitivegroup', name=name) def mpl_plot(self, ax=None, equal_aspect=True): """ @@ -1155,7 +1157,7 @@ class PrimitiveGroupsContainer(Figure): def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[float, float]] = None, coords: List[Tuple[float, float]] = None, associated_elements: List[int] = None, - x_variable: str = None, y_variable: str = None, name: str = ''): + x_variable: str = None, y_variable: str = None, width: int = 750, height: int = 400, name: str = ''): for i, value in enumerate(primitive_groups): if not isinstance(value, PrimitiveGroup): primitive_groups[i] = PrimitiveGroup(primitives=value) @@ -1173,8 +1175,7 @@ def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[flo if y_variable: attribute_names.append(y_variable) self.association['attribute_names'] = attribute_names - PlotDataObject.__init__(self, type_='primitivegroupcontainer', - name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='primitivegroupcontainer', name=name) class ParallelPlot(Figure): @@ -1193,7 +1194,8 @@ class ParallelPlot(Figure): _template_name = "parallelplot_template" def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, disposition: str = None, - axes: List[str] = None, rgbs: List[Tuple[int, int, int]] = None, name: str = ''): + axes: List[str] = None, rgbs: List[Tuple[int, int, int]] = None, width: int = 750, height: int = 400, + name: str = ''): if elements is None: elements = [] sampled_elements = [] @@ -1212,7 +1214,7 @@ def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, self.disposition = disposition self.attribute_names = axes self.rgbs = rgbs - PlotDataObject.__init__(self, type_='parallelplot', name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='parallelplot', name=name) class Attribute(PlotDataObject): @@ -1264,14 +1266,15 @@ class Histogram(Figure): _template_name = "histogram_template" def __init__(self, x_variable: str, elements=None, axis: Axis = None, graduation_nb: float = None, - edge_style: EdgeStyle = None, surface_style: SurfaceStyle = None, name: str = ''): + edge_style: EdgeStyle = None, surface_style: SurfaceStyle = None, width: int = 750, height: int = 400, + name: str = ''): self.x_variable = x_variable self.elements = elements self.axis = axis self.graduation_nb = graduation_nb self.edge_style = edge_style self.surface_style = surface_style - PlotDataObject.__init__(self, type_='histogram', name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='histogram', name=name) class MultiplePlots(Figure): @@ -1290,7 +1293,7 @@ class MultiplePlots(Figure): def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elements: List[Sample] = None, coords: List[Tuple[float, float]] = None, point_families: List[PointFamily] = None, - initial_view_on: bool = None, name: str = ''): + initial_view_on: bool = None, width: int = 750, height: int = 400, name: str = ''): if elements is None: elements = [] sampled_elements = [] @@ -1310,7 +1313,7 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem self.coords = coords self.point_families = point_families self.initial_view_on = initial_view_on - PlotDataObject.__init__(self, type_='multiplot', name=name) + PlotDataObject.__init__(self, width=width, height=height, type_='multiplot', name=name) def plot_data_path(debug_mode: bool = False, version: str = None): @@ -1347,13 +1350,12 @@ def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mo if not filepath: filepath = tempfile.mkstemp(suffix='.html')[1] - drawn_plot = plot_data_object.copy() if width: - drawn_plot.witdh = width + plot_data_object.width = width if height: - drawn_plot.height = height + plot_data_object.height = height - drawn_plot.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) + plot_data_object.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) if display: webbrowser.open('file://' + os.path.realpath(filepath)) print('file://' + filepath) diff --git a/plot_data/templates.py b/plot_data/templates.py index d0976a38..4da6916d 100644 --- a/plot_data/templates.py +++ b/plot_data/templates.py @@ -9,9 +9,7 @@
- - + @@ -41,9 +39,7 @@
- - + @@ -107,9 +101,7 @@
- - +
@@ -165,9 +151,7 @@
- - + diff --git a/script/multiplot.py b/script/multiplot.py index 09e2d2dc..c16f2a8b 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -76,5 +76,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True, width=2, height=1) # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) diff --git a/script/scatter_matrix.py b/script/scatter_matrix.py index e0ec8792..eb7d552f 100644 --- a/script/scatter_matrix.py +++ b/script/scatter_matrix.py @@ -20,4 +20,4 @@ }) plot_data_object = plot_data.ScatterMatrix(elements=elements) -plot_data.plot_canvas(plot_data_object, True) +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) From 261a286a5878db37f685f4a38016824935931e46 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 14:57:14 +0200 Subject: [PATCH 36/54] feat(plot_canvas): open file when filepath is specified and display is true --- plot_data/core.py | 3 +++ script/multiplot.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plot_data/core.py b/plot_data/core.py index 4e4045ef..49882423 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1349,6 +1349,9 @@ def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mo """ if not filepath: filepath = tempfile.mkstemp(suffix='.html')[1] + if not filepath.endswith('.html'): + filepath += '.html' + print(f'Changing name to {filepath}') if width: plot_data_object.width = width diff --git a/script/multiplot.py b/script/multiplot.py index c16f2a8b..61e7feda 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -76,5 +76,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True, width=2, height=1) +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True, width=2, height=1, filepath="test") # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) From caa6c36cebf9cf03c3a27f7c229d8ff83f84ef0e Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 14:59:46 +0200 Subject: [PATCH 37/54] feat(plot_canvas): change init methods --- plot_data/core.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 49882423..e5ef682f 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -883,7 +883,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po self.log_scale_y = log_scale_y self.heatmap = heatmap self.heatmap_view = heatmap_view - PlotDataObject.__init__(self, width=width, height=height, type_='scatterplot', name=name) + Figure.__init__(self, width=width, height=height, type_='scatterplot', name=name) class ScatterMatrix(Figure): @@ -909,7 +909,7 @@ def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_ self.axes = axes self.point_style = point_style self.surface_style = surface_style - PlotDataObject.__init__(self, width=width, height=height, type_="scattermatrix", name=name) + Figure.__init__(self, width=width, height=height, type_="scattermatrix", name=name) class Arc2D(PlotDataObject): @@ -1093,7 +1093,7 @@ def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, Circl Line2D, MultipleLabels, Wire, Point2D]], width: int = 750, height: int = 400, name: str = ''): self.primitives = primitives - PlotDataObject.__init__(self, width=width, height=height, type_='primitivegroup', name=name) + Figure.__init__(self, width=width, height=height, type_='primitivegroup', name=name) def mpl_plot(self, ax=None, equal_aspect=True): """ @@ -1175,7 +1175,7 @@ def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[flo if y_variable: attribute_names.append(y_variable) self.association['attribute_names'] = attribute_names - PlotDataObject.__init__(self, width=width, height=height, type_='primitivegroupcontainer', name=name) + Figure.__init__(self, width=width, height=height, type_='primitivegroupcontainer', name=name) class ParallelPlot(Figure): @@ -1214,7 +1214,7 @@ def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, self.disposition = disposition self.attribute_names = axes self.rgbs = rgbs - PlotDataObject.__init__(self, width=width, height=height, type_='parallelplot', name=name) + Figure.__init__(self, width=width, height=height, type_='parallelplot', name=name) class Attribute(PlotDataObject): @@ -1274,7 +1274,7 @@ def __init__(self, x_variable: str, elements=None, axis: Axis = None, graduation self.graduation_nb = graduation_nb self.edge_style = edge_style self.surface_style = surface_style - PlotDataObject.__init__(self, width=width, height=height, type_='histogram', name=name) + Figure.__init__(self, width=width, height=height, type_='histogram', name=name) class MultiplePlots(Figure): @@ -1313,7 +1313,7 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem self.coords = coords self.point_families = point_families self.initial_view_on = initial_view_on - PlotDataObject.__init__(self, width=width, height=height, type_='multiplot', name=name) + Figure.__init__(self, width=width, height=height, type_='multiplot', name=name) def plot_data_path(debug_mode: bool = False, version: str = None): From 261f3085eeae38573d064288195ce66860097d31 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 15:19:45 +0200 Subject: [PATCH 38/54] feat(update_plot_canvas): pylint --- code_pylint.py | 11 ++++--- plot_data/__init__.py | 3 +- plot_data/colors.py | 49 +++++++++++------------------- plot_data/core.py | 6 ++-- plot_data/graph.py | 68 +++++++++++++++++++----------------------- plot_data/templates.py | 2 ++ 6 files changed, 61 insertions(+), 78 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index 3e0600ab..c5a7f6c9 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -20,13 +20,12 @@ UNWATCHED_ERRORS = ['fixme', 'trailing-whitespace', 'import-error', 'missing-final-newline', 'trailing-newlines'] MAX_ERROR_BY_TYPE = { - 'protected-access': 26, - 'invalid-name': 13, - 'consider-using-f-string': 2, + 'protected-access': 1, + 'invalid-name': 12, 'no-else-return': 17, 'arguments-differ': 2, 'no-member': 1, - 'too-many-locals': 3, + 'too-many-locals': 2, 'wrong-import-order': 1, 'too-many-branches': 1, 'unused-import': 1, @@ -38,13 +37,13 @@ 'empty-docstring': 7, 'missing-module-docstring': 4, 'too-many-arguments': 16, - 'too-few-public-methods': 7, + 'too-few-public-methods': 5, 'unnecessary-comprehension': 5, 'no-value-for-parameter': 2, '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/plot_data/__init__.py b/plot_data/__init__.py index 42193568..145a0e0c 100644 --- a/plot_data/__init__.py +++ b/plot_data/__init__.py @@ -1,5 +1,6 @@ -import pkg_resources +""" Plot data package init. """ +import pkg_resources from .core import * __version__ = pkg_resources.require("plot_data")[0].version diff --git a/plot_data/colors.py b/plot_data/colors.py index f29715ae..01ce7edf 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -1,59 +1,46 @@ - +""" Basics and methods for color handling in plot_data. """ import dessia_common.core as dc from matplotlib.colors import hsv_to_rgb class Color(dc.DessiaObject): - def __init__(self, red: float, green: float, blue: float): + """ Base class for handling colors as objects. """ + + def __init__(self, red: float, green: float, blue: float, name: str = ""): self.red = red self.green = green self.blue = blue - self.rgb = (red, green, blue) + dc.DessiaObject.__init__(self, name=name) @classmethod def from_hex(cls, hex_code): - """ - :param hex_code: an hexadecimal string - :type hex_code: str - - :return: a Color object - :rtype: Color - """ + """ Get REB color from hexadecimal color. """ hex_code = hex_code.replace('#', '') - r, g, b = (int(hex_code[i:i + 2], 16) / 255. for i in (0, 2, 4)) - return cls(r, g, b) + red, green, blue = (int(hex_code[i:i + 2], 16) / 255. for i in (0, 2, 4)) + return cls(red, green, blue) @classmethod - def from_hsv(cls, h: float, s: float, v: float): - """ - :return: a Color object - :rtype: Color - """ - red, green, blue = hsv_to_rgb(h, s, v) + def from_hsv(cls, hue: float, saturation: float, value: float): + """ Get RGB color from HSV color. """ + red, green, blue = hsv_to_rgb(hue, saturation, value) return cls(red=red, green=green, blue=blue) def __str__(self): - """ - :return: a string rgb(r, g, b) in rgb255 - """ - return 'rgb({},{},{})'.format(round(self.red * 255), - round(self.green * 255), - round(self.blue * 255)) + return f"rgb({round(self.red * 255)},{round(self.green * 255)},{round(self.blue * 255)})" - def to_dict(self, *args, **kwargs): + def to_dict(self, *args, **kwargs): # SO weird + """ Get dict of color. """ # TODO: change this!!! it cannot be deserialized in generic way return str(self) @classmethod - def dict_to_object(cls, d): - """ - :return: a Color object - """ - if not d.startswith('rgb('): + def dict_to_object(cls, dict_): + """ Get color object from dict. """ + if not dict_.startswith('rgb('): raise ValueError('Color should be string starting with rgb(') - return cls(*(int(v) / 255. for v in d[4:-1].split(','))) + return cls(*(int(v) / 255. for v in dict_[4:-1].split(','))) RED = Color.dict_to_object('rgb(247,0,0)') diff --git a/plot_data/core.py b/plot_data/core.py index e5ef682f..beeb7ab2 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1402,9 +1402,9 @@ def bounding_box(plot_datas: List[PlotDataObject]): xmin, xmax, ymin, ymax = math.inf, -math.inf, math.inf, -math.inf for plot in plot_datas: if hasattr(plot, 'bounding_box'): - bb = plot.bounding_box() - xmin, xmax = min(xmin, bb[0]), max(xmax, bb[1]) - ymin, ymax = min(ymin, bb[2]), max(ymax, bb[3]) + bounding_box = plot.bounding_box() + xmin, xmax = min(xmin, bounding_box[0]), max(xmax, bounding_box[1]) + ymin, ymax = min(ymin, bounding_box[2]), max(ymax, bounding_box[3]) return xmin, xmax, ymin, ymax diff --git a/plot_data/graph.py b/plot_data/graph.py index fba4707b..542f32c0 100644 --- a/plot_data/graph.py +++ b/plot_data/graph.py @@ -1,76 +1,70 @@ -import networkx as nx +""" Module to handle nx graphs. """ +import networkx as nx import plot_data - class NetworkxGraph(plot_data.PrimitiveGroup): """ - Each node of self.graph can contain its settings in the node.data \ - dictionary. Keys can be : - 'color' with format 'rgb(xr, xg, xb)', xr, xg, xb are integers between \ - 0 and 255. - 'shape', choose between '.' for Point2D, 'o' for Circle2D, 's' for bigger \ - Circle2D. - 'name' with format str. + Each node of self.graph can contain its settings in the node.data dictionary. + + Keys can be : + - 'color' with format 'rgb(xr, xg, xb)', xr, xg, xb are integers between 0 and 255 + - 'shape', choose between '.' for Point2D, 'o' for Circle2D, 's' for bigger Circle2D + - 'name' with format str """ _non_serializable_attributes = ['graph'] def __init__(self, graph: nx.Graph, name: str = ''): self.graph = graph - primitives = self._to_primitives() - plot_data.PrimitiveGroup.__init__(self, - primitives=primitives, - name=name) + plot_data.PrimitiveGroup.__init__(self, primitives=primitives, name=name) def _to_primitives(self, text_style: plot_data.TextStyle = None): - r = 0.04 + radius = 0.04 primitives = [] pos = nx.kamada_kawai_layout(self.graph) for edge in self.graph.edges: node1, node2 = edge[0], edge[1] pos1, pos2 = pos[node1], pos[node2] - line = plot_data.LineSegment2D( - [pos1[0], pos1[1]], [pos2[0], pos2[1]], - edge_style=plot_data.EdgeStyle()) + line = plot_data.LineSegment2D([pos1[0], pos1[1]], [pos2[0], pos2[1]], edge_style=plot_data.EdgeStyle()) primitives.append(line) for node, data in self.graph.nodes(data=True): position = pos[node] color, shape, name = data['color'], data['shape'], data['name'] - x, y = position[0], position[1] + x_coord, y_coord = position[0], position[1] edge_style = plot_data.EdgeStyle(color_stroke=color) surface_style = plot_data.SurfaceStyle(color_fill=color) if shape == '.': point_style = plot_data.PointStyle(color_fill=color, color_stroke=color, size=4) - prim = plot_data.Point2D(x, y, point_style=point_style) + primitive = plot_data.Point2D(x_coord, y_coord, point_style=point_style) + elif shape == 'o': - prim = plot_data.Circle2D( - x, y, r, - edge_style=edge_style, - surface_style=surface_style) + primitive = plot_data.Circle2D( x_coord, y_coord, radius, edge_style=edge_style, + surface_style=surface_style) + elif shape == 's': - x1, x2, y1, y2 = x - r, x + r, y - r, y + r - l1 = plot_data.LineSegment2D([x1, y1], [x2, y1]) - l2 = plot_data.LineSegment2D([x2, y1], [x2, y2]) - l3 = plot_data.LineSegment2D([x2, y2], [x1, y2]) - l4 = plot_data.LineSegment2D([x1, y2], [x1, y1]) - prim = plot_data.Contour2D([l1, l2, l3, l4], - edge_style=edge_style, - surface_style=surface_style) + x_left, x_right, y_down, y_up = x_coord - radius, x_coord + radius, y_coord - radius, y_coord + radius + l1 = plot_data.LineSegment2D([x_left, y_down], [x_right, y_down]) + l2 = plot_data.LineSegment2D([x_right, y_down], [x_right, y_up]) + l3 = plot_data.LineSegment2D([x_right, y_up], [x_left, y_up]) + l4 = plot_data.LineSegment2D([x_left, y_up], [x_left, y_down]) + primitive = plot_data.Contour2D([l1, l2, l3, l4], edge_style=edge_style, surface_style=surface_style) + else: raise NotImplementedError - primitives.append(prim) + primitives.append(primitive) + if text_style is None: - text_style = plot_data.TextStyle(text_color='rgb(0,0,0)', - text_align_x='center', - text_align_y='middle') - text = plot_data.Text(name, x, y, text_style=text_style, - text_scaling=True, max_width=2 * r, multi_lines=False) + text_style = plot_data.TextStyle(text_color='rgb(0,0,0)', text_align_x='center', text_align_y='middle') + + text = plot_data.Text(name, x_coord, y_coord, text_style=text_style, text_scaling=True, + max_width=2 * radius, multi_lines=False) primitives.append(text) return primitives def to_plot_data(self): + """ Get the equivalent PlotDataObject. """ return plot_data.PrimitiveGroup(self.primitives) diff --git a/plot_data/templates.py b/plot_data/templates.py index 4da6916d..892668b0 100644 --- a/plot_data/templates.py +++ b/plot_data/templates.py @@ -1,3 +1,5 @@ +""" Templates of html files built from PlotDataObject. """ + from string import Template empty_template = Template('''''') From dcc1ef517dac28a46e72a99141387c4e07d6b193 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 15:34:00 +0200 Subject: [PATCH 39/54] feat(plot_canvas): pylint --- code_pylint.py | 2 +- plot_data/core.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index c5a7f6c9..88bd5b58 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -21,7 +21,7 @@ MAX_ERROR_BY_TYPE = { 'protected-access': 1, - 'invalid-name': 12, + 'invalid-name': 6, 'no-else-return': 17, 'arguments-differ': 2, 'no-member': 1, diff --git a/plot_data/core.py b/plot_data/core.py index beeb7ab2..590996c7 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -1402,9 +1402,9 @@ def bounding_box(plot_datas: List[PlotDataObject]): xmin, xmax, ymin, ymax = math.inf, -math.inf, math.inf, -math.inf for plot in plot_datas: if hasattr(plot, 'bounding_box'): - bounding_box = plot.bounding_box() - xmin, xmax = min(xmin, bounding_box[0]), max(xmax, bounding_box[1]) - ymin, ymax = min(ymin, bounding_box[2]), max(ymax, bounding_box[3]) + bounding_box_ = plot.bounding_box() + xmin, xmax = min(xmin, bounding_box_[0]), max(xmax, bounding_box_[1]) + ymin, ymax = min(ymin, bounding_box_[2]), max(ymax, bounding_box_[3]) return xmin, xmax, ymin, ymax From f86ec0da0d3007e23b4512bfe2ba49112e787c2d Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 15:35:30 +0200 Subject: [PATCH 40/54] feat(plot_canvs): change multiplot test --- script/multiplot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/multiplot.py b/script/multiplot.py index 61e7feda..09e2d2dc 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -76,5 +76,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True, width=2, height=1, filepath="test") +plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) From 13a03568e4c4794837062145dd7f186df5a1f273 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Mon, 12 Jun 2023 15:41:03 +0200 Subject: [PATCH 41/54] pylint and pep8 --- code_pylint.py | 2 +- plot_data/colors.py | 2 +- plot_data/core.py | 4 ++-- plot_data/graph.py | 5 +++-- script/networkx_graph.py | 8 ++++---- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index 88bd5b58..a73750fc 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -13,7 +13,7 @@ from pylint.lint import Run -MIN_NOTE = 9.10 +MIN_NOTE = 9.50 # RATCHET_NOTE = 0.4 # RATCHET_ERRORS = 3 diff --git a/plot_data/colors.py b/plot_data/colors.py index 01ce7edf..184b844a 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -30,7 +30,7 @@ def from_hsv(cls, hue: float, saturation: float, value: float): def __str__(self): return f"rgb({round(self.red * 255)},{round(self.green * 255)},{round(self.blue * 255)})" - def to_dict(self, *args, **kwargs): # SO weird + def to_dict(self, *args, **kwargs): # SO weird """ Get dict of color. """ # TODO: change this!!! it cannot be deserialized in generic way return str(self) diff --git a/plot_data/core.py b/plot_data/core.py index 590996c7..e9d5b45b 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -382,7 +382,7 @@ class Text(PlotDataObject): """ def __init__(self, comment: str, position_x: float, position_y: float, text_style: TextStyle = None, - text_scaling: bool = None, max_width: float = None, multi_lines: bool = True, name: str = ''): + text_scaling: bool = None, max_width: float = None, multi_lines: bool = True, name: str = ''): self.comment = comment self.text_style = text_style self.position_x = position_x @@ -883,7 +883,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po self.log_scale_y = log_scale_y self.heatmap = heatmap self.heatmap_view = heatmap_view - Figure.__init__(self, width=width, height=height, type_='scatterplot', name=name) + Figure.__init__(self, width=width, height=height, type_='scatterplot', name=name) class ScatterMatrix(Figure): diff --git a/plot_data/graph.py b/plot_data/graph.py index 542f32c0..1f19189e 100644 --- a/plot_data/graph.py +++ b/plot_data/graph.py @@ -3,6 +3,7 @@ import networkx as nx import plot_data + class NetworkxGraph(plot_data.PrimitiveGroup): """ Each node of self.graph can contain its settings in the node.data dictionary. @@ -41,8 +42,8 @@ def _to_primitives(self, text_style: plot_data.TextStyle = None): primitive = plot_data.Point2D(x_coord, y_coord, point_style=point_style) elif shape == 'o': - primitive = plot_data.Circle2D( x_coord, y_coord, radius, edge_style=edge_style, - surface_style=surface_style) + primitive = plot_data.Circle2D(x_coord, y_coord, radius, edge_style=edge_style, + surface_style=surface_style) elif shape == 's': x_left, x_right, y_down, y_up = x_coord - radius, x_coord + radius, y_coord - radius, y_coord + radius diff --git a/script/networkx_graph.py b/script/networkx_graph.py index 650e9669..1e7f3787 100644 --- a/script/networkx_graph.py +++ b/script/networkx_graph.py @@ -13,9 +13,9 @@ graph.add_edge('a', 3) graph.add_edge(3, 1) -plotdata_nx_graph = plot_data.PrimitiveGroup(plot_data.graph.NetworkxGraph(graph)._to_primitives(text_style = plot_data.TextStyle(text_color='rgb(0, 0, 0)', - font_size=3, - text_align_x='center', - text_align_y='middle'))) +plotdata_nx_graph = plot_data.PrimitiveGroup(plot_data.graph.NetworkxGraph(graph)._to_primitives(text_style=plot_data.TextStyle(text_color='rgb(0, 0, 0)', + font_size=3, + text_align_x='center', + text_align_y='middle'))) plot_data.plot_canvas(plotdata_nx_graph, debug_mode=True) From 7a8aba1f83efd23f01c0fc7a62e89183de900856 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Tue, 13 Jun 2023 17:28:03 +0200 Subject: [PATCH 42/54] fix(parallel_names): review requested changes --- src/plot-data.ts | 47 ++++++++--------------------------------------- src/subplots.ts | 1 - 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/src/plot-data.ts b/src/plot-data.ts index 49467087..6cbe3a92 100644 --- a/src/plot-data.ts +++ b/src/plot-data.ts @@ -855,15 +855,14 @@ export abstract class PlotData { let boxOriginX = 0; let axisLocation = 0; + const offset = Math.min(axisTitle.width, this.x_step / 2); if (i == 0 && axisTitle.width > (this.axis_x_start - this.X) * 2) { axisLocation = -1; - const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x + offset * 0.95; axisTitle.width = offset * 0.95 + (this.axis_x_start - this.X) * 0.9; axisTitle.align = "right"; } else if (i == nb_axis - 1 && axisTitle.width > (this.width - this.X - this.axis_x_end) * 2) { axisLocation = 1; - const offset = Math.min(axisTitle.width, this.x_step / 2); axisTitle.origin.x = current_x - offset * 0.95; axisTitle.width = offset * 0.95 + (this.width - this.axis_x_end + this.X) * 0.9; axisTitle.align = "left"; @@ -884,9 +883,7 @@ export abstract class PlotData { let boxOrigin = new Vertex(boxOriginX, origin.y - axisTitle.nRows * axisTitle.fontsize); this.axisNamesBoxes.push(new newRect(boxOrigin, new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize))); - if (axisTitle.text == this.selected_axis_name) { this.context.strokeStyle = 'blue' } - else { this.context.strokeStyle = 'black' }; - + this.context.strokeStyle = axisTitle.text == this.selected_axis_name? 'blue' : 'black' this.context.fillStyle = 'black'; axisTitle.draw(this.context); @@ -957,6 +954,7 @@ export abstract class PlotData { } draw_horizontal_parallel_axis(nb_axis:number, mvy:number) { + const RIGHT_SPACE = this.axis_x_start - this.X; this.axisNamesBoxes = []; for (var i=0; i (this.axis_x_start - this.X) * 2) { + if (axisTitle.width > RIGHT_SPACE * 2) { standard = false; axisTitle.align = "left"; - axisTitle.origin.x -= (this.axis_x_start - this.X) * 0.8; - control = true; + axisTitle.origin.x -= RIGHT_SPACE * 0.8; if (i == nb_axis - 1) { axisTitle.width = this.width - axisTitle.origin.x + this.X; axisTitle.height = this.height - origin.y + this.Y; @@ -991,17 +987,13 @@ export abstract class PlotData { axisTitle.format(this.context); let boxOrigin = new Vertex(origin.x, axisTitle.origin.y); - if (control) { axisTitle.origin.y += axisTitle.fontsize * (axisTitle.nRows - 1) } - if (standard) { boxOrigin.x -= axisTitle.width / 2 } + if (!standard) { axisTitle.origin.y += axisTitle.fontsize * (axisTitle.nRows - 1) }; + if (standard) { boxOrigin.x -= axisTitle.width / 2 }; let boxSize = new Vertex(axisTitle.width, axisTitle.nRows * axisTitle.fontsize); this.axisNamesBoxes.push(new newRect(boxOrigin, boxSize)); - if (axisTitle.text == this.selected_axis_name) { - this.context.strokeStyle = 'blue'; - } else { - this.context.strokeStyle = 'black'; - } + this.context.strokeStyle = axisTitle.text == this.selected_axis_name? 'blue' : 'black' this.context.fillStyle = 'black'; axisTitle.draw(this.context); @@ -3043,29 +3035,6 @@ export class Interactions { return [click_on_name, selected_name_index]; } - // public static initialize_click_on_name(nb_axis:number, mouse1X:number, mouse1Y:number, plot_data:any) { - // var click_on_name:any = false; - // var selected_name_index:any = -1; - // for (var i=0; i Date: Tue, 13 Jun 2023 17:28:03 +0200 Subject: [PATCH 43/54] fix(parallel_names): review requested changes From f395ffc57fb646c815e1f538804c91fd4327da8e Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Tue, 13 Jun 2023 18:06:51 +0200 Subject: [PATCH 44/54] fix(parallel_names): fix merge errors --- ...should add primitive group container in multiplot-base.png | 4 ++-- .../MULTIPLOT CANVAS -- should draw canvas-base.png | 4 ++-- ...emove primitive group from container in multiplot-base.png | 4 ++-- ...PLOT CANVAS -- should reorder all plots in canvas-base.png | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) 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 b6e448e1..f0e6071b 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92c3ccb0110db8932eb2f9a500f4f4358244b52a401cd8a499bc0be129b950bf -size 225589 +oid sha256:7e21da37195c50025f74a11bbacadc4466cfb4a5e814f668f80a0cc727c169ba +size 225312 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 eeca6cd9..dfe225a1 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:410927fdd516ad62b977e61439485d3490efbd19f4fdb7afb18a8259dc359ab2 -size 323282 +oid sha256:19c32e843d77a5d3e85cbf3f46f17ce1043422c3bc34e63a3262fac11f0b59b9 +size 323346 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 416e69fa..4f3e6b8f 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:25f557a019625d725d3038964f39fa7ed0e7d9d97af465cff207de0c9fe73764 -size 315188 +oid sha256:1b964ad7dac87cc576c7eb3998f2a9b2090a10bff9b2ae5d8d8fa661b9c55c07 +size 315461 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 cc84e2a9..edf94f54 100644 --- 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 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:abbbc5af62cc48855491c03845efc7404b7c4745160964f7011742e221bcf7e9 -size 312357 +oid sha256:9401cd34c939ff4e6b512c1884c1a973655a0afe317087e2beb1b0986154c155 +size 312743 From c117f95221314a97626d8c56af2aed18debe403c Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 10:37:04 +0200 Subject: [PATCH 45/54] feat(update_plot_canvas): review changes --- plot_data/core.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index e9d5b45b..8eb42cb0 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -796,7 +796,7 @@ def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis self.axis = axis self.log_scale_x = log_scale_x self.log_scale_y = log_scale_y - Figure.__init__(self, width=width, height=height, type_='graph2d', name=name) + super().__init__(width=width, height=height, type_='graph2d', name=name) def mpl_plot(self): # axs = plt.subplots(len(self.graphs)) @@ -883,7 +883,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po self.log_scale_y = log_scale_y self.heatmap = heatmap self.heatmap_view = heatmap_view - Figure.__init__(self, width=width, height=height, type_='scatterplot', name=name) + super().__init__(width=width, height=height, type_='scatterplot', name=name) class ScatterMatrix(Figure): @@ -909,7 +909,7 @@ def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_ self.axes = axes self.point_style = point_style self.surface_style = surface_style - Figure.__init__(self, width=width, height=height, type_="scattermatrix", name=name) + super().__init__(width=width, height=height, type_="scattermatrix", name=name) class Arc2D(PlotDataObject): @@ -1093,7 +1093,7 @@ def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, Circl Line2D, MultipleLabels, Wire, Point2D]], width: int = 750, height: int = 400, name: str = ''): self.primitives = primitives - Figure.__init__(self, width=width, height=height, type_='primitivegroup', name=name) + super().__init__(width=width, height=height, type_='primitivegroup', name=name) def mpl_plot(self, ax=None, equal_aspect=True): """ @@ -1175,7 +1175,7 @@ def __init__(self, primitive_groups: List[PrimitiveGroup], sizes: List[Tuple[flo if y_variable: attribute_names.append(y_variable) self.association['attribute_names'] = attribute_names - Figure.__init__(self, width=width, height=height, type_='primitivegroupcontainer', name=name) + super().__init__(width=width, height=height, type_='primitivegroupcontainer', name=name) class ParallelPlot(Figure): @@ -1214,7 +1214,7 @@ def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, self.disposition = disposition self.attribute_names = axes self.rgbs = rgbs - Figure.__init__(self, width=width, height=height, type_='parallelplot', name=name) + super().__init__(width=width, height=height, type_='parallelplot', name=name) class Attribute(PlotDataObject): @@ -1274,7 +1274,7 @@ def __init__(self, x_variable: str, elements=None, axis: Axis = None, graduation self.graduation_nb = graduation_nb self.edge_style = edge_style self.surface_style = surface_style - Figure.__init__(self, width=width, height=height, type_='histogram', name=name) + super().__init__(width=width, height=height, type_='histogram', name=name) class MultiplePlots(Figure): @@ -1313,7 +1313,7 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem self.coords = coords self.point_families = point_families self.initial_view_on = initial_view_on - Figure.__init__(self, width=width, height=height, type_='multiplot', name=name) + super().__init__(width=width, height=height, type_='multiplot', name=name) def plot_data_path(debug_mode: bool = False, version: str = None): From 92db504258f032898a8f410b9268227d7416a9b3 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 12:29:58 +0200 Subject: [PATCH 46/54] feat(update_plot_canvas): fit with pylint requirements --- plot_data/colors.py | 9 +- plot_data/core.py | 314 ++++++++++++++++++++------------------------ script/multiplot.py | 3 +- 3 files changed, 147 insertions(+), 179 deletions(-) diff --git a/plot_data/colors.py b/plot_data/colors.py index 184b844a..5161d658 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -1,9 +1,10 @@ """ Basics and methods for color handling in plot_data. """ import dessia_common.core as dc +from dessia_common.serialization import SerializableObject +from dessia_common.typings import Dict, Any, JsonSerializable from matplotlib.colors import hsv_to_rgb - class Color(dc.DessiaObject): """ Base class for handling colors as objects. """ @@ -30,13 +31,15 @@ def from_hsv(cls, hue: float, saturation: float, value: float): def __str__(self): return f"rgb({round(self.red * 255)},{round(self.green * 255)},{round(self.blue * 255)})" - def to_dict(self, *args, **kwargs): # SO weird + def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, + id_memo=None) -> JsonSerializable: """ Get dict of color. """ # TODO: change this!!! it cannot be deserialized in generic way return str(self) @classmethod - def dict_to_object(cls, dict_): + def dict_to_object(cls, dict_: JsonSerializable, force_generic: bool = False, global_dict=None, + pointers_memo: Dict[str, Any] = None, path: str = '#') -> SerializableObject: """ Get color object from dict. """ if not dict_.startswith('rgb('): raise ValueError('Color should be string starting with rgb(') diff --git a/plot_data/core.py b/plot_data/core.py index 9161c7bb..d0c326be 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -14,7 +14,7 @@ from typing import Any, Dict, List, Tuple, Union import matplotlib.pyplot as plt -from matplotlib.patches import Polygon +from matplotlib.patches import Polygon, Circle, Arc try: # dessia_common >= 0.12.0 @@ -25,9 +25,7 @@ from dessia_common.core import DessiaObject from dessia_common.exports import ExportFormat - from dessia_common.typings import JsonSerializable -from matplotlib import patches import plot_data.colors from plot_data import templates @@ -54,7 +52,8 @@ def __init__(self, type_: str, name: str = '', **kwargs): self.type_ = type_ DessiaObject.__init__(self, name=name, **kwargs) - def to_dict(self, *args, **kwargs) -> JsonSerializable: + def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, + id_memo = None) -> JsonSerializable: """ Redefines DessiaObject's to_dict() in order not to use pointers and remove keys where value is None. """ dict_ = DessiaObject.to_dict(self, use_pointers=False) del dict_['object_class'] @@ -77,14 +76,8 @@ def to_dict(self, *args, **kwargs) -> JsonSerializable: # return DessiaObject.dict_to_object(dict_=dict_, force_generic=True, global_dict=global_dict, # pointers_memo=pointers_memo, path=path) - def plot_data(self): - raise NotImplementedError("It is strange to call plot_data method from a plot_data object." - f" Check the class '{self.__class__.__name__}' you are calling") - - def mpl_plot(self, ax=None): - """ - Overloading of dessia object mpl_plot - """ + def mpl_plot(self, ax=None, **kwargs): + """ Overloading of dessia object mpl_plot. """ warnings.warn(f'class {self.__class__.__name__} does not implement mpl_plot, not plotting.') return ax @@ -99,11 +92,12 @@ def __init__(self, type_: str, width: int = 750, height: int = 400, name: str = @property def template(self): + """ Get html template of current Figure object. """ return getattr(templates, self._template_name) def _export_formats(self) -> List[ExportFormat]: """ Return a list of objects describing how to call generic exports (.json, .xlsx). """ - formats = DessiaObject._export_formats(self) + formats = super()._export_formats() formats.append(ExportFormat(selector="html", extension="html", method_name="to_html_stream", text=False)) return formats @@ -113,16 +107,23 @@ def _to_html(self, debug_mode: bool = False, canvas_id: str = 'canvas', version: width=self.width, height=self.height) def to_html_stream(self, stream, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None): + """ Export current Figure to its equivalent html stream file. """ html = self._to_html(debug_mode=debug_mode, canvas_id=canvas_id, version=version) stream.write(html.encode('utf-8')) def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', version: str = None): - """ Export current PlotDataObject to an HTML file given by the filepath. """ - if not filepath.endswith('.html'): - filepath += '.html' - print(f'Changing name to {filepath}') + """ Export current Figure to an HTML file given by the filepath. """ + filepath = make_filepath(filepath=filepath) with open(filepath, 'wb') as file: self.to_html_stream(file, debug_mode=debug_mode, canvas_id=canvas_id, version=version) + return filepath + + def plot_data(self, reference_path: str = "#", **kwargs): + return [self] + + def plot(self, reference_path: str = "#", filepath: str = None, **kwargs): + filepath = self.to_html(filepath=filepath, **kwargs) + webbrowser.open('file://' + os.path.realpath(filepath)) class Sample(PlotDataObject): @@ -159,7 +160,7 @@ def dict_to_object(cls, dict_: JsonSerializable, force_generic: bool = False, gl values = dict_["values"] return cls(values=values, reference_path=reference_path, name=name) - def plot_data(self): + def plot_data(self, reference_path: str = "#", **kwargs): raise NotImplementedError("Method plot_data is not defined for class Sample.") @@ -181,6 +182,8 @@ def __init__(self, stroke_width: float = 1, hatch_spacing: float = 10, class Window(DessiaObject): + """ Define a Window object. """ + def __init__(self, width: float, height: float, name: str = ''): self.width = width self.height = height @@ -209,6 +212,7 @@ def __init__(self, line_width: float = None, color_stroke: plot_data.colors.Colo DessiaObject.__init__(self, name=name) def mpl_arguments(self, surface=False): + """ Get matplotlib equivalent values of attributes. """ args = {} if self.color_stroke: if surface: @@ -248,6 +252,7 @@ def __init__(self, color_fill: str = None, color_stroke: str = None, stroke_widt DessiaObject.__init__(self, name=name) def mpl_arguments(self): + """ Get matplotlib equivalent values of attributes. """ args = {} if self.color_fill: args['color'] = self.color_fill.rgb @@ -257,6 +262,7 @@ def mpl_arguments(self): @classmethod def dict_to_object(cls, dict_, *args, **kwargs): + """ Overwrite generic dict_to_object. """ obj = DessiaObject.dict_to_object(dict_, *args, **kwargs) if obj.color_fill: obj.color_fill = plot_data.colors.Color.dict_to_object(obj.color_fill) @@ -306,6 +312,7 @@ def __init__(self, text_color: plot_data.colors.Color = None, font_size: float = @classmethod def dict_to_object(cls, dict_, *args, **kwargs): + """ Overwrite generic dict_to_object. """ obj = DessiaObject.dict_to_object(dict_, force_generic=True, *args, **kwargs) if obj.text_color: obj.text_color = plot_data.colors.Color.dict_to_object(obj.text_color) @@ -333,12 +340,14 @@ def __init__(self, color_fill: str = None, opacity: float = 1., hatching: Hatchi @classmethod def dict_to_object(cls, dict_, *args, **kwargs): + """ Overwrite generic dict_to_object. """ obj = DessiaObject.dict_to_object(dict_, force_generic=True, *args, **kwargs) if obj.color_fill: obj.color_fill = plot_data.colors.Color.dict_to_object(obj.color_fill) return obj def mpl_arguments(self): + """ Get matplotlib equivalent values of attributes. """ args = {} if self.color_fill: args['facecolor'] = self.color_fill.rgb @@ -389,11 +398,11 @@ def __init__(self, comment: str, position_x: float, position_y: float, text_styl self.multi_lines = multi_lines PlotDataObject.__init__(self, type_='text', name=name) - def mpl_plot(self, ax=None, color='k', alpha=1.): + def mpl_plot(self, ax=None, color='k', alpha=1., **kwargs): """ Plots using Matplotlib. """ if not ax: _, ax = plt.subplots() - ax.text(self.position_x, self.position_y, self.comment, color=color, alpha=alpha) + ax.text(self.position_x, self.position_y, self.comment, color=color, alpha=alpha, **kwargs) return ax @@ -414,10 +423,8 @@ def __init__(self, point1: List[float], point2: List[float], edge_style: EdgeSty self.edge_style = edge_style PlotDataObject.__init__(self, type_='line2d', name=name) - def mpl_plot(self, ax=None, edge_style=None): - """ - Plots using matplotlib. - """ + def mpl_plot(self, ax=None, edge_style=None, **kwargs): + """ Plots using matplotlib. """ if ax is None: _, ax = plt.subplots() @@ -430,15 +437,13 @@ def mpl_plot(self, ax=None, edge_style=None): color = style.color_stroke.rgb dashes = style.dashline - ax.axline((self.data[0], self.data[1]), (self.data[2], self.data[3]), - color=color, dashes=dashes) + ax.axline((self.data[0], self.data[1]), (self.data[2], self.data[3]), color=color, dashes=dashes, **kwargs) return ax class LineSegment2D(PlotDataObject): """ - A line segment. This is a primitive that can be called by \ - PrimitiveGroup. + A line segment. This is a primitive that can be called by PrimitiveGroup. :param point1: first endpoint of the line segment [x1, y1]. :type point1: List[float] @@ -461,27 +466,26 @@ def __init__(self, point1: List[float], point2: List[float], edge_style: EdgeSty PlotDataObject.__init__(self, type_='linesegment2d', name=name) def bounding_box(self): - """ - :return: the line segment's bounding box. - :rtype: float, float, float, float - """ + """ Get 2D bounding box of current LineSegment2D. """ return (min(self.data[0], self.data[2]), max(self.data[0], self.data[2]), min(self.data[1], self.data[3]), max(self.data[1], self.data[3])) - def to_dict(self): - dict_ = DessiaObject.to_dict(self) + def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, + id_memo = None) -> JsonSerializable: + """ Get dict of current LineSegment2D. """ + dict_ = DessiaObject.to_dict(self, use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, + id_memo=id_memo) dict_['object_class'] = 'plot_data.core.LineSegment2D' # To force migration to linesegment -> linesegment2d return dict_ def polygon_points(self): + """ Get lists of points in a merged list. """ return [self.point1, self.point2] def mpl_plot(self, ax=None, edge_style=None): - """ - Plots using matplotlib. - """ + """ Plots using matplotlib. """ if not ax: _, ax = plt.subplots() @@ -495,24 +499,25 @@ def mpl_plot(self, ax=None, edge_style=None): class LineSegment(LineSegment2D): + """ A line segment. This is a primitive that can be called by PrimitiveGroup. """ + def __init__(self, data: List[float], edge_style: EdgeStyle = None, name: str = ''): # When to remove support? - warnings.warn("LineSegment is deprecated, use LineSegment2D instead", - DeprecationWarning) - + warnings.warn("LineSegment is deprecated, use LineSegment2D instead", DeprecationWarning) self.data = data - LineSegment2D.__init__(self, point1=self.data[:2], point2=self.data[2:], edge_style=edge_style, - name=name) + LineSegment2D.__init__(self, point1=self.data[:2], point2=self.data[2:], edge_style=edge_style, name=name) - def to_dict(self, *args, **kwargs): - ls2d = LineSegment2D(point1=self.data[:2], point2=self.data[2:], edge_style=self.edge_style, - name=self.name) - return ls2d.to_dict() + def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, + id_memo = None) -> JsonSerializable: + """ Get dict of current LineSegment. """ + ls2d = LineSegment2D(point1=self.data[:2], point2=self.data[2:], edge_style=self.edge_style, name=self.name) + return ls2d.to_dict(use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, id_memo=id_memo) class Wire(PlotDataObject): """ A set of connected lines. It also provides highlighting feature. + :param lines: [(x1, y1), ..., (xn,yn)] :type lines: List[Tuple[float, float]] :param edge_style: Line settings @@ -528,16 +533,14 @@ def __init__(self, lines: List[Tuple[float, float]], edge_style: EdgeStyle = Non self.tooltip = tooltip PlotDataObject.__init__(self, type_="wire", name=name) - def mpl_plot(self, ax=None): - """ - Plots using matplotlib - """ + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ if self.edge_style: edge_style = self.edge_style else: edge_style = DEFAULT_EDGESTYLE - ax.plot([p[0] for p in self.lines], [p[1] for p in self.lines], **edge_style.mpl_arguments()) + ax.plot([p[0] for p in self.lines], [p[1] for p in self.lines], **edge_style.mpl_arguments(), **kwargs) return ax @@ -570,16 +573,11 @@ def __init__(self, cx: float, cy: float, r: float, edge_style: EdgeStyle = None, PlotDataObject.__init__(self, type_='circle', name=name) def bounding_box(self): - """ - :return: the circle's bounding box - :rtype: float, float, float, float - """ + """ Get 2D bounding box of current Circle2D. """ return self.cx - self.r, self.cx + self.r, self.cy - self.r, self.cy + self.r - def mpl_plot(self, ax=None): - """ - Plots using matplotlib - """ + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ if not ax: _, ax = plt.subplots() if self.edge_style: @@ -596,8 +594,7 @@ def mpl_plot(self, ax=None): args.update(surface_style.mpl_arguments()) - ax.add_patch(patches.Circle((self.cx, self.cy), self.r, - **args)) + ax.add_patch(Circle((self.cx, self.cy), self.r, **args), **kwargs) return ax @@ -620,13 +617,11 @@ def __init__(self, cx: float, cy: float, point_style: PointStyle = None, name: s PlotDataObject.__init__(self, type_='point', name=name) def bounding_box(self): - """ - :return: the point's bounding box. - :rtype: float, float, float, float - """ + """ Get 2D bounding box of current Circle2D. """ return self.cx, self.cx, self.cy, self.cy - def mpl_plot(self, ax=None): + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ if ax is None: _, ax = plt.subplots() @@ -635,7 +630,7 @@ def mpl_plot(self, ax=None): else: style = DEFAULT_POINTSTYLE - ax.plot([self.cx], [self.cy], marker='o', **style.mpl_arguments()) + ax.plot([self.cx], [self.cy], marker='o', **style.mpl_arguments(), **kwargs) return ax @@ -677,9 +672,7 @@ def __init__(self, nb_points_x: int = 10, nb_points_y: int = 10, graduation_styl class Tooltip(PlotDataObject): """ - A class that contains information for drawing a tooltip when \ - clicking on points. - A tooltip object is instantiated by Scatter and Dataset classes. + A class that contains information for drawing a tooltip when clicking on points. :param attributes: a list containing the attributes \ you want to display. Attributes must be taken from Dataset's or \ @@ -711,10 +704,9 @@ def __init__(self, attributes: List[str] = None, text: str = None, surface_style class Dataset(PlotDataObject): """ - Numerous points are joined by line segments to display a \ - mathematical curve. - Datasets are instantiated by Graph2D to display multiple datasets \ - on one canvas. + Numerous points are joined by line segments to display a mathematical curve. + + Datasets are instantiated by Graph2D to display multiple datasets on one canvas. :param elements: A list of vectors. Vectors must have the same \ attributes (ie the same keys) @@ -734,10 +726,11 @@ class Dataset(PlotDataObject): is the attribute displayed on y-axis. :type attribute_names: [str, str] """ + attribute_names = None def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, tooltip: Tooltip = None, - point_style: PointStyle = None, display_step: int = 1, name: str = ''): + point_style: PointStyle = None, partial_points: bool = None, display_step: int = 1, name: str = ''): self.edge_style = edge_style self.tooltip = tooltip @@ -763,8 +756,7 @@ def __init__(self, elements: List[Sample] = None, edge_style: EdgeStyle = None, class Graph2D(Figure): """ - Takes one or several Datasets as input and displays them all in \ - one canvas. + Takes one or several Datasets as input and displays them all in one canvas. :param graphs: a list of Datasets :type graphs: List[Dataset] @@ -796,7 +788,8 @@ def __init__(self, graphs: List[Dataset], x_variable: str, y_variable: str, axis self.log_scale_y = log_scale_y super().__init__(width=width, height=height, type_='graph2d', name=name) - def mpl_plot(self): + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ # axs = plt.subplots(len(self.graphs)) _, ax = plt.subplots() xname, yname = self.attribute_names[:2] @@ -806,7 +799,7 @@ def mpl_plot(self): for element in dataset.elements: x.append(element[xname]) y.append(element[yname]) - ax.plot(x, y) + ax.plot(x, y, **kwargs) ax.set_xlabel(xname) ax.set_ylabel(yname) return ax @@ -815,11 +808,12 @@ def mpl_plot(self): class Heatmap(DessiaObject): """ Heatmap is a scatter plot's view. This class contains the Heatmap's parameters. + :param size: A tuple of two integers corresponding to the number of squares on the horizontal and vertical sides. :type size: Tuple[int, int] - :param colors: The list of colors ranging from low density to high density, \ - e.g. colors=[plot_data.colors.BLUE, plot_data.colors.RED] \ - so the low density areas tend to be blue while higher density areas tend to be red. + :param colors: The list of colors ranging from low density to high density, e.g. + `colors=[plot_data.colors.BLUE, plot_data.colors.RED]` so the low density areas tend to be blue while higher + density areas tend to be red. :type colors: List[Colors] :param edge_style: The areas separating lines settings :type edge_style: EdgeStyle @@ -885,6 +879,7 @@ def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, po class ScatterMatrix(Figure): + """ ScatterMatrix of a list of Samples. """ _template_name = "scatter_matrix_template" @@ -912,8 +907,8 @@ def __init__(self, elements: List[Sample] = None, axes: List[str] = None, point_ class Arc2D(PlotDataObject): """ - A class for drawing arcs. Arc2D is a primitive and can be \ - instantiated by PrimitiveGroup. By default, the arc is drawn anticlockwise. + A class for drawing arcs. Arc2D is a primitive and can be instantiated by PrimitiveGroup. By default, + the arc is drawn anticlockwise. :param cx: the arc center's x position :type cx: float @@ -949,16 +944,11 @@ def __init__(self, cx: float, cy: float, r: float, start_angle: float, end_angle PlotDataObject.__init__(self, type_='arc', name=name) def bounding_box(self): - """ - :return: the arc's bounding box - :rtype: float, float, float, float - """ + """ Get 2D bounding box of current Circle2D. """ return self.cx - self.r, self.cx + self.r, self.cy - self.r, self.cy + self.r - def mpl_plot(self, ax=None): - """ - Plots using matplotlib - """ + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ if not ax: _, ax = plt.subplots() if self.edge_style: @@ -967,18 +957,17 @@ def mpl_plot(self, ax=None): edgecolor = plot_data.colors.BLACK.rgb ax.add_patch( - patches.Arc((self.cx, self.cy), 2 * self.r, 2 * self.r, angle=0, + Arc((self.cx, self.cy), 2 * self.r, 2 * self.r, angle=0, theta1=self.start_angle * 0.5 / math.pi * 360, theta2=self.end_angle * 0.5 / math.pi * 360, - edgecolor=edgecolor)) + edgecolor=edgecolor), **kwargs) return ax class Contour2D(PlotDataObject): """ - A Contour2D is a closed polygon that is formed by multiple \ - primitives. Contour2D can be instantiated by PrimitiveGroup. + A Contour2D is a closed polygon that is formed by multiple primitives. :param plot_data_primitives: a list of primitives \ (Arc2D, LineSegment2D) @@ -1000,29 +989,25 @@ def __init__(self, plot_data_primitives: List[Union[Arc2D, LineSegment2D]], edge PlotDataObject.__init__(self, type_='contour', name=name) def bounding_box(self): - """ - :return: the contour's bounding box - :rtype: float, float, float, float - """ + """ Get 2D bounding box of current Contour2D. """ xmin, xmax, ymin, ymax = math.inf, -math.inf, math.inf, -math.inf for plot_data_primitive in self.plot_data_primitives: if hasattr(plot_data_primitive, 'bounding_box'): - bb = plot_data_primitive.bounding_box() - xmin, xmax, ymin, ymax = min(xmin, bb[0]), max(xmax, bb[1]), \ - min(ymin, bb[2]), max(ymax, bb[3]) + bounding_box = plot_data_primitive.bounding_box() + xmin, xmax = min(xmin, bounding_box[0]), max(xmax, bounding_box[1]) + ymin, ymax = min(ymin, bounding_box[2]), max(ymax, bounding_box[3]) return xmin, xmax, ymin, ymax def polygon_points(self): + """ Get lists of points in a merged list. """ points = [] for primitive in self.plot_data_primitives: points.extend(primitive.polygon_points()) return points - def mpl_plot(self, ax=None): - """ - Plots using matplotlib - """ + def mpl_plot(self, ax=None, **kwargs): + """ Plots using matplotlib. """ for primitive in self.plot_data_primitives: ax = primitive.mpl_plot(ax=ax, edge_style=self.edge_style) @@ -1033,7 +1018,7 @@ def mpl_plot(self, ax=None): if surface_style.color_fill: points = self.polygon_points() - ax.add_patch(Polygon(points, closed=True, **surface_style.mpl_arguments())) + ax.add_patch(Polygon(points, closed=True, **surface_style.mpl_arguments()), **kwargs) return ax @@ -1063,8 +1048,7 @@ def __init__(self, title: str, text_style: TextStyle = None, rectangle_surface_s class MultipleLabels(PlotDataObject): """ - Draws one or several labels. MultipleLabels can be instantiated \ - by PrimitiveGroup. + Draws one or several labels. MultipleLabels can be instantiated by PrimitiveGroup. :param labels: a list of Labels :type labels: List[Label] @@ -1093,17 +1077,16 @@ def __init__(self, primitives: List[Union[Contour2D, Arc2D, LineSegment2D, Circl self.primitives = primitives super().__init__(width=width, height=height, type_='primitivegroup', name=name) - def mpl_plot(self, ax=None, equal_aspect=True): - """ - Plots using matplotlib - """ + def mpl_plot(self, ax=None, equal_aspect=True, **kwargs): + """ Plots using matplotlib. """ for primitive in self.primitives: - ax = primitive.mpl_plot(ax=ax) + ax = primitive.mpl_plot(ax=ax, **kwargs) if equal_aspect and ax: ax.set_aspect('equal') return ax def save_to_image(self, filepath, remove_axis=True): + """ Save PrimitiveGroup to a picture generated with matplotlib. """ ax = self.mpl_plot() if remove_axis: ax.set_axis_off() @@ -1113,10 +1096,7 @@ def save_to_image(self, filepath, remove_axis=True): plt.close(ax.figure) def bounding_box(self): - """ - :return: the primitive group's bounding box - :rtype: float, flaot, float, float - """ + """ Get 2D bounding box of current PrimitiveGroup. """ xmin, xmax, ymin, ymax = math.inf, -math.inf, math.inf, -math.inf for primitive in self.primitives: if not hasattr(primitive, 'bounding_box'): @@ -1135,15 +1115,12 @@ class PrimitiveGroupsContainer(Figure): :param primitive_groups: a list of PrimitiveGroups :type primitive_groups: List[PrimitiveGroup] - :param sizes: [size0,...,size_n] where size_i = [width_i, length_i]\ - is the size of primitive_groups[i] + :param sizes: [size0,...,size_n] where size_i = [width_i, length_i] is the size of primitive_groups[i] :type sizes: List[Tuple[float, float]] :param coords: In the same way as sizes but for coordinates. :type coords: List[Tuple[float, float]] - :param associated_elements: A list containing the associated \ - elements indices. associated_elements[i] is associated with \ - primitive_groups[i]. It only works if this object is inside a \ - MultiplePlots. + :param associated_elements: A list containing the associated elements indices. associated_elements[i] is associated + with primitive_groups[i]. It only works if this object is inside a MultiplePlots. :type associated_elements: List[int] :param x_variable: variable that you want to display on x axis :type x_variable: str @@ -1315,6 +1292,7 @@ def __init__(self, plots: List[PlotDataObject], sizes: List[Window] = None, elem def plot_data_path(debug_mode: bool = False, version: str = None): + """ Get path of plot_data package to write it in html file of Figure to draw. """ version, folder, filename = get_current_link(version=version) if debug_mode: core_path = os.sep.join(os.getcwd().split(os.sep)[:-1] + [folder, filename]) @@ -1324,7 +1302,17 @@ def plot_data_path(debug_mode: bool = False, version: str = None): return f'https://cdn.dessia.tech/js/plot-data/{version}/{filename}' -def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mode: bool = False, +def make_filepath(filepath: str = None): + """ Build path of written html file of Figure to draw. """ + if not filepath: + filepath = tempfile.mkstemp(suffix='.html')[1] + if not filepath.endswith('.html'): + filepath += '.html' + print(f'Changing name to {filepath}') + return filepath + + +def plot_canvas(plot_data_object: Figure, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, display: bool = True): """ @@ -1345,24 +1333,10 @@ def plot_canvas(plot_data_object: PlotDataObject, filepath: str = None, debug_mo :param page_name: set the created html file's name :type page_name: str """ - if not filepath: - filepath = tempfile.mkstemp(suffix='.html')[1] - if not filepath.endswith('.html'): - filepath += '.html' - print(f'Changing name to {filepath}') - - if width: - plot_data_object.width = width - if height: - plot_data_object.height = height - - plot_data_object.to_html(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) - if display: - webbrowser.open('file://' + os.path.realpath(filepath)) - print('file://' + filepath) - + plot_data_object.plot(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) def write_json_for_tests(plot_data_object: PlotDataObject, json_path: str): + """ Write JSON file of data to be used in Cypress tests of Typescript module. """ if not json_path.endswith(".json"): json_path += ".json" print("Added '.json' at the end of json_path variable.") @@ -1373,15 +1347,7 @@ def write_json_for_tests(plot_data_object: PlotDataObject, json_path: str): def get_csv_vectors(filepath): - """ - :param filepath: the csv file's relative path, starting from the \ - script's path. - :type filepath: str - - :return: a list of vectors (ie a list of dictionaries) that can be \ - set to multiple_plots' or parallelplot's elements for example. - :rtype: List[dict] - """ + """ Get csv vector of a VectoredObject (does not exist anymore). """ raise NotImplementedError("get_csv_vectors function is not implemented anymore" "as dessia_common's vectored_objects as been removed") @@ -1408,29 +1374,27 @@ def bounding_box(plot_datas: List[PlotDataObject]): def get_current_link(version: str = None) -> Tuple[str, str, str]: + """ Get link of plot_data package. """ folder = "lib" filename = "core.js" - try: - package = sys.modules[sys.modules[__name__].__package__] - if version is None: - version = package.__version__ - - splitted_version = version.split(".") - if len(splitted_version) > 3: - splitted_version.pop() - splitted_version[2] = str(int(splitted_version[2]) - 1) - formatted_version = "v" + ".".join(splitted_version) - if formatted_version == 'v0.6.2': - folder = "dist" - filename = "plot-data.js" - if formatted_version == "v0.7.0": - folder = "lib" - filename = "plot-data.js" - if int(splitted_version[0]) >= 0\ - and int(splitted_version[1]) >= 7\ - and int(splitted_version[1]) >= 1: - folder = "libdev" - filename = "plot-data.js" - return formatted_version, folder, filename - except Exception: - return 'latest', folder, filename + package = sys.modules[sys.modules[__name__].__package__] + if version is None: + version = package.__version__ + + splitted_version = version.split(".") + if len(splitted_version) > 3: + splitted_version.pop() + splitted_version[2] = str(int(splitted_version[2]) - 1) + formatted_version = "v" + ".".join(splitted_version) + if formatted_version == 'v0.6.2': + folder = "dist" + filename = "plot-data.js" + if formatted_version == "v0.7.0": + folder = "lib" + filename = "plot-data.js" + if int(splitted_version[0]) >= 0\ + and int(splitted_version[1]) >= 7\ + and int(splitted_version[1]) >= 1: + folder = "libdev" + filename = "plot-data.js" + return formatted_version, folder, filename diff --git a/script/multiplot.py b/script/multiplot.py index 47913b23..27234c19 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -75,5 +75,6 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) +# plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) +plot_data_object.plot(debug_mode = True, canvas_id = 'canvas') # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True) From b78edb0b2c901d0641fc6e8c963de69e3711df23 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 12:54:44 +0200 Subject: [PATCH 47/54] feat(plot_canvas_update): fix pylint (a priori) --- code_pylint.py | 2 +- plot_data/core.py | 37 ++++++++++++++++++------------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index a73750fc..98d4bc48 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -145,7 +145,7 @@ def extract_messages_by_type(type_): if error_detected: - raise RuntimeError('Too many errors\nRun pylint dessia_common to get the errors') + raise RuntimeError('Too many errors\nRun pylint plot_data to get the errors') if error_over_ratchet_limit: raise RuntimeError('Please lower the error limits in code_pylint.py MAX_ERROR_BY_TYPE according to warnings above') diff --git a/plot_data/core.py b/plot_data/core.py index d0c326be..c16059f7 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -52,10 +52,9 @@ def __init__(self, type_: str, name: str = '', **kwargs): self.type_ = type_ DessiaObject.__init__(self, name=name, **kwargs) - def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, - id_memo = None) -> JsonSerializable: + def to_dict(self, *args, **kwargs) -> JsonSerializable: """ Redefines DessiaObject's to_dict() in order not to use pointers and remove keys where value is None. """ - dict_ = DessiaObject.to_dict(self, use_pointers=False) + dict_ = DessiaObject.to_dict(self, use_pointers=False, *args, **kwargs) del dict_['object_class'] package_name = self.__module__.split('.', maxsplit=1)[0] if package_name in sys.modules: @@ -118,10 +117,10 @@ def to_html(self, filepath: str = None, debug_mode: bool = False, canvas_id: str self.to_html_stream(file, debug_mode=debug_mode, canvas_id=canvas_id, version=version) return filepath - def plot_data(self, reference_path: str = "#", **kwargs): + def plot_data(self, **kwargs): return [self] - def plot(self, reference_path: str = "#", filepath: str = None, **kwargs): + def plot(self, filepath: str = None, **kwargs): filepath = self.to_html(filepath=filepath, **kwargs) webbrowser.open('file://' + os.path.realpath(filepath)) @@ -132,24 +131,24 @@ class Sample(PlotDataObject): def __init__(self, values, reference_path: str = "#", name: str = ""): self.values = values self.reference_path = reference_path + super().__init__(type_="sample", name=name) - PlotDataObject.__init__(self, type_="sample", name=name) - - def to_dict(self, *args, **kwargs) -> JsonSerializable: + def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, + id_memo = None) -> JsonSerializable: """ Overwrite generic to_dict. TODO Check if it can be generic (probably) """ - dict_ = PlotDataObject.to_dict(self, *args, **kwargs) + dict_ = PlotDataObject.to_dict(self, use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, + id_memo=id_memo) dict_.update({"reference_path": self.reference_path, "name": self.name}) dict_.update(serialize(self.values)) # TODO Keeping values at dict_ level before refactor, should be removed after and use dict_["values"] instead return dict_ @classmethod - def dict_to_object(cls, dict_: JsonSerializable, force_generic: bool = False, global_dict=None, - pointers_memo: Dict[str, Any] = None, path: str = '#') -> 'Sample': + def dict_to_object(cls, dict_: JsonSerializable, **_) -> 'Sample': """ Overwrite generic dict_to_object. @@ -484,7 +483,7 @@ def polygon_points(self): """ Get lists of points in a merged list. """ return [self.point1, self.point2] - def mpl_plot(self, ax=None, edge_style=None): + def mpl_plot(self, ax=None, edge_style=None, **kwargs): """ Plots using matplotlib. """ if not ax: _, ax = plt.subplots() @@ -494,7 +493,8 @@ def mpl_plot(self, ax=None, edge_style=None): else: edge_style = DEFAULT_EDGESTYLE - ax.plot([self.point1[0], self.point2[0]], [self.point1[1], self.point2[1]], **edge_style.mpl_arguments()) + ax.plot([self.point1[0], self.point2[0]], [self.point1[1], self.point2[1]], **edge_style.mpl_arguments(), + **kwargs) return ax @@ -993,9 +993,9 @@ def bounding_box(self): xmin, xmax, ymin, ymax = math.inf, -math.inf, math.inf, -math.inf for plot_data_primitive in self.plot_data_primitives: if hasattr(plot_data_primitive, 'bounding_box'): - bounding_box = plot_data_primitive.bounding_box() - xmin, xmax = min(xmin, bounding_box[0]), max(xmax, bounding_box[1]) - ymin, ymax = min(ymin, bounding_box[2]), max(ymax, bounding_box[3]) + bounding_box_ = plot_data_primitive.bounding_box() + xmin, xmax = min(xmin, bounding_box_[0]), max(xmax, bounding_box_[1]) + ymin, ymax = min(ymin, bounding_box_[2]), max(ymax, bounding_box_[3]) return xmin, xmax, ymin, ymax @@ -1312,9 +1312,8 @@ def make_filepath(filepath: str = None): return filepath -def plot_canvas(plot_data_object: Figure, filepath: str = None, debug_mode: bool = False, - canvas_id: str = 'canvas', force_version: str = None, width: int = 750, height: int = 400, - display: bool = True): +def plot_canvas(plot_data_object: Figure, filepath: str = None, debug_mode: bool = False, canvas_id: str = 'canvas', + force_version: str = None): """ Creates a html file and plots input data in web browser. From 0d0d06df8ae89794f41173919e4a2b46eb193aa1 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 13:04:15 +0200 Subject: [PATCH 48/54] feat(plot_canvas_update): change use_pointers stuff --- plot_data/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index c16059f7..9d78333a 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -52,9 +52,11 @@ def __init__(self, type_: str, name: str = '', **kwargs): self.type_ = type_ DessiaObject.__init__(self, name=name, **kwargs) - def to_dict(self, *args, **kwargs) -> JsonSerializable: + def to_dict(self, **kwargs) -> JsonSerializable: """ Redefines DessiaObject's to_dict() in order not to use pointers and remove keys where value is None. """ - dict_ = DessiaObject.to_dict(self, use_pointers=False, *args, **kwargs) + if 'use_pointers' in kwargs: + kwargs.pop('use_pointers') + dict_ = DessiaObject.to_dict(self, use_pointers=False, **kwargs) del dict_['object_class'] package_name = self.__module__.split('.', maxsplit=1)[0] if package_name in sys.modules: From 2b0ee59265c11cd517eb1f5a2c93ef4ab5cb1de0 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 14:37:51 +0200 Subject: [PATCH 49/54] feat(update_plot_canvas): fix pylint --- plot_data/colors.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plot_data/colors.py b/plot_data/colors.py index 5161d658..59eeb3fb 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -31,15 +31,13 @@ def from_hsv(cls, hue: float, saturation: float, value: float): def __str__(self): return f"rgb({round(self.red * 255)},{round(self.green * 255)},{round(self.blue * 255)})" - def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, - id_memo=None) -> JsonSerializable: + def to_dict(self, **_) -> JsonSerializable: """ Get dict of color. """ # TODO: change this!!! it cannot be deserialized in generic way return str(self) @classmethod - def dict_to_object(cls, dict_: JsonSerializable, force_generic: bool = False, global_dict=None, - pointers_memo: Dict[str, Any] = None, path: str = '#') -> SerializableObject: + def dict_to_object(cls, dict_: JsonSerializable, **_) -> SerializableObject: """ Get color object from dict. """ if not dict_.startswith('rgb('): raise ValueError('Color should be string starting with rgb(') From d7c9f4bfeec5123feb7856281d97aaa8d8d0ff16 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 14:55:59 +0200 Subject: [PATCH 50/54] pylint --- code_pylint.py | 1 - plot_data/colors.py | 2 +- plot_data/core.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/code_pylint.py b/code_pylint.py index 98d4bc48..99e28b68 100644 --- a/code_pylint.py +++ b/code_pylint.py @@ -28,7 +28,6 @@ 'too-many-locals': 2, 'wrong-import-order': 1, 'too-many-branches': 1, - 'unused-import': 1, 'unused-argument': 4, 'cyclic-import': 11, 'no-self-use': 6, diff --git a/plot_data/colors.py b/plot_data/colors.py index 59eeb3fb..6342cc8a 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -2,7 +2,7 @@ import dessia_common.core as dc from dessia_common.serialization import SerializableObject -from dessia_common.typings import Dict, Any, JsonSerializable +from dessia_common.typings import JsonSerializable from matplotlib.colors import hsv_to_rgb class Color(dc.DessiaObject): diff --git a/plot_data/core.py b/plot_data/core.py index 9d78333a..7a1fde79 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -11,7 +11,7 @@ import tempfile import warnings import webbrowser -from typing import Any, Dict, List, Tuple, Union +from typing import Dict, List, Tuple, Union import matplotlib.pyplot as plt from matplotlib.patches import Polygon, Circle, Arc From 036d7ce27bfe6a447bcf97defe8024a3ff9ae96e Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Wed, 14 Jun 2023 15:10:37 +0200 Subject: [PATCH 51/54] pep8 --- plot_data/colors.py | 1 + plot_data/core.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/plot_data/colors.py b/plot_data/colors.py index 6342cc8a..095a1bfc 100644 --- a/plot_data/colors.py +++ b/plot_data/colors.py @@ -5,6 +5,7 @@ from dessia_common.typings import JsonSerializable from matplotlib.colors import hsv_to_rgb + class Color(dc.DessiaObject): """ Base class for handling colors as objects. """ diff --git a/plot_data/core.py b/plot_data/core.py index 7a1fde79..7905b1d7 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -135,8 +135,8 @@ def __init__(self, values, reference_path: str = "#", name: str = ""): self.reference_path = reference_path super().__init__(type_="sample", name=name) - def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, - id_memo = None) -> JsonSerializable: + def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, + id_memo=None) -> JsonSerializable: """ Overwrite generic to_dict. @@ -473,8 +473,8 @@ def bounding_box(self): min(self.data[1], self.data[3]), max(self.data[1], self.data[3])) - def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, - id_memo = None) -> JsonSerializable: + def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, + id_memo=None) -> JsonSerializable: """ Get dict of current LineSegment2D. """ dict_ = DessiaObject.to_dict(self, use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, id_memo=id_memo) @@ -509,8 +509,8 @@ def __init__(self, data: List[float], edge_style: EdgeStyle = None, name: str = self.data = data LineSegment2D.__init__(self, point1=self.data[:2], point2=self.data[2:], edge_style=edge_style, name=name) - def to_dict(self, use_pointers: bool = True, memo = None, path: str = '#', id_method = True, - id_memo = None) -> JsonSerializable: + def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, + id_memo=None) -> JsonSerializable: """ Get dict of current LineSegment. """ ls2d = LineSegment2D(point1=self.data[:2], point2=self.data[2:], edge_style=self.edge_style, name=self.name) return ls2d.to_dict(use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, id_memo=id_memo) @@ -960,9 +960,9 @@ def mpl_plot(self, ax=None, **kwargs): ax.add_patch( Arc((self.cx, self.cy), 2 * self.r, 2 * self.r, angle=0, - theta1=self.start_angle * 0.5 / math.pi * 360, - theta2=self.end_angle * 0.5 / math.pi * 360, - edgecolor=edgecolor), **kwargs) + theta1=self.start_angle * 0.5 / math.pi * 360, + theta2=self.end_angle * 0.5 / math.pi * 360, + edgecolor=edgecolor), **kwargs) return ax @@ -1336,6 +1336,7 @@ def plot_canvas(plot_data_object: Figure, filepath: str = None, debug_mode: bool """ plot_data_object.plot(filepath=filepath, debug_mode=debug_mode, canvas_id=canvas_id, version=force_version) + def write_json_for_tests(plot_data_object: PlotDataObject, json_path: str): """ Write JSON file of data to be used in Cypress tests of Typescript module. """ if not json_path.endswith(".json"): From d7532d3e2560e8c37ddf2b24569e0cc239adc56c Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 15 Jun 2023 14:37:13 +0200 Subject: [PATCH 52/54] feat(update_plot_canvas): standalone in db --- plot_data/core.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plot_data/core.py b/plot_data/core.py index 7905b1d7..5aea0475 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -86,6 +86,8 @@ def mpl_plot(self, ax=None, **kwargs): class Figure(PlotDataObject): """ Abstract interface for handling html exportable objects in module. """ + _standalone_in_db = True + def __init__(self, type_: str, width: int = 750, height: int = 400, name: str = '', **kwargs): self.width = width self.height = height From 4624151c65b4c2c3e267cbcd028ac95d4b122b61 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 15 Jun 2023 15:08:54 +0200 Subject: [PATCH 53/54] empty commit From 4cf32a14e011d06c799ae05712698518a4175600 Mon Sep 17 00:00:00 2001 From: Tanguy-DES Date: Thu, 15 Jun 2023 17:16:16 +0200 Subject: [PATCH 54/54] feat(update_plot_canvas): changes from review comments --- plot_data/core.py | 24 ------------------------ script/multiplot.py | 1 - 2 files changed, 25 deletions(-) diff --git a/plot_data/core.py b/plot_data/core.py index 5aea0475..bd60e47f 100644 --- a/plot_data/core.py +++ b/plot_data/core.py @@ -475,14 +475,6 @@ def bounding_box(self): min(self.data[1], self.data[3]), max(self.data[1], self.data[3])) - def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, - id_memo=None) -> JsonSerializable: - """ Get dict of current LineSegment2D. """ - dict_ = DessiaObject.to_dict(self, use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, - id_memo=id_memo) - dict_['object_class'] = 'plot_data.core.LineSegment2D' # To force migration to linesegment -> linesegment2d - return dict_ - def polygon_points(self): """ Get lists of points in a merged list. """ return [self.point1, self.point2] @@ -502,22 +494,6 @@ def mpl_plot(self, ax=None, edge_style=None, **kwargs): return ax -class LineSegment(LineSegment2D): - """ A line segment. This is a primitive that can be called by PrimitiveGroup. """ - - def __init__(self, data: List[float], edge_style: EdgeStyle = None, name: str = ''): - # When to remove support? - warnings.warn("LineSegment is deprecated, use LineSegment2D instead", DeprecationWarning) - self.data = data - LineSegment2D.__init__(self, point1=self.data[:2], point2=self.data[2:], edge_style=edge_style, name=name) - - def to_dict(self, use_pointers: bool = True, memo=None, path: str = '#', id_method=True, - id_memo=None) -> JsonSerializable: - """ Get dict of current LineSegment. """ - ls2d = LineSegment2D(point1=self.data[:2], point2=self.data[2:], edge_style=self.edge_style, name=self.name) - return ls2d.to_dict(use_pointers=use_pointers, memo=memo, path=path, id_method=id_method, id_memo=id_memo) - - class Wire(PlotDataObject): """ A set of connected lines. It also provides highlighting feature. diff --git a/script/multiplot.py b/script/multiplot.py index 27234c19..87c8299c 100644 --- a/script/multiplot.py +++ b/script/multiplot.py @@ -75,6 +75,5 @@ plot_data_object = plot_data.MultiplePlots(plots=plots2, elements=elements, initial_view_on=True) # Display -# plot_data.plot_canvas(plot_data_object=plot_data_object, debug_mode=True) plot_data_object.plot(debug_mode = True, canvas_id = 'canvas') # plot_data.plot_canvas(plot_data_object=multiplot, debug_mode=True)