diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c6c94ea..e9b4ea44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Dependencies of sample states between plots
- Use attributes coming from Python
- Point families implementation as PointSet
+- Graph2D implementation
### Removed
- Package Version checks is not implemented anymore
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 bb79d414..50f56eb5 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:cf27d2cd3c1466831280429cb13a78ff3dfa1007f6eee8701d8b198e5e40094e
-size 83482
+oid sha256:be1552352b38a1606a6573c952ca4c8de5b8b4ed536a5b0551a41421ca9f195a
+size 57888
diff --git a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should draw canvas-base.png
index 68d648e3..b76730a5 100644
--- a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should draw canvas-base.png
+++ b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should draw canvas-base.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8cff320851838522c80d8032021d8191c2b6853ce93cba011e8db972dc728b78
-size 38567
+oid sha256:aab405a726a6c369f1cf9d600e5a43161c2f810a5638123fac2857288c7cc23f
+size 38382
diff --git a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should scale and translate axes limits-base.png b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should scale and translate axes limits-base.png
index 3cb4d06d..1614f9ad 100644
--- a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should scale and translate axes limits-base.png
+++ b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should scale and translate axes limits-base.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:aa56bbf6ec8cbef76ca626755f2f5edd0de6330f26ae4e931e74b5b277100aa6
-size 40446
+oid sha256:011391508f32ece2f5973398065974e18e49c0804f7f8b00ff139058b8d9856c
+size 40501
diff --git a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should select with rubber band-base.png b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should select with rubber band-base.png
index 24ea4533..5738661f 100644
--- a/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should select with rubber band-base.png
+++ b/cypress/snapshots/base/histogram.cy.ts/HISTOGRAM CANVAS -- should select with rubber band-base.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:2c87e4b53e30d7f107d490ff8988de62ba3821d7ace8b21ad7b136e0cef81956
-size 38950
+oid sha256:f4593509b59f84f36f4c2e80f6e4a45b5b484e24555b0f0affdb6fd1857b90c5
+size 38802
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 8807be0b..0eb5bbf9 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:41671ab0e221e36113287a4e49a45ee82e9b92dc9306f30c13c9d8d716e0145d
-size 202006
+oid sha256:e4e6c1eb890483ce86ede658bff33ab37056658b74b7a453d9bd7c8e9c2a5994
+size 182876
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 8e3b0523..2a6dd1a0 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:1a19cb3e3d2cc87e145fe8167259eb80406aa287af2c514f1c458962a6c6a25c
-size 295028
+oid sha256:8862066f149b48238c4971d856544cd40499a53b0a4e1dcf198b53574c01ce93
+size 276052
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 c3946ce6..3ac49a55 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:adcc3eb2c2a1a02be6c626588ac4db756880781b4250ea7536681aed258b2697
-size 286229
+oid sha256:f7726922c2e389ee8568b1e09fbd5ca82a9425a9689d39ffa73ba077b7d19aa8
+size 267246
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 43c63abe..4e5fda5c 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:9f3ce0b5dcc35f26ff478827f036033645a425df32e4114f17492b5fd87add98
-size 289431
+oid sha256:dace020d94e6d20e2711637b430b1bca9972f3f5ffe811c50efdfa81b967f710
+size 273565
diff --git a/cypress/snapshots/base/plotscatter.cy.ts/PLOT SCATTER CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/plotscatter.cy.ts/PLOT SCATTER CANVAS -- should draw canvas-base.png
index 07763884..37f7c9d1 100644
--- a/cypress/snapshots/base/plotscatter.cy.ts/PLOT SCATTER CANVAS -- should draw canvas-base.png
+++ b/cypress/snapshots/base/plotscatter.cy.ts/PLOT SCATTER CANVAS -- should draw canvas-base.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8fcc8aca2defdc47266912ef0aa752fe903840d28bd2d33b0a4127d9fc6d7c29
-size 215741
+oid sha256:f3b76f0ea87eebef7ca9527d4b831f91cf0df4fd5bbb81ed43c769252eb801ff
+size 216273
diff --git a/cypress/snapshots/base/scattermatrix.cy.ts/PLOT SCATTER MATRIX CANVAS -- should draw canvas-base.png b/cypress/snapshots/base/scattermatrix.cy.ts/PLOT SCATTER MATRIX CANVAS -- should draw canvas-base.png
index 830e9e07..e6d5ddd4 100644
--- a/cypress/snapshots/base/scattermatrix.cy.ts/PLOT SCATTER MATRIX CANVAS -- should draw canvas-base.png
+++ b/cypress/snapshots/base/scattermatrix.cy.ts/PLOT SCATTER MATRIX CANVAS -- should draw canvas-base.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f2d97cb85728695584cad5c68e7b578e4e7157b237befeed32d327b9d7ca014c
-size 230695
+oid sha256:42def34d20bd5af3c6ac420de2efe8b2d327931c591dc3917e5343b557009448
+size 230645
diff --git a/cypress/templates/graph2d.template.html b/cypress/templates/graph2d.template.html
index 76be0c76..a5a0419b 100644
--- a/cypress/templates/graph2d.template.html
+++ b/cypress/templates/graph2d.template.html
@@ -45,7 +45,7 @@
var data = $data;
- var plot_data = new PlotData.PlotScatter(data, width, height, true, 0, 0, $canvas_id.id);
+ var plot_data = new PlotData.newGraph2D(data, width, height, true, 0, 0, $canvas_id.id);
plot_data.define_canvas($canvas_id.id);
plot_data.draw_initial();
plot_data.mouse_interaction(plot_data.isParallelPlot);
diff --git a/plot_data/core.py b/plot_data/core.py
index 442478c1..6ad8b58c 100644
--- a/plot_data/core.py
+++ b/plot_data/core.py
@@ -11,7 +11,7 @@
import tempfile
import warnings
import webbrowser
-from typing import Dict, List, Tuple, Union
+from typing import Dict, List, Tuple, Union # , Any
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Circle, Arc
@@ -30,6 +30,8 @@
import plot_data.colors
from plot_data import templates
+# CURVES_DATATYPE = Union(List[float], List[str], List[List[float]], List[Dict[str, Any]])
+
def delete_none_from_dict(dict1):
""" Delete input dictionary's keys where value is None. """
@@ -762,8 +764,7 @@ class Graph2D(Figure):
:type x_variable: str
:param y_variable: variable that you want to display on y axis
:type y_variable: str
- :param axis: an object containing all information needed for \
- drawing axis
+ :param axis: an object containing all information needed for drawing axis
:type axis: Axis
:param log_scale_x: True or False
:type log_scale_x: bool
@@ -801,6 +802,109 @@ def mpl_plot(self, ax=None, **kwargs):
ax.set_xlabel(xname)
ax.set_ylabel(yname)
return ax
+# TODO: commented code here is supposed to be used soon
+ # def graphs_to_curves(self):
+ # curves = []
+ # for graph in self.graphs:
+ # x_coords = []
+ # y_coords = []
+ # for sample in graph.elements:
+ # x_coords.append(sample[self.attribute_names[0]])
+ # y_coords.append(sample[self.attribute_names[1]])
+ # line_width = graph.edge_style.line_width
+ # color = graph.edge_style.color_stroke
+ # dash_line = graph.edge_style.dashline
+ # marker = graph.point_style.shape
+ # name = graph.name
+ # curves.append(Curve(x_coords, y_coords, name, line_width=line_width, color=color, dash_line=dash_line,
+ # marker=marker))
+ # return curves
+
+ # def to_plot(self):
+ # return
+
+
+# class Curve(PlotDataObject):
+
+# _KWARGS = ['line_width', 'color', 'dash_line', 'marker']
+
+# def __init__(self, x_coords: Union(List[str], List[float]), y_coords: Union(List[str], List[float]) = None,
+# name: str = '', **kwargs):
+# self.x_coords, self.y_coords = self.buildCoords(x_coords, y_coords)
+# self.line_width = None
+# self.color = None
+# self.dash_line = None
+# self.marker = None
+# self.setStyle(kwargs)
+
+# @staticmethod
+# def buildCoords(x_coords: Union(List[str], List[float]), y_coords: Union(List[str], List[float])):
+# if y_coords is None:
+# return list(range(len(y_coords))), x_coords
+# if len(x_coords) == len(y_coords):
+# return x_coords, y_coords
+# raise ValueError("x_coords and y_coords must be the same length.")
+
+# def setStyle(self, kwargs: Dict[str, Any]):
+# for attribute in self._KWARGS:
+# if attribute in kwargs:
+# setattr(self, attribute, kwargs[attribute])
+
+# @classmethod
+# def fromPlot(cls, x_values: CURVES_DATATYPE, y_values: CURVES_DATATYPE, x_variable: str, y_variable: str,
+# legend: List[str], **kwargs):
+# if isinstance(x_values[0], float):
+# if len(legend) != 1 and legend is not None:
+# raise ValueError("x_values and legend must be the same length.")
+# if legend is None:
+# return cls(x_values, y_values, **kwargs)
+# if len(legend) == 1:
+# return cls(x_values, y_values, legend[0], **kwargs)
+
+# if isinstance(x_values[0], dict):
+# if x_variable not in x_values[0]:
+# raise ValueError(f"{x_variable} not in keys of x_values.")
+
+# x_coords = [];
+# y_coords = [];
+# for elements in x_values:
+# x_coords.append(elements[x_variable])
+# y_coords.append(elements[y_variable])
+# return cls(x_coords, y_coords, legend[0], **kwargs)
+
+# raise TypeError("x_values must be a list of float or dict.")
+
+
+# class Plot(Figure):
+
+# _plot_commands = "GRAPH_COMMANDS"
+
+# def __init__(self, x_values: CURVES_DATATYPE, y_values: CURVES_DATATYPE = None, x_variable: str = None,
+# y_variable: str = None, axis: Axis = None, legend: List[str] = None, width: int = 750,
+# height: int = 400, name: str = '', **kwargs):
+# self.curves = self.buildCurves(x_values, y_values, x_variable, y_variable, legend, **kwargs)
+# if axis is None:
+# self.axis = Axis()
+# else:
+# self.axis = axis
+# super().__init__(width=width, height=height, type_='graph2d', name=name)
+
+# @staticmethod
+# def buildCurves(x_values: CURVES_DATATYPE, y_values: CURVES_DATATYPE, x_variable: str, y_variable: str,
+# legend: List[str], **kwargs):
+# if isinstance(x_values[0], (str, float, dict)):
+# return [Curve.fromPlot(x_values=x_values, y_values=y_values, x_variable=x_variable, y_variable=y_variable,
+# legend=legend, **kwargs)]
+
+# if isinstance(x_values[0], list):
+# curves = []
+# for x_subvalues, y_subvalues, sub_legend in zip(x_values, y_values, legend):
+# curves.append(Curve.fromPlot(x_values=x_subvalues, y_values=y_subvalues, x_variable=x_variable,
+# y_variable=y_variable, legend=sub_legend, **kwargs))
+# return curves
+
+# if isinstance(x_values[0], Curve):
+# return x_values
class Heatmap(DessiaObject):
@@ -844,10 +948,10 @@ class Scatter(Figure):
_plot_commands = "SCATTER_COMMANDS"
- def __init__(self, x_variable: str, y_variable: str, tooltip: Tooltip = None, point_style: PointStyle = None,
- elements: List[Sample] = None, points_sets: List[PointFamily] = None, axis: Axis = None,
- log_scale_x: bool = None, log_scale_y: bool = None, heatmap: Heatmap = None, heatmap_view: bool = None,
- width: int = 750, height: int = 400, name: str = ''):
+ def __init__(self, x_variable: str = None, y_variable: str = None, tooltip: Tooltip = None,
+ point_style: PointStyle = None, elements: List[Sample] = None, points_sets: List[PointFamily] = None,
+ axis: Axis = None, log_scale_x: bool = None, log_scale_y: bool = None, 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
diff --git a/plot_data/templates.py b/plot_data/templates.py
index 20246116..c0709fc0 100644
--- a/plot_data/templates.py
+++ b/plot_data/templates.py
@@ -39,7 +39,8 @@
Cluster:
-
+
+
@@ -70,7 +71,7 @@
plot_data.mouse_interaction(plot_data.isParallelPlot);"""
GRAPH_COMMANDS = """
- var plot_data = new PlotData.PlotScatter(data, width, height, true, 0, 0, $canvas_id.id);
+ var plot_data = new PlotData.newGraph2D(data, width, height, true, 0, 0, $canvas_id.id);
plot_data.define_canvas($canvas_id.id);
plot_data.draw_initial();
plot_data.mouse_interaction(plot_data.isParallelPlot);"""
diff --git a/script/plot_scatter.py b/script/plot_scatter.py
index b4d4835d..0ce7f445 100644
--- a/script/plot_scatter.py
+++ b/script/plot_scatter.py
@@ -48,7 +48,7 @@
color_stroke=VIOLET,
stroke_width=2,
size=8, # 1, 2, 3 or 4
- shape='^') # 'circle', 'square' or 'crux'
+ shape='cross') # 'circle', 'square' or 'crux'
# Finally, axis can be personalized too
graduation_style = plot_data.TextStyle(text_color=BLUE, font_size=10,
diff --git a/script/test_objects/graph_test.py b/script/test_objects/graph_test.py
index 8c18596e..9f13743e 100644
--- a/script/test_objects/graph_test.py
+++ b/script/test_objects/graph_test.py
@@ -3,7 +3,7 @@
import math
import plot_data
-from plot_data.colors import BLACK, BLUE, RED
+from plot_data.colors import BLUE, RED
k = 0
@@ -17,7 +17,7 @@
# The previous line instantiates a dataset with limited arguments but
# several customizations are available
-point_style = plot_data.PointStyle(color_fill=RED, color_stroke=BLACK, shape='crux')
+point_style = plot_data.PointStyle(color_fill=RED, color_stroke=RED, shape='crux')
edge_style = plot_data.EdgeStyle(color_stroke=BLUE, dashline=[10, 5])
custom_dataset = plot_data.Dataset(elements=elements1, name='I = f(t)',
@@ -33,4 +33,4 @@
elements2.append({'time': T2[k], 'electric current': I2[k]})
dataset2 = plot_data.Dataset(elements=elements2, name='I2 = f(t)')
-graph2d = plot_data.Graph2D(graphs=[dataset1, dataset2], x_variable='time', y_variable='electric current')
+graph2d = plot_data.Graph2D(graphs=[custom_dataset, dataset2], x_variable='time', y_variable='electric current')
diff --git a/src/multiplots.ts b/src/multiplots.ts
index b9f9aad6..6b216935 100644
--- a/src/multiplots.ts
+++ b/src/multiplots.ts
@@ -1,7 +1,7 @@
import {PlotData, Interactions} from './plot-data';
import {Point2D} from './primitives';
import { Attribute, PointFamily, Window, TypeOf, equals, Sort, export_to_txt, RubberBand } from './utils';
-import { PlotContour, PlotScatter, ParallelPlot, PrimitiveGroupContainer, Histogram, Frame, newScatter, BasePlot } from './subplots';
+import { PlotContour, PlotScatter, ParallelPlot, PrimitiveGroupContainer, Histogram, Frame, newScatter, BasePlot, newGraph2D } from './subplots';
import { List, Shape, MyObject } from './toolbox';
import { string_to_hex, string_to_rgb, rgb_to_string, colorHsl } from './color_conversion';
@@ -85,7 +85,7 @@ export class MultiplePlots {
let object_type_ = this.dataObjects[i]['type_'];
if (this.dataObjects[i]['type_'] == 'graph2d') {
this.dataObjects[i]['elements'] = elements;
- var newObject:any = new PlotScatter(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true);
+ var newObject:any = new newGraph2D(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true);
} else if (object_type_ === 'parallelplot') {
this.dataObjects[i]['elements'] = elements;
newObject = new ParallelPlot(this.dataObjects[i], this.sizes[i]['width'], this.sizes[i]['height'], buttons_ON, this.initial_coords[i][0], this.initial_coords[i][1], canvas_id, true);
@@ -382,25 +382,10 @@ export class MultiplePlots {
}
add_scatterplot(attr_x:Attribute, attr_y:Attribute) {
- var graduation_style = {text_color:string_to_rgb('grey'), font_size:12, font_style:'sans-serif', text_align_x:'center', text_align_y:'alphabetic', name:''};
- var axis_style = {line_width:0.5, color_stroke:string_to_rgb('grey'), dashline:[], name:''};
- var DEFAULT_AXIS = {nb_points_x:10, nb_points_y:10, graduation_style: graduation_style, axis_style: axis_style, arrow_on: false, grid_on: true, type_:'axis', name:''};
- var surface_style = {color_fill: string_to_rgb('lightblue'), opacity:0.75, hatching:undefined};
- var text_style = {text_color: string_to_rgb('black'), font_size:10, font_style:'sans-serif', text_align_x:'start', text_align_y:'alphabetic', name:''};
- var DEFAULT_TOOLTIP = {attribute_names:[attr_x.name, attr_y.name], surface_style:surface_style, text_style:text_style, tooltip_radius:5, type_:'tooltip', name:''};
- var point_style = {color_fill:string_to_rgb('lightblue'), color_stroke:string_to_rgb('grey'), stroke_width:0.5, size:2, shape:'circle', name:''};
- var new_scatter = {
- tooltip: DEFAULT_TOOLTIP,
- attribute_names: [attr_x.name, attr_y.name],
- point_style: point_style,
- elements: this.data['elements'],
- axis:DEFAULT_AXIS,
- type_:'scatterplot',
- name:''
- };
+ var new_scatter = {attribute_names: [attr_x.name, attr_y.name], elements:this.data['elements'], type_:'frame', name:''};
var DEFAULT_WIDTH = 560;
var DEFAULT_HEIGHT = 300;
- var new_plot_data = new PlotScatter(new_scatter, DEFAULT_WIDTH, DEFAULT_HEIGHT, this.buttons_ON, 0, 0, this.canvas_id);
+ var new_plot_data = new newScatter(new_scatter, DEFAULT_WIDTH, DEFAULT_HEIGHT, this.buttons_ON, 0, 0, this.canvas_id);
this.initialize_new_plot_data(new_plot_data);
}
@@ -628,15 +613,18 @@ export class MultiplePlots {
if (List.is_include(plotIndex, this.to_display_plots)) {
if (plot.type_ == 'parallelplot') { plot.refresh_axis_coords() }
if (plot instanceof BasePlot) {
- plot.selectedIndices = this.selectedIndices;
- plot.clickedIndices = [...this.clickedIndices];
- plot.hoveredIndices = [...this.hoveredIndices];
- if (plot instanceof Frame) {
- if (this.point_families.length != 0) {
- plot.pointSetColors = this.point_families.map((pointFamily, familyIdx) => {
- pointFamily.pointIndices.forEach(pointIdx => plot.pointSets[pointIdx] = familyIdx);
- return pointFamily.color
- })
+ // TODO: not so beautiful but here to avoid selecting points with unlinked graph points
+ if ( !(plot instanceof newGraph2D) ) {
+ plot.selectedIndices = this.selectedIndices;
+ plot.clickedIndices = [...this.clickedIndices];
+ plot.hoveredIndices = [...this.hoveredIndices];
+ if (plot instanceof Frame) {
+ if (this.point_families.length != 0) {
+ plot.pointSetColors = this.point_families.map((pointFamily, familyIdx) => {
+ pointFamily.pointIndices.forEach(pointIdx => plot.pointSets[pointIdx] = familyIdx);
+ return pointFamily.color
+ })
+ }
}
}
} else if (plot instanceof ParallelPlot) {
@@ -921,10 +909,13 @@ export class MultiplePlots {
this.selectedIndices = List.listIntersection(this.selectedIndices, obj.pp_selected_index);
} else if (obj instanceof BasePlot) {
obj.axes.forEach(axis => {
- if (axis.rubberBand.length != 0) {
- isSelecting = true;
- const selectedIndices = (obj as BasePlot).updateSelected(axis);
- this.selectedIndices = List.listIntersection(this.selectedIndices, selectedIndices);
+ // TODO: not so beautiful but here to avoid selecting points with unlinked graph points
+ if ( !(obj instanceof newGraph2D) ) {
+ if (axis.rubberBand.length != 0) {
+ isSelecting = true;
+ const selectedIndices = (obj as BasePlot).updateSelected(axis);
+ this.selectedIndices = List.listIntersection(this.selectedIndices, selectedIndices);
+ }
}
})
}
@@ -1843,7 +1834,9 @@ export class MultiplePlots {
this.objectList.forEach(plot => { if (plot instanceof BasePlot) plot.switchSelection() });
}
- public switchMerge() { this.objectList.forEach(plot => { if (plot instanceof newScatter) plot.switchMerge() })};
+ public switchMerge() { this.objectList.forEach(plot => { if (plot instanceof BasePlot) plot.switchMerge() })};
+
+ public togglePoints() { this.objectList.forEach(plot => { if (plot instanceof BasePlot) plot.togglePoints() })};
public switchZoom() {
this.isZooming = !this.isZooming;
@@ -1872,7 +1865,7 @@ export class MultiplePlots {
this.redrawAllObjects();
}
- mouse_interaction(): void {
+ mouse_interaction(): void { //TODO: this has to be totally refactored, with special behaviors defined in each plot class
var mouse1X:number = 0; var mouse1Y:number = 0; var mouse2X:number = 0; var mouse2Y:number = 0; var mouse3X:number = 0; var mouse3Y:number = 0;
var isDrawing = false;
var mouse_moving:boolean = false;
@@ -1969,6 +1962,7 @@ export class MultiplePlots {
this.manage_mouse_interactions(mouse2X, mouse2Y);
if (!this.isZooming) {
+ const currentPlot = this.objectList[this.last_index];
if (isDrawing) {
mouse_moving = true;
if (this.selectDependency_bool) {
@@ -1976,7 +1970,8 @@ export class MultiplePlots {
this.mouse_move_pp_communication();
this.mouse_move_frame_communication();
this.refreshRubberBands();
- this.updateSelectedPrimitives();
+ // TODO: not so beautiful but here to avoid selecting points with unlinked graph points
+ if ( !(currentPlot instanceof newGraph2D) ) this.updateSelectedPrimitives();
this.redrawAllObjects();
}
this.redraw_object();
@@ -1984,9 +1979,9 @@ export class MultiplePlots {
if (this.selectDependency_bool) {
this.mouse_over_primitive_group();
this.mouse_over_scatter_plot();
- if (this.objectList[this.last_index] instanceof BasePlot) {
- this.hoveredIndices = (this.objectList[this.last_index] as BasePlot).hoveredIndices;
- this.clickedIndices = (this.objectList[this.last_index] as BasePlot).clickedIndices;
+ if (currentPlot instanceof BasePlot && !(currentPlot instanceof newGraph2D)) {
+ this.hoveredIndices = (currentPlot as BasePlot).hoveredIndices;
+ this.clickedIndices = (currentPlot as BasePlot).clickedIndices;
}
this.redrawAllObjects();
}
@@ -2009,7 +2004,7 @@ export class MultiplePlots {
this.click_on_button_action(click_on_manip_button, click_on_selectDep_button, click_on_view, click_on_export);
}
- if (mouse_moving === false) {
+ if (!mouse_moving) {
if (this.selectDependency_bool) {
if (this.clickedPlotIndex !== -1) {
let type_ = this.objectList[this.clickedPlotIndex].type_
@@ -2034,7 +2029,7 @@ export class MultiplePlots {
this.pp_communication(this.objectList[this.clickedPlotIndex].rubber_bands, this.objectList[this.clickedPlotIndex]);
}
}
- if (this.objectList[this.last_index] instanceof BasePlot) {
+ if (this.objectList[this.last_index] instanceof BasePlot && !(this.objectList[this.last_index] instanceof newGraph2D)) {
this.hoveredIndices = (this.objectList[this.last_index] as BasePlot).hoveredIndices;
this.clickedIndices = (this.objectList[this.last_index] as BasePlot).clickedIndices;
}
@@ -2045,7 +2040,8 @@ export class MultiplePlots {
}
this.refreshRubberBands();
this.manage_selected_point_index_changes(old_selected_index);
- this.updateSelectedPrimitives();
+ // TODO: not so beautiful but here to avoid selecting points with unlinked graph points
+ if ( !(this.objectList[this.last_index] instanceof newGraph2D) ) this.updateSelectedPrimitives();
if (!shiftKey) this.isSelecting = false;
this.isZooming = false;
this.objectList.forEach(plot => {
diff --git a/src/plot-data.ts b/src/plot-data.ts
index 1abfc215..0796255d 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, newRect } 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 { colorHex, tint_rgb, hex_to_rgb, rgb_to_string, get_interpolation_colors, rgb_strToVector } from "./color_conversion";
@@ -851,7 +851,7 @@ export abstract class PlotData extends EventEmitter {
let origin = new Vertex(current_x, this.axis_y_end - 10);
let width = this.x_step * 0.95;
let align = "center";
- const textParams: textParams = {
+ const textParams: TextParams = {
width: width, height: origin.y - 2 - this.Y, align: align, baseline: "bottom",
multiLine: true, color: 'black', backgroundColor: "hsla(0, 0%, 100%, 0.5)" };
let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams);
@@ -971,7 +971,7 @@ export abstract class PlotData extends EventEmitter {
Shape.drawLine(this.context, [[this.axis_x_start, current_y], [this.axis_x_end, current_y]]);
let origin = new Vertex(this.axis_x_start, current_y + 10);
- const textParams: textParams = {
+ const textParams: TextParams = {
width: this.width * 0.25, height: this.y_step * 0.98, align: "center",
baseline: "hanging", multiLine: true, color: 'black', backgroundColor: "hsla(0, 0%, 100%, 0.5)" };
let axisTitle = new newText(this.axis_list[i]['name'], origin, textParams);
diff --git a/src/subplots.ts b/src/subplots.ts
index 6dd94ebb..b9e04436 100644
--- a/src/subplots.ts
+++ b/src/subplots.ts
@@ -1,5 +1,6 @@
import { PlotData, Buttons, Interactions } from "./plot-data";
-import { Attribute, Axis, Sort, set_default_values, TypeOf, RubberBand, Vertex, newAxis, ScatterPoint, Bar, DrawingCollection, SelectionBox, GroupCollection } from "./utils";
+import { Attribute, Axis, Sort, set_default_values, TypeOf, RubberBand, Vertex, newAxis, ScatterPoint, Bar, DrawingCollection, SelectionBox, GroupCollection,
+ LineSequence, newRect, newPointStyle } from "./utils";
import { Heatmap, PrimitiveGroup } from "./primitives";
import { List, Shape, MyObject } from "./toolbox";
import { Graph2D, Scatter } from "./primitives";
@@ -1472,6 +1473,7 @@ export class BasePlot extends PlotData {
public nSamples: number;
public pointSets: number[];
public pointSetColors: string[] = [];
+ public pointStyles: newPointStyle[] = null;
public isSelecting: boolean = false;
public selectionBox = new SelectionBox();
@@ -1501,10 +1503,10 @@ export class BasePlot extends PlotData {
public is_in_multiplot: boolean = false
) {
super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot);
- this.nSamples = data.elements.length;
this.origin = new Vertex(0, 0);
this.size = new Vertex(width - X, height - Y);
this.features = this.unpackData(data);
+ this.nSamples = this.features.entries().next().value[1].length; // a little bit cumbersome
this.initSelectors();
this.scaleX = this.scaleY = 1;
this.TRL_THRESHOLD /= Math.min(Math.abs(this.initScale.x), Math.abs(this.initScale.y));
@@ -1532,14 +1534,14 @@ export class BasePlot extends PlotData {
get falseIndicesArray(): boolean[] { return new Array(this.nSamples).fill(false) }
- public unpackAxisStyle(data:any): void {
+ protected unpackAxisStyle(data:any): void {
if (data.axis?.axis_style?.color_stroke) this.axisStyle.set("strokeStyle", data.axis.axis_style.color_stroke);
if (data.axis?.axis_style?.line_width) this.axisStyle.set("lineWidth", data.axis.axis_style.line_width);
if (data.axis?.graduation_style?.font_style) this.axisStyle.set("font", data.axis.graduation_style.font_style);
if (data.axis?.graduation_style?.font_size) this.axisStyle.set("ticksFontsize", data.axis.graduation_style.font_size);
}
- private unpackData(data: any): Map {
+ protected unpackData(data: any): Map {
const featuresKeys: string[] = Array.from(Object.keys(data.elements[0].values));
featuresKeys.push("name");
let unpackedData = new Map();
@@ -1547,7 +1549,7 @@ export class BasePlot extends PlotData {
return unpackedData
}
- public drawCanvas(): void {
+ private drawCanvas(): void {
this.context = this.context_show;
this.draw_empty_canvas(this.context_show);
if (this.settings_on) this.draw_settings_rect()
@@ -1563,6 +1565,10 @@ export class BasePlot extends PlotData {
axis.update(this.axisStyle, this.viewPoint, new Vertex(this.scaleX, this.scaleY), this.translation);
if (axis.rubberBand.length != 0) axesSelections.push(this.updateSelected(axis));
})
+ this.updateSelection(axesSelections);
+ }
+
+ public updateSelection(axesSelections: number[][]): void {
if (!this.is_in_multiplot) this.selectedIndices = BasePlot.intersectArrays(axesSelections);
}
@@ -1578,32 +1584,34 @@ export class BasePlot extends PlotData {
return arraysIntersection
}
- public updateSize(): void { this.size = new Vertex(this.width, this.height) }
+ protected updateSize(): void { this.size = new Vertex(this.width, this.height) }
- public resetAxes(): void { this.axes.forEach(axis => axis.reset()) }
+ protected resetAxes(): void { this.axes.forEach(axis => axis.reset()) }
- public reset_scales(): void {
+ public reset_scales(): void { // TODO: merge with resetView
this.updateSize();
this.axes.forEach(axis => axis.resetScale());
}
+ public resetView(): void { this.reset_scales(); this.draw() }
+
public initSelectors(): void {
this.hoveredIndices = [];
this.clickedIndices = [];
this.selectedIndices = [];
}
- public resetSelectors(): void {
+ protected resetSelectors(): void {
this.selectionBox = new SelectionBox();
this.initSelectors();
}
- public reset(): void {
+ protected reset(): void {
this.resetAxes();
this.resetSelectors();
}
- public resetSelection(): void {
+ protected resetSelection(): void {
this.axes.forEach(axis => axis.rubberBand.reset());
this.resetSelectors();
}
@@ -1615,27 +1623,27 @@ export class BasePlot extends PlotData {
return selection
}
- public isRubberBanded(): boolean {
+ protected isRubberBanded(): boolean {
let isRubberBanded = true;
this.axes.forEach(axis => isRubberBanded = isRubberBanded && axis.rubberBand.length != 0);
return isRubberBanded
}
- public drawAxes(): void { this.axes.forEach(axis => axis.draw(this.context_show)) }
+ protected drawAxes(): void { this.axes.forEach(axis => axis.draw(this.context_show)) }
- public drawZoneRectangle(context: CanvasRenderingContext2D): void {
+ private drawZoneRectangle(context: CanvasRenderingContext2D): void {
// TODO: change with newRect
Shape.rect(this.X, this.Y, this.width, this.height, context, "hsl(203, 90%, 88%)", "hsl(0, 0%, 0%)", 1, 0.3, [15,15]);
}
- public drawRelativeObjects() {}
+ protected drawRelativeObjects() {}
- public drawAbsoluteObjects(context: CanvasRenderingContext2D) {
+ protected drawAbsoluteObjects(context: CanvasRenderingContext2D) {
this.absoluteObjects = new GroupCollection();
this.drawSelectionBox(context);
}
- public computeRelativeObjects() {}
+ protected computeRelativeObjects() {}
public draw(): void {
this.context_show.save();
@@ -1674,13 +1682,17 @@ export class BasePlot extends PlotData {
public switchSelection(): void { this.isSelecting = !this.isSelecting; this.draw() }
+ public switchMerge(): void {}
+
public switchZoom(): void { this.isZooming = !this.isZooming }
- public updateSelectionBox(frameDown: Vertex, frameMouse: Vertex): void { this.selectionBox.update(frameDown, frameMouse) }
+ public togglePoints(): void {}
+
+ protected updateSelectionBox(frameDown: Vertex, frameMouse: Vertex): void { this.selectionBox.update(frameDown, frameMouse) }
public get drawingZone(): [Vertex, Vertex] { return [new Vertex(this.X, this.Y), this.size] }
- public drawSelectionBox(context: CanvasRenderingContext2D) {
+ protected drawSelectionBox(context: CanvasRenderingContext2D) {
if ((this.isSelecting || this.is_drawing_rubber_band) && this.selectionBox.isDefined) {
const [drawingOrigin, drawingSize] = this.drawingZone;
this.selectionBox.buildRectFromHTMatrix(drawingOrigin, drawingSize, this.relativeMatrix);
@@ -1692,7 +1704,7 @@ export class BasePlot extends PlotData {
}
}
- public drawZoomBox(zoomBox: SelectionBox, frameDown: Vertex, frameMouse: Vertex, context: CanvasRenderingContext2D): void {
+ private drawZoomBox(zoomBox: SelectionBox, frameDown: Vertex, frameMouse: Vertex, context: CanvasRenderingContext2D): void {
zoomBox.update(frameDown, frameMouse);
const [drawingOrigin, drawingSize] = this.drawingZone;
zoomBox.buildRectFromHTMatrix(drawingOrigin, drawingSize, this.relativeMatrix);
@@ -1700,7 +1712,7 @@ export class BasePlot extends PlotData {
zoomBox.draw(context);
}
- public zoomBoxUpdateAxes(zoomBox: SelectionBox): void { // TODO: will not work for a 3+ axes plot
+ protected zoomBoxUpdateAxes(zoomBox: SelectionBox): void { // TODO: will not work for a 3+ axes plot
this.axes[0].minValue = Math.min(zoomBox.minVertex.x, zoomBox.maxVertex.x);
this.axes[0].maxValue = Math.max(zoomBox.minVertex.x, zoomBox.maxVertex.x);
this.axes[1].minValue = Math.min(zoomBox.minVertex.y, zoomBox.maxVertex.y);
@@ -1709,12 +1721,12 @@ export class BasePlot extends PlotData {
this.updateAxes();
}
- public drawTooltips(): void {
+ private drawTooltips(): void {
this.relativeObjects.drawTooltips(new Vertex(this.X, this.Y), this.size, this.context_show, this.is_in_multiplot);
this.absoluteObjects.drawTooltips(new Vertex(this.X, this.Y), this.size, this.context_show, this.is_in_multiplot);
}
- public stateUpdate(context: CanvasRenderingContext2D, canvasMouse: Vertex, absoluteMouse: Vertex,
+ protected stateUpdate(context: CanvasRenderingContext2D, canvasMouse: Vertex, absoluteMouse: Vertex,
frameMouse: Vertex, stateName: string, keepState: boolean, invertState: boolean): void {
this.fixedObjects.updateMouseState(context, canvasMouse, stateName, keepState, invertState);
this.absoluteObjects.updateMouseState(context, absoluteMouse, stateName, keepState, invertState);
@@ -1846,7 +1858,7 @@ export class BasePlot extends PlotData {
}
}
- public drawAfterRescale(mouse3X: number, mouse3Y: number, scale: Vertex): void {
+ private drawAfterRescale(mouse3X: number, mouse3Y: number, scale: Vertex): void {
for (let axis of this.axes) {
if (axis.tickPrecision >= this.MAX_PRINTED_NUMBERS) {
if (this.scaleX > scale.x) {this.scaleX = scale.x}
@@ -1868,7 +1880,7 @@ export class BasePlot extends PlotData {
public zoomOut(): void { this.zoom(new Vertex(this.X + this.size.x / 2, this.Y + this.size.y / 2), -342) }
- public zoom(center: Vertex, zFactor: number): void {
+ private zoom(center: Vertex, zFactor: number): void {
const [mouse3X, mouse3Y] = this.wheel_interaction(center.x, center.y, zFactor);
this.drawAfterRescale(mouse3X, mouse3Y, new Vertex(1, 1));
}
@@ -1974,7 +1986,7 @@ export class Frame extends BasePlot {
return [origin.transform(this.canvasMatrix.inverse()), size]
}
- public unpackAxisStyle(data: any): void {
+ protected unpackAxisStyle(data: any): void {
if (data.axis) {
super.unpackAxisStyle(data);
this.nXTicks = data.axis.nb_points_x;
@@ -1982,7 +1994,7 @@ export class Frame extends BasePlot {
}
}
- public stateUpdate(context: CanvasRenderingContext2D, canvasMouse: Vertex, absoluteMouse: Vertex,
+ protected stateUpdate(context: CanvasRenderingContext2D, canvasMouse: Vertex, absoluteMouse: Vertex,
frameMouse: Vertex, stateName: string, keepState: boolean, invertState: boolean): void {
super.stateUpdate(context, canvasMouse, absoluteMouse, frameMouse, stateName, keepState, invertState);
if (stateName == "isHovered") this.hoveredIndices = this.sampleDrawings.updateSampleStates(stateName);
@@ -1996,7 +2008,7 @@ export class Frame extends BasePlot {
return new Vertex(Math.max(naturalOffset.x, calibratedMeasure), Math.max(naturalOffset.y, MIN_FONTSIZE));
}
- public updateSize(): void { this.size = new Vertex(this.width, this.height) }
+ protected updateSize(): void { this.size = new Vertex(this.width, this.height) }
public reset_scales(): void {
this.updateSize();
@@ -2010,27 +2022,41 @@ export class Frame extends BasePlot {
this.axes[1].transform(frameOrigin, yEnd);
}
- public setFeatures(data: any): [string, string] {
- return [data.attribute_names[0], data.attribute_names[1]];
+ protected setFeatures(data: any): [string, string] {
+ let xFeature = data.attribute_names[0];
+ let yFeature = data.attribute_names[1];
+ if (!xFeature) {
+ xFeature = "indices";
+ this.features.set("indices", Array.from(Array(this.nSamples).keys()));
+ }
+ if (!yFeature) {
+ for (let key of Array.from(this.features.keys())) {
+ if (!["name", "indices"].includes(key)) {
+ yFeature = key;
+ break;
+ }
+ }
+ }
+ return [xFeature, yFeature]
}
- public setAxes(): newAxis[] {
+ protected setAxes(): newAxis[] {
const [frameOrigin, xEnd, yEnd, freeSize] = this.setFrameBounds()
return [
this.setAxis(this.xFeature, freeSize.y, frameOrigin, xEnd, this.nXTicks),
this.setAxis(this.yFeature, freeSize.x, frameOrigin, yEnd, this.nYTicks)]
}
- public setAxis(feature: string, freeSize: number, origin: Vertex, end: Vertex, nTicks: number = undefined): newAxis {
+ protected setAxis(feature: string, freeSize: number, origin: Vertex, end: Vertex, nTicks: number = undefined): newAxis {
return new newAxis(this.features.get(feature), freeSize, origin, end, feature, this.initScale, nTicks)
}
- public drawAxes() {
+ protected drawAxes() {
super.drawAxes();
if (this.isRubberBanded()) this.updateSelectionBox(...this.rubberBandsCorners);
}
- public setFrameBounds(): [Vertex, Vertex, Vertex, Vertex] {
+ protected setFrameBounds(): [Vertex, Vertex, Vertex, Vertex] {
let frameOrigin = this.offset.add(new Vertex(this.X, this.Y).scale(this.initScale));
let xEnd = new Vertex(this.size.x - this.margin.x + this.X * this.initScale.x, frameOrigin.y);
let yEnd = new Vertex(frameOrigin.x, this.size.y - this.margin.y + this.Y * this.initScale.y);
@@ -2050,7 +2076,7 @@ export class Frame extends BasePlot {
return [frameOrigin, xEnd, yEnd, freeSize]
}
- public updateSelectionBox(frameDown: Vertex, frameMouse: Vertex) {
+ protected updateSelectionBox(frameDown: Vertex, frameMouse: Vertex) {
this.axes[0].rubberBand.minValue = Math.min(frameDown.x, frameMouse.x);
this.axes[1].rubberBand.minValue = Math.min(frameDown.y, frameMouse.y);
this.axes[0].rubberBand.maxValue = Math.max(frameDown.x, frameMouse.x);
@@ -2067,7 +2093,6 @@ export class Frame extends BasePlot {
this.is_drawing_rubber_band = true;
this.selectionBox.rubberBandUpdate(e, ["x", "y"][index]);
}));
-
super.mouse_interaction(isParallelPlot);
}
}
@@ -2102,7 +2127,7 @@ export class Histogram extends Frame {
set nYTicks(value: number) {this._nYTicks = value}
- public unpackAxisStyle(data: any): void {
+ protected unpackAxisStyle(data: any): void {
super.unpackAxisStyle(data);
if (data.graduation_nb) this.nXTicks = data.graduation_nb;
}
@@ -2114,7 +2139,7 @@ export class Histogram extends Frame {
if (data.edge_style?.dashline) this.dashLine = data.edge_style.dashline;
}
- public reset(): void {
+ protected reset(): void {
super.reset();
this.bars = [];
}
@@ -2165,18 +2190,18 @@ export class Histogram extends Frame {
return bars
}
- public computeRelativeObjects(): void {
+ protected computeRelativeObjects(): void {
this.bars = this.computeBars(this.axes[0], this.features.get(this.xFeature));
this.axes[1] = this.updateNumberAxis(this.axes[1], this.bars);
this.getBarsDrawing();
}
- public drawRelativeObjects(): void {
+ protected drawRelativeObjects(): void {
this.bars.forEach(bar => { bar.buildPath() ; bar.draw(this.context_show) });
this.relativeObjects = new GroupCollection([...this.bars], this.relativeMatrix);
}
- public getBarsDrawing(): void {
+ private getBarsDrawing(): void {
const fullTicks = this.boundedTicks(this.axes[0]);
const minY = this.boundedTicks(this.axes[1])[0];
this.bars.forEach((bar, index) => {
@@ -2195,7 +2220,7 @@ export class Histogram extends Frame {
})
}
- public setAxes(): newAxis[] {
+ protected setAxes(): newAxis[] {
const [frameOrigin, xEnd, yEnd, freeSize] = this.setFrameBounds();
const xAxis = this.setAxis(this.xFeature, freeSize.y, frameOrigin, xEnd, this.nXTicks);
const bars = this.computeBars(xAxis, this.features.get(this.xFeature));
@@ -2204,13 +2229,13 @@ export class Histogram extends Frame {
return [xAxis, yAxis];
}
- public setFeatures(data: any): [string, string] { return [data.x_variable, 'number'] }
+ protected setFeatures(data: any): [string, string] { return [data.x_variable, 'number'] }
public mouseTranslate(currentMouse: Vertex, mouseDown: Vertex): Vertex {
return new Vertex(this.axes[0].isDiscrete ? 0 : currentMouse.x - mouseDown.x, 0)
}
- wheel_interaction(mouse3X: number, mouse3Y: number, deltaY: number): [number, number] { // TODO: REALLY NEEDS A REFACTOR
+ public wheel_interaction(mouse3X: number, mouse3Y: number, deltaY: number): [number, number] { // TODO: REALLY NEEDS A REFACTOR
// e.preventDefault();
this.fusion_coeff = 1.2;
if (!this.axes[0].isDiscrete) {
@@ -2241,8 +2266,8 @@ const DEFAULT_POINT_COLOR: string = 'hsl(203, 90%, 85%)';
export class newScatter extends Frame {
public points: ScatterPoint[] = [];
- public fillStyle: string;
- public strokeStyle: string;
+ public fillStyle: string = DEFAULT_POINT_COLOR;
+ public strokeStyle: string = null;
public marker: string = 'circle';
public pointSize: number = 8;
public lineWidth: number = 1;
@@ -2262,7 +2287,7 @@ export class newScatter extends Frame {
public is_in_multiplot: boolean = false
) {
super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot);
- if (!data.tooltip) {this.tooltipAttributes = Array.from(this.features.keys()) }
+ if (!data.tooltip) this.tooltipAttributes = Array.from(this.features.keys());
else this.tooltipAttributes = data.tooltip.attribute;
this.unpackPointStyle(data);
this.computePoints();
@@ -2280,7 +2305,7 @@ export class newScatter extends Frame {
if (data.points_sets) this.unpackPointsSets(data);
}
- public unpackPointsSets(data: any): void {
+ private unpackPointsSets(data: any): void {
data.points_sets.forEach((pointSet, setIndex) => {
pointSet.point_index.forEach(pointIndex => {
this.pointSets[pointIndex] = setIndex;
@@ -2294,19 +2319,21 @@ export class newScatter extends Frame {
this.computePoints();
}
- public reset(): void {
+ protected reset(): void {
super.reset();
this.computePoints();
this.resetClusters();
}
- public drawAbsoluteObjects(context: CanvasRenderingContext2D): void {
+ protected drawAbsoluteObjects(context: CanvasRenderingContext2D): void {
this.drawPoints(context);
this.absoluteObjects = new GroupCollection([...this.points]);
this.drawSelectionBox(context);
};
- public drawPoints(context: CanvasRenderingContext2D): void {
+ protected drawPoints(context: CanvasRenderingContext2D): void {
+ const axesOrigin = this.axes[0].origin;
+ const axesEnd = new Vertex(this.axes[0].end.x, this.axes[1].end.y);
this.points.forEach(point => {
let color = this.fillStyle;
const colors = new Map();
@@ -2327,9 +2354,15 @@ export class newScatter extends Frame {
};
point.lineWidth = this.lineWidth;
point.setColors(color);
- point.marker = this.marker;
+ if (this.pointStyles) {
+ if (!this.clusterColors) point.updateStyle(this.pointStyles[point.values[0]])
+ else {
+ let clusterPointStyle = Object.assign({}, this.pointStyles[point.values[0]], { strokeStyle: null });
+ point.updateStyle(clusterPointStyle);
+ }
+ } else point.marker = this.marker;
point.update();
- if (point.isInFrame(this.axes[0], this.axes[1])) point.draw(context);
+ if (point.isInFrame(axesOrigin, axesEnd, this.initScale)) point.draw(context);
})
}
@@ -2349,7 +2382,7 @@ export class newScatter extends Frame {
this.draw();
}
- public zoomBoxUpdateAxes(zoomBox: SelectionBox): void { // TODO: will not work for a 3+ axes plot
+ protected zoomBoxUpdateAxes(zoomBox: SelectionBox): void { // TODO: will not work for a 3+ axes plot
super.zoomBoxUpdateAxes(zoomBox);
this.computePoints();
}
@@ -2502,7 +2535,7 @@ export class newScatter extends Frame {
public mouseDown(canvasMouse: Vertex, frameMouse: Vertex, absoluteMouse: Vertex): [Vertex, Vertex, any] {
let [superCanvasMouse, superFrameMouse, clickedObject] = super.mouseDown(canvasMouse, frameMouse, absoluteMouse);
- this.previousCoords = this.points.map(p => p.center)
+ this.previousCoords = this.points.map(p => p.center);
return [superCanvasMouse, superFrameMouse, clickedObject]
}
@@ -2533,6 +2566,101 @@ export class newScatter extends Frame {
}
}
+export class newGraph2D extends newScatter {
+ public curves: LineSequence[];
+ private curvesIndices: number[][];
+ public showPoints: boolean = false;
+ constructor(
+ data: any,
+ public width: number,
+ public height: number,
+ public buttons_ON: boolean,
+ public X: number,
+ public Y: number,
+ public canvas_id: string,
+ public is_in_multiplot: boolean = false
+ ) {
+ super(data, width, height, buttons_ON, X, Y, canvas_id, is_in_multiplot);
+ }
+
+ protected unpackData(data: any): Map {
+ const graphSamples = [];
+ this.pointStyles = [];
+ this.curvesIndices = [];
+ this.curves = [];
+ if (data.graphs) {
+ data.graphs.forEach(graph => {
+ if (graph.elements.length != 0) {
+ this.curves.push(LineSequence.getGraphProperties(graph));
+ const curveIndices = range(graphSamples.length, graphSamples.length + graph.elements.length);
+ const graphPointStyle = new newPointStyle(graph.point_style);
+ this.pointStyles.push(...new Array(curveIndices.length).fill(graphPointStyle));
+ this.curvesIndices.push(curveIndices);
+ graphSamples.push(...graph.elements);
+ }
+ })
+ }
+ return super.unpackData({"elements": graphSamples})
+ }
+
+ public updateSelection(axesSelections: number[][]): void { this.selectedIndices = BasePlot.intersectArrays(axesSelections) }
+
+ public drawCurves(context: CanvasRenderingContext2D): void {
+ const axesOrigin = this.axes[0].origin.transform(this.canvasMatrix);
+ const axesEnd = new Vertex(this.axes[0].end.x, this.axes[1].end.y).transform(this.canvasMatrix);
+ const drawingZone = new newRect(axesOrigin, axesEnd.subtract(axesOrigin));
+ const previousCanvas = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
+ this.curves.forEach((curve, curveIndex) => {
+ curve.update(this.curvesIndices[curveIndex].map(index => { return this.points[index] }));
+ curve.draw(context);
+ })
+ context.globalCompositeOperation = "destination-in";
+ context.fill(drawingZone.path);
+ const cutGraph = context.getImageData(this.X, this.Y, this.size.x, this.size.y);
+ context.globalCompositeOperation = "source-over";
+ context.putImageData(previousCanvas, 0, 0);
+ context.putImageData(cutGraph, this.X, this.Y);
+ }
+
+ protected drawAbsoluteObjects(context: CanvasRenderingContext2D): void {
+ this.drawCurves(context);
+ this.absoluteObjects = new GroupCollection([...this.curves]);
+ if (this.showPoints) {
+ this.drawPoints(context);
+ this.absoluteObjects.drawings = [...this.points, ...this.absoluteObjects.drawings];
+ }
+ this.drawSelectionBox(context);
+ };
+
+ public reset_scales(): void {
+ const scale = new Vertex(this.frameMatrix.a, this.frameMatrix.d).scale(this.initScale);
+ const translation = new Vertex(this.axes[0].maxValue - this.axes[0].initMaxValue, this.axes[1].maxValue - this.axes[1].initMaxValue).scale(scale);
+ this.curves.forEach(curve => curve.translateTooltip(translation));
+ super.reset_scales();
+ }
+
+ public switchMerge(): void { this.isMerged = false }
+
+ public togglePoints(): void { this.showPoints = !this.showPoints; this.draw() }
+
+ public mouseUp(canvasMouse: Vertex, frameMouse: Vertex, absoluteMouse: Vertex, canvasDown: Vertex, ctrlKey: boolean): void {
+ super.mouseUp(canvasMouse, frameMouse, absoluteMouse, canvasDown, ctrlKey);
+ this.curves.forEach(curve => curve.previousTooltipOrigin = curve.tooltipOrigin);
+ }
+
+ public mouseTranslate(currentMouse: Vertex, mouseDown: Vertex): Vertex {
+ const translation = super.mouseTranslate(currentMouse, mouseDown);
+ this.curves.forEach(curve => { if (curve.previousTooltipOrigin) curve.tooltipOrigin = curve.previousTooltipOrigin.add(translation.scale(this.initScale)) });
+ return translation
+ }
+}
+
+function range(start: number, end: number, step: number = 1): number[] {
+ let array = [];
+ for (let i = start; i < end; i = i + step) array.push(i);
+ return array
+}
+
function mean(array: number[]): number {
let sum = 0;
array.forEach(value => sum += value);
diff --git a/src/utils.ts b/src/utils.ts
index c96ec20c..d5f996fc 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1297,6 +1297,13 @@ export class Vertex {
return copy
}
+ public translate(translation: Vertex): Vertex { return this.add(translation) }
+
+ public translateSelf(translation: Vertex): void {
+ this.x += translation.x;
+ this.y += translation.y;
+ }
+
public subtract(other: Vertex): Vertex {
let copy = this.copy();
copy.x = this.x - other.x;
@@ -1334,13 +1341,12 @@ export class newShape {
public isClicked: boolean = false;
public isSelected: boolean = false;
public isScaled: boolean = true;
+ public isFilled: boolean = true;
public inFrame: boolean = true;
+ public onFrame: boolean = false;
public tooltipOrigin: Vertex;
protected _tooltipMap = new Map();
-
- protected readonly TOOLTIP_SURFACE: SurfaceStyle = new SurfaceStyle(string_to_hex("lightgrey"), 0.5, null);
- protected readonly TOOLTIP_TEXT_STYLE: TextStyle = new TextStyle(string_to_hex("black"), 14, "Calibri");
constructor() {};
get tooltipMap(): Map { return this._tooltipMap };
@@ -1358,17 +1364,26 @@ export class newShape {
context.scale(1 / contextMatrix.a, 1 / contextMatrix.d);
} else scaledPath.addPath(this.path);
this.setDrawingProperties(context);
- context.fill(scaledPath);
+ if (this.isFilled) context.fill(scaledPath);
context.stroke(scaledPath);
context.restore();
}
+ public setStrokeStyle(fillStyle: string): string {
+ const [h, s, l] = hslToArray(colorHsl(fillStyle));
+ const lValue = l <= STROKE_STYLE_OFFSET ? l + STROKE_STYLE_OFFSET : l - STROKE_STYLE_OFFSET;
+ return `hsl(${h}, ${s}%, ${lValue}%)`;
+ }
+
public setDrawingProperties(context: CanvasRenderingContext2D) {
context.lineWidth = this.lineWidth;
context.strokeStyle = this.strokeStyle;
context.setLineDash(this.dashLine);
context.globalAlpha = this.alpha;
- context.fillStyle = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.isSelected ? this.selectedStyle : this.fillStyle;
+ if (this.isFilled) {
+ context.fillStyle = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.isSelected ? this.selectedStyle : this.fillStyle;
+ context.strokeStyle = this.setStrokeStyle(context.fillStyle);
+ } else context.strokeStyle = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.isSelected ? this.selectedStyle : this.strokeStyle;
}
public initTooltip(context: CanvasRenderingContext2D): newTooltip { return new newTooltip(this.tooltipOrigin, this.tooltipMap, context) }
@@ -1395,13 +1410,12 @@ export class newCircle extends newShape {
public radius: number = 1
) {
super();
- this.path = this.buildPath();
+ this.buildPath();
}
- public buildPath(): Path2D {
- const path = new Path2D();
- path.arc(this.center.x, this.center.y, this.radius, 0, 2 * Math.PI);
- return path
+ public buildPath(): void {
+ this.path = new Path2D();
+ this.path.arc(this.center.x, this.center.y, this.radius, 0, 2 * Math.PI);
}
}
@@ -1438,9 +1452,7 @@ export class newRoundRect extends newRect {
const vLength = this.origin.y + this.size.y;
this.path.moveTo(this.origin.x + this.radius, this.origin.y);
this.path.lineTo(hLength - this.radius, this.origin.y);
-
this.path.quadraticCurveTo(hLength, this.origin.y, hLength, this.origin.y + this.radius);
-
this.path.lineTo(hLength, this.origin.y + this.size.y - this.radius);
this.path.quadraticCurveTo(hLength, vLength, hLength - this.radius, vLength);
this.path.lineTo(this.origin.x + this.radius, vLength);
@@ -1456,6 +1468,7 @@ export class Mark extends newShape {
public size: number = 1
) {
super();
+ this.isFilled = false;
this.buildPath();
}
@@ -1476,9 +1489,9 @@ export abstract class AbstractHalfLine extends newShape {
public orientation: string = 'up'
) {
super();
+ this.isFilled = false;
this.buildPath();
}
- // public abstract buildPath(): void;
}
export class UpHalfLine extends AbstractHalfLine {
@@ -1491,7 +1504,7 @@ export class UpHalfLine extends AbstractHalfLine {
}
export class DownHalfLine extends AbstractHalfLine {
- public buildPath(): void {
+ public buildPath(): void {
this.path = new Path2D();
const halfSize = this.size / 2;
this.path.moveTo(this.center.x, this.center.y);
@@ -1542,6 +1555,7 @@ export class Cross extends newShape {
public size: number = 1
) {
super();
+ this.isFilled = false;
this.buildPath();
}
@@ -1630,9 +1644,18 @@ export class Triangle extends AbstractTriangle {
}
}
-export interface textParams {
- width?: number, height?: number, fontsize?: number, multiLine?: boolean, font?: string, align?: string,
- baseline?: string, style?: string, orientation?: number, backgroundColor?: string, color?: string
+export interface TextParams {
+ width?: number,
+ height?: number,
+ fontsize?: number,
+ multiLine?: boolean,
+ font?: string,
+ align?: string,
+ baseline?: string,
+ style?: string,
+ orientation?: number,
+ backgroundColor?: string,
+ color?: string
}
const DEFAULT_FONTSIZE = 12;
@@ -1664,7 +1687,7 @@ export class newText extends newShape {
orientation = 0,
color = "hsl(0, 0%, 0%)",
backgroundColor = "hsla(0, 0%, 100%, 0)"
- }: textParams = {}) {
+ }: TextParams = {}) {
super();
this.width = width;
this.height = height;
@@ -1833,6 +1856,38 @@ export class newText extends newShape {
}
}
+export interface PointStyleInterface {
+ size?: number,
+ color_fill?: string,
+ color_stroke?: string,
+ stroke_width?: number,
+ shape?: string,
+ name?: string
+}
+
+export class newPointStyle implements PointStyleInterface {
+ public size: number;
+ public fillStyle: string;
+ public strokeStyle: string;
+ public marker: string;
+ public lineWidth: number;
+ constructor(
+ { size = null,
+ color_fill = null,
+ color_stroke = null,
+ stroke_width = null,
+ shape = 'circle',
+ name = ''
+ }: PointStyleInterface = {}
+ ) {
+ this.size = size;
+ this.fillStyle = color_fill;
+ this.strokeStyle = color_stroke;
+ this.marker = shape;
+ this.lineWidth = stroke_width;
+ }
+}
+
const CIRCLES = ['o', 'circle', 'round'];
const MARKERS = ['+', 'crux', 'mark'];
const CROSSES = ['x', 'cross', 'oblique'];
@@ -1860,15 +1915,30 @@ export class newPoint2D extends newShape {
this.lineWidth = 1;
};
- public setStrokeStyle(fillStyle: string): string {
- const [h, s, l] = hslToArray(colorHsl(fillStyle));
- const lValue = l <= STROKE_STYLE_OFFSET ? l + STROKE_STYLE_OFFSET : l - STROKE_STYLE_OFFSET;
- return `hsl(${h}, ${s}%, ${lValue}%)`;
+ public updateStyle(style: newPointStyle): void {
+ this.size = style.size ?? this.size;
+ this.fillStyle = style.fillStyle ?? this.fillStyle;
+ this.strokeStyle = style.strokeStyle ?? this.strokeStyle;
+ this.marker = style.marker ?? this.marker;
+ }
+
+ public copy(): newPoint2D {
+ const copy = new newPoint2D();
+ copy.center = this.center.copy();
+ copy.size = this.size;
+ copy.marker = this.marker;
+ copy.markerOrientation = this.markerOrientation;
+ copy.fillStyle = this.fillStyle;
+ copy.strokeStyle = this.strokeStyle;
+ copy.lineWidth = this.lineWidth;
+ return copy
}
- public setColors(fillStyle: string = null, strokeStyle: string = null) {
- this.fillStyle = fillStyle || this.fillStyle;
- this.strokeStyle = strokeStyle; // ? strokeStyle : this.setStrokeStyle(this.fillStyle);
+ public update() { this.buildPath() }
+
+ public setColors(color: string) {
+ this.fillStyle = this.isFilled ? color : null;
+ this.strokeStyle = this.isFilled ? this.setStrokeStyle(this.fillStyle) : color;
}
get drawnShape(): newShape {
@@ -1884,6 +1954,7 @@ export class newPoint2D extends newShape {
if (TRIANGLES.includes(this.marker)) marker = new Triangle(this.center.coordinates, this.size, this.markerOrientation);
if (this.marker == 'halfLine') marker = new HalfLine(this.center.coordinates, this.size, this.markerOrientation);
marker.lineWidth = this.lineWidth;
+ this.isFilled = marker.isFilled;
return marker
}
@@ -1912,12 +1983,11 @@ export class newPoint2D extends newShape {
public buildPath(): void { this.path = this.drawnShape.path };
- public setDrawingProperties(context: CanvasRenderingContext2D) {
- context.lineWidth = this.lineWidth;
- context.globalAlpha = this.alpha;
- const fillColor = this.isHovered ? this.hoverStyle : this.isClicked ? this.clickedStyle : this.isSelected ? this.selectedStyle : this.fillStyle;
- context.fillStyle = fillColor;
- context.strokeStyle = this.strokeStyle ?? this.setStrokeStyle(fillColor);
+ public isInFrame(origin: Vertex, end: Vertex, scale: Vertex): boolean {
+ const inCanvasX = this.center.x * scale.x < end.x && this.center.x * scale.x > origin.x;
+ const inCanvasY = this.center.y * scale.y < end.y && this.center.y * scale.y > origin.y;
+ this.inFrame = inCanvasX && inCanvasY;
+ return this.inFrame
}
}
@@ -1935,9 +2005,7 @@ export class ScatterPoint extends newPoint2D {
) {
super(x, y, _size, _marker, _markerOrientation, fillStyle, strokeStyle);
this.isScaled = false;
- };
-
- get tooltipMap(): Map { return this._tooltipMap };
+ }
public static fromPlottedValues(indices: number[], pointsData: {[key: string]: number[]}, pointSize: number, marker: string,
thresholdDist: number, tooltipAttributes: string[], features: Map, axes: newAxis[],
@@ -1951,22 +2019,22 @@ export class ScatterPoint extends newPoint2D {
public updateTooltipMap() { this._tooltipMap = new Map([["Number", this.values.length], ["X mean", this.mean.x], ["Y mean", this.mean.y],]) };
- public update() {
- this.isScaled = false;
- this.buildPath();
- }
-
public updateTooltip(tooltipAttributes: string[], features: Map, axes: newAxis[], xName: string, yName: string) {
this.updateTooltipMap();
if (this.values.length == 1) {
this.newTooltipMap();
tooltipAttributes.forEach(attr => this.tooltipMap.set(attr, features.get(attr)[this.values[0]]));
- } else {
- this.tooltipMap.set(`Average ${xName}`, axes[0].isDiscrete ? axes[0].labels[Math.round(this.mean.x)] : this.mean.x);
- this.tooltipMap.set(`Average ${yName}`, axes[1].isDiscrete ? axes[1].labels[Math.round(this.mean.y)] : this.mean.y);
- this.tooltipMap.delete('X mean');
- this.tooltipMap.delete('Y mean');
+ return;
}
+ this.tooltipMap.set(`Average ${xName}`, axes[0].isDiscrete ? axes[0].labels[Math.round(this.mean.x)] : this.mean.x);
+ this.tooltipMap.set(`Average ${yName}`, axes[1].isDiscrete ? axes[1].labels[Math.round(this.mean.y)] : this.mean.y);
+ this.tooltipMap.delete('X mean');
+ this.tooltipMap.delete('Y mean');
+ }
+
+ public updateStyle(style: newPointStyle): void {
+ super.updateStyle(style);
+ this.marker = this.values.length > 1 ? this.marker : style.marker ?? this.marker;
}
public computeValues(pointsData: {[key: string]: number[]}, thresholdDist: number): void {
@@ -1986,12 +2054,63 @@ export class ScatterPoint extends newPoint2D {
this.mean.x = meanX / this.values.length;
this.mean.y = meanY / this.values.length;
}
+}
- public isInFrame(xAxis: newAxis, yAxis: newAxis): boolean {
- const inCanvasX = this.mean.x < xAxis.maxValue && this.mean.x > xAxis.minValue;
- const inCanvasY = this.mean.y < yAxis.maxValue && this.mean.y > yAxis.minValue;
- this.inFrame = inCanvasX && inCanvasY;
- return this.inFrame
+export class LineSequence extends newShape {
+ public previousTooltipOrigin: Vertex;
+ constructor(
+ public points: newPoint2D[] = [],
+ public name: string = ""
+ ) {
+ super();
+ this.isScaled = false;
+ this.isFilled = false;
+ this.updateTooltipMap();
+ }
+
+ public initTooltip(context: CanvasRenderingContext2D): newTooltip {
+ const tooltip = super.initTooltip(context);
+ tooltip.isFlipper = true;
+ return tooltip
+ }
+
+ public setTooltipOrigin(vertex: Vertex): void {
+ this.previousTooltipOrigin = vertex.copy();
+ this.tooltipOrigin = this.previousTooltipOrigin.copy();
+ }
+
+ public translateTooltip(translation: Vertex): void { this.tooltipOrigin?.translateSelf(translation) }
+
+ public mouseDown(mouseDown: Vertex) { this.setTooltipOrigin(mouseDown) }
+
+ public updateTooltipMap() { this._tooltipMap = new Map([["Name", this.name]]) }
+
+ private getEdgeStyle(edgeStyle: {[key: string]: any}): void {
+ if (edgeStyle.line_width) this.lineWidth = edgeStyle.line_width;
+ if (edgeStyle.color_stroke) this.strokeStyle = edgeStyle.color_stroke;
+ if (edgeStyle.dashline) this.dashLine = edgeStyle.dashline;
+ }
+
+ public static getGraphProperties(graph: {[key: string]: any}): LineSequence {
+ const emptyLineSequence = new LineSequence([], graph.name);
+ if (graph.edge_style) emptyLineSequence.getEdgeStyle(graph.edge_style);
+ return emptyLineSequence
+ }
+
+ public setDrawingProperties(context: CanvasRenderingContext2D) {
+ super.setDrawingProperties(context);
+ context.lineWidth = (this.isHovered || this.isClicked) ? this.lineWidth * 2 : this.lineWidth;
+ }
+
+ public buildPath(): void {
+ this.path = new Path2D();
+ this.path.moveTo(this.points[0].center.x, this.points[0].center.y);
+ this.points.slice(1).forEach(point=> this.path.lineTo(point.center.x, point.center.y));
+ }
+
+ public update(points: newPoint2D[]): void {
+ this.points = points;
+ this.buildPath();
}
}
@@ -2332,6 +2451,7 @@ export class newAxis extends EventEmitter {
readonly SELECTION_RECT_SIZE = 10;
readonly SIZE_END = 7;
readonly FONT_SIZE = 12;
+ readonly isFilled = true;
// OLD
public is_drawing_rubberband: boolean = false;
@@ -2411,7 +2531,6 @@ export class newAxis extends EventEmitter {
}
public resetScale(): void {
- this.rubberBand.reset();
this.minValue = this.initMinValue;
this.maxValue = this.initMaxValue;
this._previousMin = this.initMinValue;
@@ -2535,7 +2654,7 @@ export class newAxis extends EventEmitter {
var [nameCoords, align, baseline, orientation] = this.topArrowTitleProperties();
}
nameCoords.transformSelf(canvasHTMatrix);
- const textParams: textParams = {
+ const textParams: TextParams = {
width: this.drawLength, fontsize: this.FONT_SIZE, font: this.font, align: align, color: color,
baseline: baseline, style: 'bold', orientation: orientation, backgroundColor: "hsla(0, 0%, 100%, 0.5)"
};
@@ -2601,7 +2720,7 @@ export class newAxis extends EventEmitter {
tickText.draw(context);
}
- private computeTickTextParams(): textParams {
+ private computeTickTextParams(): TextParams {
const [textAlign, baseline] = this.textAlignments();
let textWidth = null;
let textHeight = null;
@@ -2627,7 +2746,7 @@ export class newAxis extends EventEmitter {
return point
}
- private computeTickText(context: CanvasRenderingContext2D, text: string, tickTextParams: textParams, point: newPoint2D, HTMatrix: DOMMatrix): newText {
+ private computeTickText(context: CanvasRenderingContext2D, text: string, tickTextParams: TextParams, point: newPoint2D, HTMatrix: DOMMatrix): newText {
const textOrigin = this.tickTextPositions(point, HTMatrix);
const tickText = new newText(newText.capitalize(text), textOrigin, tickTextParams);
tickText.removeEndZeros();
@@ -2765,21 +2884,27 @@ export class DrawingCollection {
public updateMouseState(context: CanvasRenderingContext2D, mouseCoords: Vertex, stateName: string, keepState: boolean, invertState: boolean) {
this.drawings.forEach(drawing => {
- if (context.isPointInPath(drawing.path, mouseCoords.x, mouseCoords.y)) drawing[stateName] = invertState ? !drawing[stateName] : true
- else {
- if (!keepState) drawing[stateName] = false;
+ if (drawing.isFilled) {
+ if (context.isPointInPath(drawing.path, mouseCoords.x, mouseCoords.y)) drawing[stateName] = invertState ? !drawing[stateName] : true
+ else {
+ if (!keepState) drawing[stateName] = false;
+ }
+ } else {
+ context.save();
+ context.lineWidth = 10;
+ if (context.isPointInStroke(drawing.path, mouseCoords.x, mouseCoords.y)) drawing[stateName] = invertState ? !drawing[stateName] : true
+ else {
+ if (!keepState) drawing[stateName] = false;
+ }
+ context.restore();
}
})
}
public mouseDown(mouseCoords: Vertex): any {
let clickedObject: any = null;
- this.drawings.forEach(drawing => {
- if (drawing.isHovered) {
- clickedObject = drawing;
- clickedObject.mouseDown(mouseCoords);
- }
- });
+ this.drawings.forEach(drawing => { if (drawing.isHovered) clickedObject = drawing });
+ clickedObject?.mouseDown(mouseCoords);
return clickedObject
}
}
@@ -2805,7 +2930,7 @@ export class GroupCollection extends ShapeCollection {
super(drawings, frame);
}
- public drawingIsContainer(drawing: any): boolean { return drawing.values?.length > 1 }
+ public drawingIsContainer(drawing: any): boolean { return drawing.values?.length > 1 || drawing instanceof LineSequence }
public drawTooltips(canvasOrigin: Vertex, canvasSize: Vertex, context: CanvasRenderingContext2D, inMultiPlot: boolean): void {
this.drawings.forEach(drawing => { if ((this.drawingIsContainer(drawing) || !inMultiPlot) && drawing.inFrame) drawing.drawTooltip(canvasOrigin, canvasSize, context) });