Merge branch 'test' of https://git.code.tencent.com/lian-cbtc/jl-client into test
This commit is contained in:
commit
652e9aa5d7
@ -354,7 +354,7 @@ export function Jl3dpassflow(dom) {
|
||||
}
|
||||
}
|
||||
}
|
||||
},3000);
|
||||
},1000);
|
||||
};
|
||||
},1000);
|
||||
|
||||
@ -390,7 +390,7 @@ export function Jl3dpassflow(dom) {
|
||||
}
|
||||
moveanimateupdate();
|
||||
|
||||
scope.anime = requestAnimationFrame(animate);
|
||||
requestAnimationFrame(animate);
|
||||
|
||||
}
|
||||
|
||||
@ -468,7 +468,7 @@ export function Jl3dpassflow(dom) {
|
||||
}
|
||||
|
||||
if(humans[i].stage == 0){
|
||||
console.log(humans[i].doors);
|
||||
// console.log(humans[i].doors);
|
||||
zhajiin[humans[i].doors].waiting = 0;
|
||||
humans[i].stage = 1;
|
||||
}
|
||||
@ -805,8 +805,7 @@ export function Jl3dpassflow(dom) {
|
||||
}
|
||||
|
||||
function zhajicontrol(type,door){
|
||||
console.log(door);
|
||||
console.log(zhajiin);
|
||||
|
||||
let devicenum = door-1;
|
||||
if(type == "in"){
|
||||
deviceaction[zhajiin[devicenum].id].action.reset();
|
||||
|
1102
src/jlmap3d/jl3dpassflow/jl3dpassflownew.js
Normal file
1102
src/jlmap3d/jl3dpassflow/jl3dpassflownew.js
Normal file
File diff suppressed because it is too large
Load Diff
0
src/jlmap3d/jl3dpassflow/model/humanmanager.js
Normal file
0
src/jlmap3d/jl3dpassflow/model/humanmanager.js
Normal file
0
src/jlmap3d/jl3dpassflow/model/humanmodel.js
Normal file
0
src/jlmap3d/jl3dpassflow/model/humanmodel.js
Normal file
136
src/jlmap3d/jl3dpassflow/model/zonemanager.js
Normal file
136
src/jlmap3d/jl3dpassflow/model/zonemanager.js
Normal file
@ -0,0 +1,136 @@
|
||||
import { ZoneModel } from '@/jlmap3d/jl3dpassflow/model/zonemodel.js';
|
||||
|
||||
let enter1 = {
|
||||
code : '001',
|
||||
name : "enter1",
|
||||
type : "normal",
|
||||
stage : "0",
|
||||
railpoints : [
|
||||
new THREE.Vector3(34,9.8,-6),
|
||||
new THREE.Vector3(32,9.8,-6)
|
||||
]
|
||||
};
|
||||
|
||||
let enter2 = {
|
||||
code : '002',
|
||||
name : "enter2",
|
||||
type : "normal",
|
||||
stage : "0",
|
||||
railpoints : [
|
||||
new THREE.Vector3(35,9.8,31.5),
|
||||
new THREE.Vector3(31,9.8,31.5)
|
||||
]
|
||||
};
|
||||
|
||||
let security = {
|
||||
code : '003',
|
||||
name : "security",
|
||||
type : "device",
|
||||
stage : "1",
|
||||
railpoints : [
|
||||
new THREE.Vector3(20.5,9.8,18),
|
||||
new THREE.Vector3(21,9.8,21)
|
||||
]
|
||||
};
|
||||
|
||||
let entergate = {
|
||||
code : '004',
|
||||
name : "entergate",
|
||||
type : "device",
|
||||
stage : "2",
|
||||
railpoints : [
|
||||
new THREE.Vector3(6.3,9.8,17.4),
|
||||
new THREE.Vector3(4.8,9.8,17.4),
|
||||
new THREE.Vector3(3.4,9.8,17.4),
|
||||
new THREE.Vector3(2.1,9.8,17.4),
|
||||
new THREE.Vector3(0.7,9.8,17.4)
|
||||
]
|
||||
};
|
||||
|
||||
let standtop = {
|
||||
code : '005',
|
||||
name : "standtop",
|
||||
type : "stand",
|
||||
stage : "3",
|
||||
railpoints : [
|
||||
new THREE.Vector3(52.1,1.77,-1.8),
|
||||
new THREE.Vector3(-63.5,1.77,-1.8)
|
||||
]
|
||||
};
|
||||
|
||||
let standdown = {
|
||||
code : '006',
|
||||
name : "standdown",
|
||||
type : "stand",
|
||||
stage : "3",
|
||||
railpoints : [
|
||||
new THREE.Vector3(52.1,1.77,24),
|
||||
new THREE.Vector3(-64,1.77,24)
|
||||
]
|
||||
};
|
||||
//4是刚下车状态
|
||||
let exitgate = {
|
||||
code : '007',
|
||||
name : "exitgate",
|
||||
type : "device",
|
||||
stage : "5",
|
||||
railpoints : [
|
||||
new THREE.Vector3(18.8,9.8,-0.27),
|
||||
new THREE.Vector3(18.8,9.8,1.18),
|
||||
new THREE.Vector3(18.8,9.8,2.64),
|
||||
new THREE.Vector3(18.8,9.8,4.1),
|
||||
new THREE.Vector3(18.8,9.8,5.4)
|
||||
]
|
||||
};
|
||||
|
||||
let exit1 = {
|
||||
code : '008',
|
||||
name : "exit1",
|
||||
type : "normal",
|
||||
stage : "6",
|
||||
railpoints : [
|
||||
new THREE.Vector3(28.2,9.8,-7),
|
||||
new THREE.Vector3(30.5,9.8,-7.4)
|
||||
]
|
||||
};
|
||||
|
||||
let exit2 = {
|
||||
code : '009',
|
||||
name : "exit2",
|
||||
type : "normal",
|
||||
stage : "6",
|
||||
railpoints : [
|
||||
new THREE.Vector3(28.3,9.8,28.8),
|
||||
new THREE.Vector3(31.2,9.8,28.8)
|
||||
]
|
||||
};
|
||||
|
||||
export function ZoneManager() {
|
||||
|
||||
var scope = this;
|
||||
|
||||
this.name = "test";
|
||||
|
||||
this.list = [];
|
||||
|
||||
initzone();
|
||||
|
||||
function initzone(){
|
||||
scope.list[enter1.name] = new ZoneModel(enter1);
|
||||
scope.list[enter2.name] = new ZoneModel(enter2);
|
||||
scope.list[security.name] = new ZoneModel(security);
|
||||
scope.list[entergate.name] = new ZoneModel(entergate);
|
||||
scope.list[standtop.name] = new ZoneModel(standtop);
|
||||
scope.list[standdown.name] = new ZoneModel(standdown);
|
||||
scope.list[exitgate.name] = new ZoneModel(exitgate);
|
||||
scope.list[exit1.name] = new ZoneModel(exit1);
|
||||
scope.list[exit2.name] = new ZoneModel(exit2);
|
||||
}
|
||||
|
||||
this.getzoneposition = function(name){
|
||||
// let random = Math.random();
|
||||
let position = scope.list[name].railline.getPointAt(Math.random());
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
15
src/jlmap3d/jl3dpassflow/model/zonemodel.js
Normal file
15
src/jlmap3d/jl3dpassflow/model/zonemodel.js
Normal file
@ -0,0 +1,15 @@
|
||||
export function ZoneModel(data) {
|
||||
var scope = this;
|
||||
//code
|
||||
this.code = data.code;
|
||||
//命名
|
||||
this.name = data.name;
|
||||
this.type = data.type;
|
||||
this.stage = data.stage;
|
||||
this.status = 0;
|
||||
//轨迹点
|
||||
this.railpoints = data.railpoints;
|
||||
|
||||
this.railline = new THREE.CatmullRomCurve3(data.railpoints);
|
||||
|
||||
}
|
123
src/jlmap3d/jl3dpassflow/utils/AStar.js
Normal file
123
src/jlmap3d/jl3dpassflow/utils/AStar.js
Normal file
@ -0,0 +1,123 @@
|
||||
import { BinaryHeap } from './BinaryHeap';
|
||||
import { Utils } from './Utils.js';
|
||||
|
||||
class AStar {
|
||||
static init (graph) {
|
||||
for (let x = 0; x < graph.length; x++) {
|
||||
//for(var x in graph) {
|
||||
const node = graph[x];
|
||||
node.f = 0;
|
||||
node.g = 0;
|
||||
node.h = 0;
|
||||
node.cost = 1.0;
|
||||
node.visited = false;
|
||||
node.closed = false;
|
||||
node.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
static cleanUp (graph) {
|
||||
for (let x = 0; x < graph.length; x++) {
|
||||
const node = graph[x];
|
||||
delete node.f;
|
||||
delete node.g;
|
||||
delete node.h;
|
||||
delete node.cost;
|
||||
delete node.visited;
|
||||
delete node.closed;
|
||||
delete node.parent;
|
||||
}
|
||||
}
|
||||
|
||||
static heap () {
|
||||
return new BinaryHeap(function (node) {
|
||||
return node.f;
|
||||
});
|
||||
}
|
||||
|
||||
static search (graph, start, end) {
|
||||
this.init(graph);
|
||||
//heuristic = heuristic || astar.manhattan;
|
||||
|
||||
|
||||
const openHeap = this.heap();
|
||||
|
||||
openHeap.push(start);
|
||||
|
||||
while (openHeap.size() > 0) {
|
||||
|
||||
// Grab the lowest f(x) to process next. Heap keeps this sorted for us.
|
||||
const currentNode = openHeap.pop();
|
||||
|
||||
// End case -- result has been found, return the traced path.
|
||||
if (currentNode === end) {
|
||||
let curr = currentNode;
|
||||
const ret = [];
|
||||
while (curr.parent) {
|
||||
ret.push(curr);
|
||||
curr = curr.parent;
|
||||
}
|
||||
this.cleanUp(ret);
|
||||
return ret.reverse();
|
||||
}
|
||||
|
||||
// Normal case -- move currentNode from open to closed, process each of its neighbours.
|
||||
currentNode.closed = true;
|
||||
|
||||
// Find all neighbours for the current node. Optionally find diagonal neighbours as well (false by default).
|
||||
const neighbours = this.neighbours(graph, currentNode);
|
||||
|
||||
for (let i = 0, il = neighbours.length; i < il; i++) {
|
||||
const neighbour = neighbours[i];
|
||||
|
||||
if (neighbour.closed) {
|
||||
// Not a valid node to process, skip to next neighbour.
|
||||
continue;
|
||||
}
|
||||
|
||||
// The g score is the shortest distance from start to current node.
|
||||
// We need to check if the path we have arrived at this neighbour is the shortest one we have seen yet.
|
||||
const gScore = currentNode.g + neighbour.cost;
|
||||
const beenVisited = neighbour.visited;
|
||||
|
||||
if (!beenVisited || gScore < neighbour.g) {
|
||||
|
||||
// Found an optimal (so far) path to this node. Take score for node to see how good it is.
|
||||
neighbour.visited = true;
|
||||
neighbour.parent = currentNode;
|
||||
if (!neighbour.centroid || !end.centroid) throw new Error('Unexpected state');
|
||||
neighbour.h = neighbour.h || this.heuristic(neighbour.centroid, end.centroid);
|
||||
neighbour.g = gScore;
|
||||
neighbour.f = neighbour.g + neighbour.h;
|
||||
|
||||
if (!beenVisited) {
|
||||
// Pushing to heap will put it in proper place based on the 'f' value.
|
||||
openHeap.push(neighbour);
|
||||
} else {
|
||||
// Already seen the node, but since it has been rescored we need to reorder it in the heap
|
||||
openHeap.rescoreElement(neighbour);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No result was found - empty array signifies failure to find path.
|
||||
return [];
|
||||
}
|
||||
|
||||
static heuristic (pos1, pos2) {
|
||||
return Utils.distanceToSquared(pos1, pos2);
|
||||
}
|
||||
|
||||
static neighbours (graph, node) {
|
||||
const ret = [];
|
||||
|
||||
for (let e = 0; e < node.neighbours.length; e++) {
|
||||
ret.push(graph[node.neighbours[e]]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
export { AStar };
|
134
src/jlmap3d/jl3dpassflow/utils/BinaryHeap.js
Normal file
134
src/jlmap3d/jl3dpassflow/utils/BinaryHeap.js
Normal file
@ -0,0 +1,134 @@
|
||||
// javascript-astar
|
||||
// http://github.com/bgrins/javascript-astar
|
||||
// Freely distributable under the MIT License.
|
||||
// Implements the astar search algorithm in javascript using a binary heap.
|
||||
|
||||
class BinaryHeap {
|
||||
constructor (scoreFunction) {
|
||||
this.content = [];
|
||||
this.scoreFunction = scoreFunction;
|
||||
}
|
||||
|
||||
push (element) {
|
||||
// Add the new element to the end of the array.
|
||||
this.content.push(element);
|
||||
|
||||
// Allow it to sink down.
|
||||
this.sinkDown(this.content.length - 1);
|
||||
}
|
||||
|
||||
pop () {
|
||||
// Store the first element so we can return it later.
|
||||
const result = this.content[0];
|
||||
// Get the element at the end of the array.
|
||||
const end = this.content.pop();
|
||||
// If there are any elements left, put the end element at the
|
||||
// start, and let it bubble up.
|
||||
if (this.content.length > 0) {
|
||||
this.content[0] = end;
|
||||
this.bubbleUp(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
remove (node) {
|
||||
const i = this.content.indexOf(node);
|
||||
|
||||
// When it is found, the process seen in 'pop' is repeated
|
||||
// to fill up the hole.
|
||||
const end = this.content.pop();
|
||||
|
||||
if (i !== this.content.length - 1) {
|
||||
this.content[i] = end;
|
||||
|
||||
if (this.scoreFunction(end) < this.scoreFunction(node)) {
|
||||
this.sinkDown(i);
|
||||
} else {
|
||||
this.bubbleUp(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size () {
|
||||
return this.content.length;
|
||||
}
|
||||
|
||||
rescoreElement (node) {
|
||||
this.sinkDown(this.content.indexOf(node));
|
||||
}
|
||||
|
||||
sinkDown (n) {
|
||||
// Fetch the element that has to be sunk.
|
||||
const element = this.content[n];
|
||||
|
||||
// When at 0, an element can not sink any further.
|
||||
while (n > 0) {
|
||||
// Compute the parent element's index, and fetch it.
|
||||
const parentN = ((n + 1) >> 1) - 1;
|
||||
const parent = this.content[parentN];
|
||||
|
||||
if (this.scoreFunction(element) < this.scoreFunction(parent)) {
|
||||
// Swap the elements if the parent is greater.
|
||||
this.content[parentN] = element;
|
||||
this.content[n] = parent;
|
||||
// Update 'n' to continue at the new position.
|
||||
n = parentN;
|
||||
} else {
|
||||
// Found a parent that is less, no need to sink any further.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bubbleUp (n) {
|
||||
// Look up the target element and its score.
|
||||
const length = this.content.length,
|
||||
element = this.content[n],
|
||||
elemScore = this.scoreFunction(element);
|
||||
|
||||
while (true) {
|
||||
// Compute the indices of the child elements.
|
||||
const child2N = (n + 1) << 1,
|
||||
child1N = child2N - 1;
|
||||
// This is used to store the new position of the element,
|
||||
// if any.
|
||||
let swap = null;
|
||||
let child1Score;
|
||||
// If the first child exists (is inside the array)...
|
||||
if (child1N < length) {
|
||||
// Look it up and compute its score.
|
||||
const child1 = this.content[child1N];
|
||||
child1Score = this.scoreFunction(child1);
|
||||
|
||||
// If the score is less than our element's, we need to swap.
|
||||
if (child1Score < elemScore) {
|
||||
swap = child1N;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same checks for the other child.
|
||||
if (child2N < length) {
|
||||
const child2 = this.content[child2N],
|
||||
child2Score = this.scoreFunction(child2);
|
||||
if (child2Score < (swap === null ? elemScore : child1Score)) {
|
||||
swap = child2N;
|
||||
}
|
||||
}
|
||||
|
||||
// If the element needs to be moved, swap it, and continue.
|
||||
if (swap !== null) {
|
||||
this.content[n] = this.content[swap];
|
||||
this.content[swap] = element;
|
||||
n = swap;
|
||||
}
|
||||
|
||||
// Otherwise, we are done.
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { BinaryHeap };
|
200
src/jlmap3d/jl3dpassflow/utils/Builder.js
Normal file
200
src/jlmap3d/jl3dpassflow/utils/Builder.js
Normal file
@ -0,0 +1,200 @@
|
||||
import {
|
||||
Vector3,
|
||||
} from 'three';
|
||||
|
||||
import { Utils } from './Utils';
|
||||
|
||||
class Builder {
|
||||
/**
|
||||
* Constructs groups from the given navigation mesh.
|
||||
* @param {Geometry} geometry
|
||||
* @return {Zone}
|
||||
*/
|
||||
static buildZone (geometry) {
|
||||
|
||||
const navMesh = this._buildNavigationMesh(geometry);
|
||||
|
||||
const zone = {};
|
||||
|
||||
navMesh.vertices.forEach((v) => {
|
||||
v.x = Utils.roundNumber(v.x, 2);
|
||||
v.y = Utils.roundNumber(v.y, 2);
|
||||
v.z = Utils.roundNumber(v.z, 2);
|
||||
});
|
||||
|
||||
zone.vertices = navMesh.vertices;
|
||||
|
||||
const groups = this._buildPolygonGroups(navMesh);
|
||||
|
||||
// TODO: This block represents a large portion of navigation mesh construction time
|
||||
// and could probably be optimized. For example, construct portals while
|
||||
// determining the neighbor graph.
|
||||
zone.groups = new Array(groups.length);
|
||||
groups.forEach((group, groupIndex) => {
|
||||
|
||||
const indexByPolygon = new Map(); // { polygon: index in group }
|
||||
group.forEach((poly, polyIndex) => { indexByPolygon.set(poly, polyIndex); });
|
||||
|
||||
const newGroup = new Array(group.length);
|
||||
group.forEach((poly, polyIndex) => {
|
||||
|
||||
const neighbourIndices = [];
|
||||
poly.neighbours.forEach((n) => neighbourIndices.push(indexByPolygon.get(n)));
|
||||
|
||||
// Build a portal list to each neighbour
|
||||
const portals = [];
|
||||
poly.neighbours.forEach((n) => portals.push(this._getSharedVerticesInOrder(poly, n)));
|
||||
|
||||
const centroid = new Vector3( 0, 0, 0 );
|
||||
centroid.add( zone.vertices[ poly.vertexIds[0] ] );
|
||||
centroid.add( zone.vertices[ poly.vertexIds[1] ] );
|
||||
centroid.add( zone.vertices[ poly.vertexIds[2] ] );
|
||||
centroid.divideScalar( 3 );
|
||||
centroid.x = Utils.roundNumber(centroid.x, 2);
|
||||
centroid.y = Utils.roundNumber(centroid.y, 2);
|
||||
centroid.z = Utils.roundNumber(centroid.z, 2);
|
||||
|
||||
newGroup[polyIndex] = {
|
||||
id: polyIndex,
|
||||
neighbours: neighbourIndices,
|
||||
vertexIds: poly.vertexIds,
|
||||
centroid: centroid,
|
||||
portals: portals
|
||||
};
|
||||
});
|
||||
|
||||
zone.groups[groupIndex] = newGroup;
|
||||
});
|
||||
|
||||
return zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a navigation mesh from the given geometry.
|
||||
* @param {Geometry} geometry
|
||||
* @return {Object}
|
||||
*/
|
||||
static _buildNavigationMesh (geometry) {
|
||||
geometry.mergeVertices();
|
||||
return this._buildPolygonsFromGeometry(geometry);
|
||||
}
|
||||
|
||||
static _buildPolygonGroups (navigationMesh) {
|
||||
|
||||
const polygons = navigationMesh.polygons;
|
||||
|
||||
const polygonGroups = [];
|
||||
|
||||
const spreadGroupId = function (polygon) {
|
||||
polygon.neighbours.forEach((neighbour) => {
|
||||
if (neighbour.group === undefined) {
|
||||
neighbour.group = polygon.group;
|
||||
spreadGroupId(neighbour);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
polygons.forEach((polygon) => {
|
||||
if (polygon.group !== undefined) {
|
||||
// this polygon is already part of a group
|
||||
polygonGroups[polygon.group].push(polygon);
|
||||
} else {
|
||||
// we need to make a new group and spread its ID to neighbors
|
||||
polygon.group = polygonGroups.length;
|
||||
spreadGroupId(polygon);
|
||||
polygonGroups.push([polygon]);
|
||||
}
|
||||
});
|
||||
|
||||
return polygonGroups;
|
||||
}
|
||||
|
||||
static _buildPolygonNeighbours (polygon, vertexPolygonMap) {
|
||||
const neighbours = new Set();
|
||||
|
||||
const groupA = vertexPolygonMap[polygon.vertexIds[0]];
|
||||
const groupB = vertexPolygonMap[polygon.vertexIds[1]];
|
||||
const groupC = vertexPolygonMap[polygon.vertexIds[2]];
|
||||
|
||||
// It's only necessary to iterate groups A and B. Polygons contained only
|
||||
// in group C cannot share a >1 vertex with this polygon.
|
||||
// IMPORTANT: Bublé cannot compile for-of loops.
|
||||
groupA.forEach((candidate) => {
|
||||
if (candidate === polygon) return;
|
||||
if (groupB.includes(candidate) || groupC.includes(candidate)) {
|
||||
neighbours.add(candidate);
|
||||
}
|
||||
});
|
||||
groupB.forEach((candidate) => {
|
||||
if (candidate === polygon) return;
|
||||
if (groupC.includes(candidate)) {
|
||||
neighbours.add(candidate);
|
||||
}
|
||||
});
|
||||
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
static _buildPolygonsFromGeometry (geometry) {
|
||||
|
||||
const polygons = [];
|
||||
const vertices = geometry.vertices;
|
||||
|
||||
// Constructing the neighbor graph brute force is O(n²). To avoid that,
|
||||
// create a map from vertices to the polygons that contain them, and use it
|
||||
// while connecting polygons. This reduces complexity to O(n*m), where 'm'
|
||||
// is related to connectivity of the mesh.
|
||||
const vertexPolygonMap = new Array(vertices.length); // array of polygon objects by vertex index
|
||||
for (let i = 0; i < vertices.length; i++) {
|
||||
vertexPolygonMap[i] = [];
|
||||
}
|
||||
|
||||
// Convert the faces into a custom format that supports more than 3 vertices
|
||||
geometry.faces.forEach((face) => {
|
||||
const poly = { vertexIds: [face.a, face.b, face.c], neighbours: null };
|
||||
polygons.push(poly);
|
||||
vertexPolygonMap[face.a].push(poly);
|
||||
vertexPolygonMap[face.b].push(poly);
|
||||
vertexPolygonMap[face.c].push(poly);
|
||||
});
|
||||
|
||||
// Build a list of adjacent polygons
|
||||
polygons.forEach((polygon) => {
|
||||
polygon.neighbours = this._buildPolygonNeighbours(polygon, vertexPolygonMap);
|
||||
});
|
||||
|
||||
return {
|
||||
polygons: polygons,
|
||||
vertices: vertices
|
||||
};
|
||||
}
|
||||
|
||||
static _getSharedVerticesInOrder (a, b) {
|
||||
|
||||
const aList = a.vertexIds;
|
||||
const a0 = aList[0], a1 = aList[1], a2 = aList[2];
|
||||
|
||||
const bList = b.vertexIds;
|
||||
const shared0 = bList.includes(a0);
|
||||
const shared1 = bList.includes(a1);
|
||||
const shared2 = bList.includes(a2);
|
||||
|
||||
// it seems that we shouldn't have an a and b with <2 shared vertices here unless there's a bug
|
||||
// in the neighbor identification code, or perhaps a malformed input geometry; 3 shared vertices
|
||||
// is a kind of embarrassing but possible geometry we should handle
|
||||
if (shared0 && shared1 && shared2) {
|
||||
return Array.from(aList);
|
||||
} else if (shared0 && shared1) {
|
||||
return [a0, a1];
|
||||
} else if (shared1 && shared2) {
|
||||
return [a1, a2];
|
||||
} else if (shared0 && shared2) {
|
||||
return [a2, a0]; // this ordering will affect the string pull algorithm later, not clear if significant
|
||||
} else {
|
||||
console.warn("Error processing navigation mesh neighbors; neighbors with <2 shared vertices found.");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { Builder };
|
93
src/jlmap3d/jl3dpassflow/utils/Channel.js
Normal file
93
src/jlmap3d/jl3dpassflow/utils/Channel.js
Normal file
@ -0,0 +1,93 @@
|
||||
import { Utils } from './Utils';
|
||||
|
||||
class Channel {
|
||||
constructor () {
|
||||
this.portals = [];
|
||||
}
|
||||
|
||||
push (p1, p2) {
|
||||
if (p2 === undefined) p2 = p1;
|
||||
this.portals.push({
|
||||
left: p1,
|
||||
right: p2
|
||||
});
|
||||
}
|
||||
|
||||
stringPull () {
|
||||
const portals = this.portals;
|
||||
const pts = [];
|
||||
// Init scan state
|
||||
let portalApex, portalLeft, portalRight;
|
||||
let apexIndex = 0,
|
||||
leftIndex = 0,
|
||||
rightIndex = 0;
|
||||
|
||||
portalApex = portals[0].left;
|
||||
portalLeft = portals[0].left;
|
||||
portalRight = portals[0].right;
|
||||
|
||||
// Add start point.
|
||||
pts.push(portalApex);
|
||||
|
||||
for (let i = 1; i < portals.length; i++) {
|
||||
const left = portals[i].left;
|
||||
const right = portals[i].right;
|
||||
|
||||
// Update right vertex.
|
||||
if (Utils.triarea2(portalApex, portalRight, right) <= 0.0) {
|
||||
if (Utils.vequal(portalApex, portalRight) || Utils.triarea2(portalApex, portalLeft, right) > 0.0) {
|
||||
// Tighten the funnel.
|
||||
portalRight = right;
|
||||
rightIndex = i;
|
||||
} else {
|
||||
// Right over left, insert left to path and restart scan from portal left point.
|
||||
pts.push(portalLeft);
|
||||
// Make current left the new apex.
|
||||
portalApex = portalLeft;
|
||||
apexIndex = leftIndex;
|
||||
// Reset portal
|
||||
portalLeft = portalApex;
|
||||
portalRight = portalApex;
|
||||
leftIndex = apexIndex;
|
||||
rightIndex = apexIndex;
|
||||
// Restart scan
|
||||
i = apexIndex;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Update left vertex.
|
||||
if (Utils.triarea2(portalApex, portalLeft, left) >= 0.0) {
|
||||
if (Utils.vequal(portalApex, portalLeft) || Utils.triarea2(portalApex, portalRight, left) < 0.0) {
|
||||
// Tighten the funnel.
|
||||
portalLeft = left;
|
||||
leftIndex = i;
|
||||
} else {
|
||||
// Left over right, insert right to path and restart scan from portal right point.
|
||||
pts.push(portalRight);
|
||||
// Make current right the new apex.
|
||||
portalApex = portalRight;
|
||||
apexIndex = rightIndex;
|
||||
// Reset portal
|
||||
portalLeft = portalApex;
|
||||
portalRight = portalApex;
|
||||
leftIndex = apexIndex;
|
||||
rightIndex = apexIndex;
|
||||
// Restart scan
|
||||
i = apexIndex;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((pts.length === 0) || (!Utils.vequal(pts[pts.length - 1], portals[portals.length - 1].left))) {
|
||||
// Append last point to path.
|
||||
pts.push(portals[portals.length - 1].left);
|
||||
}
|
||||
|
||||
this.path = pts;
|
||||
return pts;
|
||||
}
|
||||
}
|
||||
|
||||
export { Channel };
|
315
src/jlmap3d/jl3dpassflow/utils/Pathfinding.js
Normal file
315
src/jlmap3d/jl3dpassflow/utils/Pathfinding.js
Normal file
@ -0,0 +1,315 @@
|
||||
import {
|
||||
Vector3,
|
||||
Plane,
|
||||
Geometry,
|
||||
Triangle,
|
||||
} from 'three';
|
||||
|
||||
import { Utils } from './Utils';
|
||||
import { AStar } from './AStar';
|
||||
import { Builder } from './Builder';
|
||||
import { Channel } from './Channel';
|
||||
|
||||
/**
|
||||
* Defines an instance of the pathfinding module, with one or more zones.
|
||||
*/
|
||||
class Pathfinding {
|
||||
constructor () {
|
||||
this.zones = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* (Static) Builds a zone/node set from navigation mesh geometry.
|
||||
* @param {BufferGeometry} geometry
|
||||
* @return {Zone}
|
||||
*/
|
||||
static createZone (geometry) {
|
||||
if ( geometry.isGeometry ) {
|
||||
// Haven't actually implemented support for BufferGeometry yet, but Geometry is somewhat
|
||||
// not-recommended these days, so go ahead and start warning.
|
||||
console.warn('[three-pathfinding]: Use BufferGeometry, not Geometry, to create zone.');
|
||||
} else {
|
||||
geometry = new Geometry().fromBufferGeometry(geometry);
|
||||
}
|
||||
|
||||
return Builder.buildZone(geometry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data for the given zone.
|
||||
* @param {string} zoneID
|
||||
* @param {Zone} zone
|
||||
*/
|
||||
setZoneData (zoneID, zone) {
|
||||
this.zones[zoneID] = zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random node within a given range of a given position.
|
||||
* @param {string} zoneID
|
||||
* @param {number} groupID
|
||||
* @param {Vector3} nearPosition
|
||||
* @param {number} nearRange
|
||||
* @return {Node}
|
||||
*/
|
||||
getRandomNode (zoneID, groupID, nearPosition, nearRange) {
|
||||
|
||||
if (!this.zones[zoneID]) return new Vector3();
|
||||
|
||||
nearPosition = nearPosition || null;
|
||||
nearRange = nearRange || 0;
|
||||
|
||||
const candidates = [];
|
||||
const polygons = this.zones[zoneID].groups[groupID];
|
||||
|
||||
polygons.forEach((p) => {
|
||||
if (nearPosition && nearRange) {
|
||||
if (Utils.distanceToSquared(nearPosition, p.centroid) < nearRange * nearRange) {
|
||||
candidates.push(p.centroid);
|
||||
}
|
||||
} else {
|
||||
candidates.push(p.centroid);
|
||||
}
|
||||
});
|
||||
|
||||
return Utils.sample(candidates) || new Vector3();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the closest node to the target position.
|
||||
* @param {Vector3} position
|
||||
* @param {string} zoneID
|
||||
* @param {number} groupID
|
||||
* @param {boolean} checkPolygon
|
||||
* @return {Node}
|
||||
*/
|
||||
getClosestNode (position, zoneID, groupID, checkPolygon = false) {
|
||||
const nodes = this.zones[zoneID].groups[groupID];
|
||||
const vertices = this.zones[zoneID].vertices;
|
||||
let closestNode = null;
|
||||
let closestDistance = Infinity;
|
||||
|
||||
nodes.forEach((node) => {
|
||||
const distance = Utils.distanceToSquared(node.centroid, position);
|
||||
if (distance < closestDistance
|
||||
&& (!checkPolygon || Utils.isVectorInPolygon(position, node, vertices))) {
|
||||
closestNode = node;
|
||||
closestDistance = distance;
|
||||
}
|
||||
});
|
||||
|
||||
return closestNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a path between given start and end points. If a complete path
|
||||
* cannot be found, will return the nearest endpoint available.
|
||||
*
|
||||
* @param {Vector3} startPosition Start position.
|
||||
* @param {Vector3} targetPosition Destination.
|
||||
* @param {string} zoneID ID of current zone.
|
||||
* @param {number} groupID Current group ID.
|
||||
* @return {Array<Vector3>} Array of points defining the path.
|
||||
*/
|
||||
findPath (startPosition, targetPosition, zoneID, groupID) {
|
||||
const nodes = this.zones[zoneID].groups[groupID];
|
||||
const vertices = this.zones[zoneID].vertices;
|
||||
|
||||
const closestNode = this.getClosestNode(startPosition, zoneID, groupID, true);
|
||||
const farthestNode = this.getClosestNode(targetPosition, zoneID, groupID, true);
|
||||
|
||||
// If we can't find any node, just go straight to the target
|
||||
if (!closestNode || !farthestNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const paths = AStar.search(nodes, closestNode, farthestNode);
|
||||
|
||||
const getPortalFromTo = function (a, b) {
|
||||
for (var i = 0; i < a.neighbours.length; i++) {
|
||||
if (a.neighbours[i] === b.id) {
|
||||
return a.portals[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We have the corridor, now pull the rope.
|
||||
const channel = new Channel();
|
||||
channel.push(startPosition);
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const polygon = paths[i];
|
||||
const nextPolygon = paths[i + 1];
|
||||
|
||||
if (nextPolygon) {
|
||||
const portals = getPortalFromTo(polygon, nextPolygon);
|
||||
channel.push(
|
||||
vertices[portals[0]],
|
||||
vertices[portals[1]]
|
||||
);
|
||||
}
|
||||
}
|
||||
channel.push(targetPosition);
|
||||
channel.stringPull();
|
||||
|
||||
// Return the path, omitting first position (which is already known).
|
||||
const path = channel.path.map((c) => new Vector3(c.x, c.y, c.z));
|
||||
path.shift();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns closest node group ID for given position.
|
||||
* @param {string} zoneID
|
||||
* @param {Vector3} position
|
||||
* @return {number}
|
||||
*/
|
||||
Pathfinding.prototype.getGroup = (function() {
|
||||
const plane = new Plane();
|
||||
return function (zoneID, position, checkPolygon = false) {
|
||||
if (!this.zones[zoneID]) return null;
|
||||
|
||||
let closestNodeGroup = null;
|
||||
let distance = Math.pow(50, 2);
|
||||
const zone = this.zones[zoneID];
|
||||
|
||||
for (let i = 0; i < zone.groups.length; i++) {
|
||||
const group = zone.groups[i];
|
||||
for (const node of group) {
|
||||
if (checkPolygon) {
|
||||
plane.setFromCoplanarPoints(
|
||||
zone.vertices[node.vertexIds[0]],
|
||||
zone.vertices[node.vertexIds[1]],
|
||||
zone.vertices[node.vertexIds[2]]
|
||||
);
|
||||
if (Math.abs(plane.distanceToPoint(position)) < 0.01) {
|
||||
const poly = [
|
||||
zone.vertices[node.vertexIds[0]],
|
||||
zone.vertices[node.vertexIds[1]],
|
||||
zone.vertices[node.vertexIds[2]]
|
||||
];
|
||||
if(Utils.isPointInPoly(poly, position)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
const measuredDistance = Utils.distanceToSquared(node.centroid, position);
|
||||
if (measuredDistance < distance) {
|
||||
closestNodeGroup = i;
|
||||
distance = measuredDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestNodeGroup;
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Clamps a step along the navmesh, given start and desired endpoint. May be
|
||||
* used to constrain first-person / WASD controls.
|
||||
*
|
||||
* @param {Vector3} start
|
||||
* @param {Vector3} end Desired endpoint.
|
||||
* @param {Node} node
|
||||
* @param {string} zoneID
|
||||
* @param {number} groupID
|
||||
* @param {Vector3} endTarget Updated endpoint.
|
||||
* @return {Node} Updated node.
|
||||
*/
|
||||
Pathfinding.prototype.clampStep = (function () {
|
||||
const point = new Vector3();
|
||||
const plane = new Plane();
|
||||
const triangle = new Triangle();
|
||||
|
||||
const endPoint = new Vector3();
|
||||
|
||||
let closestNode;
|
||||
let closestPoint = new Vector3();
|
||||
let closestDistance;
|
||||
|
||||
return function (startRef, endRef, node, zoneID, groupID, endTarget) {
|
||||
const vertices = this.zones[zoneID].vertices;
|
||||
const nodes = this.zones[zoneID].groups[groupID];
|
||||
|
||||
const nodeQueue = [node];
|
||||
const nodeDepth = {};
|
||||
nodeDepth[node.id] = 0;
|
||||
|
||||
closestNode = undefined;
|
||||
closestPoint.set(0, 0, 0);
|
||||
closestDistance = Infinity;
|
||||
|
||||
// Project the step along the current node.
|
||||
plane.setFromCoplanarPoints(
|
||||
vertices[node.vertexIds[0]],
|
||||
vertices[node.vertexIds[1]],
|
||||
vertices[node.vertexIds[2]]
|
||||
);
|
||||
plane.projectPoint(endRef, point);
|
||||
endPoint.copy(point);
|
||||
|
||||
for (let currentNode = nodeQueue.pop(); currentNode; currentNode = nodeQueue.pop()) {
|
||||
|
||||
triangle.set(
|
||||
vertices[currentNode.vertexIds[0]],
|
||||
vertices[currentNode.vertexIds[1]],
|
||||
vertices[currentNode.vertexIds[2]]
|
||||
);
|
||||
|
||||
triangle.closestPointToPoint(endPoint, point);
|
||||
|
||||
if (point.distanceToSquared(endPoint) < closestDistance) {
|
||||
closestNode = currentNode;
|
||||
closestPoint.copy(point);
|
||||
closestDistance = point.distanceToSquared(endPoint);
|
||||
}
|
||||
|
||||
const depth = nodeDepth[currentNode.id];
|
||||
if (depth > 2) continue;
|
||||
|
||||
for (let i = 0; i < currentNode.neighbours.length; i++) {
|
||||
const neighbour = nodes[currentNode.neighbours[i]];
|
||||
if (neighbour.id in nodeDepth) continue;
|
||||
|
||||
nodeQueue.push(neighbour);
|
||||
nodeDepth[neighbour.id] = depth + 1;
|
||||
}
|
||||
}
|
||||
|
||||
endTarget.copy(closestPoint);
|
||||
return closestNode;
|
||||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Defines a zone of interconnected groups on a navigation mesh.
|
||||
*
|
||||
* @type {Object}
|
||||
* @property {Array<Group>} groups
|
||||
* @property {Array<Vector3>} vertices
|
||||
*/
|
||||
const Zone = {}; // jshint ignore:line
|
||||
|
||||
/**
|
||||
* Defines a group within a navigation mesh.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const Group = {}; // jshint ignore:line
|
||||
|
||||
/**
|
||||
* Defines a node (or polygon) within a group.
|
||||
*
|
||||
* @type {Object}
|
||||
* @property {number} id
|
||||
* @property {Array<number>} neighbours IDs of neighboring nodes.
|
||||
* @property {Array<number>} vertexIds
|
||||
* @property {Vector3} centroid
|
||||
* @property {Array<Array<number>>} portals Array of portals, each defined by two vertex IDs.
|
||||
* @property {boolean} closed
|
||||
* @property {number} cost
|
||||
*/
|
||||
const Node = {}; // jshint ignore:line
|
||||
|
||||
export { Pathfinding };
|
53
src/jlmap3d/jl3dpassflow/utils/PathfindingHelper.js
Normal file
53
src/jlmap3d/jl3dpassflow/utils/PathfindingHelper.js
Normal file
@ -0,0 +1,53 @@
|
||||
import {
|
||||
Color,
|
||||
Object3D,
|
||||
LineBasicMaterial,
|
||||
MeshBasicMaterial,
|
||||
SphereBufferGeometry,
|
||||
BoxGeometry,
|
||||
Mesh,
|
||||
SphereGeometry,
|
||||
Geometry,
|
||||
Vector3,
|
||||
Line,
|
||||
} from 'three';
|
||||
|
||||
const OFFSET = 0.2;
|
||||
|
||||
/**
|
||||
* Helper for debugging pathfinding behavior.
|
||||
*/
|
||||
class PathfindingHelper extends Object3D {
|
||||
constructor () {
|
||||
super();
|
||||
|
||||
this._playerMarker = null;
|
||||
|
||||
this._markers = [
|
||||
this._playerMarker,
|
||||
];
|
||||
|
||||
this._markers.forEach( ( marker ) => {
|
||||
this.add( marker );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {Vector3} position
|
||||
* @return {this}
|
||||
*/
|
||||
setPlayerPosition( position ) {
|
||||
// this._playerMarker.lookAt(position);
|
||||
this._playerMarker.position.copy( position );
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export { PathfindingHelper };
|
66
src/jlmap3d/jl3dpassflow/utils/Utils.js
Normal file
66
src/jlmap3d/jl3dpassflow/utils/Utils.js
Normal file
@ -0,0 +1,66 @@
|
||||
class Utils {
|
||||
|
||||
static roundNumber (value, decimals) {
|
||||
const factor = Math.pow(10, decimals);
|
||||
return Math.round(value * factor) / factor;
|
||||
}
|
||||
|
||||
static sample (list) {
|
||||
return list[Math.floor(Math.random() * list.length)];
|
||||
}
|
||||
|
||||
static distanceToSquared (a, b) {
|
||||
|
||||
var dx = a.x - b.x;
|
||||
var dy = a.y - b.y;
|
||||
var dz = a.z - b.z;
|
||||
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
|
||||
}
|
||||
|
||||
//+ Jonas Raoni Soares Silva
|
||||
//@ http://jsfromhell.com/math/is-point-in-poly [rev. #0]
|
||||
static isPointInPoly (poly, pt) {
|
||||
for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
|
||||
((poly[i].z <= pt.z && pt.z < poly[j].z) || (poly[j].z <= pt.z && pt.z < poly[i].z)) && (pt.x < (poly[j].x - poly[i].x) * (pt.z - poly[i].z) / (poly[j].z - poly[i].z) + poly[i].x) && (c = !c);
|
||||
return c;
|
||||
}
|
||||
|
||||
static isVectorInPolygon (vector, polygon, vertices) {
|
||||
|
||||
// reference point will be the centroid of the polygon
|
||||
// We need to rotate the vector as well as all the points which the polygon uses
|
||||
|
||||
var lowestPoint = 100000;
|
||||
var highestPoint = -100000;
|
||||
|
||||
var polygonVertices = [];
|
||||
|
||||
polygon.vertexIds.forEach((vId) => {
|
||||
lowestPoint = Math.min(vertices[vId].y, lowestPoint);
|
||||
highestPoint = Math.max(vertices[vId].y, highestPoint);
|
||||
polygonVertices.push(vertices[vId]);
|
||||
});
|
||||
|
||||
if (vector.y < highestPoint + 0.5 && vector.y > lowestPoint - 0.5 &&
|
||||
this.isPointInPoly(polygonVertices, vector)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static triarea2 (a, b, c) {
|
||||
var ax = b.x - a.x;
|
||||
var az = b.z - a.z;
|
||||
var bx = c.x - a.x;
|
||||
var bz = c.z - a.z;
|
||||
return bx * az - ax * bz;
|
||||
}
|
||||
|
||||
static vequal (a, b) {
|
||||
return this.distanceToSquared(a, b) < 0.00001;
|
||||
}
|
||||
}
|
||||
|
||||
export { Utils };
|
4
src/jlmap3d/jl3dpassflow/utils/index.js
Normal file
4
src/jlmap3d/jl3dpassflow/utils/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
import { Pathfinding } from './Pathfinding';
|
||||
import { PathfindingHelper } from './PathfindingHelper';
|
||||
|
||||
export { Pathfinding, PathfindingHelper };
|
@ -53,12 +53,12 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
|
||||
jsonwebwork.onmessage = function (event) {
|
||||
|
||||
|
||||
|
||||
// console.log(event.data);
|
||||
// if(event.data.deviceType == "TRAIN"){
|
||||
// // console.log(event.data);
|
||||
//
|
||||
// }
|
||||
console.log(event.data);
|
||||
// console.log(event.data);
|
||||
if(event.data.type == "Device_Load_Destroy_3D"){
|
||||
DeviceDestroy(event.data);
|
||||
return;
|
||||
@ -150,7 +150,6 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
|
||||
let code = data.code;
|
||||
|
||||
if(trainlisttest.list[code].right != data.right){
|
||||
|
||||
if(data.right == "0"){
|
||||
trainlisttest.list[code].right = "0";
|
||||
trainlisttest.list[code].rotation.y = Math.PI;
|
||||
@ -442,27 +441,28 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
|
||||
for(let i=0,leni=data.length;i<leni;i++){
|
||||
if(data[i].deviceType == "SWITCH"){
|
||||
initswitch(data[i]);
|
||||
}
|
||||
if(data[i].deviceType == "PSD"){
|
||||
}else if(data[i].deviceType == "PSD"){
|
||||
initstand(data[i]);
|
||||
}else{
|
||||
console.log(data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function DeviceDestroy(data){
|
||||
for(let i=0,leni=data.body.length;i<leni;i++){
|
||||
for(let i=0,leni=data.body.deviceList.length;i<leni;i++){
|
||||
|
||||
if(data.body[i].type == "TRAIN"){
|
||||
code =data.body[i].code;
|
||||
if (trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == "0") {
|
||||
if(data.body.deviceList[i].type == "TRAIN"){
|
||||
code =data.body.deviceList[i].code;
|
||||
if (trainlisttest.list[code].dispose != data.body.deviceList[i].dispose && data.body.deviceList[i].dispose == "0") {
|
||||
|
||||
if (rails.sectionrail[data.body[i].section]) {
|
||||
if (rails.sectionrail[data.body.deviceList[i].section]) {
|
||||
|
||||
trainlisttest.group.add(trainlisttest.list[code]);
|
||||
trainlisttest.list[code].position.y = 0;
|
||||
// trainlisttest.list[code].progress = 0;
|
||||
trainlisttest.list[code].dispose = "0";
|
||||
trainlisttest.list[code].nowcode = data.body[i].section;
|
||||
trainlisttest.list[code].nowcode = data.body.deviceList[i].section;
|
||||
trainlisttest.list[code].nextcode = null;
|
||||
trainlisttest.list[code].curve = null;
|
||||
trainlisttest.list[code].nextcurve = null;
|
||||
@ -476,7 +476,7 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
|
||||
trainlisttest.list[code].mixerpush = true;
|
||||
}
|
||||
}
|
||||
} else if (trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == "1") {
|
||||
} else if (trainlisttest.list[code].dispose != data.body.deviceList[i].dispose && data.body.deviceList[i].dispose == "1") {
|
||||
trainlisttest.list[code].status = 1;
|
||||
trainlisttest.group.remove(trainlisttest.list[code]);
|
||||
trainlisttest.list[code].progress = null;
|
||||
@ -493,17 +493,17 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
|
||||
|
||||
}
|
||||
}
|
||||
if(data.body[i].type == "SIGNAL"){
|
||||
signalupdate(data.body[i]);
|
||||
if(data.body.deviceList[i].type == "SIGNAL"){
|
||||
signalupdate(data.body.deviceList[i]);
|
||||
}
|
||||
if(data.body[i].type == "SWITCH"){
|
||||
switchupdate(data.body[i]);
|
||||
if(data.body.deviceList[i].type == "SWITCH"){
|
||||
switchupdate(data.body.deviceList[i]);
|
||||
}
|
||||
if(data.body[i].type == "PSD"){
|
||||
standupdate(data.body[i]);
|
||||
if(data.body.deviceList[i].type == "PSD"){
|
||||
standupdate(data.body.deviceList[i]);
|
||||
}
|
||||
if(data.body[i].type == "TRAIN_DOOR"){
|
||||
traindoorupdate(data.body[i]);
|
||||
if(data.body.deviceList[i].type == "TRAIN_DOOR"){
|
||||
traindoorupdate(data.body.deviceList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,6 +250,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.StationStand] = {
|
||||
common: { // 通用属性
|
||||
textFontSize: 8, // 站台默认字体大小
|
||||
|
@ -363,6 +363,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.Switch] = {
|
||||
text: {
|
||||
show: true, // 道岔名称显示
|
||||
|
@ -200,6 +200,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.StationStand] = {
|
||||
common: { // 通用属性
|
||||
textFontSize: 10, // 站台默认字体大小
|
||||
|
@ -383,6 +383,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.Switch] = {
|
||||
text: {
|
||||
show: true, // 道岔名称显示
|
||||
|
@ -329,8 +329,8 @@ class SkinCode extends defaultStyle {
|
||||
},
|
||||
StationControl:{
|
||||
text: {
|
||||
distance: 2, // 灯和文字之间的距离
|
||||
fontSize: 11, // 字体大小
|
||||
distance: 10, // 灯和文字之间的距离
|
||||
fontSize: 14, // 字体大小
|
||||
fontFormat: 'consolas', // 字体格式
|
||||
fontColor: '#ffffff', // 字体颜色
|
||||
fontWeight: 'normal', // 字体粗细
|
||||
@ -345,9 +345,9 @@ class SkinCode extends defaultStyle {
|
||||
},
|
||||
lamp: {
|
||||
count: 2, // 控制模式灯个数
|
||||
offset: {x: 0, y: 0}, // 控制模式灯偏移量
|
||||
radiusR: 4, // 控制模式灯的半径
|
||||
distance: 36, // 控制模式之间灯之间的距离
|
||||
offset: {x: 0, y: 3}, // 控制模式灯偏移量
|
||||
radiusR: 7, // 控制模式灯的半径
|
||||
distance: 42, // 控制模式之间灯之间的距离
|
||||
grayColor: '#7F7F7F', // 控制模式灰色
|
||||
greenColor: '#00FF00', // 控制模式绿色
|
||||
redColor: '#FF0000', // 控制模式红色
|
||||
@ -418,6 +418,12 @@ class SkinCode extends defaultStyle {
|
||||
this[deviceType.Line] = {
|
||||
lineColor: '#FFFFFF' // 线条颜色
|
||||
};
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 8 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.LcControl] = {
|
||||
text: {
|
||||
|
@ -392,6 +392,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.Switch] = {
|
||||
text: {
|
||||
show: true, // 道岔名称显示
|
||||
|
@ -411,6 +411,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.StationDelayUnlock] = {
|
||||
text: {
|
||||
distance: 3, // 延迟解锁和设备之间的距离
|
||||
|
@ -387,6 +387,13 @@ class SkinCode extends defaultStyle {
|
||||
}
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.Switch] = {
|
||||
text: {
|
||||
show: true, // 道岔名称显示
|
||||
|
@ -509,6 +509,14 @@ class SkinCode extends defaultStyle {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
lineDash: [8, 4]
|
||||
};
|
||||
|
||||
// 供电线路
|
||||
this[deviceType.Power] = {
|
||||
lineColor: '#FFFFFF', // 线条颜色
|
||||
strokeColor: '#ccc', // 线条颜色
|
||||
extendLength: 10 // 延伸长度
|
||||
};
|
||||
|
||||
this[deviceType.AutomaticRoute] = {
|
||||
// 是否显示
|
||||
displayCondition: '03', // 显示条件 prdType
|
||||
|
@ -245,4 +245,9 @@ deviceRender[deviceType.Arrow] = {
|
||||
_type: deviceType.Arrow,
|
||||
zlevel: 1
|
||||
};
|
||||
/** 供电线路 */
|
||||
deviceRender[deviceType.Power] = {
|
||||
_type: deviceType.Power,
|
||||
zlevel: 1
|
||||
};
|
||||
export default deviceRender;
|
||||
|
@ -42,7 +42,8 @@ const deviceType = {
|
||||
Axle: 'Axle',
|
||||
SplitStation:'SplitStation',
|
||||
SwitchFault: 'SwitchFault',
|
||||
Arrow: 'Arrow'
|
||||
Arrow: 'Arrow',
|
||||
Power: 'Power'
|
||||
};
|
||||
|
||||
export default deviceType;
|
||||
|
47
src/jmapNew/shape/Power/ESeparator.js
Normal file
47
src/jmapNew/shape/Power/ESeparator.js
Normal file
@ -0,0 +1,47 @@
|
||||
import Group from 'zrender/src/container/Group';
|
||||
import Polyline from 'zrender/src/graphic/shape/Polyline';
|
||||
|
||||
/** 分隔符*/
|
||||
export default class ESeparator extends Group {
|
||||
constructor(model) {
|
||||
super();
|
||||
this.model = model;
|
||||
this.zlevel = model.zlevel;
|
||||
this.z = model.z || 6;
|
||||
this.style = model.style;
|
||||
this.setType();
|
||||
}
|
||||
|
||||
createModel(points) {
|
||||
const model = this.model;
|
||||
this.partition = new Polyline({
|
||||
zlevel: this.zlevel,
|
||||
progressive: model.progressive,
|
||||
z: this.z,
|
||||
shape: {
|
||||
points: points
|
||||
},
|
||||
style: {
|
||||
lineWidth: model.width,
|
||||
stroke: model.stroke
|
||||
}
|
||||
});
|
||||
this.add(this.partition);
|
||||
}
|
||||
|
||||
setType() {
|
||||
const model = this.model;
|
||||
if (model && model.traingle) {
|
||||
const points = [
|
||||
[model.point.x, model.point.y - (this.style.Power.extendLength)],
|
||||
[model.point.x, model.point.y + (this.style.Power.extendLength)]
|
||||
];
|
||||
this.createModel(points);
|
||||
}
|
||||
if (model.traingle) {
|
||||
this.origin = [model.point.x, model.point.y];
|
||||
this.rotation = Math.PI * 2 - Math.atan2(model.traingle.absy, model.traingle.absx) * model.traingle.drictx * model.traingle.dricty;
|
||||
this.dirty(); // 可以无需调用
|
||||
}
|
||||
}
|
||||
}
|
131
src/jmapNew/shape/Power/index.js
Normal file
131
src/jmapNew/shape/Power/index.js
Normal file
@ -0,0 +1,131 @@
|
||||
import Polyline from 'zrender/src/graphic/shape/Polyline';
|
||||
import Group from 'zrender/src/container/Group';
|
||||
import JTriangle from '../../utils/JTriangle';
|
||||
import ESeparator from './ESeparator';
|
||||
import {isShowThePrdType} from '../../utils/handlePath';
|
||||
|
||||
export default class Line2 extends Group {
|
||||
constructor(model, style) {
|
||||
super();
|
||||
this._code = model.code;
|
||||
this._type = model._type;
|
||||
this.zlevel = model.zlevel;
|
||||
this.z = 0;
|
||||
this.model = model;
|
||||
this.style = style;
|
||||
this.isShowShape = true;
|
||||
if (isShowThePrdType(model.prdType, model.showConditions) || model.previewOrMapDraw) {
|
||||
this.create();
|
||||
this.createTerminal();
|
||||
this.setState(model);
|
||||
}
|
||||
if (model.previewOrMapDraw) {
|
||||
this.setShowMode();
|
||||
}
|
||||
}
|
||||
|
||||
create() {
|
||||
const model = this.model;
|
||||
const style = this.style;
|
||||
if (model && model.points.length > 1) {
|
||||
const points = [];
|
||||
for (let i = 0; i < model.points.length; i++) {
|
||||
points.push([model.points[i].x, model.points[i].y]);
|
||||
}
|
||||
this.line = new Polyline({
|
||||
zlevel: this.zlevel,
|
||||
progressive: model.progressive,
|
||||
z: this.z,
|
||||
shape: {
|
||||
points: points
|
||||
},
|
||||
style: {
|
||||
lineWidth: model.width,
|
||||
stroke: style.Power.strokeColor
|
||||
}
|
||||
});
|
||||
this.add(this.line);
|
||||
}
|
||||
}
|
||||
|
||||
createTerminal() { // 创建左右端点
|
||||
const model = this.model;
|
||||
const style = this.style;
|
||||
if (model && model.leftTerminal) { // 左端点
|
||||
const traingle = new JTriangle(model.points[0], model.points[1]);
|
||||
this.leftTerminal = new ESeparator({
|
||||
style: style,
|
||||
zlevel: this.zlevel,
|
||||
z: this.z + 3,
|
||||
traingle: traingle,
|
||||
width: model.width,
|
||||
stroke: style.Power.strokeColor,
|
||||
point: {
|
||||
x: model.points[0].x,
|
||||
y: model.points[0].y
|
||||
}
|
||||
});
|
||||
this.add(this.leftTerminal);
|
||||
}
|
||||
if (model && model.rightTerminal) { // 右端点
|
||||
const traingle = new JTriangle(model.points[model.points.length - 2], model.points[model.points.length - 1]);
|
||||
this.rightTerminal = new ESeparator({
|
||||
style: style,
|
||||
zlevel: this.zlevel,
|
||||
z: this.z + 3,
|
||||
traingle: traingle,
|
||||
width: model.width,
|
||||
stroke: style.Power.strokeColor,
|
||||
point: {
|
||||
x: model.points[model.points.length - 1].x,
|
||||
y: model.points[model.points.length - 1].y
|
||||
}
|
||||
});
|
||||
this.add(this.rightTerminal);
|
||||
}
|
||||
}
|
||||
|
||||
setLineType(type) {
|
||||
switch (type) {
|
||||
case '01': break;
|
||||
case '02':
|
||||
this.eachChild((child) => {
|
||||
child.setStyle('lineDash', this.style.Line.lineDash || [4]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setState(model) {
|
||||
if (!this.isShowShape) return;
|
||||
this.setLineType(model.type);
|
||||
}
|
||||
// 设置显示模式
|
||||
setShowMode() {
|
||||
const showMode = this.model.showMode;
|
||||
const showConditions = this.model.showConditions;
|
||||
if (!showConditions || showConditions === '01' || showMode === showConditions) {
|
||||
this.eachChild((child) => {
|
||||
child.show();
|
||||
});
|
||||
} else {
|
||||
this.eachChild((child) => {
|
||||
child.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
setShowStation(stationCode) {
|
||||
if (!stationCode || this.model.stationCode === stationCode) {
|
||||
this.eachChild((child) => {
|
||||
child.show();
|
||||
});
|
||||
this.isShowShape = true;
|
||||
this.setState(this.model);
|
||||
} else {
|
||||
this.eachChild((child) => {
|
||||
child.hide();
|
||||
});
|
||||
this.isShowShape = false;
|
||||
}
|
||||
}
|
||||
}
|
@ -70,6 +70,12 @@ export default class ESingleControl extends Group {
|
||||
this.control.setStyle('fill', color);
|
||||
}
|
||||
}
|
||||
|
||||
setTextColor(color) {
|
||||
if (color) {
|
||||
this.text.setStyle('textFill', color);
|
||||
}
|
||||
}
|
||||
getArcBoundingRect() {
|
||||
const rect = this.control.getBoundingRect().clone();
|
||||
const scale = this.control.scale[0];
|
||||
|
@ -291,12 +291,15 @@ export default class Station extends Group {
|
||||
this.emergencyControl && this.emergencyControl.setColor(this.style.Station.StationControl.lamp.grayColor);
|
||||
this.substationControl && this.substationControl.setColor(this.style.Station.StationControl.lamp.grayColor);
|
||||
this.centerControl && this.centerControl.setColor(this.style.Station.StationControl.lamp.greenColor);
|
||||
this.centerControl && this.centerControl.setTextColor(this.style.Station.StationControl.lamp.greenColor); // 文字颜色
|
||||
}
|
||||
|
||||
handleLocal() { // 站控
|
||||
this.emergencyControl && this.emergencyControl.setColor(this.style.Station.StationControl.lamp.grayColor);
|
||||
this.substationControl && this.substationControl.setColor(this.style.Station.StationControl.lamp.yellowColor);
|
||||
this.substationControl && this.substationControl.setTextColor(this.style.Station.StationControl.lamp.yellowColor); // 文字颜色
|
||||
this.centerControl && this.centerControl.setColor(this.style.Station.StationControl.lamp.grayColor);
|
||||
this.arrowsControl && this.arrowsControl.setColor(this.style.Station.StationControl.lamp.greenColor);
|
||||
}
|
||||
|
||||
handleEmergency() { // 紧急站控
|
||||
@ -315,7 +318,8 @@ export default class Station extends Group {
|
||||
setState(model) {
|
||||
if (!this.isShowShape) return;
|
||||
this.recover();
|
||||
model.controlMode && this['handle' + model.controlMode]();
|
||||
// model.controlMode && this['handle' + model.controlMode]();
|
||||
this.handleLocal();
|
||||
}
|
||||
|
||||
getShapeTipPoint(opts) {
|
||||
|
@ -24,6 +24,7 @@ import AutomaticRoute from './AutomacticRoute/index.js';
|
||||
import SaidLamp from './SaidLamp/index.js';
|
||||
import SplitStation from './SplitStation/index';
|
||||
import Arrow from './Arrow/index';
|
||||
import Power from './Power/index';
|
||||
|
||||
/** 图库*/
|
||||
const mapShape = {};
|
||||
@ -69,6 +70,7 @@ mapShape[deviceType.Axle] = SaidLamp;
|
||||
mapShape[deviceType.SwitchFault] = SaidLamp;
|
||||
mapShape[deviceType.SplitStation] = SplitStation;
|
||||
mapShape[deviceType.Arrow] = Arrow;
|
||||
mapShape[deviceType.Power] = Power;
|
||||
|
||||
function shapefactory(device, jmap) {
|
||||
const type = device._type;
|
||||
|
@ -144,9 +144,12 @@ export function parser(data, skinCode, showConfig) {
|
||||
zrUtil.each(data.splitStationList || [], elem => {
|
||||
mapDevice[elem.code] = createDevice(deviceType.SplitStation, elem, propConvert, showConfig);
|
||||
}, this);
|
||||
zrUtil.each( data.arrowList || [], elem => {
|
||||
zrUtil.each(data.arrowList || [], elem => {
|
||||
mapDevice[elem.code] = createDevice(deviceType.Arrow, elem, propConvert, showConfig);
|
||||
}, this);
|
||||
zrUtil.each(data.powerList || [], elem => {
|
||||
mapDevice[elem.code] = createDevice(deviceType.Power, elem, propConvert, showConfig);
|
||||
}, this);
|
||||
zrUtil.each(data.indicatorLightList || [], elem => {
|
||||
switch (elem.type) {
|
||||
case 'AtsControl':
|
||||
@ -326,6 +329,7 @@ export function updateMapData(state, model) {
|
||||
case deviceType.SplitStation: updateForList(model, state, 'splitStationList'); break;
|
||||
case deviceType.SwitchFault: updateForList(model, state, 'indicatorLightList'); break;
|
||||
case deviceType.Arrow: updateForList(model, state, 'arrowList'); break;
|
||||
case deviceType.Power: updateForList(model, state, 'powerList'); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +342,13 @@ const map = {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
powerList: (state) => {
|
||||
if (state.map) {
|
||||
return state.map.powerList || [];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
tempSpeedLimitList: (state) => {
|
||||
if (state.map) {
|
||||
return state.map.tempSpeedLimitList;
|
||||
|
@ -30,7 +30,7 @@ export default {
|
||||
return {
|
||||
jl3d: null,
|
||||
rendermode:'监控视角',
|
||||
renderswitch:true
|
||||
renderswitch:false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -133,6 +133,14 @@
|
||||
@setCenter="setCenter"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="供电线" class="tab_pane_box" name="Power" :lazy="lazy">
|
||||
<power-draft
|
||||
ref="Power"
|
||||
:selected="selected"
|
||||
@updateMapModel="updateMapModel"
|
||||
@setCenter="setCenter"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('map.text')" class="tab_pane_box" name="Text" :lazy="lazy">
|
||||
<text-draft
|
||||
ref="Text"
|
||||
@ -211,6 +219,7 @@ import PsdDraft from './psdDraft';
|
||||
import EspDraft from './espDraft';
|
||||
import TrainDraft from './train/index';
|
||||
import LineDraft from './line';
|
||||
import PowerDraft from './power';
|
||||
import TextDraft from './text';
|
||||
import ControlDraft from './ControlDraft';
|
||||
import TrainWindowDraft from './trainwindow';
|
||||
@ -242,6 +251,7 @@ export default {
|
||||
TrainWindowDraft,
|
||||
TrainDraft,
|
||||
LineDraft,
|
||||
PowerDraft,
|
||||
TextDraft,
|
||||
ZcControlDraft,
|
||||
OutFrameDraft,
|
||||
|
@ -225,7 +225,7 @@ export default {
|
||||
this.editModel.points.splice(index + 1, 0, data);
|
||||
},
|
||||
delPoint(index) {
|
||||
this.editModel.points.splice(index + 1, 0);
|
||||
this.editModel.points.splice(index, 1);
|
||||
},
|
||||
addPointAddModel(index) {
|
||||
const data = { x: 0, y: 0};
|
||||
|
315
src/views/newMap/newMapdraft/mapoperate/power.vue
Normal file
315
src/views/newMap/newMapdraft/mapoperate/power.vue
Normal file
@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<el-tabs v-model="activeName" class="card">
|
||||
<el-tab-pane class="view-control" :label="$t('map.property')" name="first" :lazy="lazy">
|
||||
<div style="height: calc(100% - 46px);">
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<config-list ref="form" :form="form" :form-model="editModel" :rules="rules" />
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-button-group class="map-draft-group">
|
||||
<el-button type="primary" size="small" @click="edit">{{ $t('map.updateObj') }}</el-button>
|
||||
<el-button type="danger" size="small" @click="deleteObj">{{ $t('map.deleteObj') }}</el-button>
|
||||
</el-button-group>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane class="view-control" :label="$t('map.newConstruction')" name="second" :lazy="lazy">
|
||||
<div style="height: calc(100% - 46px);">
|
||||
<el-scrollbar wrap-class="scrollbar-wrapper">
|
||||
<el-form ref="make" label-width="120px" :model="addModel" size="mini" :rules="makeRules">
|
||||
<el-form-item :label="$t('map.lineWidth')" prop="width">
|
||||
<el-input-number v-model="addModel.width" :min="1" />px
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('map.showConditions')" prop="showConditions">
|
||||
<el-radio-group v-model="addModel.showConditions">
|
||||
<el-radio label="01">{{ $t('map.localCenter') }}</el-radio>
|
||||
<el-radio label="02">{{ $t('map.center') }}</el-radio>
|
||||
<el-radio label="03">{{ $t('map.local') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<div class="coordinate">
|
||||
<span class="title">{{ $t('map.linePoint') }}</span>
|
||||
<div class="point-section">
|
||||
<template v-for="(point, index) in addModel.points">
|
||||
<div :key="index" style="overflow: hidden;">
|
||||
<el-form-item
|
||||
label=""
|
||||
:prop="'points[' + index + '].x'"
|
||||
style="display: table; float: left;"
|
||||
label-width="0px"
|
||||
>
|
||||
<el-input-number v-model="point.x" />
|
||||
</el-form-item>
|
||||
<span style="display: table; margin-left: 8px; float: left; line-height: 28px;">
|
||||
, </span>
|
||||
<el-form-item
|
||||
label=""
|
||||
:prop="'points[' + index + '].y'"
|
||||
style="display: table; float: left; margin-right: 5px;"
|
||||
label-width="10px"
|
||||
>
|
||||
<el-input-number v-model="point.y" />
|
||||
</el-form-item>
|
||||
<el-button
|
||||
icon="el-icon-plus"
|
||||
circle
|
||||
class="point-button"
|
||||
@click="addPointAddModel(index)"
|
||||
/>
|
||||
<el-button
|
||||
icon="el-icon-minus"
|
||||
:disabled="index == 0 || index == addModel.points.length - 1"
|
||||
circle
|
||||
class="point-button"
|
||||
@click="delPointAddModel(index)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-button-group class="map-draft-group">
|
||||
<el-button type="primary" size="small" @click="create">{{ $t('map.create') }}</el-button>
|
||||
</el-button-group>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { getUID } from '@/jmapNew/utils/Uid';
|
||||
import ConfigList from './config/list';
|
||||
import { deepAssign } from '@/utils/index';
|
||||
|
||||
export default {
|
||||
name: 'StationStandDraft',
|
||||
components: {
|
||||
ConfigList
|
||||
},
|
||||
props: {
|
||||
selected: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeName: 'first',
|
||||
lazy: true,
|
||||
showConditionsList: [
|
||||
{ label: this.$t('map.localCenter'), value: '01'},
|
||||
{ label: this.$t('map.center'), value: '02' },
|
||||
{ label: this.$t('map.local'), value: '03'}
|
||||
],
|
||||
terminalList: [
|
||||
{ label: '显示', value: true},
|
||||
{ label: '隐藏', value: false }
|
||||
],
|
||||
editModel: {
|
||||
code: '',
|
||||
width: 1,
|
||||
leftTerminal: true,
|
||||
rightTerminal: true,
|
||||
showConditions: '01', // 显示条件
|
||||
points: []
|
||||
},
|
||||
addModel: {
|
||||
width: 1,
|
||||
showConditions: '01',
|
||||
points: [
|
||||
{ x: 0, y: 0 },
|
||||
{ x: 100, y: 100 }
|
||||
]
|
||||
},
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: this.$t('rules.pleaseSelectLine'), trigger: 'blur' }
|
||||
],
|
||||
width: [
|
||||
{ required: true, message: this.$t('rules.pleaseSelectLineWidth'), trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
makeRules: {
|
||||
code: [
|
||||
{ required: true, message: this.$t('rules.pleaseSelectLine'), trigger: 'blur' }
|
||||
],
|
||||
width: [
|
||||
{ required: true, message: this.$t('rules.pleaseSelectLineWidth'), trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('map', [
|
||||
'powerList'
|
||||
]),
|
||||
form() {
|
||||
const form = {
|
||||
labelWidth: '120px',
|
||||
items: {
|
||||
code: {
|
||||
name: '',
|
||||
item: []
|
||||
},
|
||||
draw: {
|
||||
name: this.$t('map.drawData'),
|
||||
item: [
|
||||
{ prop: 'code', label: '供电线路', type: 'select', optionLabel: 'code', optionValue: 'code', options: this.powerList, change: true, deviceChange: this.deviceChange },
|
||||
{ prop: 'width', label: '供电线路宽度', type: 'number', min: 1, placeholder: 'px' },
|
||||
{ prop: 'showConditions', label: this.$t('map.showConditions'), type: 'radio', optionLabel: 'label', optionValue:'value', radioList: this.showConditionsList},
|
||||
{ prop: 'leftTerminal', label: '左侧端点:', type: 'select', optionLabel: 'label', optionValue:'value', options: this.terminalList},
|
||||
{ prop: 'rightTerminal', label: '右侧端点:', type: 'select', optionLabel: 'label', optionValue:'value', options: this.terminalList},
|
||||
{ prop: 'points', label: this.$t('map.segmentCoordinates'), type: 'points', width: '100px', isHidden: !this.isPointsShow, addPoint: this.addPoint, delPoint: this.delPoint }
|
||||
]
|
||||
},
|
||||
map: {
|
||||
name: this.$t('map.mapData'),
|
||||
item: [
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
return form;
|
||||
},
|
||||
isPointsShow() {
|
||||
return this.editModel.points.length > 0;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
deviceChange(code) {
|
||||
this.$emit('setCenter', code);
|
||||
this.deviceSelect(this.$store.getters['map/getDeviceByCode'](code));
|
||||
},
|
||||
deviceSelect(selected) {
|
||||
if (selected && selected._type.toUpperCase() === 'Power'.toUpperCase()) {
|
||||
this.$refs.form && this.$refs.form.resetFields();
|
||||
this.$refs.make && this.$refs.make.resetFields();
|
||||
this.activeName = 'first';
|
||||
this.editModel = deepAssign(this.editModel, selected);
|
||||
}
|
||||
},
|
||||
addPoint(index) {
|
||||
const data = { x: 0, y: 0 };
|
||||
this.editModel.points.splice(index + 1, 0, data);
|
||||
},
|
||||
delPoint(index) {
|
||||
this.editModel.points.splice(index, 1);
|
||||
},
|
||||
addPointAddModel(index) {
|
||||
const data = { x: 0, y: 0};
|
||||
this.addModel.points.splice(index + 1, 0, data);
|
||||
},
|
||||
delPointAddModel(index) {
|
||||
this.addModel.points.splice(index, 1);
|
||||
},
|
||||
create() {
|
||||
this.$refs['make'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (JSON.stringify(this.addModel.points[0]) !== JSON.stringify(this.addModel.points[this.addModel.points.length - 1])) {
|
||||
const pointArr = JSON.stringify(this.addModel.points);
|
||||
const model = {
|
||||
_type: 'Power',
|
||||
code: getUID('Power', this.powerList),
|
||||
width: this.addModel.width,
|
||||
leftTerminal: true,
|
||||
rightTerminal: true,
|
||||
showConditions: this.addModel.showConditions,
|
||||
points: JSON.parse(pointArr)
|
||||
};
|
||||
this.$emit('updateMapModel', model);
|
||||
this.$refs.make && this.$refs.make.resetFields();
|
||||
} else {
|
||||
this.$message.console.error(this.$t('tip.cannotCoincide'));
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// 修改对象
|
||||
edit() {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
const data = Object.assign({_type: 'Line'}, this.editModel);
|
||||
this.$emit('updateMapModel', data);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 删除对象
|
||||
deleteObj() {
|
||||
const selected = this.$store.getters['map/getDeviceByCode'](this.editModel.code);
|
||||
if (selected && selected._type.toUpperCase() === 'Line'.toUpperCase()) {
|
||||
this.$confirm(this.$t('tip.confirmDeletion'), this.$t('tip.hint'), {
|
||||
confirmButtonText: this.$t('tip.confirm'),
|
||||
cancelButtonText: this.$t('tip.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$emit('updateMapModel', {...selected, _dispose: true});
|
||||
this.$refs.form && this.$refs.form.resetFields();
|
||||
}).catch(() => {
|
||||
this.$message.info(this.$t('tip.cancelledDelete'));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
@import "src/styles/mixin.scss";
|
||||
.view-control{
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.card {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.coordinate {
|
||||
overflow: hidden;
|
||||
|
||||
.title {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 40px;
|
||||
padding: 0 12px 0 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
line-height: 28px;
|
||||
width: 120px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.point-section {
|
||||
/*float: left;*/
|
||||
position: absolute;
|
||||
left: 120px;
|
||||
width: calc(100% - 120px);
|
||||
}
|
||||
|
||||
.point-button {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
float: left;
|
||||
|
||||
/deep/ {
|
||||
.el-icon-plus,
|
||||
.el-icon-minus {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-input-number--mini {
|
||||
width: 110px;
|
||||
}
|
||||
</style>
|
2339
static/model/path/path.obj
Normal file
2339
static/model/path/path.obj
Normal file
File diff suppressed because it is too large
Load Diff
25
static/workertest/passsimulation/passer.js
Normal file
25
static/workertest/passsimulation/passer.js
Normal file
@ -0,0 +1,25 @@
|
||||
var i=0;
|
||||
var update = null;
|
||||
function timedCount(){
|
||||
|
||||
onmessage = (e) => {
|
||||
|
||||
|
||||
if(e.data == "on"){
|
||||
|
||||
update = setInterval("passerupdate()", 50);
|
||||
}
|
||||
|
||||
if(e.data == "off"){
|
||||
clearInterval(update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
function passerupdate(){
|
||||
|
||||
postMessage("up");
|
||||
}
|
||||
|
||||
timedCount();
|
25
static/workertest/passsimulation/station.js
Normal file
25
static/workertest/passsimulation/station.js
Normal file
@ -0,0 +1,25 @@
|
||||
var i=0;
|
||||
var update = null;
|
||||
function timedCount(){
|
||||
|
||||
onmessage = (e) => {
|
||||
|
||||
|
||||
if(e.data == "on"){
|
||||
console.log("on");
|
||||
update = setInterval("stationupdate()", 3000);
|
||||
}
|
||||
|
||||
if(e.data == "off"){
|
||||
clearInterval(update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
function stationupdate(){
|
||||
|
||||
postMessage("up");
|
||||
}
|
||||
|
||||
timedCount();
|
Loading…
Reference in New Issue
Block a user