@eturnity/eturnity_maths 1.0.2

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.
@@ -0,0 +1,553 @@
1
+ import { PlanarFaceTree } from 'planar-face-discovery'
2
+ import {
3
+ substractVector,
4
+ addVector,
5
+ multiplyVector,
6
+ dotProduct,
7
+ vectorLength
8
+ } from './vector'
9
+ import {
10
+ isAlmostSamePoint2D,
11
+ getPointOnLine,
12
+ getDistanceBetweenPoints,
13
+ isPointBetweenSegment,
14
+ isInsidePolygon,
15
+ verticalProjectionOnPlane,
16
+ polygonsHasSame2DOutline
17
+ } from './geometry'
18
+ import { groupBy } from 'lodash'
19
+ import { Polygon } from './objects/Polygon'
20
+ import { defaultBaseHeight, mmTolerance } from './config'
21
+
22
+ export function mergePolygons(polygonIdsToMerge, edges, layer) {
23
+ const outsideEdge = edges.filter((e) => e.belongsTo.length == 1)
24
+ const insideEdge = edges.filter((e) => e.belongsTo.length == 2)
25
+ const insideEdgeToKeep = insideEdge.filter((e) => {
26
+ return (
27
+ !polygonIdsToMerge.includes(e.belongsTo[0].polygonId) ||
28
+ !polygonIdsToMerge.includes(e.belongsTo[1].polygonId)
29
+ )
30
+ })
31
+ const newEdges = [...outsideEdge, ...insideEdgeToKeep]
32
+ //nodeList:
33
+ let dirtyNodeList = []
34
+ newEdges.forEach((edge) => {
35
+ dirtyNodeList.push(edge.outline[0])
36
+ dirtyNodeList.push(edge.outline[1])
37
+ })
38
+ const nodeList = []
39
+ for (let node of dirtyNodeList) {
40
+ const nodeFound = nodeList.find((n) => {
41
+ return n && isAlmostSamePoint2D(n, node, mmTolerance)
42
+ })
43
+ if (!nodeFound) {
44
+ nodeList.push(node)
45
+ }
46
+ }
47
+ //edgeList (just in term of indexes)
48
+ const edgeList = getEdgeListSimple(nodeList, newEdges)
49
+ const outlineList = getOutlineList(nodeList, edgeList)
50
+
51
+ const polygonList = []
52
+ for (let k = 0; k < outlineList.length; k++) {
53
+ polygonList.push(new Polygon(outlineList[k], layer))
54
+ }
55
+ return polygonList
56
+ }
57
+
58
+ export function splitPolygons(
59
+ constructionPolyline,
60
+ edges,
61
+ roofs,
62
+ isClosedContructionPolyline
63
+ ) {
64
+ //if(isClosedContructionPolyline){
65
+ constructionPolyline.outline.forEach((p) => {
66
+ let supportRoof = roofs.find((roof) => isInsidePolygon(p, roof.outline))
67
+ if (supportRoof) {
68
+ p.z = verticalProjectionOnPlane(
69
+ p,
70
+ supportRoof.normalVector,
71
+ supportRoof.flatOutline[0]
72
+ ).z
73
+ } else {
74
+ p.z = defaultBaseHeight
75
+ }
76
+ })
77
+
78
+ isClosedContructionPolyline = isClosedContructionPolyline ? 1 : 0
79
+ //1. find intersection between constructionPolyline and edges
80
+ const roofEdge = edges.filter((e) => e.layer == 'roof')
81
+ let intersections = getIntersections(constructionPolyline, roofEdge,isClosedContructionPolyline)
82
+ //2. gather all nodes+intersection in a list[[x,y],[x,y]]
83
+ const nodeList = getNodeList(intersections, roofEdge, constructionPolyline)
84
+ //3. generate all edges not cut, those cut and those from construction polyline
85
+
86
+ const edgeList = getEdgeList(
87
+ nodeList,
88
+ intersections,
89
+ roofEdge,
90
+ constructionPolyline,
91
+ isClosedContructionPolyline
92
+ )
93
+ //4. run planar-face-discovery to get all cycles and rebuild our polygons
94
+ const outlineList = getOutlineList(nodeList, edgeList, roofs)
95
+ const polygonList = []
96
+ for (let k = 0; k < outlineList.length; k++) {
97
+ polygonList.push(new Polygon(outlineList[k], 'roof'))
98
+ }
99
+ return polygonList
100
+ }
101
+
102
+ export function getIntersections(constructionPolyline, edges, isClosedContructionPolyline=0) {
103
+ let intersections = []
104
+ const polylineLength = constructionPolyline.outline.length
105
+ for (let k = 0; k < polylineLength - 1 + isClosedContructionPolyline; k++) {
106
+ const segStart = constructionPolyline.outline[k]
107
+ const segEnd = constructionPolyline.outline[(k + 1) % polylineLength]
108
+ for (let j = 0; j < edges.length; j++) {
109
+ const edgeStart = edges[j].outline[0]
110
+ const edgeEnd = edges[j].outline[1]
111
+ const inters = getIntersectionSegmentWithParams(segStart, segEnd, edgeStart, edgeEnd)
112
+ inters.forEach((inter) => {
113
+ intersections.push({
114
+ polylineIndex: k,
115
+ edgeIndex: j,
116
+ polylineParam: inter.param1,
117
+ edgeParam: inter.param2,
118
+ point: inter.point
119
+ })
120
+ })
121
+ }
122
+ }
123
+ intersections = intersections.filter(
124
+ (inter) =>
125
+ inter.polylineParam >= 0 &&
126
+ inter.polylineParam <= 1 &&
127
+ inter.edgeParam >= 0 &&
128
+ inter.edgeParam <= 1
129
+ )
130
+ return intersections
131
+ }
132
+
133
+ export function getNodeList(intersections, edges, constructionPolyline) {
134
+ let nodeList = []
135
+ edges.forEach((edge) => {
136
+ nodeList.push(edge.outline[0])
137
+ nodeList.push(edge.outline[1])
138
+ })
139
+
140
+ constructionPolyline.outline.forEach((p) => {
141
+ const node = nodeList.find((n) => {
142
+ return isAlmostSamePoint2D(p, n, mmTolerance)
143
+ })
144
+ if (!node) {
145
+ nodeList.push(p)
146
+ }
147
+ })
148
+ const polylineLength = constructionPolyline.outline.length
149
+ const polylineStart = nodeList.find((p) => {
150
+ return isAlmostSamePoint2D(p, constructionPolyline.outline[0], mmTolerance)
151
+ })
152
+ const polylineEnd = nodeList.find((p) =>
153
+ isAlmostSamePoint2D(
154
+ p,
155
+ constructionPolyline.outline[polylineLength - 1],
156
+ mmTolerance
157
+ )
158
+ )
159
+ constructionPolyline.outline.forEach((p, index) => {
160
+ if (p.z == defaultBaseHeight || !p.z) {
161
+ p.z =
162
+ polylineStart.z +
163
+ (index / (polylineLength - 1)) * (polylineEnd.z - polylineStart.z)
164
+ }
165
+ nodeList.push(p)
166
+ })
167
+ intersections.forEach((inter) => {
168
+ nodeList.push(inter.point)
169
+ })
170
+ const nodeListClean = []
171
+ for (let k = 0; k < nodeList.length; k++) {
172
+ const node = nodeList[k]
173
+ const newNode = nodeListClean.find((n) => {
174
+ return n && isAlmostSamePoint2D(n, node, mmTolerance)
175
+ })
176
+ if (!newNode) {
177
+ nodeListClean.push(node)
178
+ }
179
+ }
180
+ return nodeListClean
181
+ }
182
+
183
+ function getEdgeListSimple(nodeList, edges) {
184
+ //create a list of edge in term of node Index from nodeList
185
+ const edgeList = []
186
+ for (let index in edges) {
187
+ const node_0_index = nodeList.findIndex((n) =>
188
+ isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
189
+ )
190
+ const node_1_index = nodeList.findIndex((n) =>
191
+ isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
192
+ )
193
+ edgeList.push([node_0_index, node_1_index])
194
+ }
195
+
196
+ const cleanEdgeList = []
197
+ edgeList.forEach((edge) => {
198
+ const cleanEdge = cleanEdgeList.find(
199
+ (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
200
+ )
201
+ if (!cleanEdge && edge[0] != edge[1]) {
202
+ cleanEdgeList.push(edge)
203
+ }
204
+ })
205
+ return cleanEdgeList
206
+ }
207
+
208
+ export function getEdgeList(
209
+ nodeList,
210
+ intersections,
211
+ edges,
212
+ constructionPolyline,
213
+ isClosedContructionPolyline
214
+ ) {
215
+ //to find all edges, edges untouched and untouched construction line are first added
216
+ //then we sort intersection according to their ratio within an edge to add cut edges,
217
+ //same operation for construction line.
218
+ const edgeList = []
219
+ let intersectionByEdge = groupBy(intersections, (inter) => {
220
+ return inter.edgeIndex
221
+ })
222
+ let intersectionByPolyline = groupBy(intersections, (inter) => {
223
+ return inter.polylineIndex
224
+ })
225
+ intersectionByEdge = Object.values(intersectionByEdge)
226
+ intersectionByPolyline = Object.values(intersectionByPolyline)
227
+ intersectionByEdge.forEach((intersections) =>
228
+ intersections.sort((a, b) => {
229
+ return a.edgeParam - b.edgeParam
230
+ })
231
+ )
232
+ intersectionByPolyline.forEach((intersections) =>
233
+ intersections.sort((a, b) => {
234
+ return a.polylineParam - b.polylineParam
235
+ })
236
+ )
237
+ //get used and untouched edge from polyline and edges
238
+
239
+ const polylineUsedIndexes = intersectionByPolyline.map(
240
+ (i) => i[0].polylineIndex
241
+ )
242
+ const edgeUsedIndexes = intersectionByEdge.map((i) => i[0].edgeIndex)
243
+
244
+ const untouchedEdgeIndexes = [...Array(edges.length).keys()].filter(
245
+ (k) => !edgeUsedIndexes.includes(k)
246
+ )
247
+
248
+ const untouchedPolylineIndexes = [
249
+ ...Array(
250
+ constructionPolyline.outline.length - 1 + isClosedContructionPolyline
251
+ ).keys()
252
+ ].filter((k) => !polylineUsedIndexes.includes(k))
253
+
254
+ //get untouchEdge edge
255
+ untouchedEdgeIndexes.forEach((index) => {
256
+ const node_0_index = nodeList.findIndex((n) =>
257
+ isAlmostSamePoint2D(edges[index].outline[0], n, mmTolerance)
258
+ )
259
+ const node_1_index = nodeList.findIndex((n) =>
260
+ isAlmostSamePoint2D(edges[index].outline[1], n, mmTolerance)
261
+ )
262
+ edgeList.push([node_0_index, node_1_index])
263
+ })
264
+ //get untouchPolyline edge
265
+ untouchedPolylineIndexes.forEach((index) => {
266
+ const length = constructionPolyline.outline.length
267
+ const node_0_index = nodeList.findIndex((n) =>
268
+ isAlmostSamePoint2D(constructionPolyline.outline[index], n, mmTolerance)
269
+ )
270
+ const node_1_index = nodeList.findIndex((n) =>
271
+ isAlmostSamePoint2D(
272
+ constructionPolyline.outline[(index + 1) % length],
273
+ n,
274
+ mmTolerance
275
+ )
276
+ )
277
+ edgeList.push([node_0_index, node_1_index])
278
+ })
279
+ //get touchedEdge edge
280
+ intersectionByEdge.forEach((intersectionOnEdge) => {
281
+ const length = intersectionOnEdge.length
282
+ const node_0_index_start = nodeList.findIndex((n) =>
283
+ isAlmostSamePoint2D(
284
+ edges[intersectionOnEdge[0].edgeIndex].outline[0],
285
+ n,
286
+ mmTolerance
287
+ )
288
+ )
289
+ const node_1_index_start = nodeList.findIndex((n) =>
290
+ isAlmostSamePoint2D(intersectionOnEdge[0].point, n, mmTolerance)
291
+ )
292
+ edgeList.push([node_0_index_start, node_1_index_start])
293
+
294
+ for (let k = 0; k < length - 1; k++) {
295
+ const node_0_index = nodeList.findIndex((n) =>
296
+ isAlmostSamePoint2D(intersectionOnEdge[k].point, n, mmTolerance)
297
+ )
298
+ const node_1_index = nodeList.findIndex((n) =>
299
+ isAlmostSamePoint2D(
300
+ intersectionOnEdge[(k + 1) % length].point,
301
+ n,
302
+ mmTolerance
303
+ )
304
+ )
305
+ edgeList.push([node_0_index, node_1_index])
306
+ }
307
+
308
+ const node_0_index_end = nodeList.findIndex((n) =>
309
+ isAlmostSamePoint2D(intersectionOnEdge[length - 1].point, n, mmTolerance)
310
+ )
311
+ const node_1_index_end = nodeList.findIndex((n) =>
312
+ isAlmostSamePoint2D(
313
+ edges[intersectionOnEdge[0].edgeIndex].outline[1],
314
+ n,
315
+ mmTolerance
316
+ )
317
+ )
318
+ edgeList.push([node_0_index_end, node_1_index_end])
319
+ })
320
+ //get touchedPolyline edge
321
+ intersectionByPolyline.forEach((intersectionOnPolyline) => {
322
+ const length = intersectionOnPolyline.length
323
+ const lengthPolyline = constructionPolyline.outline.length
324
+ const node_0_index_start = nodeList.findIndex((n) =>
325
+ isAlmostSamePoint2D(
326
+ constructionPolyline.outline[intersectionOnPolyline[0].polylineIndex],
327
+ n,
328
+ mmTolerance
329
+ )
330
+ )
331
+ const node_1_index_start = nodeList.findIndex((n) =>
332
+ isAlmostSamePoint2D(intersectionOnPolyline[0].point, n, mmTolerance)
333
+ )
334
+ edgeList.push([node_0_index_start, node_1_index_start])
335
+
336
+ for (let k = 0; k < length - 1; k++) {
337
+ const node_0_index = nodeList.findIndex((n) =>
338
+ isAlmostSamePoint2D(intersectionOnPolyline[k].point, n, mmTolerance)
339
+ )
340
+ const node_1_index = nodeList.findIndex((n) =>
341
+ isAlmostSamePoint2D(
342
+ intersectionOnPolyline[(k + 1) % length].point,
343
+ n,
344
+ mmTolerance
345
+ )
346
+ )
347
+ edgeList.push([node_0_index, node_1_index])
348
+ }
349
+
350
+ const node_0_index_end = nodeList.findIndex((n) =>
351
+ isAlmostSamePoint2D(
352
+ intersectionOnPolyline[length - 1].point,
353
+ n,
354
+ mmTolerance
355
+ )
356
+ )
357
+ const node_1_index_end = nodeList.findIndex((n) =>
358
+ isAlmostSamePoint2D(
359
+ constructionPolyline.outline[
360
+ (intersectionOnPolyline[0].polylineIndex + 1)%lengthPolyline
361
+ ],
362
+ n,
363
+ mmTolerance
364
+ )
365
+ )
366
+ edgeList.push([node_0_index_end, node_1_index_end])
367
+ })
368
+
369
+ const cleanEdgeList = []
370
+ edgeList.forEach((edge) => {
371
+ const cleanEdge = cleanEdgeList.find(
372
+ (cleanEdge) => edge[0] == cleanEdge[0] && edge[1] == cleanEdge[1]
373
+ )
374
+ if (!cleanEdge && edge[0] != edge[1]) {
375
+ cleanEdgeList.push(edge)
376
+ }
377
+ })
378
+
379
+ return cleanEdgeList
380
+ }
381
+ function forestRecursion(cycleTree) {
382
+ if (cycleTree.children.length > 0) {
383
+ let cycles = cycleTree.children.reduce(function (done, curr) {
384
+ done.push(...forestRecursion(curr))
385
+ return done
386
+ }, [])
387
+ if (cycleTree.cycle.length) {
388
+ //cycles.push(cycleTree.cycle)
389
+ }
390
+ return cycles
391
+ } else {
392
+ return [cycleTree.cycle]
393
+ }
394
+ }
395
+ export function getOutlineList(nodeList, edgeList, roofs = []) {
396
+ const mode = 'outline'
397
+ const minX = Math.min(...nodeList.map((n) => n.x))
398
+ const minY = Math.min(...nodeList.map((n) => n.y))
399
+ const nodes = nodeList.map((n) => [n.x - minX, n.y - minY])
400
+ const solver = new PlanarFaceTree()
401
+ const result = solver.discover(nodes, edgeList)
402
+ const cycles = []
403
+ if (mode == 'forest') {
404
+ return result.forest
405
+ }
406
+ for (let k in result.forest) {
407
+ const cycleTree = result.forest[k]
408
+ let cycleArray = forestRecursion(cycleTree)
409
+ cycles.push(...cycleArray)
410
+ }
411
+
412
+ let outlines = cycles
413
+ .map((cycle) => {
414
+ return cycle.map((index) => {
415
+ return {
416
+ x: nodeList[index].x,
417
+ y: nodeList[index].y,
418
+ z: nodeList[index].z
419
+ }
420
+ })
421
+ })
422
+ .filter((outline) => outline.length > 0)
423
+ outlines.forEach((outline) => outline.pop())
424
+
425
+ //check for existing roofs
426
+ outlines = outlines.map((outline) => {
427
+ let roof = roofs.find((roof) =>
428
+ polygonsHasSame2DOutline(outline, roof.outline)
429
+ )
430
+ if (roof) {
431
+ outline = JSON.parse(JSON.stringify(roof.outline))
432
+ }
433
+ return outline
434
+ })
435
+ return outlines
436
+ }
437
+
438
+ function getIntersectionSegmentWithParams(A, B, C, D) {
439
+ const u = substractVector(B, A)
440
+ const v = substractVector(D, C)
441
+ let h, j, M, P
442
+ //A+hu=C+jv
443
+ const u_normal = { x: -u.y, y: u.x, z: 0 }
444
+ const v_normal = { x: -v.y, y: v.x, z: 0 }
445
+ const denom_u = dotProduct(v, u_normal)
446
+ const denom_v = dotProduct(u, v_normal)
447
+ const AB = vectorLength(u)
448
+ const CD = vectorLength(v)
449
+ if (AB < mmTolerance && CD < mmTolerance) {
450
+ if (getDistanceBetweenPoints(A, C) < mmTolerance * 2) {
451
+ return [
452
+ {
453
+ point: A,
454
+ param1: 0.5,
455
+ param2: 0.5
456
+ }
457
+ ]
458
+ }
459
+ } else if (AB == 0) {
460
+ //let's check if CD go through A
461
+ P = getPointOnLine(A, C, D)
462
+ if (getDistanceBetweenPoints(P, A) < mmTolerance) {
463
+ j = vectorLength(substractVector(C, A)) / CD
464
+ return [
465
+ {
466
+ point: A,
467
+ param1: 0,
468
+ param2: j
469
+ }
470
+ ]
471
+ }
472
+ } else if (CD == 0) {
473
+ //let's check if AB go through C
474
+ M = getPointOnLine(C, A, B)
475
+ if (getDistanceBetweenPoints(M, C) < mmTolerance) {
476
+ h = vectorLength(substractVector(C, A)) / AB
477
+ return [
478
+ {
479
+ point: C,
480
+ param1: h,
481
+ param2: 0
482
+ }
483
+ ]
484
+ }
485
+ }
486
+ if (Math.abs(denom_u) / (AB * CD) < 0.01) {
487
+ //u,v parallel
488
+ //let's check if they are close
489
+ //distance from A to CD
490
+ P = getPointOnLine(A, C, D)
491
+ M = getPointOnLine(B, C, D)
492
+ if (
493
+ getDistanceBetweenPoints(A, P) < mmTolerance ||
494
+ getDistanceBetweenPoints(B, M) < mmTolerance
495
+ ) {
496
+ //AB and CD are colinear
497
+ //let's find which point are in the middle
498
+ const intersections = []
499
+ //A between CD?
500
+ if (isPointBetweenSegment(A, C, D)) {
501
+ j = getDistanceBetweenPoints(A, C) / CD
502
+ intersections.push({
503
+ point: A,
504
+ param1: 0,
505
+ param2: j
506
+ })
507
+ }
508
+ //B between CD
509
+ if (isPointBetweenSegment(B, C, D)) {
510
+ j = getDistanceBetweenPoints(B, C) / CD
511
+ intersections.push({
512
+ point: B,
513
+ param1: 1,
514
+ param2: j
515
+ })
516
+ }
517
+ //C between AB
518
+ if (isPointBetweenSegment(C, A, B)) {
519
+ h = getDistanceBetweenPoints(A, C) / AB
520
+ intersections.push({
521
+ point: C,
522
+ param1: h,
523
+ param2: 0
524
+ })
525
+ }
526
+ //D between AB
527
+ if (isPointBetweenSegment(D, A, B)) {
528
+ h = getDistanceBetweenPoints(A, D) / AB
529
+ intersections.push({
530
+ point: D,
531
+ param1: h,
532
+ param2: 1
533
+ })
534
+ }
535
+ return intersections
536
+ }
537
+ return []
538
+ }
539
+ j = dotProduct(substractVector(A, C), u_normal) / denom_u
540
+ h = dotProduct(substractVector(C, A), v_normal) / denom_v
541
+ if (j < 0 || j > 1 || h < 0 || h > 1) {
542
+ return []
543
+ }
544
+ const point = addVector(A, multiplyVector(h, u))
545
+ point.z = C.z + j * (D.z - C.z)
546
+ return [
547
+ {
548
+ point,
549
+ param1: h,
550
+ param2: j
551
+ }
552
+ ]
553
+ }
@@ -0,0 +1,10 @@
1
+ import { deltaLatLngToDistance,distanceToDeltaLatLng } from "../index"
2
+
3
+ test('deltaLatLngToDistance is inverse of distanceToDeltaLatLng x', () => {
4
+ let {lat,lng}=distanceToDeltaLatLng(1000, 2000,45)
5
+ expect(deltaLatLngToDistance(lat,lng,45).x).toBe(1000);
6
+ });
7
+ test('deltaLatLngToDistance is inverse of distanceToDeltaLatLng y', () => {
8
+ let {lat,lng}=distanceToDeltaLatLng(1000, 2000,45)
9
+ expect(deltaLatLngToDistance(lat,lng,45).y).toBe(2000);
10
+ });
package/src/vector.js ADDED
@@ -0,0 +1,56 @@
1
+ export function addVector(u, v) {
2
+ const result = {}
3
+ result.x = u.x + v.x
4
+ result.y = u.y + v.y
5
+ result.z = u.z + v.z
6
+ return result
7
+ }
8
+
9
+ export function substractVector(u, v) {
10
+ const result = {}
11
+ result.x = u.x - v.x
12
+ result.y = u.y - v.y
13
+ result.z = u.z - v.z
14
+ return result
15
+ }
16
+
17
+ export function multiplyVector(lambda, u) {
18
+ const result = {}
19
+ result.x = u.x * lambda
20
+ result.y = u.y * lambda
21
+ result.z = u.z * lambda
22
+ return result
23
+ }
24
+ export function dotProduct(u, v) {
25
+ const result = u.x * v.x + u.y * v.y + u.z * v.z
26
+ return result
27
+ }
28
+ export function crossProduct(u, v) {
29
+ const result = {}
30
+ result.x = u.y * v.z - u.z * v.y
31
+ result.y = u.z * v.x - u.x * v.z
32
+ result.z = u.x * v.y - u.y * v.x
33
+ return result
34
+ }
35
+ export function meanVector(listVector) {
36
+ let meanPoint = {}
37
+ let sumPoint = { x: 0, y: 0, z: 0 }
38
+ listVector.forEach((p) => {
39
+ sumPoint = addVector(sumPoint, p)
40
+ })
41
+ meanPoint = multiplyVector(1 / listVector.length, sumPoint)
42
+ return meanPoint
43
+ }
44
+ export function vectorLength(v) {
45
+ return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
46
+ }
47
+ export function vectorLength2D(v) {
48
+ return Math.sqrt(v.x * v.x + v.y * v.y)
49
+ }
50
+ export function normalizeVector(v) {
51
+ let length = vectorLength(v)
52
+ if (length == 0) {
53
+ return v
54
+ }
55
+ return multiplyVector(1 / length, v)
56
+ }
@@ -0,0 +1,11 @@
1
+ const path = require('path')
2
+
3
+ module.exports = {
4
+ configureWebpack: {
5
+ resolve: {
6
+ alias: {
7
+ '@': path.resolve(__dirname, '/')
8
+ }
9
+ }
10
+ }
11
+ }