plotboilerplate 1.26.0 → 1.27.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.
Files changed (86) hide show
  1. package/README.md +2 -0
  2. package/dist/index.esm.js +33 -11
  3. package/dist/index.esm.js.map +1 -1
  4. package/dist/index.esm.min.js +1 -1
  5. package/dist/index.esm.min.js.map +1 -1
  6. package/dist/plotboilerplate.browser.min.js +1 -1
  7. package/dist/plotboilerplate.browser.min.js.map +1 -1
  8. package/dist/plotboilerplate.js +33 -11
  9. package/dist/plotboilerplate.min.js +1 -1
  10. package/package.json +6 -2
  11. package/src/cjs/PlotBoilerplate.d.ts +4 -3
  12. package/src/cjs/PlotBoilerplate.js +2 -1
  13. package/src/cjs/PlotBoilerplate.js.map +1 -1
  14. package/src/cjs/draw.d.ts +4 -2
  15. package/src/cjs/draw.js +9 -4
  16. package/src/cjs/draw.js.map +1 -1
  17. package/src/cjs/drawgl.d.ts +3 -2
  18. package/src/cjs/drawgl.js +2 -1
  19. package/src/cjs/drawgl.js.map +1 -1
  20. package/src/cjs/drawutilssvg.d.ts +9 -4
  21. package/src/cjs/drawutilssvg.js +20 -5
  22. package/src/cjs/drawutilssvg.js.map +1 -1
  23. package/src/cjs/interfaces/DrawLib.d.ts +2 -0
  24. package/src/cjs/interfaces/DrawLib.js +1 -0
  25. package/src/cjs/interfaces/DrawLib.js.map +1 -1
  26. package/src/cjs/utils/WebColors.d.ts +2 -1
  27. package/src/cjs/utils/WebColors.js +14 -25
  28. package/src/cjs/utils/WebColors.js.map +1 -1
  29. package/src/cjs/utils/algorithms/PolygonTesselationOutlines.d.ts +145 -0
  30. package/src/cjs/utils/algorithms/PolygonTesselationOutlines.js +269 -0
  31. package/src/cjs/utils/algorithms/PolygonTesselationOutlines.js.map +1 -0
  32. package/src/cjs/utils/datastructures/Color.js +0 -19
  33. package/src/cjs/utils/datastructures/Color.js.map +1 -1
  34. package/src/cjs/utils/datastructures/ColorGradient.d.ts +2 -0
  35. package/src/cjs/utils/datastructures/ColorGradient.js +5 -0
  36. package/src/cjs/utils/datastructures/ColorGradient.js.map +1 -1
  37. package/src/cjs/utils/datastructures/Girih.d.ts +38 -1
  38. package/src/cjs/utils/datastructures/Girih.js +71 -1
  39. package/src/cjs/utils/datastructures/Girih.js.map +1 -1
  40. package/src/cjs/utils/io/FileDrop.d.ts +2 -0
  41. package/src/cjs/utils/io/FileDrop.js +4 -0
  42. package/src/cjs/utils/io/FileDrop.js.map +1 -1
  43. package/src/esm/PlotBoilerplate.d.ts +4 -3
  44. package/src/esm/PlotBoilerplate.js +2 -1
  45. package/src/esm/PlotBoilerplate.js.map +1 -1
  46. package/src/esm/draw.d.ts +4 -2
  47. package/src/esm/draw.js +9 -4
  48. package/src/esm/draw.js.map +1 -1
  49. package/src/esm/drawgl.d.ts +3 -2
  50. package/src/esm/drawgl.js +2 -1
  51. package/src/esm/drawgl.js.map +1 -1
  52. package/src/esm/drawutilssvg.d.ts +9 -4
  53. package/src/esm/drawutilssvg.js +20 -5
  54. package/src/esm/drawutilssvg.js.map +1 -1
  55. package/src/esm/interfaces/DrawLib.d.ts +2 -0
  56. package/src/esm/interfaces/DrawLib.js +1 -0
  57. package/src/esm/interfaces/DrawLib.js.map +1 -1
  58. package/src/esm/utils/WebColors.d.ts +2 -1
  59. package/src/esm/utils/WebColors.js +14 -25
  60. package/src/esm/utils/WebColors.js.map +1 -1
  61. package/src/esm/utils/algorithms/PolygonTesselationOutlines.d.ts +145 -0
  62. package/src/esm/utils/algorithms/PolygonTesselationOutlines.js +262 -0
  63. package/src/esm/utils/algorithms/PolygonTesselationOutlines.js.map +1 -0
  64. package/src/esm/utils/datastructures/Color.js +0 -19
  65. package/src/esm/utils/datastructures/Color.js.map +1 -1
  66. package/src/esm/utils/datastructures/ColorGradient.d.ts +2 -0
  67. package/src/esm/utils/datastructures/ColorGradient.js +5 -0
  68. package/src/esm/utils/datastructures/ColorGradient.js.map +1 -1
  69. package/src/esm/utils/datastructures/Girih.d.ts +38 -1
  70. package/src/esm/utils/datastructures/Girih.js +71 -1
  71. package/src/esm/utils/datastructures/Girih.js.map +1 -1
  72. package/src/esm/utils/io/FileDrop.d.ts +2 -0
  73. package/src/esm/utils/io/FileDrop.js +4 -0
  74. package/src/esm/utils/io/FileDrop.js.map +1 -1
  75. package/src/ts/PlotBoilerplate.ts +4 -3
  76. package/src/ts/draw.ts +8 -3
  77. package/src/ts/drawgl.ts +3 -2
  78. package/src/ts/drawutilssvg.ts +27 -5
  79. package/src/ts/interfaces/DrawLib.ts +3 -0
  80. package/src/ts/utils/WebColors.ts +20 -33
  81. package/src/ts/utils/algorithms/PolygonTesselationOutlines.ts +296 -0
  82. package/src/ts/utils/datastructures/Color.ts +0 -19
  83. package/src/ts/utils/datastructures/ColorGradient.ts +6 -0
  84. package/src/ts/utils/datastructures/Girih.ts +78 -1
  85. package/src/ts/utils/datastructures/interfaces.ts +4 -4
  86. package/src/ts/utils/io/FileDrop.ts +5 -0
