plotboilerplate 1.24.0 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/dist/index.esm.js +433 -162
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/dist/plotboilerplate.browser.min.js +1 -1
- package/dist/plotboilerplate.browser.min.js.map +1 -1
- package/dist/plotboilerplate.js +12312 -12041
- package/dist/plotboilerplate.min.js +1 -1
- package/package.json +9 -5
- package/src/cjs/Bounds.d.ts +79 -25
- package/src/cjs/Bounds.js +102 -29
- package/src/cjs/Bounds.js.map +1 -1
- package/src/cjs/CircleSector.d.ts +3 -0
- package/src/cjs/CircleSector.js +28 -1
- package/src/cjs/CircleSector.js.map +1 -1
- package/src/cjs/PlotBoilerplate.d.ts +37 -6
- package/src/cjs/PlotBoilerplate.js +222 -116
- package/src/cjs/PlotBoilerplate.js.map +1 -1
- package/src/cjs/Polygon.d.ts +26 -7
- package/src/cjs/Polygon.js +56 -10
- package/src/cjs/Polygon.js.map +1 -1
- package/src/cjs/Triangle.js +2 -2
- package/src/cjs/Triangle.js.map +1 -1
- package/src/cjs/Vertex.d.ts +15 -3
- package/src/cjs/Vertex.js +18 -1
- package/src/cjs/Vertex.js.map +1 -1
- package/src/cjs/drawutilssvg.d.ts +2 -1
- package/src/cjs/drawutilssvg.js +9 -3
- package/src/cjs/drawutilssvg.js.map +1 -1
- package/src/cjs/interfaces/core.d.ts +28 -0
- package/src/cjs/interfaces/core.js +2 -0
- package/src/cjs/interfaces/core.js.map +1 -1
- package/src/cjs/utils/LissajousFigure.d.ts +34 -0
- package/src/cjs/utils/LissajousFigure.js +100 -0
- package/src/cjs/utils/LissajousFigure.js.map +1 -0
- package/src/cjs/utils/PerlinNoise.d.ts +19 -0
- package/src/cjs/utils/PerlinNoise.js +352 -0
- package/src/cjs/utils/PerlinNoise.js.map +1 -0
- package/src/cjs/utils/SVGPathUtils.d.ts +22 -0
- package/src/cjs/utils/SVGPathUtils.js +45 -0
- package/src/cjs/utils/SVGPathUtils.js.map +1 -0
- package/src/cjs/utils/algorithms/pixelCornersToRoundPaths.d.ts +23 -0
- package/src/cjs/utils/algorithms/pixelCornersToRoundPaths.js +270 -0
- package/src/cjs/utils/algorithms/pixelCornersToRoundPaths.js.map +1 -0
- package/src/cjs/utils/datastructures/Color.d.ts +147 -1
- package/src/cjs/utils/datastructures/Color.js +385 -41
- package/src/cjs/utils/datastructures/Color.js.map +1 -1
- package/src/cjs/utils/datastructures/ColorGradient.d.ts +79 -0
- package/src/cjs/utils/datastructures/ColorGradient.js +171 -0
- package/src/cjs/utils/datastructures/ColorGradient.js.map +1 -0
- package/src/cjs/utils/datastructures/DataGrid2d.d.ts +6 -0
- package/src/cjs/utils/datastructures/DataGrid2dArrayMatrix.d.ts +88 -0
- package/src/cjs/utils/datastructures/DataGrid2dArrayMatrix.js +155 -0
- package/src/cjs/utils/datastructures/DataGrid2dArrayMatrix.js.map +1 -0
- package/src/cjs/utils/datastructures/DataGrid2dListAdapter.d.ts +1 -7
- package/src/cjs/utils/datastructures/DataGrid2dListAdapter.js +3 -3
- package/src/cjs/utils/datastructures/DataGrid2dListAdapter.js.map +1 -1
- package/src/cjs/utils/datastructures/GeometryMesh.d.ts +49 -0
- package/src/cjs/utils/datastructures/GeometryMesh.js +58 -0
- package/src/cjs/utils/datastructures/GeometryMesh.js.map +1 -0
- package/src/cjs/utils/detectDarkMode.js +16 -7
- package/src/cjs/utils/detectDarkMode.js.map +1 -1
- package/src/cjs/utils/dom/components/ColorGradientPicker.d.ts +246 -0
- package/src/cjs/utils/dom/components/ColorGradientPicker.js +667 -0
- package/src/cjs/utils/dom/components/ColorGradientPicker.js.map +1 -0
- package/src/cjs/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
- package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js +520 -0
- package/src/cjs/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
- package/src/cjs/utils/dom/components/ColorGradientSelector.d.ts +118 -0
- package/src/cjs/utils/dom/components/ColorGradientSelector.js +327 -0
- package/src/cjs/utils/dom/components/ColorGradientSelector.js.map +1 -0
- package/src/cjs/utils/dom/components/Test.d.ts +7 -0
- package/src/cjs/utils/dom/components/Test.js +30 -0
- package/src/cjs/utils/dom/components/Test.js.map +1 -0
- package/src/cjs/utils/dom/guiSizeToggler.js +1 -1
- package/src/cjs/utils/dom/guiSizeToggler.js.map +1 -1
- package/src/cjs/utils/helpers/BezierPathInteractionHelper.d.ts +9 -3
- package/src/cjs/utils/helpers/BezierPathInteractionHelper.js +20 -13
- package/src/cjs/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
- package/src/cjs/utils/helpers/CircleHelper.d.ts +10 -1
- package/src/cjs/utils/helpers/CircleHelper.js +13 -2
- package/src/cjs/utils/helpers/CircleHelper.js.map +1 -1
- package/src/cjs/utils/helpers/CircleSectorHelper.d.ts +2 -2
- package/src/cjs/utils/helpers/CircleSectorHelper.js +1 -0
- package/src/cjs/utils/helpers/CircleSectorHelper.js.map +1 -1
- package/src/cjs/utils/helpers/TriangleHelper.d.ts +57 -0
- package/src/cjs/utils/helpers/TriangleHelper.js +83 -0
- package/src/cjs/utils/helpers/TriangleHelper.js.map +1 -0
- package/src/cjs/utils/helpers/VEllipseHelper.d.ts +6 -4
- package/src/cjs/utils/helpers/VEllipseHelper.js +4 -2
- package/src/cjs/utils/helpers/VEllipseHelper.js.map +1 -1
- package/src/cjs/utils/helpers/VEllipseSectorHelper.d.ts +3 -2
- package/src/cjs/utils/helpers/VEllipseSectorHelper.js +1 -0
- package/src/cjs/utils/helpers/VEllipseSectorHelper.js.map +1 -1
- package/src/cjs/utils/parsers/LinearColorGradientParser.d.ts +78 -0
- package/src/cjs/utils/parsers/LinearColorGradientParser.js +224 -0
- package/src/cjs/utils/parsers/LinearColorGradientParser.js.map +1 -0
- package/src/cjs/utils/parsers/svg/types.d.ts +8 -8
- package/src/createDemoDrawables.js +1 -0
- package/src/esm/Bounds.d.ts +79 -25
- package/src/esm/Bounds.js +102 -29
- package/src/esm/Bounds.js.map +1 -1
- package/src/esm/CircleSector.d.ts +3 -0
- package/src/esm/CircleSector.js +28 -1
- package/src/esm/CircleSector.js.map +1 -1
- package/src/esm/PlotBoilerplate.d.ts +37 -6
- package/src/esm/PlotBoilerplate.js +222 -116
- package/src/esm/PlotBoilerplate.js.map +1 -1
- package/src/esm/Polygon.d.ts +26 -7
- package/src/esm/Polygon.js +53 -10
- package/src/esm/Polygon.js.map +1 -1
- package/src/esm/Triangle.js +2 -2
- package/src/esm/Triangle.js.map +1 -1
- package/src/esm/Vertex.d.ts +15 -3
- package/src/esm/Vertex.js +18 -1
- package/src/esm/Vertex.js.map +1 -1
- package/src/esm/drawutilssvg.d.ts +2 -1
- package/src/esm/drawutilssvg.js +9 -3
- package/src/esm/drawutilssvg.js.map +1 -1
- package/src/esm/interfaces/core.d.ts +28 -0
- package/src/esm/interfaces/core.js +2 -0
- package/src/esm/interfaces/core.js.map +1 -1
- package/src/esm/utils/LissajousFigure.d.ts +34 -0
- package/src/esm/utils/LissajousFigure.js +95 -0
- package/src/esm/utils/LissajousFigure.js.map +1 -0
- package/src/esm/utils/PerlinNoise.d.ts +19 -0
- package/src/esm/utils/PerlinNoise.js +346 -0
- package/src/esm/utils/PerlinNoise.js.map +1 -0
- package/src/esm/utils/SVGPathUtils.d.ts +22 -0
- package/src/esm/utils/SVGPathUtils.js +42 -0
- package/src/esm/utils/SVGPathUtils.js.map +1 -0
- package/src/esm/utils/algorithms/pixelCornersToRoundPaths.d.ts +23 -0
- package/src/esm/utils/algorithms/pixelCornersToRoundPaths.js +266 -0
- package/src/esm/utils/algorithms/pixelCornersToRoundPaths.js.map +1 -0
- package/src/esm/utils/datastructures/Color.d.ts +147 -1
- package/src/esm/utils/datastructures/Color.js +386 -42
- package/src/esm/utils/datastructures/Color.js.map +1 -1
- package/src/esm/utils/datastructures/ColorGradient.d.ts +79 -0
- package/src/esm/utils/datastructures/ColorGradient.js +166 -0
- package/src/esm/utils/datastructures/ColorGradient.js.map +1 -0
- package/src/esm/utils/datastructures/DataGrid2d.d.ts +6 -0
- package/src/esm/utils/datastructures/DataGrid2dArrayMatrix.d.ts +88 -0
- package/src/esm/utils/datastructures/DataGrid2dArrayMatrix.js +150 -0
- package/src/esm/utils/datastructures/DataGrid2dArrayMatrix.js.map +1 -0
- package/src/esm/utils/datastructures/DataGrid2dListAdapter.d.ts +1 -7
- package/src/esm/utils/datastructures/DataGrid2dListAdapter.js +3 -3
- package/src/esm/utils/datastructures/DataGrid2dListAdapter.js.map +1 -1
- package/src/esm/utils/datastructures/GeometryMesh.d.ts +49 -0
- package/src/esm/utils/datastructures/GeometryMesh.js +53 -0
- package/src/esm/utils/datastructures/GeometryMesh.js.map +1 -0
- package/src/esm/utils/detectDarkMode.js +16 -7
- package/src/esm/utils/detectDarkMode.js.map +1 -1
- package/src/esm/utils/dom/components/ColorGradientPicker.d.ts +246 -0
- package/src/esm/utils/dom/components/ColorGradientPicker.js +743 -0
- package/src/esm/utils/dom/components/ColorGradientPicker.js.map +1 -0
- package/src/esm/utils/dom/components/ColorGradientPickerJSX.d.ts +129 -0
- package/src/esm/utils/dom/components/ColorGradientPickerJSX.js +582 -0
- package/src/esm/utils/dom/components/ColorGradientPickerJSX.js.map +1 -0
- package/src/esm/utils/dom/components/ColorGradientSelector.d.ts +118 -0
- package/src/esm/utils/dom/components/ColorGradientSelector.js +339 -0
- package/src/esm/utils/dom/components/ColorGradientSelector.js.map +1 -0
- package/src/esm/utils/dom/components/Test.d.ts +7 -0
- package/src/esm/utils/dom/components/Test.js +26 -0
- package/src/esm/utils/dom/components/Test.js.map +1 -0
- package/src/esm/utils/dom/guiSizeToggler.js +1 -1
- package/src/esm/utils/dom/guiSizeToggler.js.map +1 -1
- package/src/esm/utils/helpers/BezierPathInteractionHelper.d.ts +9 -3
- package/src/esm/utils/helpers/BezierPathInteractionHelper.js +20 -13
- package/src/esm/utils/helpers/BezierPathInteractionHelper.js.map +1 -1
- package/src/esm/utils/helpers/CircleHelper.d.ts +10 -1
- package/src/esm/utils/helpers/CircleHelper.js +13 -2
- package/src/esm/utils/helpers/CircleHelper.js.map +1 -1
- package/src/esm/utils/helpers/CircleSectorHelper.d.ts +2 -2
- package/src/esm/utils/helpers/CircleSectorHelper.js +1 -0
- package/src/esm/utils/helpers/CircleSectorHelper.js.map +1 -1
- package/src/esm/utils/helpers/TriangleHelper.d.ts +57 -0
- package/src/esm/utils/helpers/TriangleHelper.js +78 -0
- package/src/esm/utils/helpers/TriangleHelper.js.map +1 -0
- package/src/esm/utils/helpers/VEllipseHelper.d.ts +6 -4
- package/src/esm/utils/helpers/VEllipseHelper.js +4 -2
- package/src/esm/utils/helpers/VEllipseHelper.js.map +1 -1
- package/src/esm/utils/helpers/VEllipseSectorHelper.d.ts +3 -2
- package/src/esm/utils/helpers/VEllipseSectorHelper.js +1 -0
- package/src/esm/utils/helpers/VEllipseSectorHelper.js.map +1 -1
- package/src/esm/utils/parsers/LinearColorGradientParser.d.ts +78 -0
- package/src/esm/utils/parsers/LinearColorGradientParser.js +217 -0
- package/src/esm/utils/parsers/LinearColorGradientParser.js.map +1 -0
- package/src/esm/utils/parsers/svg/types.d.ts +8 -8
- package/src/external.d.ts +9 -0
- package/src/ts/Bounds.ts +107 -22
- package/src/ts/CircleSector.ts +39 -6
- package/src/ts/PlotBoilerplate.ts +233 -105
- package/src/ts/Polygon.ts +68 -14
- package/src/ts/Triangle.ts +2 -2
- package/src/ts/Vertex.ts +21 -3
- package/src/ts/drawutilssvg.ts +8 -3
- package/src/ts/interfaces/core.ts +31 -0
- package/src/ts/utils/LissajousFigure.ts +110 -0
- package/src/ts/utils/PerlinNoise.ts +368 -0
- package/src/ts/utils/SVGPathUtils.ts +55 -0
- package/src/ts/utils/algorithms/pixelCornersToRoundPaths.ts +379 -0
- package/src/ts/utils/datastructures/Color.ts +423 -44
- package/src/ts/utils/datastructures/ColorGradient.ts +196 -0
- package/src/ts/utils/datastructures/DataGrid2d.ts +7 -0
- package/src/ts/utils/datastructures/DataGrid2dArrayMatrix.ts +182 -0
- package/src/ts/utils/datastructures/DataGrid2dListAdapter.ts +4 -11
- package/src/ts/utils/datastructures/GeometryMesh.ts +79 -0
- package/src/ts/utils/detectDarkMode.ts +17 -8
- package/src/ts/utils/dom/components/ColorGradientPicker.tsx +889 -0
- package/src/ts/utils/dom/components/ColorGradientSelector.tsx +452 -0
- package/src/ts/utils/dom/components/Test.tsx +44 -0
- package/src/ts/utils/dom/components/_ColorGradientPicker_raw.ts_ +392 -0
- package/src/ts/utils/dom/guiSizeToggler.ts +1 -1
- package/src/ts/utils/helpers/BezierPathInteractionHelper.ts +25 -15
- package/src/ts/utils/helpers/CircleHelper.ts +17 -3
- package/src/ts/utils/helpers/CircleSectorHelper.ts +4 -2
- package/src/ts/utils/helpers/TriangleHelper.ts +102 -0
- package/src/ts/utils/helpers/VEllipseHelper.ts +6 -4
- package/src/ts/utils/helpers/VEllipseSectorHelper.ts +3 -2
- package/src/ts/utils/parsers/LinearColorGradientParser.ts +297 -0
- package/src/ts/utils/parsers/svg/types.ts +8 -8
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A script to convert any boolean matrix to rounded paths.
|
|
3
|
+
*
|
|
4
|
+
* @author Ikaros Kappler
|
|
5
|
+
* @date 2025-10-17 (ported to Typescript from a demo-script)
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Bounds } from "../../Bounds";
|
|
10
|
+
import { SVGPathParams, XYCoords } from "../../interfaces";
|
|
11
|
+
import { RasterPosition } from "../datastructures/DataGrid2d";
|
|
12
|
+
import { DataGrid2dArrayMatrix } from "../datastructures/DataGrid2dArrayMatrix";
|
|
13
|
+
|
|
14
|
+
const LEFT_BORDER: number = 0;
|
|
15
|
+
const TOP_BORDER: number = 1;
|
|
16
|
+
const RIGHT_BORDER: number = 2;
|
|
17
|
+
const BOTTOM_BORDER: number = 3;
|
|
18
|
+
|
|
19
|
+
export type PixelCornerPathOptions = {
|
|
20
|
+
origin?: XYCoords;
|
|
21
|
+
squareSize?: number;
|
|
22
|
+
gapSize?: number;
|
|
23
|
+
curveFactor?: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Computes an array of paths from the given boolean matrix.
|
|
28
|
+
*
|
|
29
|
+
* @param {DataGrid2dArrayMatrix<boolean>} matrix
|
|
30
|
+
* @param {PixelCornerPathOptions} options
|
|
31
|
+
* @returns
|
|
32
|
+
*/
|
|
33
|
+
export const pixelCornersToRoundPaths = (
|
|
34
|
+
matrix: DataGrid2dArrayMatrix<boolean>,
|
|
35
|
+
options: PixelCornerPathOptions
|
|
36
|
+
): Array<SVGPathParams> => {
|
|
37
|
+
const paths: Array<SVGPathParams> = [];
|
|
38
|
+
const visitedLeftBorderMatrix = new DataGrid2dArrayMatrix(matrix.xSegmentCount, matrix.ySegmentCount, false);
|
|
39
|
+
|
|
40
|
+
const origin: XYCoords = options && options.origin ? options.origin : { x: 0, y: 0 };
|
|
41
|
+
const squareSize: number = options && typeof options.squareSize === "number" ? options.squareSize : 10;
|
|
42
|
+
const gapSize: number = options && typeof options.gapSize === "number" ? options.gapSize : 2;
|
|
43
|
+
const curveFactor: number = options && typeof options.curveFactor === "number" ? options.curveFactor : 0.666;
|
|
44
|
+
|
|
45
|
+
// Find an unvisited pixel with a free left neighbour
|
|
46
|
+
const condition = (value: boolean, x: number, y: number, _matrix: DataGrid2dArrayMatrix<boolean>) => {
|
|
47
|
+
return (
|
|
48
|
+
value === true &&
|
|
49
|
+
visitedLeftBorderMatrix.get(x, y) === false &&
|
|
50
|
+
// is left neighbour free?
|
|
51
|
+
(x - 1 < 0 || _matrix.get(x - 1, y) === false)
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Just to be sure the while loop terminates after a maximum number of iterations.
|
|
56
|
+
const safetyLimit: number = matrix.xSegmentCount * matrix.ySegmentCount + 1;
|
|
57
|
+
|
|
58
|
+
// The next unvisited pixel position.
|
|
59
|
+
var nonVisitedPosition: RasterPosition;
|
|
60
|
+
|
|
61
|
+
var i: number = 0;
|
|
62
|
+
while ((nonVisitedPosition = matrix.find(condition)) && i++ < safetyLimit) {
|
|
63
|
+
// console.log("nonVisitedPosition", nonVisitedPosition);
|
|
64
|
+
// Condition: if a point was found then its left neighbour is empty.
|
|
65
|
+
// So by this the current pixel's left bound is definitely a path start.
|
|
66
|
+
const squareBox: Bounds = getSquareBox(origin, squareSize, gapSize, nonVisitedPosition.xIndex, nonVisitedPosition.yIndex);
|
|
67
|
+
const west: XYCoords = squareBox.getWestPoint();
|
|
68
|
+
// A starting point was found: Initialize the next path.
|
|
69
|
+
var pathData: SVGPathParams = [
|
|
70
|
+
// Start point
|
|
71
|
+
"M",
|
|
72
|
+
west.x,
|
|
73
|
+
west.y
|
|
74
|
+
];
|
|
75
|
+
// Now from this point starting (left pixel corner) construct the next sequence of path commands.
|
|
76
|
+
__constructPath(
|
|
77
|
+
pathData,
|
|
78
|
+
matrix,
|
|
79
|
+
visitedLeftBorderMatrix,
|
|
80
|
+
nonVisitedPosition,
|
|
81
|
+
LEFT_BORDER,
|
|
82
|
+
origin,
|
|
83
|
+
squareSize,
|
|
84
|
+
gapSize,
|
|
85
|
+
curveFactor
|
|
86
|
+
);
|
|
87
|
+
paths.push(pathData);
|
|
88
|
+
// console.log("pathData", pathData);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return paths;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Construct a bounding box representing the visual square bounds for the given pixel.
|
|
96
|
+
*
|
|
97
|
+
* @param {XYCoords} origin - The origin to build the paths from (upper left corner of the graphics).
|
|
98
|
+
* @param {number} squareSize - The number of pixels to use to represent a pixel square.
|
|
99
|
+
* @param {number} gapSize - The number of pixels to use for gaps between squares.
|
|
100
|
+
* @param {number} x - The x position inside the data grid (matrix offset).
|
|
101
|
+
* @param {number} y - They position inside the data grid (matrix offset).
|
|
102
|
+
* @returns
|
|
103
|
+
*/
|
|
104
|
+
const getSquareBox = (origin: XYCoords, squareSize: number, gapSize: number, x: number, y: number): Bounds => {
|
|
105
|
+
return new Bounds(
|
|
106
|
+
{
|
|
107
|
+
x: origin.x + x * (squareSize + gapSize),
|
|
108
|
+
y: origin.y + y * (squareSize + gapSize)
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
x: origin.x + x * (squareSize + gapSize) + squareSize,
|
|
112
|
+
y: origin.y + y * (squareSize + gapSize) + squareSize
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Lerp between these two numbers using the passed lerp amount (usually a value in 0.0-1.0).
|
|
119
|
+
*
|
|
120
|
+
* @param {number} a
|
|
121
|
+
* @param {number} b
|
|
122
|
+
* @param {number} lerpFactor
|
|
123
|
+
* @returns {number}
|
|
124
|
+
*/
|
|
125
|
+
const lerp = (a: number, b: number, lerpFactor): number => {
|
|
126
|
+
return a + (b - a) * lerpFactor;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Construct a path command for a cubic Bézier curve.
|
|
131
|
+
*
|
|
132
|
+
* @param {SVGPathParams} pathData
|
|
133
|
+
* @param {XYCoords} start
|
|
134
|
+
* @param {XYCoords} controlA
|
|
135
|
+
* @param {XYCoords} controlB
|
|
136
|
+
* @param {XYCoords} end
|
|
137
|
+
* @param {number} curveFactor
|
|
138
|
+
*/
|
|
139
|
+
const addRoundCorner = (
|
|
140
|
+
pathData: SVGPathParams,
|
|
141
|
+
start: XYCoords,
|
|
142
|
+
controlA: XYCoords,
|
|
143
|
+
controlB: XYCoords,
|
|
144
|
+
end: XYCoords,
|
|
145
|
+
curveFactor: number
|
|
146
|
+
) => {
|
|
147
|
+
pathData.push(
|
|
148
|
+
"C",
|
|
149
|
+
lerp(start.x, controlA.x, curveFactor),
|
|
150
|
+
lerp(start.y, controlA.y, curveFactor),
|
|
151
|
+
lerp(end.x, controlB.x, curveFactor),
|
|
152
|
+
lerp(end.y, controlB.y, curveFactor),
|
|
153
|
+
end.x,
|
|
154
|
+
end.y
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Construct a new path command for a straight line.
|
|
160
|
+
*
|
|
161
|
+
* @param {SVGPathParams} pathData
|
|
162
|
+
* @param {XYCoords} target
|
|
163
|
+
*/
|
|
164
|
+
const addStraightLine = (pathData: SVGPathParams, target: XYCoords) => {
|
|
165
|
+
pathData.push("L", target.x, target.y);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A helper function for constructing the next path, beginning at the passed matrix position.
|
|
170
|
+
*
|
|
171
|
+
* Note the path will be created at the left border of the given 'pixel', constructe
|
|
172
|
+
* in clockwise order.
|
|
173
|
+
*
|
|
174
|
+
* @param {SVGPathParams} pathData
|
|
175
|
+
* @param {DataGrid2dArrayMatrix<boolean>} matrix - The data matrix to use.
|
|
176
|
+
* @param {DataGrid2dArrayMatrix<boolean>} visitedLeftBordersMatrix - A helper matrix to keep track of visited pixels.
|
|
177
|
+
* @param {RasterPosition} startingPosition
|
|
178
|
+
* @param {number} startingBorderDirection
|
|
179
|
+
* @param {XYCoords} origin
|
|
180
|
+
* @param {number} squareSize
|
|
181
|
+
* @param {number} gapSize
|
|
182
|
+
* @param {number} curveFactor
|
|
183
|
+
*/
|
|
184
|
+
const __constructPath = (
|
|
185
|
+
pathData: SVGPathParams,
|
|
186
|
+
matrix: DataGrid2dArrayMatrix<boolean>,
|
|
187
|
+
visitedLeftBordersMatrix: DataGrid2dArrayMatrix<boolean>,
|
|
188
|
+
startingPosition: RasterPosition,
|
|
189
|
+
startingBorderDirection: number,
|
|
190
|
+
origin: XYCoords,
|
|
191
|
+
squareSize: number,
|
|
192
|
+
gapSize: number,
|
|
193
|
+
curveFactor: number
|
|
194
|
+
) => {
|
|
195
|
+
// Pre: position's neighbour pixel at ehe given border direction is unset.
|
|
196
|
+
var isPathComplete: boolean = false;
|
|
197
|
+
const maxInterations = matrix.xSegmentCount * matrix.ySegmentCount * 4 + 1;
|
|
198
|
+
var i = 0;
|
|
199
|
+
var position = startingPosition;
|
|
200
|
+
var borderDirection = startingBorderDirection;
|
|
201
|
+
|
|
202
|
+
while (!isPathComplete && i++ < maxInterations) {
|
|
203
|
+
var x = position.xIndex;
|
|
204
|
+
var y = position.yIndex;
|
|
205
|
+
// console.log("x", x, "y", y, "borderDirection", borderDirection);
|
|
206
|
+
// Get the pixel's box bounds.
|
|
207
|
+
const squareBox: Bounds = getSquareBox(origin, squareSize, gapSize, x, y);
|
|
208
|
+
// Find next step when walking 'clockwise'
|
|
209
|
+
if (borderDirection === LEFT_BORDER) {
|
|
210
|
+
// Only track visits on left borders. By this we can detect non-visited holes :)
|
|
211
|
+
visitedLeftBordersMatrix.set(x, y, true);
|
|
212
|
+
// We want to expand the left border
|
|
213
|
+
const west: XYCoords = squareBox.getWestPoint();
|
|
214
|
+
// Pixel in the north?
|
|
215
|
+
if (y - 1 < 0 || matrix.get(x, y - 1) === false) {
|
|
216
|
+
// Northwise pixel is empty
|
|
217
|
+
// -> construct round corner to right
|
|
218
|
+
addRoundCorner(
|
|
219
|
+
pathData,
|
|
220
|
+
west,
|
|
221
|
+
squareBox.getNorthWestPoint(),
|
|
222
|
+
squareBox.getNorthWestPoint(),
|
|
223
|
+
squareBox.getNorthPoint(),
|
|
224
|
+
curveFactor
|
|
225
|
+
);
|
|
226
|
+
borderDirection = TOP_BORDER;
|
|
227
|
+
// Keep pixel position unchanged
|
|
228
|
+
} else if (x - 1 >= 0 && matrix.get(x - 1, y - 1) === true) {
|
|
229
|
+
// Top neighbour pixel is set AND top-left pixel is set too.
|
|
230
|
+
// -> construct rounded edge to top-left.
|
|
231
|
+
const squareBoxNW: Bounds = getSquareBox(origin, squareSize, gapSize, x - 1, y - 1);
|
|
232
|
+
addRoundCorner(
|
|
233
|
+
pathData,
|
|
234
|
+
west,
|
|
235
|
+
squareBox.getNorthWestPoint(),
|
|
236
|
+
squareBoxNW.getSouthEastPoint(),
|
|
237
|
+
squareBoxNW.getSouthPoint(),
|
|
238
|
+
curveFactor
|
|
239
|
+
);
|
|
240
|
+
borderDirection = BOTTOM_BORDER;
|
|
241
|
+
position = { xIndex: x - 1, yIndex: y - 1 };
|
|
242
|
+
} else {
|
|
243
|
+
// Straight to top
|
|
244
|
+
const squareBoxN: Bounds = getSquareBox(origin, squareSize, gapSize, x, y - 1);
|
|
245
|
+
addStraightLine(pathData, squareBoxN.getWestPoint());
|
|
246
|
+
position = { xIndex: x, yIndex: y - 1 };
|
|
247
|
+
// Keep borderDirection: LEFT_BORDER
|
|
248
|
+
// isPathComplete = true; // TODO
|
|
249
|
+
}
|
|
250
|
+
} else if (borderDirection === TOP_BORDER) {
|
|
251
|
+
// North pixel is definitely empty.
|
|
252
|
+
const north: XYCoords = squareBox.getNorthPoint();
|
|
253
|
+
if (x + 1 >= matrix.xSegmentCount || matrix.get(x + 1, y) === false) {
|
|
254
|
+
// Top border and right pixel is empty
|
|
255
|
+
// --> create rounded corner to right south
|
|
256
|
+
addRoundCorner(
|
|
257
|
+
pathData,
|
|
258
|
+
north,
|
|
259
|
+
squareBox.getNorthEastPoint(),
|
|
260
|
+
squareBox.getNorthEastPoint(),
|
|
261
|
+
squareBox.getEastPoint(),
|
|
262
|
+
curveFactor
|
|
263
|
+
);
|
|
264
|
+
borderDirection = RIGHT_BORDER;
|
|
265
|
+
// Keep pixel position unchanged
|
|
266
|
+
} else if (y - 1 < 0 || matrix.get(x + 1, y - 1) === false) {
|
|
267
|
+
// Top border and right pixel is set, and north-east pixel is clear.
|
|
268
|
+
// -> linear path to right
|
|
269
|
+
const squareBoxE: Bounds = getSquareBox(origin, squareSize, gapSize, x + 1, y);
|
|
270
|
+
addStraightLine(pathData, squareBoxE.getNorthPoint());
|
|
271
|
+
position = { xIndex: x + 1, yIndex: y };
|
|
272
|
+
// Keep borderDirection TOP
|
|
273
|
+
} else {
|
|
274
|
+
// Last case: right neighbour is set and north-east pixel is set
|
|
275
|
+
//-> construct a rounded corner to east-north.
|
|
276
|
+
const squareBoxNE: Bounds = getSquareBox(origin, squareSize, gapSize, x + 1, y - 1);
|
|
277
|
+
addRoundCorner(
|
|
278
|
+
pathData,
|
|
279
|
+
north,
|
|
280
|
+
squareBox.getNorthEastPoint(),
|
|
281
|
+
squareBoxNE.getSouthWestPoint(),
|
|
282
|
+
squareBoxNE.getWestPoint(),
|
|
283
|
+
curveFactor
|
|
284
|
+
);
|
|
285
|
+
borderDirection = LEFT_BORDER;
|
|
286
|
+
position = { xIndex: x + 1, yIndex: y - 1 };
|
|
287
|
+
// isPathComplete = true; // TODO!!!
|
|
288
|
+
}
|
|
289
|
+
} else if (borderDirection === RIGHT_BORDER) {
|
|
290
|
+
// East pixel is definitely empty.
|
|
291
|
+
var east = squareBox.getEastPoint();
|
|
292
|
+
if (y + 1 >= matrix.ySegmentCount || matrix.get(x, y + 1) === false) {
|
|
293
|
+
// South pixel is empty.
|
|
294
|
+
// -> construct rounded corner to south-east
|
|
295
|
+
addRoundCorner(
|
|
296
|
+
pathData,
|
|
297
|
+
east,
|
|
298
|
+
squareBox.getSouthEastPoint(),
|
|
299
|
+
squareBox.getSouthEastPoint(),
|
|
300
|
+
squareBox.getSouthPoint(),
|
|
301
|
+
curveFactor
|
|
302
|
+
);
|
|
303
|
+
borderDirection = BOTTOM_BORDER;
|
|
304
|
+
// Keep pixel position unchanged
|
|
305
|
+
} else if (x + 1 < matrix.xSegmentCount && y + 1 < matrix.ySegmentCount && matrix.get(x + 1, y + 1) === true) {
|
|
306
|
+
// South pixel is set AND right lower pixel is set, too.
|
|
307
|
+
// -> construct round corner to south-east.
|
|
308
|
+
const squareBoxSE: Bounds = getSquareBox(origin, squareSize, gapSize, x + 1, y + 1);
|
|
309
|
+
addRoundCorner(
|
|
310
|
+
pathData,
|
|
311
|
+
east,
|
|
312
|
+
squareBox.getSouthEastPoint(),
|
|
313
|
+
squareBoxSE.getNorthWestPoint(),
|
|
314
|
+
squareBoxSE.getNorthPoint(),
|
|
315
|
+
curveFactor
|
|
316
|
+
);
|
|
317
|
+
borderDirection = TOP_BORDER;
|
|
318
|
+
position = { xIndex: x + 1, yIndex: y + 1 };
|
|
319
|
+
} else {
|
|
320
|
+
// South pixel is set and south-east pixel is clear.
|
|
321
|
+
// -> construct linear connection to south
|
|
322
|
+
const squareBoxS: Bounds = getSquareBox(origin, squareSize, gapSize, x, y + 1);
|
|
323
|
+
addStraightLine(pathData, squareBoxS.getEastPoint());
|
|
324
|
+
position = { xIndex: x, yIndex: y + 1 };
|
|
325
|
+
// Keep borderDirection RIGHT
|
|
326
|
+
}
|
|
327
|
+
} else if (borderDirection === BOTTOM_BORDER) {
|
|
328
|
+
// Pre: south pixel is definitely empty
|
|
329
|
+
const south: XYCoords = squareBox.getSouthPoint();
|
|
330
|
+
if (x - 1 < 0 || matrix.get(x - 1, y) === false) {
|
|
331
|
+
// Left pixel is empty
|
|
332
|
+
// -> construct round corner to west-north
|
|
333
|
+
addRoundCorner(
|
|
334
|
+
pathData,
|
|
335
|
+
south,
|
|
336
|
+
squareBox.getSouthWestPoint(),
|
|
337
|
+
squareBox.getSouthWestPoint(),
|
|
338
|
+
squareBox.getWestPoint(),
|
|
339
|
+
curveFactor
|
|
340
|
+
);
|
|
341
|
+
borderDirection = LEFT_BORDER;
|
|
342
|
+
} else if (x - 1 >= 0 && y + 1 < matrix.ySegmentCount && matrix.get(x - 1, y + 1) === true) {
|
|
343
|
+
// Left pixel AND left lower pixel are set
|
|
344
|
+
// -> construct rounded edge to south-west
|
|
345
|
+
const squareBoxSW: Bounds = getSquareBox(origin, squareSize, gapSize, x - 1, y + 1);
|
|
346
|
+
addRoundCorner(
|
|
347
|
+
pathData,
|
|
348
|
+
south,
|
|
349
|
+
squareBox.getSouthWestPoint(),
|
|
350
|
+
squareBoxSW.getNorthEastPoint(),
|
|
351
|
+
squareBoxSW.getEastPoint(),
|
|
352
|
+
curveFactor
|
|
353
|
+
);
|
|
354
|
+
borderDirection = RIGHT_BORDER;
|
|
355
|
+
position = { xIndex: x - 1, yIndex: y + 1 };
|
|
356
|
+
} else {
|
|
357
|
+
// Left pixel is set AND south pixel is empty
|
|
358
|
+
// -> construct linear edge to left neighbour.
|
|
359
|
+
const squareBoxW: Bounds = getSquareBox(origin, squareSize, gapSize, x - 1, y);
|
|
360
|
+
addStraightLine(pathData, squareBoxW.getSouthPoint());
|
|
361
|
+
position = { xIndex: x - 1, yIndex: y };
|
|
362
|
+
// Keep borderDirection BOTTOM
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
console.warn(`[Error] Cannot construct full rounded path; 'borderDirection' has wrong value (${borderDirection}).`);
|
|
366
|
+
isPathComplete = true;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// End condition reached?
|
|
370
|
+
if (
|
|
371
|
+
startingPosition.xIndex === position.xIndex &&
|
|
372
|
+
startingPosition.yIndex === position.yIndex &&
|
|
373
|
+
startingBorderDirection === borderDirection
|
|
374
|
+
) {
|
|
375
|
+
pathData.push("Z");
|
|
376
|
+
isPathComplete = true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|