gridviz 2.1.3 → 2.1.5
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/dist/gridviz.js +360 -111
- package/dist/gridviz.min.js +1 -1
- package/package.json +1 -1
- package/src/App.js +2 -1
- package/src/Layer.js +1 -1
- package/src/Style.js +1 -1
- package/src/dataset/TiledGrid.js +72 -72
- package/src/index.js +1 -0
- package/src/style/IsoFenceStyle.js +232 -0
- package/src/style/SideStyle.js +2 -4
package/package.json
CHANGED
package/src/App.js
CHANGED
|
@@ -354,13 +354,14 @@ export class App {
|
|
|
354
354
|
const layer = this.layers[i]
|
|
355
355
|
if (!layer.visible) continue
|
|
356
356
|
if (!layer.cellInfoHTML) continue
|
|
357
|
-
if (layer.cellInfoHTML === 'none') continue
|
|
357
|
+
//if (layer.cellInfoHTML === 'none') continue
|
|
358
358
|
const dsc = layer.getDatasetComponent(zf)
|
|
359
359
|
if (!dsc) continue
|
|
360
360
|
|
|
361
361
|
//get cell at mouse position
|
|
362
362
|
/** @type {import('./Dataset').Cell|undefined} */
|
|
363
363
|
const cell = dsc.getCellFromPosition(posGeo, dsc.getViewCache())
|
|
364
|
+
//console.log(cell, dsc.resolution)
|
|
364
365
|
if (!cell) return undefined
|
|
365
366
|
const html = layer.cellInfoHTML(cell, dsc.getResolution())
|
|
366
367
|
if (!html) return undefined
|
package/src/Layer.js
CHANGED
|
@@ -34,7 +34,7 @@ export class Layer {
|
|
|
34
34
|
/** A function returning the blend operation. The function parameter is the zoom factor.
|
|
35
35
|
* (see CanvasRenderingContext2D: globalCompositeOperation property)
|
|
36
36
|
* @type {GlobalCompositeOperation} */
|
|
37
|
-
this.blendOperation = opts.blendOperation || (zf =>
|
|
37
|
+
this.blendOperation = opts.blendOperation || (zf => "source-over")
|
|
38
38
|
|
|
39
39
|
/** The minimum zoom factor: Below this level, the layer is not shown.
|
|
40
40
|
* @type {number} */
|
package/src/Style.js
CHANGED
|
@@ -46,7 +46,7 @@ export class Style {
|
|
|
46
46
|
/** A function returning the blend operation. The function parameter is the zoom factor.
|
|
47
47
|
* (see CanvasRenderingContext2D: globalCompositeOperation property)
|
|
48
48
|
* @type {function(number):GlobalCompositeOperation} */
|
|
49
|
-
this.blendOperation = opts.blendOperation || (zf =>
|
|
49
|
+
this.blendOperation = opts.blendOperation || (zf => "source-over")
|
|
50
50
|
|
|
51
51
|
/** The minimum zoom factor: Below this level, the layer is not shown.
|
|
52
52
|
* @type {number}
|
package/src/dataset/TiledGrid.js
CHANGED
|
@@ -137,82 +137,82 @@ export class TiledGrid extends DatasetComponent {
|
|
|
137
137
|
if (tile) continue
|
|
138
138
|
|
|
139
139
|
//mark tile as loading
|
|
140
|
-
this.cache[xT][yT] =
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
this.cache[xT][yT] = "loading";
|
|
141
|
+
(async () => {
|
|
142
|
+
//request tile
|
|
143
|
+
/** @type {Array.<import("../Dataset").Cell>} */
|
|
144
|
+
let cells
|
|
145
|
+
|
|
146
|
+
try {
|
|
143
147
|
/** @type {Array.<import("../Dataset").Cell>} */
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
cells
|
|
156
|
-
for (const c of data) {
|
|
157
|
-
const b = this.preprocess(c)
|
|
158
|
-
if (b == false) continue
|
|
159
|
-
cells.push(c)
|
|
160
|
-
}
|
|
161
|
-
} else {
|
|
162
|
-
cells = data
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
const data = await csv(this.url + xT + '/' + yT + '.csv')
|
|
150
|
+
|
|
151
|
+
//if (monitor) monitorDuration('*** TiledGrid parse start')
|
|
152
|
+
|
|
153
|
+
//preprocess/filter
|
|
154
|
+
if (this.preprocess) {
|
|
155
|
+
cells = []
|
|
156
|
+
for (const c of data) {
|
|
157
|
+
const b = this.preprocess(c)
|
|
158
|
+
if (b == false) continue
|
|
159
|
+
cells.push(c)
|
|
163
160
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
} catch (error) {
|
|
167
|
-
//mark as failed
|
|
168
|
-
this.cache[xT][yT] = 'failed'
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
//store tile in cache
|
|
173
|
-
if (!this.info) {
|
|
174
|
-
console.error('Tile info inknown')
|
|
175
|
-
return
|
|
161
|
+
} else {
|
|
162
|
+
cells = data
|
|
176
163
|
}
|
|
177
|
-
const tile_ = new GridTile(cells, xT, yT, this.info)
|
|
178
|
-
this.cache[xT][yT] = tile_
|
|
179
|
-
|
|
180
|
-
if (monitor) monitorDuration('storage')
|
|
181
|
-
|
|
182
|
-
//if no redraw is specified, then leave
|
|
183
|
-
if (!redrawFun) return
|
|
184
|
-
|
|
185
|
-
//check if redraw is really needed, that is if:
|
|
186
|
-
|
|
187
|
-
// 1. the dataset belongs to a layer which is visible at the current zoom level
|
|
188
|
-
let redraw = false
|
|
189
|
-
//go through the layers
|
|
190
|
-
const zf = this.app.getZoomFactor()
|
|
191
|
-
for (const lay of this.app.layers) {
|
|
192
|
-
if (!lay.visible) continue
|
|
193
|
-
if (lay.getDatasetComponent(zf) != this) continue
|
|
194
|
-
//found one layer. No need to seek more.
|
|
195
|
-
redraw = true
|
|
196
|
-
break
|
|
197
|
-
}
|
|
198
|
-
if (monitor) monitorDuration('check redraw 1')
|
|
199
|
-
|
|
200
|
-
if (!redraw) return
|
|
201
|
-
|
|
202
|
-
// 2. the tile is within the view, that is its geo envelope intersects the viewer geo envelope.
|
|
203
|
-
const env = this.app.updateExtentGeo()
|
|
204
|
-
const envT = tile_.extGeo
|
|
205
|
-
if (env.xMax <= envT.xMin) return
|
|
206
|
-
if (env.xMin >= envT.xMax) return
|
|
207
|
-
if (env.yMax <= envT.yMin) return
|
|
208
|
-
if (env.yMin >= envT.yMax) return
|
|
209
|
-
|
|
210
|
-
if (monitor) monitorDuration('check redraw 2')
|
|
211
|
-
if (monitor) monitorDuration('*** TiledGrid parse end')
|
|
212
164
|
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
165
|
+
//if (monitor) monitorDuration('preprocess / filter')
|
|
166
|
+
} catch (error) {
|
|
167
|
+
//mark as failed
|
|
168
|
+
this.cache[xT][yT] = 'failed'
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
//store tile in cache
|
|
173
|
+
if (!this.info) {
|
|
174
|
+
console.error('Tile info inknown')
|
|
175
|
+
return
|
|
176
|
+
}
|
|
177
|
+
const tile_ = new GridTile(cells, xT, yT, this.info)
|
|
178
|
+
this.cache[xT][yT] = tile_
|
|
179
|
+
|
|
180
|
+
//if (monitor) monitorDuration('storage')
|
|
181
|
+
|
|
182
|
+
//if no redraw is specified, then leave
|
|
183
|
+
if (!redrawFun) return
|
|
184
|
+
|
|
185
|
+
//check if redraw is really needed, that is if:
|
|
186
|
+
|
|
187
|
+
// 1. the dataset belongs to a layer which is visible at the current zoom level
|
|
188
|
+
let redraw = false
|
|
189
|
+
//go through the layers
|
|
190
|
+
const zf = this.app.getZoomFactor()
|
|
191
|
+
for (const lay of this.app.layers) {
|
|
192
|
+
if (!lay.visible) continue
|
|
193
|
+
if (lay.getDatasetComponent(zf) != this) continue
|
|
194
|
+
//found one layer. No need to seek more.
|
|
195
|
+
redraw = true
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
//if (monitor) monitorDuration('check redraw 1')
|
|
199
|
+
|
|
200
|
+
if (!redraw) return
|
|
201
|
+
|
|
202
|
+
// 2. the tile is within the view, that is its geo envelope intersects the viewer geo envelope.
|
|
203
|
+
const env = this.app.updateExtentGeo()
|
|
204
|
+
const envT = tile_.extGeo
|
|
205
|
+
if (env.xMax <= envT.xMin) return
|
|
206
|
+
if (env.xMin >= envT.xMax) return
|
|
207
|
+
if (env.yMax <= envT.yMin) return
|
|
208
|
+
if (env.yMin >= envT.yMax) return
|
|
209
|
+
|
|
210
|
+
//if (monitor) monitorDuration('check redraw 2')
|
|
211
|
+
//if (monitor) monitorDuration('*** TiledGrid parse end')
|
|
212
|
+
|
|
213
|
+
//redraw
|
|
214
|
+
redrawFun()
|
|
215
|
+
})()
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
return this
|
package/src/index.js
CHANGED
|
@@ -35,6 +35,7 @@ export { SquareColorCatWGLStyle } from './style/SquareColorCatWGLStyle.js'
|
|
|
35
35
|
export { MosaicStyle } from './style/MosaicStyle.js'
|
|
36
36
|
export { NinjaStarStyle } from './style/NinjaStarStyle.js'
|
|
37
37
|
export { TimeSeriesStyle } from './style/TimeSeriesStyle.js'
|
|
38
|
+
export { IsoFenceStyle } from './style/IsoFenceStyle.js'
|
|
38
39
|
|
|
39
40
|
// export additional layers
|
|
40
41
|
export { BackgroundLayer } from './BackgroundLayer.js'
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
import { Style } from '../Style.js'
|
|
5
|
+
|
|
6
|
+
/** @typedef {{x:number,y:number,or:"v"|"h",c1:import('../Dataset.js').Cell|undefined,c2:import('../Dataset.js').Cell|undefined}} Side */
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @author Julien Gaffuri
|
|
10
|
+
*/
|
|
11
|
+
export class IsoFenceStyle extends Style {
|
|
12
|
+
|
|
13
|
+
/** @param {object} opts */
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
super(opts)
|
|
16
|
+
opts = opts || {}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The dictionary (string -> color) which give the color of each category.
|
|
20
|
+
* @type {object} */
|
|
21
|
+
this.color = opts.color
|
|
22
|
+
|
|
23
|
+
/** The column where to get the height values.
|
|
24
|
+
* @type {string} */
|
|
25
|
+
this.heightCol = opts.heightCol
|
|
26
|
+
|
|
27
|
+
/** A function returning the height of a cell.
|
|
28
|
+
* @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
29
|
+
this.height = opts.height || ((v, r, s, zf) => r * 0.4)
|
|
30
|
+
|
|
31
|
+
/** The perspective angle.
|
|
32
|
+
* @type {number} */
|
|
33
|
+
this.angle = opts.angle != undefined ? opts.angle : 45
|
|
34
|
+
|
|
35
|
+
/** A function returning the corner line stroke style.
|
|
36
|
+
* @type {function(import('../Dataset.js').Cell,number,number):string} */
|
|
37
|
+
this.cornerLineStrokeColor = opts.cornerLineStrokeColor || ((c, r, zf) => "#333")
|
|
38
|
+
|
|
39
|
+
/** A function returning the corner line width.
|
|
40
|
+
* @type {function(import('../Dataset.js').Cell,number,number):number} */
|
|
41
|
+
this.cornerLineWidth = opts.cornerLineWidth || ((c, r, zf) => 0.8 * zf)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Draw cells as segments.
|
|
46
|
+
*
|
|
47
|
+
* @param {Array.<import("../Dataset.js").Cell>} cells
|
|
48
|
+
* @param {number} r
|
|
49
|
+
* @param {import("../GeoCanvas.js").GeoCanvas} cg
|
|
50
|
+
*/
|
|
51
|
+
draw(cells, r, cg) {
|
|
52
|
+
//filter
|
|
53
|
+
if (this.filter) cells = cells.filter(this.filter)
|
|
54
|
+
|
|
55
|
+
//zoom factor
|
|
56
|
+
const zf = cg.getZf()
|
|
57
|
+
|
|
58
|
+
let stat
|
|
59
|
+
if (this.heightCol) {
|
|
60
|
+
//if size is used, sort cells by size so that the biggest are drawn first
|
|
61
|
+
cells.sort((c1, c2) => c2[this.heightCol] - c1[this.heightCol])
|
|
62
|
+
//and compute statistics
|
|
63
|
+
stat = Style.getStatistics(cells, (c) => c[this.heightCol], true)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//nb categories - used for radar and agepyramid
|
|
67
|
+
const cats = Object.keys(this.color)
|
|
68
|
+
|
|
69
|
+
//draw in geo coordinates
|
|
70
|
+
cg.setCanvasTransform()
|
|
71
|
+
|
|
72
|
+
//half resolution
|
|
73
|
+
const r2 = r / 2
|
|
74
|
+
|
|
75
|
+
//height
|
|
76
|
+
/** @type {function(number,number,import("../Style").Stat|undefined,number):number} */
|
|
77
|
+
let h_ = this.height
|
|
78
|
+
|
|
79
|
+
//make sides
|
|
80
|
+
/** @type {Array.<Side>} */
|
|
81
|
+
const sides = []
|
|
82
|
+
|
|
83
|
+
//make horizontal sides
|
|
84
|
+
//sort cells by x and y
|
|
85
|
+
cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
|
|
86
|
+
let c1 = cells[0]
|
|
87
|
+
for (let i = 1; i < cells.length; i++) {
|
|
88
|
+
let c2 = cells[i]
|
|
89
|
+
|
|
90
|
+
if ((c1.y + r == c2.y) && (c1.x == c2.x))
|
|
91
|
+
//cells in same column and touch along horizontal side
|
|
92
|
+
//make shared side
|
|
93
|
+
sides.push({ x: c1.x + r2, y: c2.y, or: 'h', c1: c1, c2: c2 })
|
|
94
|
+
else {
|
|
95
|
+
//cells do not touch along horizontal side
|
|
96
|
+
//make two sides: top one for c1, bottom for c2
|
|
97
|
+
sides.push({ x: c1.x + r2, y: c1.y + r, or: 'h', c1: c1, c2: undefined })
|
|
98
|
+
sides.push({ x: c2.x + r2, y: c2.y, or: 'h', c1: undefined, c2: c2 })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
c1 = c2
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//make vertical sides
|
|
105
|
+
//sort cells by y and x
|
|
106
|
+
cells.sort((c1, c2) => (c2.y == c1.y ? c1.x - c2.x : c1.y - c2.y))
|
|
107
|
+
c1 = cells[0]
|
|
108
|
+
for (let i = 1; i < cells.length; i++) {
|
|
109
|
+
let c2 = cells[i]
|
|
110
|
+
|
|
111
|
+
if ((c1.x + r == c2.x) && (c1.y == c2.y))
|
|
112
|
+
//cells in same row and touch along vertical side
|
|
113
|
+
//make shared side
|
|
114
|
+
sides.push({ x: c2.x, y: c1.y + r2, or: 'v', c1: c1, c2: c2 })
|
|
115
|
+
else {
|
|
116
|
+
//cells do not touch along vertical side
|
|
117
|
+
//make two sides: right one for c1, left for c2
|
|
118
|
+
sides.push({ x: c1.x + r, y: c1.y + r2, or: 'v', c1: c1, c2: undefined })
|
|
119
|
+
sides.push({ x: c2.x, y: c2.y + r2, or: 'v', c1: undefined, c2: c2 })
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
c1 = c2
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//
|
|
126
|
+
if (sides.length == 0) return
|
|
127
|
+
|
|
128
|
+
//angle in radians
|
|
129
|
+
const aRad = this.angle * Math.PI / 180, cos = Math.cos(aRad), sin = Math.sin(aRad)
|
|
130
|
+
|
|
131
|
+
//draw in geo coordinates
|
|
132
|
+
cg.setCanvasTransform()
|
|
133
|
+
|
|
134
|
+
//sort sides so that the north east ones are drown first
|
|
135
|
+
sides.sort((s1, s2) => (Math.hypot(s2.x, s2.y) - Math.hypot(s1.x, s1.y)))
|
|
136
|
+
|
|
137
|
+
//draw sides
|
|
138
|
+
for (let s of sides) {
|
|
139
|
+
|
|
140
|
+
//heights - in geo
|
|
141
|
+
/** @type {number} */
|
|
142
|
+
const hG1 = s.c1 ? h_(s.c1[this.heightCol], r, stat, zf) : 0
|
|
143
|
+
/** @type {number} */
|
|
144
|
+
const hG2 = s.c2 ? h_(s.c2[this.heightCol], r, stat, zf) : 0
|
|
145
|
+
|
|
146
|
+
//compute totals for both cells
|
|
147
|
+
const total1 = computeTotal(s.c1, cats)
|
|
148
|
+
const total2 = computeTotal(s.c2, cats)
|
|
149
|
+
if (total1 == 0 && total2 == 0) continue
|
|
150
|
+
|
|
151
|
+
let cumul1 = 0, cumul2 = 0
|
|
152
|
+
for (let [column, color] of Object.entries(this.color)) {
|
|
153
|
+
//draw stripe of side s and category column
|
|
154
|
+
|
|
155
|
+
//get values for both cells
|
|
156
|
+
let v1 = s.c1 ? +s.c1[column] : 0
|
|
157
|
+
let v2 = s.c2 ? +s.c2[column] : 0
|
|
158
|
+
if (v1 == 0 && v2 == 0) continue
|
|
159
|
+
|
|
160
|
+
//compute heights
|
|
161
|
+
const h1 = total1 > 0 ? hG1 * cumul1 / total1 : 0
|
|
162
|
+
const h1n = total1 > 0 ? hG1 * (cumul1 + v1) / total1 : 0
|
|
163
|
+
const h2 = total2 > 0 ? hG2 * cumul2 / total2 : 0
|
|
164
|
+
const h2n = total2 > 0 ? hG2 * (cumul2 + v2) / total2 : 0
|
|
165
|
+
|
|
166
|
+
//make path
|
|
167
|
+
cg.ctx.beginPath()
|
|
168
|
+
if (s.or == "h") {
|
|
169
|
+
//horizontal side - vertical section
|
|
170
|
+
//bottom left
|
|
171
|
+
cg.ctx.moveTo(s.x + h1 * cos, s.y - r2 + h1 * sin)
|
|
172
|
+
//top left
|
|
173
|
+
cg.ctx.lineTo(s.x + h2 * cos, s.y + r2 + h2 * sin)
|
|
174
|
+
//top right
|
|
175
|
+
cg.ctx.lineTo(s.x + h2n * cos, s.y + r2 + h2n * sin)
|
|
176
|
+
//bottom right
|
|
177
|
+
cg.ctx.lineTo(s.x + h1n * cos, s.y - r2 + h1n * sin)
|
|
178
|
+
} else {
|
|
179
|
+
//vertical side - horizontal section
|
|
180
|
+
//bottom left
|
|
181
|
+
cg.ctx.moveTo(s.x - r2 + h1 * cos, s.y + h1 * sin)
|
|
182
|
+
//bottom right
|
|
183
|
+
cg.ctx.lineTo(s.x + r2 + h2 * cos, s.y + h2 * sin)
|
|
184
|
+
//top right
|
|
185
|
+
cg.ctx.lineTo(s.x + r2 + h2n * cos, s.y + h2n * sin)
|
|
186
|
+
//top left
|
|
187
|
+
cg.ctx.lineTo(s.x - r2 + h1n * cos, s.y + h1n * sin)
|
|
188
|
+
}
|
|
189
|
+
cg.ctx.closePath()
|
|
190
|
+
|
|
191
|
+
//fill
|
|
192
|
+
cg.ctx.fillStyle = color
|
|
193
|
+
cg.ctx.fill()
|
|
194
|
+
|
|
195
|
+
cumul1 += v1
|
|
196
|
+
cumul2 += v2
|
|
197
|
+
//break
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//draw corner lines
|
|
202
|
+
for (let c of cells) {
|
|
203
|
+
//height - in geo
|
|
204
|
+
const hG = h_(c[this.heightCol], r, stat, zf)
|
|
205
|
+
|
|
206
|
+
cg.ctx.strokeStyle = this.cornerLineStrokeColor ? this.cornerLineStrokeColor(c, r, zf) : "#333"
|
|
207
|
+
cg.ctx.lineWidth = this.cornerLineWidth ? this.cornerLineWidth(c, r, zf) : 0.8 * zf
|
|
208
|
+
|
|
209
|
+
cg.ctx.beginPath()
|
|
210
|
+
cg.ctx.moveTo(c.x + r2, c.y + r2)
|
|
211
|
+
cg.ctx.lineTo(c.x + r2 + hG * cos, c.y + r2 + hG * sin)
|
|
212
|
+
cg.ctx.closePath()
|
|
213
|
+
cg.ctx.stroke()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
//update legends
|
|
217
|
+
this.updateLegends({ style: this, r: r, zf: zf, sSize: stat })
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
const computeTotal = (cell, categories) => {
|
|
224
|
+
if (!cell) return 0
|
|
225
|
+
let total = 0
|
|
226
|
+
for (let column of categories) {
|
|
227
|
+
const v = cell[column]
|
|
228
|
+
if (!v) continue
|
|
229
|
+
total += +v
|
|
230
|
+
}
|
|
231
|
+
return total || 0
|
|
232
|
+
}
|
package/src/style/SideStyle.js
CHANGED
|
@@ -64,8 +64,6 @@ export class SideStyle extends Style {
|
|
|
64
64
|
/** @type {Array.<Side>} */
|
|
65
65
|
const sides = []
|
|
66
66
|
|
|
67
|
-
const epsilon = r * 0.001
|
|
68
|
-
|
|
69
67
|
//make horizontal sides
|
|
70
68
|
//sort cells by x and y
|
|
71
69
|
cells.sort((c1, c2) => (c2.x == c1.x ? c1.y - c2.y : c1.x - c2.x))
|
|
@@ -73,7 +71,7 @@ export class SideStyle extends Style {
|
|
|
73
71
|
for (let i = 1; i < cells.length; i++) {
|
|
74
72
|
let c2 = cells[i]
|
|
75
73
|
|
|
76
|
-
if (
|
|
74
|
+
if ((c1.y + r == c2.y) && (c1.x == c2.x))
|
|
77
75
|
//cells in same column and touch along horizontal side
|
|
78
76
|
//make shared side
|
|
79
77
|
sides.push({
|
|
@@ -109,7 +107,7 @@ export class SideStyle extends Style {
|
|
|
109
107
|
for (let i = 1; i < cells.length; i++) {
|
|
110
108
|
let c2 = cells[i]
|
|
111
109
|
|
|
112
|
-
if (
|
|
110
|
+
if ((c1.x + r == c2.x) && (c1.y == c2.y))
|
|
113
111
|
//cells in same row and touch along vertical side
|
|
114
112
|
//make shared side
|
|
115
113
|
sides.push({
|