microboard-temp 0.5.75 → 0.5.77
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/cjs/browser.js +116 -99
- package/dist/cjs/index.js +116 -99
- package/dist/cjs/node.js +116 -99
- package/dist/esm/browser.js +116 -99
- package/dist/esm/index.js +116 -99
- package/dist/esm/node.js +116 -99
- package/dist/types/Settings.d.ts +1 -0
- package/package.json +1 -1
package/dist/cjs/node.js
CHANGED
|
@@ -8110,7 +8110,8 @@ var conf = {
|
|
|
8110
8110
|
DECK_VERTICAL_OFFSET: 2,
|
|
8111
8111
|
CARD_DIMENSIONS: { width: 250, height: 400 },
|
|
8112
8112
|
DEFAULT_GAME_ITEM_DIMENSIONS: { width: 200, height: 200 },
|
|
8113
|
-
MAX_CARD_SIZE: 500
|
|
8113
|
+
MAX_CARD_SIZE: 500,
|
|
8114
|
+
CONNECTOR_ITEM_OFFSET: 20
|
|
8114
8115
|
};
|
|
8115
8116
|
initDefaultI18N();
|
|
8116
8117
|
|
|
@@ -39142,7 +39143,6 @@ function radiansBetweenPoints(point1, point22) {
|
|
|
39142
39143
|
}
|
|
39143
39144
|
|
|
39144
39145
|
// src/Items/Connector/getLine/findOrthogonalPath.ts
|
|
39145
|
-
var ITEM_OFFSET = 1;
|
|
39146
39146
|
function getDirection(from, to) {
|
|
39147
39147
|
if (!to) {
|
|
39148
39148
|
return null;
|
|
@@ -39183,9 +39183,9 @@ function getNeighbors(node2, grid, obstacles) {
|
|
|
39183
39183
|
{ x: node2.xGrid, y: node2.yGrid + 1 }
|
|
39184
39184
|
];
|
|
39185
39185
|
for (const pos of potentialNeighbors) {
|
|
39186
|
-
if (pos.x >= 0 && pos.x < grid.length && pos.y >= 0) {
|
|
39186
|
+
if (pos.x >= 0 && pos.x < grid.length && pos.y >= 0 && grid[pos.x] && grid[pos.x][pos.y]) {
|
|
39187
39187
|
const newPoint = grid[pos.x][pos.y];
|
|
39188
|
-
if (newPoint && !obstacles.some((obstacle) => obstacle.isAlmostInside(newPoint,
|
|
39188
|
+
if (newPoint && !obstacles.some((obstacle) => obstacle.isAlmostInside(newPoint, conf.CONNECTOR_ITEM_OFFSET - 1))) {
|
|
39189
39189
|
neighbors.push({
|
|
39190
39190
|
point: newPoint,
|
|
39191
39191
|
costSoFar: 0,
|
|
@@ -39213,16 +39213,20 @@ function findCenterLine(grid, start, end, middle) {
|
|
|
39213
39213
|
}
|
|
39214
39214
|
if (width > height) {
|
|
39215
39215
|
const centerIdx = grid.findIndex((row2) => row2[0].x === middlePoint.x || Math.abs(row2[0].x - middlePoint.x) < 0.01);
|
|
39216
|
-
|
|
39217
|
-
|
|
39218
|
-
|
|
39216
|
+
if (centerIdx !== -1) {
|
|
39217
|
+
for (let y = 0;y < grid[0].length; y++) {
|
|
39218
|
+
if (grid[centerIdx][y] && grid[centerIdx][y].x >= min2.x - 0.01 && grid[centerIdx][y].x <= max2.x + 0.01 && grid[centerIdx][y].y >= min2.y - 0.01 && grid[centerIdx][y].y <= max2.y + 0.01) {
|
|
39219
|
+
centerLine.push(grid[centerIdx][y]);
|
|
39220
|
+
}
|
|
39219
39221
|
}
|
|
39220
39222
|
}
|
|
39221
39223
|
} else {
|
|
39222
39224
|
const centerIdx = grid[0].findIndex((point5) => point5.y === middlePoint.y || Math.abs(point5.y - middlePoint.y) < 0.01);
|
|
39223
|
-
|
|
39224
|
-
|
|
39225
|
-
|
|
39225
|
+
if (centerIdx !== -1) {
|
|
39226
|
+
for (let x = 0;x < grid.length; x++) {
|
|
39227
|
+
if (grid[x][centerIdx] && grid[x][centerIdx].x >= min2.x - 0.01 && grid[x][centerIdx].x <= max2.x + 0.01 && grid[x][centerIdx].y >= min2.y - 0.01 && grid[x][centerIdx].y <= max2.y + 0.01) {
|
|
39228
|
+
centerLine.push(grid[x][centerIdx]);
|
|
39229
|
+
}
|
|
39226
39230
|
}
|
|
39227
39231
|
}
|
|
39228
39232
|
}
|
|
@@ -39233,69 +39237,24 @@ function createGrid(start, end, toVisitPoints = []) {
|
|
|
39233
39237
|
const endDir = getPointerDirection(end);
|
|
39234
39238
|
const revertMapDir = { top: 0, bottom: 1, right: 2, left: 3 };
|
|
39235
39239
|
const offsetMap = {
|
|
39236
|
-
top: { x: 0, y: -
|
|
39237
|
-
bottom: { x: 0, y:
|
|
39238
|
-
right: { x:
|
|
39239
|
-
left: { x: -
|
|
39240
|
+
top: { x: 0, y: -conf.CONNECTOR_ITEM_OFFSET },
|
|
39241
|
+
bottom: { x: 0, y: conf.CONNECTOR_ITEM_OFFSET },
|
|
39242
|
+
right: { x: conf.CONNECTOR_ITEM_OFFSET, y: 0 },
|
|
39243
|
+
left: { x: -conf.CONNECTOR_ITEM_OFFSET, y: 0 }
|
|
39240
39244
|
};
|
|
39241
39245
|
const horizontalLines = [];
|
|
39242
39246
|
const verticalLines = [];
|
|
39243
|
-
if (start.pointType !== "Board") {
|
|
39244
|
-
const itemMbr = start.item.getMbr();
|
|
39245
|
-
verticalLines.push(itemMbr.left - ITEM_OFFSET, itemMbr.left, itemMbr.right, itemMbr.right + ITEM_OFFSET);
|
|
39246
|
-
horizontalLines.push(itemMbr.top - ITEM_OFFSET, itemMbr.top, itemMbr.bottom, itemMbr.bottom + ITEM_OFFSET);
|
|
39247
|
-
}
|
|
39248
|
-
if (end.pointType !== "Board") {
|
|
39249
|
-
const itemMbr = end.item.getMbr();
|
|
39250
|
-
verticalLines.push(itemMbr.left - ITEM_OFFSET, itemMbr.left, itemMbr.right, itemMbr.right + ITEM_OFFSET);
|
|
39251
|
-
horizontalLines.push(itemMbr.top - ITEM_OFFSET, itemMbr.top, itemMbr.bottom, itemMbr.bottom + ITEM_OFFSET);
|
|
39252
|
-
}
|
|
39253
|
-
const tempStart = start;
|
|
39254
|
-
const tempEnd = end;
|
|
39255
|
-
const middle = new Point((tempStart.x + tempEnd.x) / 2, (tempStart.y + tempEnd.y) / 2);
|
|
39256
|
-
horizontalLines.push(middle.y, tempStart.y, tempEnd.y);
|
|
39257
|
-
verticalLines.push(middle.x, tempStart.x, tempEnd.x);
|
|
39258
|
-
toVisitPoints.forEach((p3) => {
|
|
39259
|
-
horizontalLines.push(p3.y);
|
|
39260
|
-
verticalLines.push(p3.x);
|
|
39261
|
-
});
|
|
39262
|
-
const uniqueHorizontalLines = Array.from(new Set(horizontalLines)).sort((a2, b) => a2 - b);
|
|
39263
|
-
const uniqueVerticalLines = Array.from(new Set(verticalLines)).sort((a2, b) => a2 - b);
|
|
39264
39247
|
let newStart;
|
|
39265
39248
|
let newEnd;
|
|
39266
39249
|
const processPoint = (point5, dir2) => {
|
|
39267
39250
|
const itemMbr = point5.item.getMbr();
|
|
39268
|
-
const
|
|
39251
|
+
const mbrFloored = new Mbr(Math.floor(itemMbr.left), Math.floor(itemMbr.top), Math.floor(itemMbr.right), Math.floor(itemMbr.bottom));
|
|
39252
|
+
const pointOnMbr = mbrFloored.getLines()[revertMapDir[dir2]].getNearestPointOnLineSegment(point5);
|
|
39269
39253
|
const newPoint = Object.create(Object.getPrototypeOf(point5), Object.getOwnPropertyDescriptors(point5));
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
newPoint.y = uniqueHorizontalLines[nextYIndex];
|
|
39275
|
-
newPoint.x = pointOnMbr.x;
|
|
39276
|
-
}
|
|
39277
|
-
} else if (dir2 === "bottom") {
|
|
39278
|
-
const currentYIndex = uniqueHorizontalLines.findIndex((y) => Math.abs(y - pointOnMbr.y) < 0.01);
|
|
39279
|
-
const nextYIndex = currentYIndex + 1;
|
|
39280
|
-
if (nextYIndex < uniqueHorizontalLines.length) {
|
|
39281
|
-
newPoint.y = uniqueHorizontalLines[nextYIndex];
|
|
39282
|
-
newPoint.x = pointOnMbr.x;
|
|
39283
|
-
}
|
|
39284
|
-
} else if (dir2 === "left") {
|
|
39285
|
-
const currentXIndex = uniqueVerticalLines.findIndex((x) => Math.abs(x - pointOnMbr.x) < 0.01);
|
|
39286
|
-
const nextXIndex = currentXIndex - 1;
|
|
39287
|
-
if (nextXIndex >= 0) {
|
|
39288
|
-
newPoint.x = uniqueVerticalLines[nextXIndex];
|
|
39289
|
-
newPoint.y = pointOnMbr.y;
|
|
39290
|
-
}
|
|
39291
|
-
} else if (dir2 === "right") {
|
|
39292
|
-
const currentXIndex = uniqueVerticalLines.findIndex((x) => Math.abs(x - pointOnMbr.x) < 0.01);
|
|
39293
|
-
const nextXIndex = currentXIndex + 1;
|
|
39294
|
-
if (nextXIndex < uniqueVerticalLines.length) {
|
|
39295
|
-
newPoint.x = uniqueVerticalLines[nextXIndex];
|
|
39296
|
-
newPoint.y = pointOnMbr.y;
|
|
39297
|
-
}
|
|
39298
|
-
}
|
|
39254
|
+
newPoint.x = pointOnMbr.x + offsetMap[dir2].x;
|
|
39255
|
+
newPoint.y = pointOnMbr.y + offsetMap[dir2].y;
|
|
39256
|
+
verticalLines.push(mbrFloored.left - conf.CONNECTOR_ITEM_OFFSET, mbrFloored.left, pointOnMbr.x, mbrFloored.right, mbrFloored.right + conf.CONNECTOR_ITEM_OFFSET);
|
|
39257
|
+
horizontalLines.push(mbrFloored.top - conf.CONNECTOR_ITEM_OFFSET, mbrFloored.top, pointOnMbr.y, mbrFloored.bottom, mbrFloored.bottom + conf.CONNECTOR_ITEM_OFFSET);
|
|
39299
39258
|
return newPoint;
|
|
39300
39259
|
};
|
|
39301
39260
|
if (start.pointType !== "Board" && startDir) {
|
|
@@ -39304,6 +39263,17 @@ function createGrid(start, end, toVisitPoints = []) {
|
|
|
39304
39263
|
if (end.pointType !== "Board" && endDir) {
|
|
39305
39264
|
newEnd = processPoint(end, endDir);
|
|
39306
39265
|
}
|
|
39266
|
+
const finalStart = newStart || start;
|
|
39267
|
+
const finalEnd = newEnd || end;
|
|
39268
|
+
const middle = new Point((finalStart.x + finalEnd.x) / 2, (finalStart.y + finalEnd.y) / 2);
|
|
39269
|
+
horizontalLines.push(middle.y, finalStart.y, finalEnd.y);
|
|
39270
|
+
verticalLines.push(middle.x, finalStart.x, finalEnd.x);
|
|
39271
|
+
toVisitPoints.forEach((p3) => {
|
|
39272
|
+
horizontalLines.push(p3.y);
|
|
39273
|
+
verticalLines.push(p3.x);
|
|
39274
|
+
});
|
|
39275
|
+
const uniqueHorizontalLines = Array.from(new Set(horizontalLines)).sort((a2, b) => a2 - b);
|
|
39276
|
+
const uniqueVerticalLines = Array.from(new Set(verticalLines)).sort((a2, b) => a2 - b);
|
|
39307
39277
|
const grid = uniqueVerticalLines.map((x) => uniqueHorizontalLines.map((y) => new Point(x, y)));
|
|
39308
39278
|
return {
|
|
39309
39279
|
grid,
|
|
@@ -39312,18 +39282,27 @@ function createGrid(start, end, toVisitPoints = []) {
|
|
|
39312
39282
|
middlePoint: middle
|
|
39313
39283
|
};
|
|
39314
39284
|
}
|
|
39315
|
-
function findPath(start, end, grid, obstacles, newStart, newEnd) {
|
|
39316
|
-
const
|
|
39317
|
-
|
|
39318
|
-
|
|
39319
|
-
|
|
39285
|
+
function findPath(start, end, grid, obstacles, existingPath, newStart, newEnd) {
|
|
39286
|
+
const startRowIndex = grid.findIndex((row2) => row2.some((point5) => point5.barelyEqual(start)));
|
|
39287
|
+
if (startRowIndex === -1) {
|
|
39288
|
+
throw new Error("Start point not found in the grid row");
|
|
39289
|
+
}
|
|
39290
|
+
const startPointIndex = grid[startRowIndex].findIndex((point5) => point5.barelyEqual(start));
|
|
39291
|
+
if (startPointIndex === -1) {
|
|
39292
|
+
throw new Error("Start point not found in the grid column");
|
|
39293
|
+
}
|
|
39294
|
+
const endRowIndex = grid.findIndex((row2) => row2.some((point5) => point5.barelyEqual(end)));
|
|
39295
|
+
if (endRowIndex === -1) {
|
|
39296
|
+
throw new Error("End point not found in the grid row");
|
|
39297
|
+
}
|
|
39298
|
+
const endPointIndex = grid[endRowIndex].findIndex((point5) => point5.barelyEqual(end));
|
|
39299
|
+
if (endPointIndex === -1) {
|
|
39300
|
+
throw new Error("End point not found in the grid column");
|
|
39320
39301
|
}
|
|
39321
|
-
const startPointIdx = grid[startIdx].findIndex((point5) => point5.barelyEqual(start));
|
|
39322
|
-
const endPointIdx = grid[endIdx].findIndex((point5) => point5.barelyEqual(end));
|
|
39323
39302
|
const endNode = {
|
|
39324
39303
|
point: end,
|
|
39325
|
-
xGrid:
|
|
39326
|
-
yGrid:
|
|
39304
|
+
xGrid: endRowIndex,
|
|
39305
|
+
yGrid: endPointIndex,
|
|
39327
39306
|
costSoFar: 0,
|
|
39328
39307
|
heuristic: 0,
|
|
39329
39308
|
toFinish: 0
|
|
@@ -39331,53 +39310,72 @@ function findPath(start, end, grid, obstacles, newStart, newEnd) {
|
|
|
39331
39310
|
const startNode = {
|
|
39332
39311
|
point: start,
|
|
39333
39312
|
costSoFar: 0,
|
|
39334
|
-
heuristic: heuristic({ point: start, xGrid:
|
|
39335
|
-
toFinish: heuristic({ point: start, xGrid:
|
|
39336
|
-
xGrid:
|
|
39337
|
-
yGrid:
|
|
39313
|
+
heuristic: heuristic({ point: start, xGrid: startRowIndex, yGrid: startPointIndex }, endNode),
|
|
39314
|
+
toFinish: heuristic({ point: start, xGrid: startRowIndex, yGrid: startPointIndex }, endNode),
|
|
39315
|
+
xGrid: startRowIndex,
|
|
39316
|
+
yGrid: startPointIndex
|
|
39338
39317
|
};
|
|
39339
39318
|
const openSet = [startNode];
|
|
39340
39319
|
const closedSet = new Set;
|
|
39341
39320
|
while (openSet.length > 0) {
|
|
39342
39321
|
openSet.sort((aa, bb) => aa.toFinish - bb.toFinish);
|
|
39343
39322
|
const current = openSet.shift();
|
|
39323
|
+
const currentKey = `${current.point.x},${current.point.y}`;
|
|
39344
39324
|
if (current.point.barelyEqual(end)) {
|
|
39345
39325
|
const path2 = reconstructPath(current);
|
|
39346
39326
|
return path2;
|
|
39347
39327
|
}
|
|
39348
|
-
closedSet.add(
|
|
39328
|
+
closedSet.add(currentKey);
|
|
39349
39329
|
const neighbors = getNeighbors(current, grid, obstacles);
|
|
39350
39330
|
for (const neighbor of neighbors) {
|
|
39351
|
-
|
|
39331
|
+
const neighborKey = `${neighbor.point.x},${neighbor.point.y}`;
|
|
39332
|
+
if (closedSet.has(neighborKey) || existingPath.has(neighborKey) && !neighbor.point.barelyEqual(end)) {
|
|
39352
39333
|
continue;
|
|
39353
39334
|
}
|
|
39354
39335
|
const extraCost = isChangingDirection(current, neighbor, newStart, newEnd);
|
|
39355
|
-
const
|
|
39356
|
-
|
|
39357
|
-
|
|
39358
|
-
|
|
39359
|
-
|
|
39360
|
-
|
|
39336
|
+
const pathOverlapCost = existingPath.has(neighborKey) ? 1000 : 0;
|
|
39337
|
+
const tentativeCost = current.costSoFar + 1 + pathOverlapCost;
|
|
39338
|
+
let existingNodeInOpenSet = openSet.find((node2) => node2.point.barelyEqual(neighbor.point));
|
|
39339
|
+
if (!existingNodeInOpenSet || tentativeCost < existingNodeInOpenSet.costSoFar) {
|
|
39340
|
+
if (existingNodeInOpenSet) {
|
|
39341
|
+
existingNodeInOpenSet.costSoFar = tentativeCost + extraCost;
|
|
39342
|
+
existingNodeInOpenSet.heuristic = heuristic(neighbor, endNode);
|
|
39343
|
+
existingNodeInOpenSet.toFinish = existingNodeInOpenSet.costSoFar + existingNodeInOpenSet.heuristic;
|
|
39344
|
+
existingNodeInOpenSet.parent = current;
|
|
39345
|
+
} else {
|
|
39346
|
+
neighbor.costSoFar = tentativeCost + extraCost;
|
|
39347
|
+
neighbor.heuristic = heuristic(neighbor, endNode);
|
|
39348
|
+
neighbor.toFinish = neighbor.costSoFar + neighbor.heuristic;
|
|
39349
|
+
openSet.push(neighbor);
|
|
39350
|
+
}
|
|
39361
39351
|
}
|
|
39362
39352
|
}
|
|
39363
39353
|
}
|
|
39364
39354
|
return;
|
|
39365
39355
|
}
|
|
39366
39356
|
function findPathPoints(points, grid, obstacles, newStart, newEnd) {
|
|
39367
|
-
const
|
|
39357
|
+
const finalPath = [];
|
|
39358
|
+
const existingPathSegments = new Set;
|
|
39359
|
+
if (points.length > 0) {
|
|
39360
|
+
finalPath.push(points[0]);
|
|
39361
|
+
const startKey = `${points[0].x},${points[0].y}`;
|
|
39362
|
+
existingPathSegments.add(startKey);
|
|
39363
|
+
}
|
|
39368
39364
|
for (let i = 0;i < points.length - 1; i += 1) {
|
|
39369
|
-
const segmentPath = findPath(points[i], points[i + 1], grid, obstacles, newStart, newEnd);
|
|
39370
|
-
if (segmentPath) {
|
|
39371
|
-
|
|
39365
|
+
const segmentPath = findPath(points[i], points[i + 1], grid, obstacles, existingPathSegments, newStart, newEnd);
|
|
39366
|
+
if (segmentPath && segmentPath.length > 0) {
|
|
39367
|
+
for (let j = 1;j < segmentPath.length; j++) {
|
|
39368
|
+
const point5 = segmentPath[j];
|
|
39369
|
+
const key = `${point5.x},${point5.y}`;
|
|
39370
|
+
finalPath.push(point5);
|
|
39371
|
+
existingPathSegments.add(key);
|
|
39372
|
+
}
|
|
39372
39373
|
} else {
|
|
39373
39374
|
points.splice(i + 1, 1);
|
|
39374
39375
|
i--;
|
|
39375
39376
|
}
|
|
39376
39377
|
}
|
|
39377
|
-
|
|
39378
|
-
pathPoints.push(points[points.length - 1]);
|
|
39379
|
-
}
|
|
39380
|
-
return pathPoints;
|
|
39378
|
+
return finalPath;
|
|
39381
39379
|
}
|
|
39382
39380
|
function reducePoints(points) {
|
|
39383
39381
|
const uniquePoints = new Map;
|
|
@@ -39388,6 +39386,12 @@ function reducePoints(points) {
|
|
|
39388
39386
|
if (uniquePoints.has(key)) {
|
|
39389
39387
|
const loopStartIndex = uniquePoints.get(key);
|
|
39390
39388
|
result.splice(loopStartIndex + 1);
|
|
39389
|
+
const removedPoints = points.slice(loopStartIndex + 1, i + 1);
|
|
39390
|
+
removedPoints.forEach((p3) => {
|
|
39391
|
+
uniquePoints.delete(`${p3.x},${p3.y}`);
|
|
39392
|
+
});
|
|
39393
|
+
uniquePoints.set(key, result.length);
|
|
39394
|
+
result.push(point5);
|
|
39391
39395
|
} else {
|
|
39392
39396
|
uniquePoints.set(key, result.length);
|
|
39393
39397
|
result.push(point5);
|
|
@@ -39397,19 +39401,32 @@ function reducePoints(points) {
|
|
|
39397
39401
|
}
|
|
39398
39402
|
function getLines(pathPoints) {
|
|
39399
39403
|
const lines = [];
|
|
39404
|
+
if (pathPoints.length < 2) {
|
|
39405
|
+
return [];
|
|
39406
|
+
}
|
|
39400
39407
|
const reducedPoints = reducePoints(pathPoints);
|
|
39408
|
+
if (reducedPoints.length < 2) {
|
|
39409
|
+
return [];
|
|
39410
|
+
}
|
|
39401
39411
|
let startPoint = reducedPoints[0];
|
|
39402
|
-
for (let i = 1;i < reducedPoints.length
|
|
39412
|
+
for (let i = 1;i < reducedPoints.length; i += 1) {
|
|
39403
39413
|
const prevPoint = reducedPoints[i - 1];
|
|
39404
39414
|
const currPoint = reducedPoints[i];
|
|
39405
|
-
const nextPoint = reducedPoints[i + 1];
|
|
39406
|
-
if (prevPoint.x !== nextPoint.x && prevPoint.y !== nextPoint.y) {
|
|
39415
|
+
const nextPoint = i + 1 < reducedPoints.length ? reducedPoints[i + 1] : null;
|
|
39416
|
+
if (!nextPoint || prevPoint.x !== nextPoint.x && prevPoint.y !== nextPoint.y) {
|
|
39407
39417
|
lines.push(new Line(startPoint, currPoint));
|
|
39408
39418
|
startPoint = currPoint;
|
|
39409
39419
|
}
|
|
39410
39420
|
}
|
|
39411
|
-
if (lines.length >
|
|
39412
|
-
lines.push(new Line(
|
|
39421
|
+
if (lines.length === 0 && reducedPoints.length > 1) {
|
|
39422
|
+
lines.push(new Line(reducedPoints[0], reducedPoints[reducedPoints.length - 1]));
|
|
39423
|
+
} else if (lines.length > 0) {
|
|
39424
|
+
const lastLine = lines[lines.length - 1];
|
|
39425
|
+
const lastPointInLines = lastLine.getEndPoint();
|
|
39426
|
+
const lastPointInReduced = reducedPoints[reducedPoints.length - 1];
|
|
39427
|
+
if (!lastPointInLines.barelyEqual(lastPointInReduced)) {
|
|
39428
|
+
lines.push(new Line(lastPointInLines, lastPointInReduced));
|
|
39429
|
+
}
|
|
39413
39430
|
}
|
|
39414
39431
|
return lines;
|
|
39415
39432
|
}
|