diff --git a/docs/BaseGL.BaseGL.html b/docs/BaseGL.BaseGL.html index 85eefb3..90384fd 100644 --- a/docs/BaseGL.BaseGL.html +++ b/docs/BaseGL.BaseGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -75,7 +75,7 @@

new BaseGLSource:
@@ -231,7 +231,7 @@
Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/BaseGL.html b/docs/BaseGL.html index e8f8c95..2ed966c 100644 --- a/docs/BaseGL.html +++ b/docs/BaseGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -73,7 +73,7 @@

new BaseGLSource:
@@ -191,7 +191,7 @@

(static) Dotplo
Source:
@@ -372,7 +372,7 @@

(static) Dotp
Source:
@@ -730,7 +730,7 @@

Source:
@@ -918,7 +918,7 @@

atta
Source:
@@ -1005,7 +1005,7 @@

calcBounds<
Source:
@@ -1114,7 +1114,7 @@

clearHi
Source:
@@ -1207,7 +1207,7 @@

Source:
@@ -1318,7 +1318,7 @@

clickCal
Source:
@@ -1476,7 +1476,7 @@

cre
Source:
@@ -1657,7 +1657,7 @@

disab
Source:
@@ -1751,7 +1751,7 @@

enable
Source:
@@ -1845,7 +1845,7 @@

generateS
Source:
@@ -1934,7 +1934,7 @@

Source:
@@ -2052,7 +2052,7 @@

highl
Source:
@@ -2217,7 +2217,7 @@

hoverCal
Source:
@@ -2376,7 +2376,7 @@

l
Source:
@@ -2539,7 +2539,7 @@

Source:
@@ -2702,7 +2702,7 @@

renderSource:
@@ -2883,7 +2883,7 @@

Source:
@@ -2971,7 +2971,7 @@

r
Source:
@@ -3171,7 +3171,7 @@

renderLeg
Source:
@@ -3259,7 +3259,7 @@

Source:
@@ -3347,7 +3347,7 @@

re
Source:
@@ -3434,7 +3434,7 @@

resizeSource:
@@ -3593,7 +3593,7 @@

sele
Source:
@@ -3751,7 +3751,7 @@

setInputSource:
@@ -3979,7 +3979,7 @@

Source:
@@ -4138,7 +4138,7 @@

setInte
Source:
@@ -4278,7 +4278,7 @@

setLab
Source:
@@ -4332,6 +4332,10 @@

setLab