@@ -0,0 +1,296 @@
1
+ /**
2
+ * Find the outer hull outlines of a (not necessarily full connected) polygon tesellation.
3
+ *
4
+ * The method `findAllOutlinesGraph` will return a simple graph containing all vertices and edges
5
+ * that belong to the outer hull.
6
+ *
7
+ * The method `findAllOutlinesPolygons` will detect connected paths inside the graph and return
8
+ * an array of Polygons.
9
+ *
10
+ *
11
+ * @require detectPaths
12
+ *
13
+ * @author Ikaros Kappler
14
+ * @date 2026-01-20
15
+ * @version 1.0.0
16
+ */
17
+
18
+ import { Line } from "../../Line";
19
+ import { Polygon } from "../../Polygon";
20
+ import { Vertex } from "../../Vertex";
21
+ import { PathSegment, XYCoords } from "../../interfaces";
22
+ import { GenericPath } from "../datastructures/GenericPath";
23
+ import { IndexPair } from "../datastructures/interfaces";
24
+ import { arrayFill } from "./arrayFill";
25
+ import { detectPaths } from "./detectPaths";
26
+
27
+ export interface PolygonTesselationOutlinesOptions {
28
+ tolerance?: number;
29
+ removeUnusedVertices?: boolean;
30
+ }
31
+
32
+ export interface TesselationGraph {
33
+ vertices: Array<Vertex>;
34
+ edges: Array<IndexPair>;
35
+ }
36
+
37
+ /**
38
+ * Convert any non-intersecting tesselation to a graph.
39
+ * Adjadent tiles (polygons) will result in duplicate edges.
40
+ *
41
+ * @param {Polygon[]} polygons - The polygon tesselation to use.
42
+ * @return {TesselationGraph} A tesselation graph.
43
+ */
44
+ export const polygonTesselationToGraph = (polygons: Polygon[]): TesselationGraph => {
45
+ const vertices: Array<Vertex> = [];
46
+ const edges: Array<IndexPair> = [];
47
+ // const vertexPolygonMapping: Array<number>[] = []; // Map polygon+vertex to graph vertices
48
+ for (var i = 0; i < polygons.length; i++) {
49
+ const polygon = polygons[i];
50
+ const polygonMapping: Array<number> = [];
51
+ // vertexPolygonMapping.push(polygonMapping);
52
+ // Add vertices
53
+ for (var j = 0; j < polygon.vertices.length; j++) {
54
+ const graphVertIndex = vertices.length;
55
+ vertices.push(polygon.vertices[j]);
56
+ polygonMapping.push(graphVertIndex);
57
+ }
58
+ // Now add edges
59
+ for (var j = 0; j < polygon.vertices.length; j++) {
60
+ const polyEdgeIndexA = j;
61
+ const polyEdgeIndexB = (j + 1) % polygon.vertices.length;
62
+ const grapgVertIndexA = polygonMapping[polyEdgeIndexA];
63
+ const grapgVertIndexB = polygonMapping[polyEdgeIndexB];
64
+ edges.push({ i: grapgVertIndexA, j: grapgVertIndexB });
65
+ }
66
+ }
67
+ return { vertices, edges };
68
+ };
69
+
70
+ /**
71
+ * A class to calculate the tesselation's outer hull.
72
+ */
73
+ export class PolygonTesselationOutlines {
74
+ public static readonly DEFAULT_TOLERANCE = 0.01;
75
+
76
+ private readonly polygons: Polygon[];
77
+ // private readonly tesselationGraph: TesselationGraph;
78
+ private readonly tolerance: number;
79
+ private readonly removeUnusedVertices: boolean;
80
+
81
+ /**
82
+ * @constructor
83
+ * @param {Polygon[]} polygons - The polygon tesselation to use.
84
+ * @param {PolygonTesselationOutlinesOptions} options - (optional)
85
+ */
86
+ constructor(polygons: Polygon[], options?: PolygonTesselationOutlinesOptions) {
87
+ this.polygons = polygons;
88
+ // this.tesselationGraph = tesselationToGraph(polygons);
89
+ this.tolerance = options?.tolerance ?? PolygonTesselationOutlines.DEFAULT_TOLERANCE;
90
+ this.removeUnusedVertices = options?.removeUnusedVertices ?? true;
91
+ }
92
+
93
+ /**
94
+ * Clears duplicate edges.
95
+ *
96
+ * Note: this works only for duplicates. Triplets will not be removed.
97
+ *
98
+ * @name __eliminateDuplicateEdges
99
+ * @private
100
+ * @memberof TesselationGraph
101
+ * @instance
102
+ * @param {TesselationGraph} tesselationGraph - The tesselation graph to remove duplicate edges from.
103
+ * @return {TesselationGraph} A new tesselation graph with cleaned up edges.
104
+ */
105
+ private __eliminateDuplicateEdges(tesselationGraph: TesselationGraph): TesselationGraph {
106
+ const newEdges: Array<IndexPair> = [];
107
+ for (var e = 0; e < tesselationGraph.edges.length; e++) {
108
+ const curEdge: IndexPair = tesselationGraph.edges[e];
109
+ if (!curEdge) {
110
+ continue;
111
+ }
112
+ const curA: Vertex = tesselationGraph.vertices[curEdge.i];
113
+ const curB: Vertex = tesselationGraph.vertices[curEdge.j];
114
+ for (var f = 0; f < tesselationGraph.edges.length; f++) {
115
+ if (e === f) {
116
+ continue;
117
+ }
118
+ const otherEdge: IndexPair = tesselationGraph.edges[f];
119
+ if (!otherEdge) {
120
+ continue;
121
+ }
122
+ const otherA: Vertex = tesselationGraph.vertices[otherEdge.i];
123
+ const otherB: Vertex = tesselationGraph.vertices[otherEdge.j];
124
+ if (this.__isEqualEdge(curA, curB, otherA, otherB)) {
125
+ // Both edges are equal -> remove both
126
+ tesselationGraph.edges[e] = null;
127
+ tesselationGraph.edges[f] = null;
128
+ }
129
+ }
130
+ }
131
+ // return tesselationGraph; // TODO: create clean clone with new mapping
132
+ // Clear null-records.
133
+ return {
134
+ vertices: tesselationGraph.vertices,
135
+ edges: tesselationGraph.edges.filter((edge: IndexPair) => edge != null)
136
+ }; // TODO: create clean clone with new mapping
137
+ }
138
+
139
+ /**
140
+ * Locate some edge that adjacent to the given vertex index
141
+ *
142
+ * @name __locateEdgeWithVertIndex
143
+ * @private
144
+ * @memberof TesselationGraph
145
+ * @instance
146
+ * @param {number} vertIndex - The vertex index to find an edge for.
147
+ * @param {TesselationGraph} tesselationGraph - The tesselation graph to remove unused vertices from.
148
+ * @return {number} The edge index that's adjacent to the given vertex index.
149
+ * @return -1 if no such edge is found.
150
+ */
151
+ private __locateEdgeWithVertIndex(vertIndex: number, tesselationGraph: TesselationGraph): number {
152
+ return tesselationGraph.edges.findIndex((edge: IndexPair) => edge.i == vertIndex || edge.j == vertIndex);
153
+ }
154
+
155
+ /**
156
+ * Removes all unused vertices from the graph and re-matps the edges.
157
+ *
158
+ * @name __removeUnusedVertices
159
+ * @private
160
+ * @memberof TesselationGraph
161
+ * @instance
162
+ * @param {TesselationGraph} tesselationGraph - The tesselation graph to remove unused vertices from.
163
+ * @return {TesselationGraph} A new tesselation graph.
164
+ */
165
+ private __removeUnusedVertices(tesselationGraph: TesselationGraph): TesselationGraph {
166
+ // First remove un-used vertices and re-map indices.
167
+ const newVertices: Array<Vertex> = [];
168
+ const indexMapping: Array<number> = arrayFill(tesselationGraph.vertices.length, -1);
169
+ for (var j = 0; j < tesselationGraph.vertices.length; j++) {
170
+ const edgeIndex: number = this.__locateEdgeWithVertIndex(j, tesselationGraph);
171
+ if (edgeIndex === -1) {
172
+ // No adjacent edge found -> vertex is not in use
173
+ // Ignore; index mapping remains to -1
174
+ } else {
175
+ // Keep vertex
176
+ const newIndex: number = newVertices.length;
177
+ newVertices.push(tesselationGraph.vertices[j]);
178
+ indexMapping[j] = newIndex;
179
+ }
180
+ }
181
+
182
+ // Now re-map the egdes
183
+ const newEdges: Array<IndexPair> = [];
184
+ for (var j = 0; j < tesselationGraph.edges.length; j++) {
185
+ const edge = tesselationGraph.edges[j];
186
+ newEdges.push({ i: indexMapping[edge.i], j: indexMapping[edge.j] });
187
+ }
188
+ return { vertices: newVertices, edges: newEdges };
189
+ }
190
+
191
+ /**
192
+ * Creates a tesselation graph and removes all inner edges.
193
+ *
194
+ * @name findAllOutlinesGraph
195
+ * @private
196
+ * @memberof PolygonTesselationOutlines
197
+ * @instance
198
+ * @return {TesselationGraph} A tesselation graph containing the outer hull edges only.
199
+ */
200
+ findAllOutlinesGraph(): TesselationGraph {
201
+ // First build tesselation graph from polygons, containing all edges and vertices.
202
+ const tesselationGraph: TesselationGraph = polygonTesselationToGraph(this.polygons);
203
+
204
+ // Now eliminate all duplicate edges. By construction only inner edges are duplicates.
205
+ const outerHull = this.__eliminateDuplicateEdges(tesselationGraph);
206
+ // Only of requested remove unused vertices.
207
+ if (this.removeUnusedVertices) {
208
+ const cleanOuterHull = this.__removeUnusedVertices(outerHull);
209
+ return cleanOuterHull;
210
+ } else {
211
+ return outerHull;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Convert any generic path to a polygon. Each path segment (start point and end point)
217
+ * are interpreted as a linear segment here.
218
+ *
219
+ * @name __convertGenericPathToPolygon
220
+ * @private
221
+ * @memberof PolygonTesselationOutlines
222
+ * @instance
223
+ * @param {GenericPath} path - The path to convert.
224
+ */
225
+ private __convertGenericPathToPolygon(path: GenericPath): Polygon {
226
+ const vertices: Array<Vertex> = [];
227
+ const n: number = path.getSegmentCount();
228
+ let isOpen: boolean = false;
229
+ for (var i = 0; i < n; i++) {
230
+ const segment: PathSegment = path.getSegmentAt(i);
231
+ vertices.push(segment.getStartPoint());
232
+ if (i > 0 && i + 1 >= n && !this.__isEqualPoint(segment.getEndPoint(), vertices[0])) {
233
+ isOpen = true;
234
+ }
235
+ }
236
+ return new Polygon(vertices, isOpen);
237
+ }
238
+
239
+ /**
240
+ * Creates a tesselation graph and removes all inner edges.
241
+ *
242
+ * @name findAllOutlinesPolygons
243
+ * @private
244
+ * @memberof PolygonTesselationOutlines
245
+ * @instance
246
+ * @param {TesselationGraph} tesselationGraph - Find all outlines on the tesselation graph.
247
+ */
248
+ findAllOutlinesPolygons(tesselationGraph: TesselationGraph): Array<Polygon> {
249
+ // Convert to real line segments:
250
+ const lineSegments: Array<Line> = tesselationGraph.edges.map(
251
+ (edge: IndexPair) => new Line(tesselationGraph.vertices[edge.i], tesselationGraph.vertices[edge.j])
252
+ );
253
+ // Now run the path detection.
254
+ const paths: Array<GenericPath> = detectPaths(lineSegments, this.tolerance);
255
+
256
+ // And now convert the generic paths back to polygons
257
+ // (we have put in line segments, so line segments must have returned)
258
+ return paths.map((path: GenericPath) => this.__convertGenericPathToPolygon(path));
259
+ }
260
+
261
+ /**
262
+ * Checks if two vertices are equal – regarding to the current tolerance setting.
263
+ *
264
+ * @name __isEqualPoint
265
+ * @private
266
+ * @memberof PolygonTesselationOutlines
267
+ * @instance
268
+ * @param {boolean} vertA - The first point.
269
+ * @param {boolean} vertB - The second point.
270
+ * @return True if the vertices are equal.
271
+ */
272
+ private __isEqualPoint(vertA: Vertex, vertB: Vertex): boolean {
273
+ return vertA.distance(vertB) < this.tolerance;
274
+ }
275
+
276
+ /**
277
+ * Checks if two edges (identified as two pairs of vertices) are equal – regarding to the
278
+ * current tolerance setting.
279
+ *
280
+ * @name __isEqualEdge
281
+ * @private
282
+ * @memberof PolygonTesselationOutlines
283
+ * @instance
284
+ * @param {boolean} edgeAvertA - The first edge's point A.
285
+ * @param {boolean} edgeAvertB - The first edge's point B.
286
+ * @param {boolean} edgeBvertA - The second edge's point A.
287
+ * @param {boolean} edgeBvertB - The second edge's point B.
288
+ * @return True if the edges are equal.
289
+ */
290
+ private __isEqualEdge(edgeAvertA: Vertex, edgeAvertB: Vertex, edgeBvertA: Vertex, edgeBvertB: Vertex): boolean {
291
+ return (
292
+ (this.__isEqualPoint(edgeAvertA, edgeBvertA) && this.__isEqualPoint(edgeAvertB, edgeBvertB)) ||
293
+ (this.__isEqualPoint(edgeAvertA, edgeBvertB) && this.__isEqualPoint(edgeAvertB, edgeBvertA))
294
+ );
295
+ }
296
+ }
@@ -376,25 +376,6 @@ export class Color {
376
376
  Color.Converter.HSLToRGB(this);
377
377
  return this;
378
378
  }
379
- // lighten(v: string | number): Color {
380
- // if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
381
- // this.l += v / 100;
382
- // } else if ("number" == typeof v) {
383
- // if (v >= -1.0 && v <= 1.0) {
384
- // // range 0.0...1.0
385
- // this.l += v;
386
- // } else {
387
- // // range 255
388
- // this.l += v / 255;
389
- // }
390
- // } else {
391
- // throw new Error("error: bad modifier format (percent or number)");
392
- // }
393
- // if (this.l > 1) this.l = 1;
394
- // else if (this.l < 0) this.l = 0;
395
- // Color.Converter.HSLToRGB(this);
396
- // return this;
397
- // }
398
379
  lighten(v: string | number): Color {
399
380
  // if ("string" == typeof v && v.indexOf("%") > -1 && (v = parseInt(v)) != NaN) {
400
381
  if ("string" == typeof v && v.indexOf("%") > -1 && !Number.isNaN((v = parseInt(v)))) {
@@ -8,6 +8,7 @@
8
8
  * @author Ikaros Kappler
9
9
  * @version 1.0.0
10
10
  * @date 2025-08-05
11
+ * @modified 2025-11-26 Fixing a bug in `ColorGradient.getColorAt(ratio)`: value at 1.0 caused runtime error.
11
12
  */
12
13
 
13
14
  import { Color } from "./Color";
@@ -34,6 +35,8 @@ export class ColorGradient {
34
35
  values: Array<ColorGradientItem>;
35
36
  angle: ColorGradientRotation;
36
37
 
38
+ static DEFAULT_COLORSET = DEFAULT_COLORSET;
39
+
37
40
  /**
38
41
  * Creates a new ColorGradient with the given values. Please be sure that the values appear in the correct
39
42
  * order regarding the `ratio` information. This will not be validated.
@@ -87,6 +90,9 @@ export class ColorGradient {
87
90
  const intervalLocation: [number, ColorGradientItem] = this.locateClosestRatio(ratio);
88
91
  const leftItem: ColorGradientItem = this.values[intervalLocation[0]];
89
92
  const rightItem: ColorGradientItem = this.values[intervalLocation[0] + 1];
93
+ if (!rightItem) {
94
+ return leftItem.color.clone();
95
+ }
90
96
  const relativeInnerIntervalPosition: number = (ratio - leftItem.ratio) / (rightItem.ratio - leftItem.ratio);
91
97
  return leftItem.color.clone().interpolate(rightItem.color, relativeInnerIntervalPosition);
92
98
  }
@@ -3,7 +3,10 @@
3
3
  * @date 2020-11-24
4
4
  * @modified 2020-11-25 Ported to TypeScript from vanilla JS.
5
5
  * @modified 2024-03-10 Fixed some types for Typescript 5 compatibility.
6
- * @version 1.0.2
6
+ * @modified 2026-01-06 Added method `Girih.locateContainingTileAndEdge` to locate tile/edge pairs.
7
+ * @modified 2026-01-12 Added method `Girih.getTileByCenter` to locate tiles by position.
8
+ * @modified 2026-01-18 Added method `Girih.removeAllTiles`.
9
+ * @version 1.1.0
7
10
  * @file Girih
8
11
  **/
9
12
 
@@ -15,6 +18,7 @@ import { GirihHexagon } from "./GirihHexagon";
15
18
  import { GirihPenroseRhombus } from "./GirihPenroseRhombus";
16
19
  import { GirihPentagon } from "./GirihPentagon";
17
20
  import { GirihRhombus } from "./GirihRhombus";
21
+ import { XYCoords } from "../../interfaces";
18
22
 
19
23
  /**
20
24
  * @classdesc The Girih datastructure for generating patterns.
@@ -135,6 +139,19 @@ export class Girih {
135
139
  }
136
140
 
137
141
  /**
142
+ * Remove all tiles.
143
+ *
144
+ * @name removeAllTiles
145
+ * @memberof Girih
146
+ * @instance
147
+ * @return {void}
148
+ */
149
+ removeAllTiles(): void {
150
+ this.tiles.splice(0, this.tiles.length);
151
+ }
152
+
153
+ /**
154
+ * Replace all current tiles with the given ones.
138
155
  *
139
156
  * @param tiles
140
157
  */
@@ -145,6 +162,25 @@ export class Girih {
145
162
  }
146
163
  }
147
164
 
165
+ /**
166
+ * Find the tile with the given center.
167
+ *
168
+ * @name getTileByCenter
169
+ * @memberof Girih
170
+ * @instance
171
+ * @param {XYCoords} center - The center point to look for.
172
+ * @return {GirihTile} The tile or null if not found.
173
+ */
174
+ getTileByCenter(center: XYCoords): GirihTile {
175
+ for (var i = 0; i < this.tiles.length; i++) {
176
+ const pos = this.tiles[i].position;
177
+ if (pos === center || (pos.x == center.x && pos.y == center.y)) {
178
+ return this.tiles[i];
179
+ }
180
+ }
181
+ return null;
182
+ }
183
+
148
184
  /**
149
185
  * Find that tile (index) which contains the given position. First match will be returned.
150
186
  *
@@ -161,6 +197,47 @@ export class Girih {
161
197
  return -1;
162
198
  }
163
199
 
200
+ /**
201
+ * Find find a tile-edge-pair (indices) that contain the given position. First match will be returned.
202
+ *
203
+ * @name locateContainingTileAndEdge
204
+ * @memberof Girih
205
+ * @instance
206
+ * @param {Vertex} position
207
+ * @return {{ tileIndex: number; edgeIndex: number }} The index of the containing tile and edge or null if none was found.
208
+ **/
209
+ locateContainingTileAndEdge(position): { tileIndex: number; edgeIndex: number } {
210
+ var containedTileIndex = this.locateConatiningTile(position);
211
+
212
+ // Reset currently highlighted tile/edge (if re-detected nothing changed in the end)
213
+ var hoverTileIndex = -1;
214
+ var hoverEdgeIndex = -1;
215
+
216
+ // Find Girih edge nearby ...
217
+ if (containedTileIndex == -1) {
218
+ return null;
219
+ }
220
+ var i = containedTileIndex;
221
+ do {
222
+ var tile = this.tiles[i];
223
+ // May be -1
224
+ hoverEdgeIndex = tile.locateEdgeAtPoint(position, this.edgeLength / 2);
225
+ if (hoverEdgeIndex != -1) {
226
+ hoverTileIndex = i;
227
+ }
228
+ i++;
229
+ } while (i < this.tiles.length && containedTileIndex == -1 && hoverEdgeIndex == -1);
230
+ if (hoverTileIndex == -1) {
231
+ hoverTileIndex = containedTileIndex;
232
+ }
233
+
234
+ // Find the next possible tile to place?
235
+ if (hoverTileIndex == -1 || hoverEdgeIndex == -1) {
236
+ return null;
237
+ }
238
+ return { tileIndex: hoverTileIndex, edgeIndex: hoverEdgeIndex };
239
+ }
240
+
164
241
  /**
165
242
  * Turn the tile the mouse is hovering over.
166
243
  * The turnCount is ab abstract number: -1 for one turn left, +1 for one turn right.
@@ -6,11 +6,11 @@
6
6
  * @version 1.0.0
7
7
  **/
8
8
 
9
- export type Interval = [ number, number ];
9
+ export type Interval = [number, number];
10
10
 
11
11
  export type IndexPair = {
12
- i : number;
13
- j : number;
14
- }
12
+ i: number;
13
+ j: number;
14
+ };
15
15
 
16
16
  export type Matrix<T> = Array<Array<T>>;
@@ -102,10 +102,15 @@ export class FileDrop {
102
102
  this.fileDroppedCallbackText = callback;
103
103
  }
104
104
 
105
+ // TODO: docs
105
106
  onFileSVGDropped(callback: IDroppedCallbackSVG) {
106
107
  this.fileDroppedCallbackSVG = callback;
107
108
  }
108
109
 
110
+ onFileBinaryDropped(callback: IDroppedCallbackBinary) {
111
+ this.fileDroppedCallbackBinary = callback;
112
+ }
113
+
109
114
  /**
110
115
  * Internally handle a drop event.
111
116
  *