This commit is contained in:
joylink_cuiweidong 2020-05-28 14:51:24 +08:00
commit 652e9aa5d7
40 changed files with 5259 additions and 35 deletions

View File

@ -354,7 +354,7 @@ export function Jl3dpassflow(dom) {
} }
} }
} }
},3000); },1000);
}; };
},1000); },1000);
@ -390,7 +390,7 @@ export function Jl3dpassflow(dom) {
} }
moveanimateupdate(); moveanimateupdate();
scope.anime = requestAnimationFrame(animate); requestAnimationFrame(animate);
} }
@ -468,7 +468,7 @@ export function Jl3dpassflow(dom) {
} }
if(humans[i].stage == 0){ if(humans[i].stage == 0){
console.log(humans[i].doors); // console.log(humans[i].doors);
zhajiin[humans[i].doors].waiting = 0; zhajiin[humans[i].doors].waiting = 0;
humans[i].stage = 1; humans[i].stage = 1;
} }
@ -805,8 +805,7 @@ export function Jl3dpassflow(dom) {
} }
function zhajicontrol(type,door){ function zhajicontrol(type,door){
console.log(door);
console.log(zhajiin);
let devicenum = door-1; let devicenum = door-1;
if(type == "in"){ if(type == "in"){
deviceaction[zhajiin[devicenum].id].action.reset(); deviceaction[zhajiin[devicenum].id].action.reset();

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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);
}

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View 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 };

View File

@ -0,0 +1,4 @@
import { Pathfinding } from './Pathfinding';
import { PathfindingHelper } from './PathfindingHelper';
export { Pathfinding, PathfindingHelper };

View File

@ -53,12 +53,12 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
jsonwebwork.onmessage = function (event) { jsonwebwork.onmessage = function (event) {
// console.log(event.data);
// if(event.data.deviceType == "TRAIN"){ // if(event.data.deviceType == "TRAIN"){
// // console.log(event.data); // // console.log(event.data);
// //
// } // }
console.log(event.data); // console.log(event.data);
if(event.data.type == "Device_Load_Destroy_3D"){ if(event.data.type == "Device_Load_Destroy_3D"){
DeviceDestroy(event.data); DeviceDestroy(event.data);
return; return;
@ -150,7 +150,6 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
let code = data.code; let code = data.code;
if(trainlisttest.list[code].right != data.right){ if(trainlisttest.list[code].right != data.right){
if(data.right == "0"){ if(data.right == "0"){
trainlisttest.list[code].right = "0"; trainlisttest.list[code].right = "0";
trainlisttest.list[code].rotation.y = Math.PI; 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++){ for(let i=0,leni=data.length;i<leni;i++){
if(data[i].deviceType == "SWITCH"){ if(data[i].deviceType == "SWITCH"){
initswitch(data[i]); initswitch(data[i]);
} }else if(data[i].deviceType == "PSD"){
if(data[i].deviceType == "PSD"){
initstand(data[i]); initstand(data[i]);
}else{
console.log(data[i]);
} }
} }
} }
function DeviceDestroy(data){ 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"){ if(data.body.deviceList[i].type == "TRAIN"){
code =data.body[i].code; code =data.body.deviceList[i].code;
if (trainlisttest.list[code].dispose != data.body[i].dispose && data.body[i].dispose == "0") { 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.group.add(trainlisttest.list[code]);
trainlisttest.list[code].position.y = 0; trainlisttest.list[code].position.y = 0;
// trainlisttest.list[code].progress = 0; // trainlisttest.list[code].progress = 0;
trainlisttest.list[code].dispose = "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].nextcode = null;
trainlisttest.list[code].curve = null; trainlisttest.list[code].curve = null;
trainlisttest.list[code].nextcurve = null; trainlisttest.list[code].nextcurve = null;
@ -476,7 +476,7 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
trainlisttest.list[code].mixerpush = true; 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.list[code].status = 1;
trainlisttest.group.remove(trainlisttest.list[code]); trainlisttest.group.remove(trainlisttest.list[code]);
trainlisttest.list[code].progress = null; trainlisttest.list[code].progress = null;
@ -493,17 +493,17 @@ export function Jlmap3dSubscribeNew(jlmap3d,routegroup,jsonwebwork) {
} }
} }
if(data.body[i].type == "SIGNAL"){ if(data.body.deviceList[i].type == "SIGNAL"){
signalupdate(data.body[i]); signalupdate(data.body.deviceList[i]);
} }
if(data.body[i].type == "SWITCH"){ if(data.body.deviceList[i].type == "SWITCH"){
switchupdate(data.body[i]); switchupdate(data.body.deviceList[i]);
} }
if(data.body[i].type == "PSD"){ if(data.body.deviceList[i].type == "PSD"){
standupdate(data.body[i]); standupdate(data.body.deviceList[i]);
} }
if(data.body[i].type == "TRAIN_DOOR"){ if(data.body.deviceList[i].type == "TRAIN_DOOR"){
traindoorupdate(data.body[i]); traindoorupdate(data.body.deviceList[i]);
} }
} }
} }

View File

@ -250,6 +250,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.StationStand] = { this[deviceType.StationStand] = {
common: { // 通用属性 common: { // 通用属性
textFontSize: 8, // 站台默认字体大小 textFontSize: 8, // 站台默认字体大小

View File

@ -363,6 +363,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.Switch] = { this[deviceType.Switch] = {
text: { text: {
show: true, // 道岔名称显示 show: true, // 道岔名称显示

View File

@ -200,6 +200,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.StationStand] = { this[deviceType.StationStand] = {
common: { // 通用属性 common: { // 通用属性
textFontSize: 10, // 站台默认字体大小 textFontSize: 10, // 站台默认字体大小

View File

@ -383,6 +383,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.Switch] = { this[deviceType.Switch] = {
text: { text: {
show: true, // 道岔名称显示 show: true, // 道岔名称显示

View File

@ -329,8 +329,8 @@ class SkinCode extends defaultStyle {
}, },
StationControl:{ StationControl:{
text: { text: {
distance: 2, // 灯和文字之间的距离 distance: 10, // 灯和文字之间的距离
fontSize: 11, // 字体大小 fontSize: 14, // 字体大小
fontFormat: 'consolas', // 字体格式 fontFormat: 'consolas', // 字体格式
fontColor: '#ffffff', // 字体颜色 fontColor: '#ffffff', // 字体颜色
fontWeight: 'normal', // 字体粗细 fontWeight: 'normal', // 字体粗细
@ -345,9 +345,9 @@ class SkinCode extends defaultStyle {
}, },
lamp: { lamp: {
count: 2, // 控制模式灯个数 count: 2, // 控制模式灯个数
offset: {x: 0, y: 0}, // 控制模式灯偏移量 offset: {x: 0, y: 3}, // 控制模式灯偏移量
radiusR: 4, // 控制模式灯的半径 radiusR: 7, // 控制模式灯的半径
distance: 36, // 控制模式之间灯之间的距离 distance: 42, // 控制模式之间灯之间的距离
grayColor: '#7F7F7F', // 控制模式灰色 grayColor: '#7F7F7F', // 控制模式灰色
greenColor: '#00FF00', // 控制模式绿色 greenColor: '#00FF00', // 控制模式绿色
redColor: '#FF0000', // 控制模式红色 redColor: '#FF0000', // 控制模式红色
@ -418,6 +418,12 @@ class SkinCode extends defaultStyle {
this[deviceType.Line] = { this[deviceType.Line] = {
lineColor: '#FFFFFF' // 线条颜色 lineColor: '#FFFFFF' // 线条颜色
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 8 // 延伸长度
};
this[deviceType.LcControl] = { this[deviceType.LcControl] = {
text: { text: {

View File

@ -392,6 +392,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.Switch] = { this[deviceType.Switch] = {
text: { text: {
show: true, // 道岔名称显示 show: true, // 道岔名称显示

View File

@ -411,6 +411,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.StationDelayUnlock] = { this[deviceType.StationDelayUnlock] = {
text: { text: {
distance: 3, // 延迟解锁和设备之间的距离 distance: 3, // 延迟解锁和设备之间的距离

View File

@ -387,6 +387,13 @@ class SkinCode extends defaultStyle {
} }
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.Switch] = { this[deviceType.Switch] = {
text: { text: {
show: true, // 道岔名称显示 show: true, // 道岔名称显示

View File

@ -509,6 +509,14 @@ class SkinCode extends defaultStyle {
lineColor: '#FFFFFF', // 线条颜色 lineColor: '#FFFFFF', // 线条颜色
lineDash: [8, 4] lineDash: [8, 4]
}; };
// 供电线路
this[deviceType.Power] = {
lineColor: '#FFFFFF', // 线条颜色
strokeColor: '#ccc', // 线条颜色
extendLength: 10 // 延伸长度
};
this[deviceType.AutomaticRoute] = { this[deviceType.AutomaticRoute] = {
// 是否显示 // 是否显示
displayCondition: '03', // 显示条件 prdType displayCondition: '03', // 显示条件 prdType

View File

@ -245,4 +245,9 @@ deviceRender[deviceType.Arrow] = {
_type: deviceType.Arrow, _type: deviceType.Arrow,
zlevel: 1 zlevel: 1
}; };
/** 供电线路 */
deviceRender[deviceType.Power] = {
_type: deviceType.Power,
zlevel: 1
};
export default deviceRender; export default deviceRender;

View File

@ -42,7 +42,8 @@ const deviceType = {
Axle: 'Axle', Axle: 'Axle',
SplitStation:'SplitStation', SplitStation:'SplitStation',
SwitchFault: 'SwitchFault', SwitchFault: 'SwitchFault',
Arrow: 'Arrow' Arrow: 'Arrow',
Power: 'Power'
}; };
export default deviceType; export default deviceType;

View 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(); // 可以无需调用
}
}
}

View 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;
}
}
}

View File

@ -70,6 +70,12 @@ export default class ESingleControl extends Group {
this.control.setStyle('fill', color); this.control.setStyle('fill', color);
} }
} }
setTextColor(color) {
if (color) {
this.text.setStyle('textFill', color);
}
}
getArcBoundingRect() { getArcBoundingRect() {
const rect = this.control.getBoundingRect().clone(); const rect = this.control.getBoundingRect().clone();
const scale = this.control.scale[0]; const scale = this.control.scale[0];

View File

@ -291,12 +291,15 @@ export default class Station extends Group {
this.emergencyControl && this.emergencyControl.setColor(this.style.Station.StationControl.lamp.grayColor); this.emergencyControl && this.emergencyControl.setColor(this.style.Station.StationControl.lamp.grayColor);
this.substationControl && this.substationControl.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.setColor(this.style.Station.StationControl.lamp.greenColor);
this.centerControl && this.centerControl.setTextColor(this.style.Station.StationControl.lamp.greenColor); // 文字颜色
} }
handleLocal() { // 站控 handleLocal() { // 站控
this.emergencyControl && this.emergencyControl.setColor(this.style.Station.StationControl.lamp.grayColor); 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.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.centerControl && this.centerControl.setColor(this.style.Station.StationControl.lamp.grayColor);
this.arrowsControl && this.arrowsControl.setColor(this.style.Station.StationControl.lamp.greenColor);
} }
handleEmergency() { // 紧急站控 handleEmergency() { // 紧急站控
@ -315,7 +318,8 @@ export default class Station extends Group {
setState(model) { setState(model) {
if (!this.isShowShape) return; if (!this.isShowShape) return;
this.recover(); this.recover();
model.controlMode && this['handle' + model.controlMode](); // model.controlMode && this['handle' + model.controlMode]();
this.handleLocal();
} }
getShapeTipPoint(opts) { getShapeTipPoint(opts) {

View File

@ -24,6 +24,7 @@ import AutomaticRoute from './AutomacticRoute/index.js';
import SaidLamp from './SaidLamp/index.js'; import SaidLamp from './SaidLamp/index.js';
import SplitStation from './SplitStation/index'; import SplitStation from './SplitStation/index';
import Arrow from './Arrow/index'; import Arrow from './Arrow/index';
import Power from './Power/index';
/** 图库*/ /** 图库*/
const mapShape = {}; const mapShape = {};
@ -69,6 +70,7 @@ mapShape[deviceType.Axle] = SaidLamp;
mapShape[deviceType.SwitchFault] = SaidLamp; mapShape[deviceType.SwitchFault] = SaidLamp;
mapShape[deviceType.SplitStation] = SplitStation; mapShape[deviceType.SplitStation] = SplitStation;
mapShape[deviceType.Arrow] = Arrow; mapShape[deviceType.Arrow] = Arrow;
mapShape[deviceType.Power] = Power;
function shapefactory(device, jmap) { function shapefactory(device, jmap) {
const type = device._type; const type = device._type;

View File

@ -147,6 +147,9 @@ export function parser(data, skinCode, showConfig) {
zrUtil.each(data.arrowList || [], elem => { zrUtil.each(data.arrowList || [], elem => {
mapDevice[elem.code] = createDevice(deviceType.Arrow, elem, propConvert, showConfig); mapDevice[elem.code] = createDevice(deviceType.Arrow, elem, propConvert, showConfig);
}, this); }, this);
zrUtil.each(data.powerList || [], elem => {
mapDevice[elem.code] = createDevice(deviceType.Power, elem, propConvert, showConfig);
}, this);
zrUtil.each(data.indicatorLightList || [], elem => { zrUtil.each(data.indicatorLightList || [], elem => {
switch (elem.type) { switch (elem.type) {
case 'AtsControl': case 'AtsControl':
@ -326,6 +329,7 @@ export function updateMapData(state, model) {
case deviceType.SplitStation: updateForList(model, state, 'splitStationList'); break; case deviceType.SplitStation: updateForList(model, state, 'splitStationList'); break;
case deviceType.SwitchFault: updateForList(model, state, 'indicatorLightList'); break; case deviceType.SwitchFault: updateForList(model, state, 'indicatorLightList'); break;
case deviceType.Arrow: updateForList(model, state, 'arrowList'); break; case deviceType.Arrow: updateForList(model, state, 'arrowList'); break;
case deviceType.Power: updateForList(model, state, 'powerList'); break;
} }
} }
} }

View File

@ -342,6 +342,13 @@ const map = {
return []; return [];
} }
}, },
powerList: (state) => {
if (state.map) {
return state.map.powerList || [];
} else {
return [];
}
},
tempSpeedLimitList: (state) => { tempSpeedLimitList: (state) => {
if (state.map) { if (state.map) {
return state.map.tempSpeedLimitList; return state.map.tempSpeedLimitList;

View File

@ -30,7 +30,7 @@ export default {
return { return {
jl3d: null, jl3d: null,
rendermode:'监控视角', rendermode:'监控视角',
renderswitch:true renderswitch:false
}; };
}, },
computed: { computed: {

View File

@ -133,6 +133,14 @@
@setCenter="setCenter" @setCenter="setCenter"
/> />
</el-tab-pane> </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"> <el-tab-pane :label="$t('map.text')" class="tab_pane_box" name="Text" :lazy="lazy">
<text-draft <text-draft
ref="Text" ref="Text"
@ -211,6 +219,7 @@ import PsdDraft from './psdDraft';
import EspDraft from './espDraft'; import EspDraft from './espDraft';
import TrainDraft from './train/index'; import TrainDraft from './train/index';
import LineDraft from './line'; import LineDraft from './line';
import PowerDraft from './power';
import TextDraft from './text'; import TextDraft from './text';
import ControlDraft from './ControlDraft'; import ControlDraft from './ControlDraft';
import TrainWindowDraft from './trainwindow'; import TrainWindowDraft from './trainwindow';
@ -242,6 +251,7 @@ export default {
TrainWindowDraft, TrainWindowDraft,
TrainDraft, TrainDraft,
LineDraft, LineDraft,
PowerDraft,
TextDraft, TextDraft,
ZcControlDraft, ZcControlDraft,
OutFrameDraft, OutFrameDraft,

View File

@ -225,7 +225,7 @@ export default {
this.editModel.points.splice(index + 1, 0, data); this.editModel.points.splice(index + 1, 0, data);
}, },
delPoint(index) { delPoint(index) {
this.editModel.points.splice(index + 1, 0); this.editModel.points.splice(index, 1);
}, },
addPointAddModel(index) { addPointAddModel(index) {
const data = { x: 0, y: 0}; const data = { x: 0, y: 0};

View 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

File diff suppressed because it is too large Load Diff

View 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();

View 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();