Examples
this.labelOptions = {
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
 columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
@@ -4341,8 +4345,12 @@ 
Examples
}
this.setLabelOptions({
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
- columnLabelMaxCharacters: 10,
+columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
 columnLabelSlintAngle: 0,
 rowLabelFontSize: "7px",
@@ -4563,6 +4571,245 @@ 
Parameters:
+ + + + +

setMargins(margins,)

+ + + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Set the margins for the visualization. +all properties are optional, if not provided, the default values will be used.

+
+ + + + + + + + + +
Example
+ +
this.setMargins({
+top: '10px',
+bottom: '10px',
+left: '10px',
+right: '10px',
+})
+ + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
margins, + + +object + + + +

an object containing the margins

margins.top, + + +number + + + +

top margin

margins.bottom, + + +number + + + +

bottom margin

margins.left, + + +number + + + +

left margin

margins.right, + + +number + + + +

right margin

+ + + + + + + + + + + + + + + + + + + + @@ -4579,7 +4826,7 @@

setStateSource:
@@ -4914,7 +5161,7 @@

Source:
@@ -5001,7 +5248,7 @@

Source:
@@ -5140,7 +5387,7 @@

Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/BaseGL.js.html b/docs/BaseGL.js.html index 564024f..d531053 100644 --- a/docs/BaseGL.js.html +++ b/docs/BaseGL.js.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -69,6 +69,7 @@

BaseGL.js

INTENSITY_LEGEND_GRADIENT_SIZE_IN_PX, INTENSITY_LEGEND_SIZE_IN_PX, GROUPING_LEGEND_SIZE_IN_PX, + DEFAULT_MARGINS, } from "./constants"; /** @@ -121,8 +122,14 @@

BaseGL.js

ygap: 0.3, }; + this.margins = DEFAULT_MARGINS; + //Default Data for labelOptions this.labelOptions = { + rowLabelsSvgXOffset: -1.05, + rowLabelsSvgYOffset: -1.02, + columnLabelsSvgXOffset: -1.02, + columnLabelsSvgYOffset: 1.05, rowLabelMaxCharacters: DEFAULT_ROW_MAX_LABEL_LENGTH_ALLOWED, columnLabelMaxCharacters: DEFAULT_COLUMN_MAX_LABEL_LENGTH_ALLOWED, rowLabelSlintAngle: DEFAULT_ROW_LABEL_SLINT_ANGLE, @@ -229,6 +236,10 @@

BaseGL.js

_generateSpecForLabels(spec) { const { + rowLabelsSvgXOffset, + rowLabelsSvgYOffset, + columnLabelsSvgXOffset, + columnLabelsSvgYOffset, rowLabelMaxCharacters, columnLabelMaxCharacters, rowLabelSlintAngle, @@ -258,8 +269,8 @@

BaseGL.js

maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.02 + (2 * ilx + 1) / xlabels_len, - y: 1.05, + x: columnLabelsSvgXOffset + (2 * ilx + 1) / xlabels_len, + y: columnLabelsSvgYOffset, type: "row", index: ilx, text: truncatedLabel, @@ -292,8 +303,8 @@

BaseGL.js

); maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.05, - y: -1.02 + (2 * ily + 1) / ylabels_len, + x: rowLabelsSvgXOffset, + y: rowLabelsSvgYOffset + (2 * ily + 1) / ylabels_len, type: "column", index: ily, text: truncatedLabel, @@ -315,7 +326,7 @@

BaseGL.js

...spec["margins"], top: `${topMarginToAccountForLabels}px`, left: `${leftMarginToAccountForLabels}px`, - right: "20px", + right: `${GROUPING_LEGEND_SIZE_IN_PX}px`, }; } @@ -544,6 +555,10 @@

BaseGL.js

* @memberof BaseGL * @example * this.labelOptions = { + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, @@ -553,8 +568,12 @@

BaseGL.js

* } * @example * this.setLabelOptions({ + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, - * columnLabelMaxCharacters: 10, + * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, * columnLabelSlintAngle: 0, * rowLabelFontSize: "7px", @@ -568,6 +587,30 @@

BaseGL.js

}; } + /** + * Set the margins for the visualization. + * all properties are optional, if not provided, the default values will be used. + * @param {object} margins, an object containing the margins + * @param {number} margins.top, top margin + * @param {number} margins.bottom, bottom margin + * @param {number} margins.left, left margin + * @param {number} margins.right, right margin + * @memberof BaseGL + * @example + * this.setMargins({ + * top: '10px', + * bottom: '10px', + * left: '10px', + * right: '10px', + * }) + **/ + setMargins(margins) { + this.margins = { + ...this.margins, + ...margins, + }; + } + /** * resize the plot, without having to send the data to the GPU. * @@ -1069,7 +1112,19 @@

BaseGL.js

* @param {string} orientation - The orientation of the grouping labels * @returns {void} **/ - renderGroupingLabels(parentElement, groupingRowData, orientation) { + renderGroupingLabels(parentElement, groupingData, orientation) { + // Filter out duplicate labels in the grouping data + groupingData = groupingData.reduce( + (acc, obj) => { + if (!acc.seen[obj.label]) { + acc.seen[obj.label] = true; + acc.result.push(obj); + } + return acc; + }, + { seen: {}, result: [] } + ).result; + const parent = select(parentElement); const svg = parent.append("svg"); @@ -1077,14 +1132,14 @@

BaseGL.js

if (orientation === "horizontal") { svg.attr("height", 25); } else { - svg.attr("height", groupingRowData.length * 25); + svg.attr("height", groupingData.length * 25); } const labelHeight = 25; let xOffset = 0; let yOffset = 0; - groupingRowData.forEach((data) => { + groupingData.forEach((data) => { const group = svg.append("g"); group @@ -1353,7 +1408,7 @@

BaseGL.js


- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/DotplotGL.DotplotGL.html b/docs/DotplotGL.DotplotGL.html index ec26f85..b2a125e 100644 --- a/docs/DotplotGL.DotplotGL.html +++ b/docs/DotplotGL.DotplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -75,7 +75,7 @@

new DotplotG
Source:
@@ -231,7 +231,7 @@

Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/DotplotGL.html b/docs/DotplotGL.html index f7dc026..a0ef8b6 100644 --- a/docs/DotplotGL.html +++ b/docs/DotplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -73,7 +73,7 @@

new DotplotG
Source:
@@ -199,7 +199,7 @@

Source:
@@ -392,7 +392,7 @@

atta
Source:
@@ -484,7 +484,7 @@

calcBounds<
Source:
@@ -598,7 +598,7 @@

clearHi
Source:
@@ -696,7 +696,7 @@

Source:
@@ -812,7 +812,7 @@

clickCal
Source:
@@ -975,7 +975,7 @@

Source:
@@ -1332,7 +1332,7 @@

Source:
@@ -1559,7 +1559,7 @@

cre
Source:
@@ -1745,7 +1745,7 @@

disab
Source:
@@ -1844,7 +1844,7 @@

enable
Source:
@@ -1943,7 +1943,7 @@

generateS
Source:
@@ -2058,7 +2058,7 @@

Source:
@@ -2181,7 +2181,7 @@

highl
Source:
@@ -2351,7 +2351,7 @@

hoverCal
Source:
@@ -2515,7 +2515,7 @@

l
Source:
@@ -2683,7 +2683,7 @@

Source:
@@ -2851,7 +2851,7 @@

renderSource:
@@ -3037,7 +3037,7 @@

Source:
@@ -3130,7 +3130,7 @@

r
Source:
@@ -3335,7 +3335,7 @@

renderLeg
Source:
@@ -3428,7 +3428,7 @@

Source:
@@ -3521,7 +3521,7 @@

rende
Source:
@@ -3610,7 +3610,7 @@

re
Source:
@@ -3702,7 +3702,7 @@

resizeSource:
@@ -3866,7 +3866,7 @@

sele
Source:
@@ -4029,7 +4029,7 @@

setInputSource:
@@ -4262,7 +4262,7 @@

Source:
@@ -4426,7 +4426,7 @@

setInte
Source:
@@ -4571,7 +4571,7 @@

setLab
Source:
@@ -4630,6 +4630,10 @@

setLab

Examples
this.labelOptions = {
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
 columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
@@ -4639,8 +4643,12 @@ 
Examples
}
this.setLabelOptions({
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
- columnLabelMaxCharacters: 10,
+columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
 columnLabelSlintAngle: 0,
 rowLabelFontSize: "7px",
@@ -4861,6 +4869,250 @@ 
Parameters:
+ + + + +

setMargins(margins,)

+ + + + + + +
+ + +
Source:
+
+ + + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Set the margins for the visualization. +all properties are optional, if not provided, the default values will be used.

+
+ + + + + + + + + +
Example
+ +
this.setMargins({
+top: '10px',
+bottom: '10px',
+left: '10px',
+right: '10px',
+})
+ + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
margins, + + +object + + + +

an object containing the margins

margins.top, + + +number + + + +

top margin

margins.bottom, + + +number + + + +

bottom margin

margins.left, + + +number + + + +

left margin

margins.right, + + +number + + + +

right margin

+ + + + + + + + + + + + + + + + + + + + @@ -4877,7 +5129,7 @@

s
Source:
@@ -5057,7 +5309,7 @@

setStateSource:
@@ -5397,7 +5649,7 @@

Source:
@@ -5489,7 +5741,7 @@

Source:
@@ -5578,7 +5830,7 @@

Source:
@@ -5722,7 +5974,7 @@

Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/DotplotGL.js.html b/docs/DotplotGL.js.html index c2dfc38..0d0ef4b 100644 --- a/docs/DotplotGL.js.html +++ b/docs/DotplotGL.js.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -59,6 +59,7 @@

DotplotGL.js

getMinMax, getScaledRadiusForDotplot, parseMargins, + mapArrayOrTypedArray, } from "./utils"; /** @@ -144,16 +145,17 @@

DotplotGL.js

const [, maxY] = getMinMax(this.input.y); let xlen = maxX + 1, ylen = maxY + 1; - spec_inputs.x = this.input.x.map((e, i) => -1 + (2 * e + 1) / xlen); - spec_inputs.y = this.input.y.map((e, i) => -1 + (2 * e + 1) / ylen); + spec_inputs.x = mapArrayOrTypedArray( + this.input.x, + (e, i) => -1 + (2 * e + 1) / xlen + ); + spec_inputs.y = mapArrayOrTypedArray( + this.input.y, + (e, i) => -1 + (2 * e + 1) / ylen + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -613,7 +615,7 @@

DotplotGL.js


- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/RectplotGL.RectplotGL.html b/docs/RectplotGL.RectplotGL.html index 67770cd..70b26fe 100644 --- a/docs/RectplotGL.RectplotGL.html +++ b/docs/RectplotGL.RectplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -231,7 +231,7 @@
Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/RectplotGL.html b/docs/RectplotGL.html index 5c9b7ac..96119b8 100644 --- a/docs/RectplotGL.html +++ b/docs/RectplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -199,7 +199,7 @@

Source:
@@ -392,7 +392,7 @@

atta
Source:
@@ -484,7 +484,7 @@

calcBounds<
Source:
@@ -598,7 +598,7 @@

clearHi
Source:
@@ -696,7 +696,7 @@

Source:
@@ -812,7 +812,7 @@

clickCal
Source:
@@ -975,7 +975,7 @@

cre
Source:
@@ -1161,7 +1161,7 @@

disab
Source:
@@ -1260,7 +1260,7 @@

enable
Source:
@@ -1474,7 +1474,7 @@

Source:
@@ -1597,7 +1597,7 @@

highl
Source:
@@ -1767,7 +1767,7 @@

hoverCal
Source:
@@ -1931,7 +1931,7 @@

l
Source:
@@ -2099,7 +2099,7 @@

Source:
@@ -2267,7 +2267,7 @@

renderSource:
@@ -2453,7 +2453,7 @@

Source:
@@ -2546,7 +2546,7 @@

r
Source:
@@ -2751,7 +2751,7 @@

renderLeg
Source:
@@ -2844,7 +2844,7 @@

Source:
@@ -2937,7 +2937,7 @@

re
Source:
@@ -3029,7 +3029,7 @@

resizeSource:
@@ -3193,7 +3193,7 @@

sele
Source:
@@ -3356,7 +3356,7 @@

setInputSource:
@@ -3589,7 +3589,7 @@

Source:
@@ -3753,7 +3753,7 @@

setInte
Source:
@@ -3898,7 +3898,7 @@

setLab
Source:
@@ -3957,6 +3957,10 @@

setLab

Examples
this.labelOptions = {
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
 columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
@@ -3966,8 +3970,12 @@ 
Examples
}
this.setLabelOptions({
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
- columnLabelMaxCharacters: 10,
+columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
 columnLabelSlintAngle: 0,
 rowLabelFontSize: "7px",
@@ -4188,6 +4196,250 @@ 
Parameters:
+ + + + +

setMargins(margins,)

+ + + + + + +
+ + +
Source:
+
+ + + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Set the margins for the visualization. +all properties are optional, if not provided, the default values will be used.

+
+ + + + + + + + + +
Example
+ +
this.setMargins({
+top: '10px',
+bottom: '10px',
+left: '10px',
+right: '10px',
+})
+ + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
margins, + + +object + + + +

an object containing the margins

margins.top, + + +number + + + +

top margin

margins.bottom, + + +number + + + +

bottom margin

margins.left, + + +number + + + +

left margin

margins.right, + + +number + + + +

right margin

+ + + + + + + + + + + + + + + + + + + + @@ -4204,7 +4456,7 @@

setStateSource:
@@ -4544,7 +4796,7 @@

Source:
@@ -4636,7 +4888,7 @@

Source:
@@ -4780,7 +5032,7 @@

Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/RectplotGL.js.html b/docs/RectplotGL.js.html index 50106ba..0b823f8 100644 --- a/docs/RectplotGL.js.html +++ b/docs/RectplotGL.js.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -45,7 +45,7 @@

RectplotGL.js

import BaseGL from "./BaseGL";
-import { getMinMax } from "./utils";
+import { getMinMax, mapArrayOrTypedArray } from "./utils";
 
 /**
  * Class to create traditional heatmap plots
@@ -99,22 +99,23 @@ 

RectplotGL.js

}; let spec_inputs = {}; - spec_inputs.x = this.input.x.map((e, i) => String(e)); - spec_inputs.y = this.input.y.map((e, i) => String(e)); + spec_inputs.x = mapArrayOrTypedArray(this.input.x, (e, i) => String(e)); + spec_inputs.y = mapArrayOrTypedArray(this.input.y, (e, i) => String(e)); let default_width = 198 / (getMinMax(this.input.x)[1] + 1); let default_height = 198 / (getMinMax(this.input.y)[1] + 1); - spec_inputs.width = this.input.x.map((e, i) => default_width - xGaps(i)); - spec_inputs.height = this.input.y.map((e, i) => default_height - yGaps(i)); + spec_inputs.width = mapArrayOrTypedArray( + this.input.x, + (e, i) => default_width - xGaps(i) + ); + spec_inputs.height = mapArrayOrTypedArray( + this.input.y, + (e, i) => default_height - yGaps(i) + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -169,7 +170,7 @@

RectplotGL.js


- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/TickplotGL.TickplotGL.html b/docs/TickplotGL.TickplotGL.html index edddaec..0c59010 100644 --- a/docs/TickplotGL.TickplotGL.html +++ b/docs/TickplotGL.TickplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -231,7 +231,7 @@
Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/TickplotGL.html b/docs/TickplotGL.html index 1957196..8c63c19 100644 --- a/docs/TickplotGL.html +++ b/docs/TickplotGL.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -200,7 +200,7 @@

Source:
@@ -393,7 +393,7 @@

atta
Source:
@@ -485,7 +485,7 @@

calcBounds<
Source:
@@ -599,7 +599,7 @@

clearHi
Source:
@@ -697,7 +697,7 @@

Source:
@@ -813,7 +813,7 @@

clickCal
Source:
@@ -976,7 +976,7 @@

cre
Source:
@@ -1162,7 +1162,7 @@

disab
Source:
@@ -1261,7 +1261,7 @@

enable
Source:
@@ -1475,7 +1475,7 @@

Source:
@@ -1598,7 +1598,7 @@

highl
Source:
@@ -1768,7 +1768,7 @@

hoverCal
Source:
@@ -1932,7 +1932,7 @@

l
Source:
@@ -2100,7 +2100,7 @@

Source:
@@ -2268,7 +2268,7 @@

renderSource:
@@ -2454,7 +2454,7 @@

Source:
@@ -2547,7 +2547,7 @@

r
Source:
@@ -2752,7 +2752,7 @@

renderLeg
Source:
@@ -2845,7 +2845,7 @@

Source:
@@ -2938,7 +2938,7 @@

re
Source:
@@ -3030,7 +3030,7 @@

resizeSource:
@@ -3194,7 +3194,7 @@

sele
Source:
@@ -3357,7 +3357,7 @@

setInputSource:
@@ -3590,7 +3590,7 @@

Source:
@@ -3754,7 +3754,7 @@

setInte
Source:
@@ -3899,7 +3899,7 @@

setLab
Source:
@@ -3958,6 +3958,10 @@

setLab

Examples
this.labelOptions = {
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
 columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
@@ -3967,8 +3971,12 @@ 
Examples
}
this.setLabelOptions({
+rowLabelsSvgXOffset: 0,
+rowLabelsSvgYOffset: 0,
+columnLabelsSvgXOffset: 0,
+columnLabelsSvgYOffset: 0,
 rowLabelMaxCharacters: 10,
- columnLabelMaxCharacters: 10,
+columnLabelMaxCharacters: 10,
 rowLabelSlintAngle: 0,
 columnLabelSlintAngle: 0,
 rowLabelFontSize: "7px",
@@ -4189,6 +4197,250 @@ 
Parameters:
+ + + + +

setMargins(margins,)

+ + + + + + +
+ + +
Source:
+
+ + + + + + + +
Inherited From:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Set the margins for the visualization. +all properties are optional, if not provided, the default values will be used.

+
+ + + + + + + + + +
Example
+ +
this.setMargins({
+top: '10px',
+bottom: '10px',
+left: '10px',
+right: '10px',
+})
+ + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
margins, + + +object + + + +

an object containing the margins

margins.top, + + +number + + + +

top margin

margins.bottom, + + +number + + + +

bottom margin

margins.left, + + +number + + + +

left margin

margins.right, + + +number + + + +

right margin

+ + + + + + + + + + + + + + + + + + + + @@ -4205,7 +4457,7 @@

setStateSource:
@@ -4545,7 +4797,7 @@

Source:
@@ -4637,7 +4889,7 @@

Source:
@@ -4781,7 +5033,7 @@

Parameters:

- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/TickplotGL.js.html b/docs/TickplotGL.js.html index 86690d4..7664a63 100644 --- a/docs/TickplotGL.js.html +++ b/docs/TickplotGL.js.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -123,12 +123,7 @@

TickplotGL.js

} let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: this.input.x, y: this.input.y, @@ -182,7 +177,7 @@

TickplotGL.js


- Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
diff --git a/docs/global.html b/docs/global.html new file mode 100644 index 0000000..1163a45 --- /dev/null +++ b/docs/global.html @@ -0,0 +1,700 @@ + + + + + + Global - Documentation + + + + + + + + + + + + + + + + + + + +
+ +

Global

+ + + + + + + +
+ +
+ +

+ +

+ + +
+ +
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + +

Members

+ + + +

(constant) createTooltip

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Create a tooltip on a specified container at the given position.

+
+ + + + + + + + + + +

(constant) getMinMax

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Get the minimum and maximum values from an array.

+
+ + + + + + + + + + +

(constant) getTextWidth

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Measure the width of a text string for a given font size using SVG.

+
+ + + + + + + + + + +

(constant) mapArrayOrTypedArray

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

A function to map over both regular JavaScript arrays and typed arrays.

+
+ + + + + + + + + + +

(constant) parseMargins

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Parses an object of margins and returns an object with top, bottom, left, and right margins as integers.

+
+ + + + + + + + + + +

(constant) removeTooltip

+ + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Remove a tooltip from the specified container.

+
+ + + + + + + + + + + + +

Methods

+ + + + + + +

isObject(object) → {boolean}

+ + + + + + +
+ + +
Source:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+

Check if a given variable is an object and not an array.

+
+ + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
object + + +any + + + +

The variable to check.

+ + + + + + + + + + + + + + + + +
Returns:
+ + +
+
    +
  • Returns true if the variable is an object, and not an array.
  • +
+
+ + + +
+
+ Type +
+
+ +boolean + + +
+
+ + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ +
+ Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme. +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 0da3f04..bff6b21 100644 --- a/docs/index.html +++ b/docs/index.html @@ -29,7 +29,7 @@ -

Home

Github repo

Demo

Classes

+

Home

Github repo

Demo

Classes

Global

@@ -151,10 +151,14 @@

Customizing Labels with setLabelOptions

  • labelOptions.columnLabelSlintAngle (number, optional): slant angle for column labels (default: 0)
  • labelOptions.rowLabelFontSize (string | number, optional): font size for row labels (default: 7px)
  • labelOptions.columnLabelFontSize (string | number, optional): font size for column labels (default: 7px)
  • +
  • labelOptions.rowLabelsSvgXOffset (number, optional): x offset for row labels (default: -1.05)
  • +
  • labelOptions.rowLabelsSvgYOffset (number, optional): y offset for row labels (default: -1.02)
  • +
  • labelOptions.columnLabelsSvgXOffset (number, optional): x offset for column labels (default: -1.02)
  • +
  • labelOptions.columnLabelsSvgYOffset (number, optional): y offset for column labels (default: 1.05) +Example:
  • -

    Example:

    plot.setLabelOptions({
       rowLabelMaxCharacters: 10,
       columnLabelMaxCharacters: 10,
    @@ -164,6 +168,27 @@ 

    Customizing Labels with setLabelOptions

    columnLabelFontSize: "10px", });
    +

    Customizing Margins with setMargins

    +

    This method allows you to customize the margins for your visualization. All parameters are optional, providing you the flexibility to specify the options that best suit your needs.

    +

    Parameters:

    +
      +
    • margins (object): an object containing the margin options +
        +
      • margins.top (number, optional): top margin (default: 25px)
      • +
      • margins.bottom (number, optional): bottom margin (default: 50px)
      • +
      • margins.left (number, optional): left margin (default: 50px)
      • +
      • margins.right (number, optional): right margin (default: 10px)
      • +
      +
    • +
    +

    Example:

    +
    plot.setMargins({
    +  top: 10,
    +  bottom: 10,
    +  left: 10,
    +  right: 10,
    +});
    +

    Grouping Bars and Labels for Rows and Columns

    This feature allows users to add grouping bars and labels to both rows and columns of the heatmap. It provides a convenient way to visually represent different groups in your heatmap data.

    Usage
    @@ -391,7 +416,7 @@

    Encodings


    - Documentation generated by JSDoc 3.6.11 on Tue Oct 24 2023 16:24:42 GMT+0000 (Coordinated Universal Time) using the docdash theme. + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme.
    diff --git a/docs/utils.js.html b/docs/utils.js.html new file mode 100644 index 0000000..fc03caa --- /dev/null +++ b/docs/utils.js.html @@ -0,0 +1,257 @@ + + + + + + utils.js - Documentation + + + + + + + + + + + + + + + + + + + +
    + +

    utils.js

    + + + + + + + +
    +
    +
    import { select } from "d3-selection";
    +import {
    +  DEFAULT_MIN_RADIUS_FOR_DOTPLOT,
    +  TOOLTIP_IDENTIFIER,
    +} from "./constants";
    +
    +/**
    + * Check if a given variable is an object and not an array.
    + *
    + * @param {any} object - The variable to check.
    + * @returns {boolean} - Returns true if the variable is an object, and not an array.
    + */
    +export function isObject(object) {
    +  return typeof object === "object" && Array.isArray(object) === false;
    +}
    +
    +/**
    + * Get the minimum and maximum values from an array.
    + *
    + * @param {Array<number>} arr - An array of numbers.
    + * @returns {Array<number>} - An array containing the minimum and maximum values, in that order.
    + */
    +export const getMinMax = (arr) => {
    +  var max = -Number.MAX_VALUE,
    +    min = Number.MAX_VALUE;
    +  arr.forEach(function (x) {
    +    if (max < x) max = x;
    +    if (min > x) min = x;
    +  });
    +  return [min, max];
    +};
    +
    +/**
    + * Parses an object of margins and returns an object with top, bottom, left, and right margins as integers.
    + *
    + * @param {Object} margins - An object with potential margin properties.
    + * @returns {Object} - An object with top, bottom, left, and right margins as integers.
    + */
    +export const parseMargins = (margins) => {
    +  const parsedMargins = {
    +    top: 0,
    +    bottom: 0,
    +    left: 0,
    +    right: 0,
    +  };
    +
    +  for (let key in margins) {
    +    if (margins.hasOwnProperty(key)) {
    +      const value = margins[key];
    +      const parsedValue = parseInt(value, 10);
    +
    +      if (!isNaN(parsedValue)) {
    +        parsedMargins[key] = parsedValue;
    +      }
    +    }
    +  }
    +
    +  return parsedMargins;
    +};
    +
    +/**
    + * Measure the width of a text string for a given font size using SVG.
    + *
    + * @param {string} text - The text to measure.
    + * @param {string} fontSize - The font size to use for the measurement, e.g., '16px'.
    + * @returns {number} - The width of the text in pixels.
    + */
    +export const getTextWidth = (text, fontSize = "16px") => {
    +  // Create a temporary SVG to measure the text width
    +  const svg = select("body").append("svg");
    +  const textNode = svg.append("text").style("font-size", fontSize).text(text);
    +  const width = textNode.node().getBBox().width;
    +  svg.remove();
    +  return width;
    +};
    +
    +/**
    + * Create a tooltip on a specified container at the given position.
    + *
    + * @param {HTMLElement} container - The container element.
    + * @param {string} text - The text for the tooltip.
    + * @param {number} posX - The x-coordinate for the tooltip.
    + * @param {number} posY - The y-coordinate for the tooltip.
    + */
    +export const createTooltip = (container, text, posX, posY) => {
    +  let tooltip = select(container)
    +    .append("div")
    +    .attr("id", TOOLTIP_IDENTIFIER)
    +    .style("position", "absolute")
    +    .style("background", "#f9f9f9")
    +    .style("padding", "8px")
    +    .style("border", "1px solid #ccc")
    +    .style("border-radius", "6px")
    +    .style("z-index", "1000")
    +    .style("visibility", "hidden");
    +
    +  tooltip
    +    .style("visibility", "visible")
    +    .text(text)
    +    .style("left", posX + 10 + "px")
    +    .style("top", posY - 10 + "px");
    +};
    +
    +/**
    + * Remove a tooltip from the specified container.
    + *
    + * @param {HTMLElement} container - The container from which to remove the tooltip.
    + */
    +export const removeTooltip = (container) => {
    +  const tooltip = select(container).select(`#${TOOLTIP_IDENTIFIER}`);
    +
    +  if (tooltip) {
    +    tooltip.remove();
    +  }
    +};
    +
    +export const getMaxRadiusForDotplot = (xlen, ylen, padding) => {
    +  return Math.max(
    +    Math.min(198 / (xlen + 1), 198 / (ylen + 1)) - padding,
    +    DEFAULT_MIN_RADIUS_FOR_DOTPLOT
    +  );
    +};
    +
    +export const getScaledRadiusForDotplot = (
    +  radius,
    +  maxRadiusScaled,
    +  minRadiusOriginal,
    +  maxRadiusOriginal,
    +  defaultMinRadius = DEFAULT_MIN_RADIUS_FOR_DOTPLOT
    +) => {
    +  return (
    +    defaultMinRadius +
    +    (maxRadiusScaled - defaultMinRadius) *
    +      ((radius - minRadiusOriginal) / (maxRadiusOriginal - minRadiusOriginal))
    +  );
    +};
    +
    +/**
    + * A function to map over both regular JavaScript arrays and typed arrays.
    + *
    + * @param {Array|TypedArray} array - The input array or typed array.
    + * @param {Function} callback - A function that produces an element of the new array,
    + *      taking three arguments:
    + *      currentValue - The current element being processed in the array.
    + *      index - The index of the current element being processed in the array.
    + *      array - The array map was called upon.
    + * @returns {Array|TypedArray} - A new array or typed array with each element being the result
    + *      of the callback function.
    + * @throws {Error} - Throws an error if the input is neither a regular array nor a typed array.
    + */
    +export const mapArrayOrTypedArray = (array, callback) => {
    +  // Check if the input is a regular JavaScript array.
    +  if (Array.isArray(array)) {
    +    return array.map(callback);
    +  }
    +  // Check if the input is a typed array.
    +  else if (
    +    array instanceof Int8Array ||
    +    array instanceof Uint8Array ||
    +    array instanceof Uint8ClampedArray ||
    +    array instanceof Int16Array ||
    +    array instanceof Uint16Array ||
    +    array instanceof Int32Array ||
    +    array instanceof Uint32Array ||
    +    array instanceof Float32Array ||
    +    array instanceof Float64Array
    +  ) {
    +    // Create a new typed array of the same type and size as the input.
    +    let result = new array.constructor(array.length);
    +
    +    // Use forEach to emulate the map functionality for typed arrays.
    +    array.forEach((value, index) => {
    +      result[index] = callback(value, index);
    +    });
    +
    +    return result;
    +  }
    +  // Handle the case where the input is neither a regular array nor a typed array.
    +  else {
    +    throw new Error("Input is neither a normal array nor a typed array.");
    +  }
    +};
    +
    +
    +
    + + + + + + +
    + +
    + +
    + Documentation generated by JSDoc 3.6.11 on Wed Nov 15 2023 21:33:51 GMT+0000 (Coordinated Universal Time) using the docdash theme. +
    + + + + + + + + + + + + + diff --git a/ehgl.js b/ehgl.js index a39f5e6..00f12c8 100644 --- a/ehgl.js +++ b/ehgl.js @@ -33,24 +33,45 @@ const DEFAULT_SIZE_LEGEND_CIRCLE_TEXT_GAP = 10; const DEFAULT_MIN_RADIUS_FOR_DOTPLOT = 3; const DEFAULT_MARGIN_BETWEEN_DOTS = 2; +const DEFAULT_MARGINS = { + top: "25px", + bottom: "50px", + left: "50px", + right: "10px", +}; + +/** + * Check if a given variable is an object and not an array. + * + * @param {any} object - The variable to check. + * @returns {boolean} - Returns true if the variable is an object, and not an array. + */ function isObject(object) { return typeof object === "object" && Array.isArray(object) === false; } +/** + * Get the minimum and maximum values from an array. + * + * @param {Array} arr - An array of numbers. + * @returns {Array} - An array containing the minimum and maximum values, in that order. + */ const getMinMax = (arr) => { var max = -Number.MAX_VALUE, min = Number.MAX_VALUE; arr.forEach(function (x) { - if (max < x) { - max = x; - } - if (min > x) { - min = x; - } + if (max < x) max = x; + if (min > x) min = x; }); return [min, max]; }; +/** + * Parses an object of margins and returns an object with top, bottom, left, and right margins as integers. + * + * @param {Object} margins - An object with potential margin properties. + * @returns {Object} - An object with top, bottom, left, and right margins as integers. + */ const parseMargins = (margins) => { const parsedMargins = { top: 0, @@ -73,6 +94,13 @@ const parseMargins = (margins) => { return parsedMargins; }; +/** + * Measure the width of a text string for a given font size using SVG. + * + * @param {string} text - The text to measure. + * @param {string} fontSize - The font size to use for the measurement, e.g., '16px'. + * @returns {number} - The width of the text in pixels. + */ const getTextWidth = (text, fontSize = "16px") => { // Create a temporary SVG to measure the text width const svg = d3Selection.select("body").append("svg"); @@ -82,6 +110,14 @@ const getTextWidth = (text, fontSize = "16px") => { return width; }; +/** + * Create a tooltip on a specified container at the given position. + * + * @param {HTMLElement} container - The container element. + * @param {string} text - The text for the tooltip. + * @param {number} posX - The x-coordinate for the tooltip. + * @param {number} posY - The y-coordinate for the tooltip. + */ const createTooltip = (container, text, posX, posY) => { let tooltip = d3Selection.select(container) .append("div") @@ -101,6 +137,11 @@ const createTooltip = (container, text, posX, posY) => { .style("top", posY - 10 + "px"); }; +/** + * Remove a tooltip from the specified container. + * + * @param {HTMLElement} container - The container from which to remove the tooltip. + */ const removeTooltip = (container) => { const tooltip = d3Selection.select(container).select(`#${TOOLTIP_IDENTIFIER}`); @@ -130,6 +171,52 @@ const getScaledRadiusForDotplot = ( ); }; +/** + * A function to map over both regular JavaScript arrays and typed arrays. + * + * @param {Array|TypedArray} array - The input array or typed array. + * @param {Function} callback - A function that produces an element of the new array, + * taking three arguments: + * currentValue - The current element being processed in the array. + * index - The index of the current element being processed in the array. + * array - The array map was called upon. + * @returns {Array|TypedArray} - A new array or typed array with each element being the result + * of the callback function. + * @throws {Error} - Throws an error if the input is neither a regular array nor a typed array. + */ +const mapArrayOrTypedArray = (array, callback) => { + // Check if the input is a regular JavaScript array. + if (Array.isArray(array)) { + return array.map(callback); + } + // Check if the input is a typed array. + else if ( + array instanceof Int8Array || + array instanceof Uint8Array || + array instanceof Uint8ClampedArray || + array instanceof Int16Array || + array instanceof Uint16Array || + array instanceof Int32Array || + array instanceof Uint32Array || + array instanceof Float32Array || + array instanceof Float64Array + ) { + // Create a new typed array of the same type and size as the input. + let result = new array.constructor(array.length); + + // Use forEach to emulate the map functionality for typed arrays. + array.forEach((value, index) => { + result[index] = callback(value, index); + }); + + return result; + } + // Handle the case where the input is neither a regular array nor a typed array. + else { + throw new Error("Input is neither a normal array nor a typed array."); + } +}; + /** * Base class for all matrix like layout plots. * This class is not to be used directly. @@ -180,8 +267,14 @@ class BaseGL { ygap: 0.3, }; + this.margins = DEFAULT_MARGINS; + //Default Data for labelOptions this.labelOptions = { + rowLabelsSvgXOffset: -1.05, + rowLabelsSvgYOffset: -1.02, + columnLabelsSvgXOffset: -1.02, + columnLabelsSvgYOffset: 1.05, rowLabelMaxCharacters: DEFAULT_ROW_MAX_LABEL_LENGTH_ALLOWED, columnLabelMaxCharacters: DEFAULT_COLUMN_MAX_LABEL_LENGTH_ALLOWED, rowLabelSlintAngle: DEFAULT_ROW_LABEL_SLINT_ANGLE, @@ -288,6 +381,10 @@ class BaseGL { _generateSpecForLabels(spec) { const { + rowLabelsSvgXOffset, + rowLabelsSvgYOffset, + columnLabelsSvgXOffset, + columnLabelsSvgYOffset, rowLabelMaxCharacters, columnLabelMaxCharacters, rowLabelSlintAngle, @@ -317,8 +414,8 @@ class BaseGL { maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.02 + (2 * ilx + 1) / xlabels_len, - y: 1.05, + x: columnLabelsSvgXOffset + (2 * ilx + 1) / xlabels_len, + y: columnLabelsSvgYOffset, type: "row", index: ilx, text: truncatedLabel, @@ -351,8 +448,8 @@ class BaseGL { ); maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.05, - y: -1.02 + (2 * ily + 1) / ylabels_len, + x: rowLabelsSvgXOffset, + y: rowLabelsSvgYOffset + (2 * ily + 1) / ylabels_len, type: "column", index: ily, text: truncatedLabel, @@ -374,7 +471,7 @@ class BaseGL { ...spec["margins"], top: `${topMarginToAccountForLabels}px`, left: `${leftMarginToAccountForLabels}px`, - right: "20px", + right: `${GROUPING_LEGEND_SIZE_IN_PX}px`, }; } @@ -603,6 +700,10 @@ class BaseGL { * @memberof BaseGL * @example * this.labelOptions = { + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, @@ -612,8 +713,12 @@ class BaseGL { * } * @example * this.setLabelOptions({ + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, - * columnLabelMaxCharacters: 10, + * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, * columnLabelSlintAngle: 0, * rowLabelFontSize: "7px", @@ -627,6 +732,30 @@ class BaseGL { }; } + /** + * Set the margins for the visualization. + * all properties are optional, if not provided, the default values will be used. + * @param {object} margins, an object containing the margins + * @param {number} margins.top, top margin + * @param {number} margins.bottom, bottom margin + * @param {number} margins.left, left margin + * @param {number} margins.right, right margin + * @memberof BaseGL + * @example + * this.setMargins({ + * top: '10px', + * bottom: '10px', + * left: '10px', + * right: '10px', + * }) + **/ + setMargins(margins) { + this.margins = { + ...this.margins, + ...margins, + }; + } + /** * resize the plot, without having to send the data to the GPU. * @@ -1128,7 +1257,19 @@ class BaseGL { * @param {string} orientation - The orientation of the grouping labels * @returns {void} **/ - renderGroupingLabels(parentElement, groupingRowData, orientation) { + renderGroupingLabels(parentElement, groupingData, orientation) { + // Filter out duplicate labels in the grouping data + groupingData = groupingData.reduce( + (acc, obj) => { + if (!acc.seen[obj.label]) { + acc.seen[obj.label] = true; + acc.result.push(obj); + } + return acc; + }, + { seen: {}, result: [] } + ).result; + const parent = d3Selection.select(parentElement); const svg = parent.append("svg"); @@ -1136,14 +1277,14 @@ class BaseGL { if (orientation === "horizontal") { svg.attr("height", 25); } else { - svg.attr("height", groupingRowData.length * 25); + svg.attr("height", groupingData.length * 25); } const labelHeight = 25; let xOffset = 0; let yOffset = 0; - groupingRowData.forEach((data) => { + groupingData.forEach((data) => { const group = svg.append("g"); group @@ -1463,16 +1604,17 @@ class DotplotGL extends BaseGL { const [, maxY] = getMinMax(this.input.y); let xlen = maxX + 1, ylen = maxY + 1; - spec_inputs.x = this.input.x.map((e, i) => -1 + (2 * e + 1) / xlen); - spec_inputs.y = this.input.y.map((e, i) => -1 + (2 * e + 1) / ylen); + spec_inputs.x = mapArrayOrTypedArray( + this.input.x, + (e, i) => -1 + (2 * e + 1) / xlen + ); + spec_inputs.y = mapArrayOrTypedArray( + this.input.y, + (e, i) => -1 + (2 * e + 1) / ylen + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -1969,22 +2111,23 @@ class RectplotGL extends BaseGL { }; let spec_inputs = {}; - spec_inputs.x = this.input.x.map((e, i) => String(e)); - spec_inputs.y = this.input.y.map((e, i) => String(e)); + spec_inputs.x = mapArrayOrTypedArray(this.input.x, (e, i) => String(e)); + spec_inputs.y = mapArrayOrTypedArray(this.input.y, (e, i) => String(e)); let default_width = 198 / (getMinMax(this.input.x)[1] + 1); let default_height = 198 / (getMinMax(this.input.y)[1] + 1); - spec_inputs.width = this.input.x.map((e, i) => default_width - xGaps(i)); - spec_inputs.height = this.input.y.map((e, i) => default_height - yGaps(i)); + spec_inputs.width = mapArrayOrTypedArray( + this.input.x, + (e, i) => default_width - xGaps(i) + ); + spec_inputs.height = mapArrayOrTypedArray( + this.input.y, + (e, i) => default_height - yGaps(i) + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -2100,12 +2243,7 @@ class TickplotGL extends BaseGL { } let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: this.input.x, y: this.input.y, diff --git a/index.js b/index.js index a14b9d6..385bf91 100644 --- a/index.js +++ b/index.js @@ -10001,24 +10001,45 @@ const DEFAULT_SIZE_LEGEND_CIRCLE_TEXT_GAP = 10; const DEFAULT_MIN_RADIUS_FOR_DOTPLOT = 3; const DEFAULT_MARGIN_BETWEEN_DOTS = 2; +const DEFAULT_MARGINS = { + top: "25px", + bottom: "50px", + left: "50px", + right: "10px", +}; + +/** + * Check if a given variable is an object and not an array. + * + * @param {any} object - The variable to check. + * @returns {boolean} - Returns true if the variable is an object, and not an array. + */ function isObject(object) { return typeof object === "object" && Array.isArray(object) === false; } +/** + * Get the minimum and maximum values from an array. + * + * @param {Array} arr - An array of numbers. + * @returns {Array} - An array containing the minimum and maximum values, in that order. + */ const getMinMax = (arr) => { var max = -Number.MAX_VALUE, min = Number.MAX_VALUE; arr.forEach(function (x) { - if (max < x) { - max = x; - } - if (min > x) { - min = x; - } + if (max < x) max = x; + if (min > x) min = x; }); return [min, max]; }; +/** + * Parses an object of margins and returns an object with top, bottom, left, and right margins as integers. + * + * @param {Object} margins - An object with potential margin properties. + * @returns {Object} - An object with top, bottom, left, and right margins as integers. + */ const parseMargins = (margins) => { const parsedMargins = { top: 0, @@ -10041,6 +10062,13 @@ const parseMargins = (margins) => { return parsedMargins; }; +/** + * Measure the width of a text string for a given font size using SVG. + * + * @param {string} text - The text to measure. + * @param {string} fontSize - The font size to use for the measurement, e.g., '16px'. + * @returns {number} - The width of the text in pixels. + */ const getTextWidth = (text, fontSize = "16px") => { // Create a temporary SVG to measure the text width const svg = select("body").append("svg"); @@ -10050,6 +10078,14 @@ const getTextWidth = (text, fontSize = "16px") => { return width; }; +/** + * Create a tooltip on a specified container at the given position. + * + * @param {HTMLElement} container - The container element. + * @param {string} text - The text for the tooltip. + * @param {number} posX - The x-coordinate for the tooltip. + * @param {number} posY - The y-coordinate for the tooltip. + */ const createTooltip = (container, text, posX, posY) => { let tooltip = select(container) .append("div") @@ -10069,6 +10105,11 @@ const createTooltip = (container, text, posX, posY) => { .style("top", posY - 10 + "px"); }; +/** + * Remove a tooltip from the specified container. + * + * @param {HTMLElement} container - The container from which to remove the tooltip. + */ const removeTooltip = (container) => { const tooltip = select(container).select(`#${TOOLTIP_IDENTIFIER}`); @@ -10098,6 +10139,52 @@ const getScaledRadiusForDotplot = ( ); }; +/** + * A function to map over both regular JavaScript arrays and typed arrays. + * + * @param {Array|TypedArray} array - The input array or typed array. + * @param {Function} callback - A function that produces an element of the new array, + * taking three arguments: + * currentValue - The current element being processed in the array. + * index - The index of the current element being processed in the array. + * array - The array map was called upon. + * @returns {Array|TypedArray} - A new array or typed array with each element being the result + * of the callback function. + * @throws {Error} - Throws an error if the input is neither a regular array nor a typed array. + */ +const mapArrayOrTypedArray = (array, callback) => { + // Check if the input is a regular JavaScript array. + if (Array.isArray(array)) { + return array.map(callback); + } + // Check if the input is a typed array. + else if ( + array instanceof Int8Array || + array instanceof Uint8Array || + array instanceof Uint8ClampedArray || + array instanceof Int16Array || + array instanceof Uint16Array || + array instanceof Int32Array || + array instanceof Uint32Array || + array instanceof Float32Array || + array instanceof Float64Array + ) { + // Create a new typed array of the same type and size as the input. + let result = new array.constructor(array.length); + + // Use forEach to emulate the map functionality for typed arrays. + array.forEach((value, index) => { + result[index] = callback(value, index); + }); + + return result; + } + // Handle the case where the input is neither a regular array nor a typed array. + else { + throw new Error("Input is neither a normal array nor a typed array."); + } +}; + /** * Base class for all matrix like layout plots. * This class is not to be used directly. @@ -10148,8 +10235,14 @@ class BaseGL { ygap: 0.3, }; + this.margins = DEFAULT_MARGINS; + //Default Data for labelOptions this.labelOptions = { + rowLabelsSvgXOffset: -1.05, + rowLabelsSvgYOffset: -1.02, + columnLabelsSvgXOffset: -1.02, + columnLabelsSvgYOffset: 1.05, rowLabelMaxCharacters: DEFAULT_ROW_MAX_LABEL_LENGTH_ALLOWED, columnLabelMaxCharacters: DEFAULT_COLUMN_MAX_LABEL_LENGTH_ALLOWED, rowLabelSlintAngle: DEFAULT_ROW_LABEL_SLINT_ANGLE, @@ -10256,6 +10349,10 @@ class BaseGL { _generateSpecForLabels(spec) { const { + rowLabelsSvgXOffset, + rowLabelsSvgYOffset, + columnLabelsSvgXOffset, + columnLabelsSvgYOffset, rowLabelMaxCharacters, columnLabelMaxCharacters, rowLabelSlintAngle, @@ -10285,8 +10382,8 @@ class BaseGL { maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.02 + (2 * ilx + 1) / xlabels_len, - y: 1.05, + x: columnLabelsSvgXOffset + (2 * ilx + 1) / xlabels_len, + y: columnLabelsSvgYOffset, type: "row", index: ilx, text: truncatedLabel, @@ -10319,8 +10416,8 @@ class BaseGL { ); maxWidth = Math.max(maxWidth, truncatedLabelWidth); labels.push({ - x: -1.05, - y: -1.02 + (2 * ily + 1) / ylabels_len, + x: rowLabelsSvgXOffset, + y: rowLabelsSvgYOffset + (2 * ily + 1) / ylabels_len, type: "column", index: ily, text: truncatedLabel, @@ -10342,7 +10439,7 @@ class BaseGL { ...spec["margins"], top: `${topMarginToAccountForLabels}px`, left: `${leftMarginToAccountForLabels}px`, - right: "20px", + right: `${GROUPING_LEGEND_SIZE_IN_PX}px`, }; } @@ -10571,6 +10668,10 @@ class BaseGL { * @memberof BaseGL * @example * this.labelOptions = { + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, @@ -10580,8 +10681,12 @@ class BaseGL { * } * @example * this.setLabelOptions({ + * rowLabelsSvgXOffset: 0, + * rowLabelsSvgYOffset: 0, + * columnLabelsSvgXOffset: 0, + * columnLabelsSvgYOffset: 0, * rowLabelMaxCharacters: 10, - * columnLabelMaxCharacters: 10, + * columnLabelMaxCharacters: 10, * rowLabelSlintAngle: 0, * columnLabelSlintAngle: 0, * rowLabelFontSize: "7px", @@ -10595,6 +10700,30 @@ class BaseGL { }; } + /** + * Set the margins for the visualization. + * all properties are optional, if not provided, the default values will be used. + * @param {object} margins, an object containing the margins + * @param {number} margins.top, top margin + * @param {number} margins.bottom, bottom margin + * @param {number} margins.left, left margin + * @param {number} margins.right, right margin + * @memberof BaseGL + * @example + * this.setMargins({ + * top: '10px', + * bottom: '10px', + * left: '10px', + * right: '10px', + * }) + **/ + setMargins(margins) { + this.margins = { + ...this.margins, + ...margins, + }; + } + /** * resize the plot, without having to send the data to the GPU. * @@ -11096,7 +11225,19 @@ class BaseGL { * @param {string} orientation - The orientation of the grouping labels * @returns {void} **/ - renderGroupingLabels(parentElement, groupingRowData, orientation) { + renderGroupingLabels(parentElement, groupingData, orientation) { + // Filter out duplicate labels in the grouping data + groupingData = groupingData.reduce( + (acc, obj) => { + if (!acc.seen[obj.label]) { + acc.seen[obj.label] = true; + acc.result.push(obj); + } + return acc; + }, + { seen: {}, result: [] } + ).result; + const parent = select(parentElement); const svg = parent.append("svg"); @@ -11104,14 +11245,14 @@ class BaseGL { if (orientation === "horizontal") { svg.attr("height", 25); } else { - svg.attr("height", groupingRowData.length * 25); + svg.attr("height", groupingData.length * 25); } const labelHeight = 25; let xOffset = 0; let yOffset = 0; - groupingRowData.forEach((data) => { + groupingData.forEach((data) => { const group = svg.append("g"); group @@ -11431,16 +11572,17 @@ class DotplotGL extends BaseGL { const [, maxY] = getMinMax(this.input.y); let xlen = maxX + 1, ylen = maxY + 1; - spec_inputs.x = this.input.x.map((e, i) => -1 + (2 * e + 1) / xlen); - spec_inputs.y = this.input.y.map((e, i) => -1 + (2 * e + 1) / ylen); + spec_inputs.x = mapArrayOrTypedArray( + this.input.x, + (e, i) => -1 + (2 * e + 1) / xlen + ); + spec_inputs.y = mapArrayOrTypedArray( + this.input.y, + (e, i) => -1 + (2 * e + 1) / ylen + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -11937,22 +12079,23 @@ class RectplotGL extends BaseGL { }; let spec_inputs = {}; - spec_inputs.x = this.input.x.map((e, i) => String(e)); - spec_inputs.y = this.input.y.map((e, i) => String(e)); + spec_inputs.x = mapArrayOrTypedArray(this.input.x, (e, i) => String(e)); + spec_inputs.y = mapArrayOrTypedArray(this.input.y, (e, i) => String(e)); let default_width = 198 / (getMinMax(this.input.x)[1] + 1); let default_height = 198 / (getMinMax(this.input.y)[1] + 1); - spec_inputs.width = this.input.x.map((e, i) => default_width - xGaps(i)); - spec_inputs.height = this.input.y.map((e, i) => default_height - yGaps(i)); + spec_inputs.width = mapArrayOrTypedArray( + this.input.x, + (e, i) => default_width - xGaps(i) + ); + spec_inputs.height = mapArrayOrTypedArray( + this.input.y, + (e, i) => default_height - yGaps(i) + ); let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: spec_inputs.x, y: spec_inputs.y, @@ -12068,12 +12211,7 @@ class TickplotGL extends BaseGL { } let spec = { - margins: { - top: "25px", - bottom: "50px", - left: "50px", - right: "10px", - }, + margins: this.margins, defaultData: { x: this.input.x, y: this.input.y,