You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1743 lines
56 KiB
1743 lines
56 KiB
import * as THREE from '@/lib/three/build/three.module.js'
|
|
import { WEBGL } from '@/lib/three/examples/jsm/WebGL.js'
|
|
import { OrbitControls } from '@/lib/three/examples/jsm/controls/OrbitControls.js'
|
|
import { Line2 } from '@/lib/three/examples/jsm/lines/Line2.js'
|
|
import { LineGeometry } from '@/lib/three/examples/jsm/lines/LineGeometry.js'
|
|
import { LineMaterial } from '@/lib/three/examples/jsm/lines/LineMaterial.js'
|
|
import { FBXLoader } from '@/lib/three/examples/jsm/loaders/FBXLoader.js'
|
|
|
|
const EventEmitter = require('events')
|
|
// var latticeForPiler1 = JSON.parse(JSON.stringify(localStorage.getItem('latticeForPiler1')))
|
|
|
|
// console.log(this.property.latticeForPiler1);
|
|
class MapDrawing extends EventEmitter {
|
|
constructor (container, property) {
|
|
super()
|
|
this.initDefaultOptions()
|
|
this.container = container
|
|
this.property = this.mergeObject(this.property, property)
|
|
// this.property.latticeForPiler1=JSON.parse(JSON.stringify(localStorage.getItem('latticeForPiler1')))
|
|
}
|
|
|
|
/**
|
|
* 初始化默认配置
|
|
*/
|
|
initDefaultOptions () {
|
|
this.property = {
|
|
width: 500,
|
|
height: 500,
|
|
event: {
|
|
onClick: (data) => {
|
|
|
|
}
|
|
},
|
|
scene: null,
|
|
camera: null,
|
|
renderer: null,
|
|
videoGround: null,
|
|
light: null,
|
|
mesh: null,
|
|
material: {
|
|
baseLine: null,
|
|
line: null
|
|
},
|
|
layer: {
|
|
storageRack: [],
|
|
storageRackLabel: [],
|
|
freightSpace: [],
|
|
fourwayCarRoad: [],
|
|
roadChild: [],
|
|
otherPoint: [],
|
|
pointChild: [],
|
|
levels: {
|
|
// nowLevel: localStorage.getItem('level').slice(6, 7)
|
|
nowLevel:localStorage.getItem('level')?localStorage.getItem('level').slice(6, 7) : 'level-1'
|
|
}
|
|
},
|
|
textures: [],
|
|
cars: [],
|
|
trajectorys: [],
|
|
errorNodes: {},
|
|
raycaster: new THREE.Raycaster(),
|
|
pointer: new THREE.Vector2(),
|
|
config: {},
|
|
latticeForPiler1 : JSON.parse((localStorage.getItem('latticeForPiler1')))
|
|
}
|
|
let fps = 60 // 多少毫秒执行一次
|
|
this.animateControlInfo = {
|
|
fps: fps,
|
|
fpsInterval: 1000 / fps,
|
|
last: new Date().getTime() // 上次执行的时刻
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 合并json对象
|
|
* @param obj1
|
|
* @param obj2
|
|
* @returns {{}}
|
|
*/
|
|
mergeObject (obj1, obj2) {
|
|
let obj3 = {}
|
|
for (let attrname in obj1) {
|
|
if (obj1.hasOwnProperty(attrname)) {
|
|
obj3[attrname] = obj1[attrname]
|
|
}
|
|
}
|
|
for (let attrname1 in obj2) {
|
|
if (obj2.hasOwnProperty(attrname1)) {
|
|
obj3[attrname1] = obj2[attrname1]
|
|
}
|
|
}
|
|
return obj3
|
|
}
|
|
|
|
/**
|
|
* 初始化场景
|
|
*/
|
|
initScene () {
|
|
this.property.scene = new THREE.Scene()
|
|
this.property.scene.background = null
|
|
}
|
|
|
|
/**
|
|
* 初始化摄像头
|
|
*/
|
|
initCamera () {
|
|
// 设置透视投影的相机,默认情况下相机的上方向为Y轴,右方向为X轴,沿着Z轴朝里(视野角:fov 纵横比:aspect 相机离视体积最近的距离:near 相机离视体积最远的距离:far)
|
|
this.property.camera = new THREE.PerspectiveCamera(45, this.property.width / this.property.height, 1, 15000)
|
|
let eyePoint = this.property.config.eyePoint || [0, 1000, 0]
|
|
this.property.camera.position.set(eyePoint[0], eyePoint[1], eyePoint[2])
|
|
// camera.position.x = 0;//设置相机的位置坐标
|
|
// camera.position.y = 0;//设置相机的位置坐标
|
|
// camera.position.z = 100;//设置相机的位置坐标
|
|
// camera.up.x = 1;//设置相机的上为「x」轴方向
|
|
// camera.up.y = 1;//设置相机的上为「y」轴方向
|
|
// camera.up.z = 0;//设置相机的上为「z」轴方向
|
|
// 设置视野的中心坐标
|
|
this.property.camera.lookAt(0, 0, 0)
|
|
}
|
|
|
|
/**
|
|
* 初始化渲染器
|
|
*/
|
|
initRender () {
|
|
this.property.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
|
|
// console.log(this.property.width,this.property.height);
|
|
this.property.renderer.setSize(this.property.width, this.property.height - 180)
|
|
document.getElementById('mapContainer').appendChild(this.property.renderer.domElement)
|
|
// 设置背景色
|
|
this.property.renderer.setClearColor(0x000000, 1.0)
|
|
}
|
|
|
|
/**
|
|
* 初始化事件
|
|
*/
|
|
initEvent () {
|
|
let self = this
|
|
document.getElementById('mapContainer').addEventListener('click', (event) => {
|
|
self.mouseClick(event)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 初始化通用材质
|
|
*/
|
|
initMaterials () {
|
|
// Color:线条的颜色,用16进制来表示,默认的颜色是白色。
|
|
// Linewidth:线条的宽度,默认时候1个单位宽度。
|
|
// Linecap:线条两端的外观,默认是圆角端点,当线条较粗的时候才看得出效果,如果线条很细,那么你几乎看不出效果了。
|
|
// Linejoin:两个线条的连接点处的外观,默认是“round”,表示圆角。
|
|
// VertexColors:定义线条材质是否使用顶点颜色,这是一个boolean值。意思是,线条各部分的颜色会根据顶点的颜色来进行插值。
|
|
this.property.material.baseLine = new THREE.LineBasicMaterial({ color: 0x0000ff })
|
|
this.property.material.line = new LineMaterial({ color: 0x0000ff, linewidth: 5 })
|
|
this.property.material.line.resolution.set(this.property.width, this.property.height)
|
|
this.property.material.colorByPointLine = new THREE.LineBasicMaterial({
|
|
vertexColors: THREE.VertexColors
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 初始化视角交互控制器
|
|
*/
|
|
initControls () {
|
|
this.property.controls = new OrbitControls(this.property.camera, this.property.renderer.domElement)
|
|
// controls.addEventListener('change', this.render) // 持续渲染的场景不需要,静态场景才需要监听
|
|
// controls.target.set(64, 64, 128)
|
|
this.property.controls.minZoom = 0.5
|
|
this.property.controls.maxZoom = 4
|
|
|
|
// 限制最大仰视角和俯视角
|
|
this.property.controls.minPolarAngle = 0
|
|
this.property.controls.maxPolarAngle = 1.5
|
|
// 禁止缩放
|
|
// controls.enableZoom=false
|
|
// 缩放限制
|
|
// controls.minDistance = 50
|
|
// controls.maxDistance = 450
|
|
// 是否使用键盘
|
|
this.property.controls.enableKeys = true
|
|
// 修改鼠标按键
|
|
this.property.controls.mouseButtons = {
|
|
LEFT: THREE.MOUSE.PAN,
|
|
MIDDLE: THREE.MOUSE.DOLLY,
|
|
RIGHT: THREE.MOUSE.ROTATE
|
|
}
|
|
this.property.controls.touches = {
|
|
ONE: THREE.TOUCH.PAN, // 单个手指 拖动(默认旋转:ROTATE)
|
|
TWO: THREE.TOUCH.DOLLY_PAN // 两个手指 缩放
|
|
}
|
|
// controls.update()
|
|
}
|
|
|
|
/**
|
|
* 初始化光源
|
|
*/
|
|
initLight () {
|
|
// this.property.light = new THREE.DirectionalLight(0xFF0000, 11.0, 0)
|
|
// this.property.light.position.set(200, 2000, 200)
|
|
// this.property.scene.add(this.property.light)
|
|
this.property.light = new THREE.AmbientLight(0xffffff)
|
|
this.property.light.position.set(100, 100, 200)
|
|
this.property.scene.add(this.property.light)
|
|
// this.property.light = new THREE.PointLight(0xffffff, 1, 0)
|
|
// this.property.light.position.set(300, 2000, 300)
|
|
// this.property.scene.add(this.property.light)
|
|
}
|
|
|
|
layerControl (layerId, showFlg,sliceIndex) {
|
|
let layer
|
|
if (layerId.indexOf('level') > -1) {
|
|
let index = layerId.split('-')[1]
|
|
layer = this.property.layer.levels[index]
|
|
if (showFlg) {
|
|
this.property.layer.levels.nowLevel = sliceIndex
|
|
} else {
|
|
this.property.layer.levels.nowLevel = null
|
|
}
|
|
} else {
|
|
layer = this.property.layer[layerId]
|
|
}
|
|
if (layerId === 'freightSpace' && Object.keys(this.property.layer.levels).length > 1 && showFlg) {
|
|
layer = null
|
|
Object.keys(this.property.layer.levels).map(levelKey => {
|
|
if (this.property.layer.levels.nowLevel) {
|
|
layer = this.property.layer.levels[this.property.layer.levels.nowLevel]
|
|
}
|
|
})
|
|
}
|
|
if (layer) {
|
|
layer.map(obj => {
|
|
if (obj.type === 'GridHelper') {
|
|
|
|
} else if (obj.type === 'Mesh') {
|
|
obj.visible = showFlg
|
|
} else if (obj.type === 'Group') {
|
|
obj.visible = showFlg
|
|
obj.children.forEach((child, i) => {
|
|
child.visible = showFlg
|
|
})
|
|
}
|
|
})
|
|
}
|
|
if (layerId === 'freightSpace') {
|
|
layer = this.property.layer['storageRackLabel']
|
|
layer.map(obj => {
|
|
if (obj.type === 'GridHelper') {
|
|
|
|
} else if (obj.type === 'Mesh') {
|
|
obj.visible = !showFlg
|
|
} else if (obj.type === 'Group') {
|
|
obj.visible = !showFlg
|
|
obj.children.forEach((child, i) => {
|
|
child.visible = !showFlg
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 执行渲染
|
|
*/
|
|
render () {
|
|
this.property.renderer.render(this.property.scene, this.property.camera)
|
|
}
|
|
|
|
/**
|
|
* 绘制基础线条
|
|
* @param points
|
|
* @param info
|
|
* @returns {Line}
|
|
*/
|
|
drawBaseLine (points, info, nodeId = '') {
|
|
let lineMaterial = info ? new THREE.LineBasicMaterial(info) : this.property.material.baseLine
|
|
var geometry = new THREE.Geometry()
|
|
points.map((point) => {
|
|
// 长宽高分别对应桌面的xy以及竖直与屏幕,正值基于000点向右上及屏幕前方延伸
|
|
geometry.vertices.push(new THREE.Vector3(point[0], point[1], point[2]))
|
|
})
|
|
var line = new THREE.Line(geometry, lineMaterial)
|
|
this.property.scene.add(line)
|
|
line.nodeId = nodeId
|
|
line.customNodeType = 'baseLine'
|
|
line.customDataType = info.type || ''
|
|
line.nodeInfo = info
|
|
return line
|
|
}
|
|
|
|
/**
|
|
* 绘制有宽度的线条
|
|
* @param points
|
|
* @returns {Line2|Line2}
|
|
*/
|
|
drawLine (points, info = { id: '' }) {
|
|
if (!info.id) {
|
|
info.id = ''
|
|
}
|
|
var geometry = new LineGeometry()
|
|
let pointsBuffer = []
|
|
points.map((point) => {
|
|
// 长宽高分别对应桌面的xy以及竖直与屏幕,正值基于000点向右上及屏幕前方延伸
|
|
pointsBuffer.push(point[0])
|
|
pointsBuffer.push(point[1])
|
|
pointsBuffer.push(point[2])
|
|
})
|
|
geometry.setPositions(pointsBuffer)
|
|
var line = new Line2(geometry, this.property.material.line)
|
|
line.computeLineDistances()
|
|
this.property.scene.add(line)
|
|
line.nodeId = info.id
|
|
line.customNodeType = 'line'
|
|
line.customDataType = info.type || ''
|
|
line.nodeInfo = info
|
|
return line
|
|
}
|
|
|
|
/**
|
|
* 画网格
|
|
* @param width
|
|
* @param heigth
|
|
*/
|
|
initGrid (width = 1000, heigth = 1000, singleWidth, singleHeight) {
|
|
singleWidth = singleWidth || 100
|
|
singleHeight = singleHeight || 100
|
|
let xNum = parseInt(width / singleWidth)
|
|
let zNum = parseInt(heigth / singleHeight)
|
|
for (let i = 0; i <= xNum; i++) {
|
|
let xLine = this.drawBaseLine([[0, 0, -heigth / 2], [0, 0, heigth / 2]], { color: 0xffffff, opacity: 0.1 })
|
|
xLine.position.x = (i * width / xNum) - width / 2
|
|
}
|
|
for (let j = 0; j <= zNum; j++) {
|
|
let zLine = this.drawBaseLine([[-width / 2, 0, 0], [width / 2, 0, 0]], { color: 0xffffff, opacity: 0.1 })
|
|
zLine.position.z = (j * heigth / zNum) - heigth / 2
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 画视频地板
|
|
* @param videoDomId
|
|
* @param height
|
|
*/
|
|
initVideoGround () {
|
|
if (this.property.videoGround) {
|
|
this.property.videoGround.video = document.createElement(this.property.videoGround.videoDomId)
|
|
this.property.videoGround.video.src = this.property.videoGround.src
|
|
this.property.videoGround.video.autoplay = true
|
|
// this.property.videoGround.video.muted = true
|
|
this.property.videoGround.video.volume = 0.001
|
|
this.property.videoGround.video.loop = true
|
|
this.property.videoGround.video.load()
|
|
this.property.videoGround.video.crossOrigin = 'anonymous'
|
|
|
|
// 通过video对象实例化纹理
|
|
let texture = new THREE.VideoTexture(this.property.videoGround.video)
|
|
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping
|
|
texture.minFilter = THREE.LinearFilter
|
|
// 创建几何体
|
|
let geometry = new THREE.PlaneGeometry(this.property.videoGround.width, this.property.videoGround.height)
|
|
// 几何体材质对象
|
|
const materials = new THREE.MeshBasicMaterial({ map: texture })
|
|
// 创建网格模型对象
|
|
this.property.videoGround.node = new THREE.Mesh(geometry, materials)
|
|
// 设置几何体位置
|
|
this.property.videoGround.node.position.x = 0
|
|
this.property.videoGround.node.position.y = this.property.videoGround.elevation
|
|
this.property.videoGround.node.position.z = 0
|
|
// 设置平躺
|
|
this.property.videoGround.node.rotation.x = -Math.PI / 2
|
|
this.property.scene.add(this.property.videoGround.node)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 计算轨迹路线
|
|
* @param points
|
|
* @returns {CurvePath}
|
|
*/
|
|
computeCurve (points) {
|
|
let line, p1, p2, p3
|
|
// 作为拐弯的弧度
|
|
let R = 20
|
|
// 添加一些点作为开头和拐点
|
|
let arr = []
|
|
points.map(point => {
|
|
arr.push(new THREE.Vector3(point[0], point[1], point[2]))
|
|
})
|
|
|
|
let curvePath = new THREE.CurvePath()
|
|
for (let i = 0; i < arr.length - 1; i++) {
|
|
if (i === 0) {
|
|
let dir = arr[0].clone().sub(arr[1])
|
|
dir.normalize()
|
|
p2 = arr[1].clone()
|
|
if (arr.length === 2) {
|
|
p2.add(dir.clone())
|
|
} else {
|
|
p2.add(dir.clone().multiplyScalar(R))
|
|
}
|
|
line = new THREE.LineCurve3(arr[0], p2)
|
|
curvePath.curves.push(line)
|
|
} else {
|
|
// 计算三个点构成的两条线的方向
|
|
let dir1 = arr[i - 1].clone().sub(arr[i])
|
|
dir1.normalize()
|
|
let dir2 = arr[i + 1].clone().sub(arr[i])
|
|
dir2.normalize()
|
|
let p12_ = arr[i].clone()
|
|
p12_.add(dir1.clone().multiplyScalar(R))
|
|
p1 = arr[i].clone().add(dir1.clone().multiplyScalar(R))
|
|
p2 = arr[i].clone()
|
|
p3 = arr[i].clone().add(dir2.clone().multiplyScalar(R))
|
|
let beziercurve = new THREE.QuadraticBezierCurve3(p1, p2, p3)
|
|
let line1 = arr[i].clone()
|
|
line1.add(dir2.clone().multiplyScalar(R))
|
|
let line2 = arr[i + 1].clone()
|
|
if (i < arr.length - 2) {
|
|
// 最后一段不用减掉半径尺寸
|
|
line2.add(dir2.clone().multiplyScalar(-R))
|
|
}
|
|
line = new THREE.LineCurve3(line1, line2)
|
|
// 把转换曲线和直线插入曲线中
|
|
curvePath.curves.push(beziercurve, line)
|
|
}
|
|
}
|
|
return curvePath
|
|
}
|
|
|
|
/**
|
|
* 绘制流动管线
|
|
* @param curve
|
|
* @param imageName
|
|
* @returns {{tube: Mesh, curve: *}}
|
|
*/
|
|
createTube (curve, imageName = 'arrow', info) {
|
|
if (!info.id) {
|
|
info.id = ''
|
|
}
|
|
let width = info.width || 5
|
|
let tubeGeometry = new THREE.TubeGeometry(curve, 200, width, 6, false)
|
|
let textureLoader = new THREE.TextureLoader()
|
|
let texture = textureLoader.load(require('../../assets/image/' + imageName + '.png'))
|
|
this.property.textures[info.id] = {
|
|
texture: texture
|
|
}
|
|
texture.wrapS = THREE.RepeatWrapping
|
|
texture.wrapT = THREE.RepeatWrapping
|
|
texture.repeat.x = 10
|
|
texture.repeat.y = 4
|
|
texture.offset.y = 0.5
|
|
let tubeMaterial = new THREE.MeshMatcapMaterial({
|
|
map: texture,
|
|
transparent: true
|
|
})
|
|
let tube = new THREE.Mesh(tubeGeometry, tubeMaterial)
|
|
this.property.scene.add(tube)
|
|
tube.nodeId = info.id
|
|
tube.customNodeType = 'pipeline'
|
|
tube.customDataType = (info.type || '') + 'Trajectory'
|
|
tube.nodeInfo = info
|
|
return {
|
|
curve: curve,
|
|
tube: tube
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制管线
|
|
*/
|
|
drawPipeline (pathPoints, imageName = 'arrow', info) {
|
|
return this.createTube(this.computeCurve(pathPoints), imageName, info)
|
|
}
|
|
|
|
/**
|
|
* 绘制矩形
|
|
* @param info
|
|
* @returns {Mesh}
|
|
*/
|
|
//3.14
|
|
drawRectangle (info) {
|
|
let self = this
|
|
if (!info.id) {
|
|
info.id = ''
|
|
}
|
|
let defaultInfo = {
|
|
s3: [20, 20, 20],
|
|
segments: [1, 1, 1],
|
|
color: 0x666666
|
|
}
|
|
if (!info) {
|
|
info = defaultInfo
|
|
} else {
|
|
if (!info.s3) {
|
|
info.s3 = defaultInfo.s3
|
|
}
|
|
if (!info.segments) {
|
|
info.segments = defaultInfo.segments
|
|
}
|
|
if (!info.color) {
|
|
info.color = defaultInfo.color
|
|
}
|
|
if (isNaN(parseFloat(info.rotationX))) {
|
|
info.rotationX = -Math.PI / 2
|
|
}
|
|
}
|
|
//构建几何体
|
|
let gube = new THREE.CubeGeometry(info.s3[0], info.s3[1], info.s3[2], info.segments[0], info.segments[1], info.segments[2])
|
|
// console.log(info.s3[0], info.s3[1], info.s3[2], info.segments[0], info.segments[1], info.segments[2]);
|
|
var gubeMaterial = new THREE.MeshMatcapMaterial({
|
|
color: self.loadColor(info.color)
|
|
})
|
|
var gubeMesh = new THREE.Mesh(gube, gubeMaterial)
|
|
// gubeMesh.position.set(info.p3[0], info.p3[1], info.p3[2])
|
|
gubeMesh.nodeId = info.id
|
|
gubeMesh.customNodeType = 'rectangle'
|
|
gubeMesh.customDataType = info.type || ''
|
|
gubeMesh.nodeInfo = info
|
|
const group = new THREE.Group()
|
|
group.position.set(info.p3[0], info.p3[1], info.p3[2])
|
|
group.add(gubeMesh)
|
|
if (info.label) {
|
|
let label = this.addTextGeometry({
|
|
id: info.id,
|
|
type: info.type + 'Label',
|
|
label: info.label
|
|
})
|
|
group.add(label)
|
|
if (info.isStorageRack) {
|
|
label.visible = false
|
|
self.property.layer.storageRackLabel.push(label)
|
|
}
|
|
if (info.isRoad) {
|
|
label.visible = false
|
|
}
|
|
if (info.isOtherPoint) {
|
|
label.visible = false
|
|
}
|
|
}
|
|
this.property.scene.add(group)
|
|
return group
|
|
}
|
|
|
|
/**
|
|
* 创建矩形
|
|
* @param info
|
|
* @returns {Mesh}
|
|
*/
|
|
createRectangle (info) {
|
|
// console.log('我执行了');
|
|
let self = this
|
|
if (!info.id) {
|
|
info.id = ''
|
|
}
|
|
let defaultInfo = {
|
|
s3: [20, 20, 20],
|
|
segments: [1, 1, 1],
|
|
color: 0x666666
|
|
}
|
|
if (!info) {
|
|
info = defaultInfo
|
|
} else {
|
|
if (!info.s3) {
|
|
info.s3 = defaultInfo.s3
|
|
}
|
|
if (!info.segments) {
|
|
info.segments = defaultInfo.segments
|
|
}
|
|
if (!info.color) {
|
|
info.color = defaultInfo.color
|
|
}
|
|
}
|
|
let gube = new THREE.CubeGeometry(info.s3[0], info.s3[1], info.s3[2], info.segments[0], info.segments[1], info.segments[2])
|
|
var gubeMaterial = new THREE.MeshMatcapMaterial({
|
|
color: self.loadColor(info.color)
|
|
})
|
|
var gubeMesh = new THREE.Mesh(gube, gubeMaterial)
|
|
gubeMesh.position.set(info.p3[0], info.p3[1], info.p3[2])
|
|
gubeMesh.nodeId = info.id
|
|
gubeMesh.customNodeType = 'rectangle'
|
|
gubeMesh.customDataType = info.type || ''
|
|
gubeMesh.nodeInfo = info
|
|
return gubeMesh
|
|
}
|
|
|
|
/**
|
|
* 创建矩形
|
|
* @param info
|
|
* @returns {Mesh}
|
|
*/
|
|
addTextGeometry (info) {
|
|
// console.log(1111);
|
|
// debugger
|
|
if (typeof info === 'string') {
|
|
info = {
|
|
id: '',
|
|
label: info
|
|
}
|
|
}
|
|
let text = info.label
|
|
if (typeof text !== 'string') {
|
|
text = text.toString()
|
|
}
|
|
let rows = text.split('\r\n')
|
|
if (!info.lineHeight) {
|
|
info.lineHeight = 140
|
|
}
|
|
if (!info.scale) {
|
|
info.scale = 0.6
|
|
}
|
|
if (!info.width) {
|
|
info.width = 160
|
|
}
|
|
if (!info.height) {
|
|
info.height = 20 * rows.length + 70
|
|
}
|
|
if (isNaN(parseFloat(info.rotationX))) {
|
|
info.rotationX = -Math.PI / 2
|
|
}
|
|
// 用canvas生成图片
|
|
let canvas = document.createElement('canvas')
|
|
let ctx = canvas.getContext('2d')
|
|
canvas.width = info.width
|
|
canvas.height = info.height
|
|
// 设置文字
|
|
ctx.fillStyle = 'transparent'
|
|
|
|
ctx.fillRect(0, 0, info.width, info.height)
|
|
rows.map((row, index) => {
|
|
// if(latticeForPiler1)
|
|
// 制作矩形
|
|
ctx.fillStyle = 'black'
|
|
ctx.font = '600 40px "宋体"'
|
|
|
|
// ctx.font = '600 40px "宋体"'
|
|
// ctx.color = 'red'
|
|
ctx.fillStyle = 'black'
|
|
ctx.fillText(row, 5, info.lineHeight * index + 60)
|
|
})
|
|
// console.log(ctx);
|
|
// 生成图片
|
|
let url = canvas.toDataURL('image/png')
|
|
// 将图片构建到纹理中
|
|
let geometry1 = new THREE.PlaneGeometry(info.width, info.height)
|
|
let material1 = new THREE.MeshBasicMaterial({
|
|
map: new THREE.TextureLoader().load(url),
|
|
side: THREE.DoubleSide,
|
|
opacity: 1,
|
|
transparent: true
|
|
})
|
|
let rect = new THREE.Mesh(geometry1, material1)
|
|
rect.position.set(0, 10, 0)
|
|
rect.rotation.x = info.rotationX
|
|
if (this.property.config.textRotation) {
|
|
rect.rotation.z = this.property.config.textRotation
|
|
}
|
|
rect.scale.set(info.scale, info.scale, info.scale)
|
|
rect.nodeId = info.id
|
|
rect.customNodeType = 'label'
|
|
rect.customDataType = info.type || ''
|
|
rect.nodeInfo = info
|
|
return rect
|
|
}
|
|
|
|
/**
|
|
* 绘制内部矩形
|
|
* @param paren
|
|
* @returns {[]}
|
|
*/
|
|
drawRactangleInParent (paren, children, dictionary) {
|
|
let position = paren.node.position
|
|
let fullHeight = paren.s3[2] * 0.1 > 4 ? paren.s3[2] - 4 : paren.s3[2] * 0.9
|
|
let offset = (paren.s3[2] - fullHeight) / 4
|
|
let height = (paren.s3[2]) / paren.capacity
|
|
let y = position.z - paren.s3[2] / 2 + height / 2 + offset
|
|
let childNodes = []
|
|
for (let i = 0; i < children.length; i++) {
|
|
let child = children[i]
|
|
let width = paren.s3[0] * 0.1 > 4 ? paren.s3[0] - 4 : paren.s3[0] * 0.9
|
|
let stateDictionary = dictionary[dictionary.hasOwnProperty(child.state) ? child.state : '0']
|
|
let freightSpace = this.drawRectangle({
|
|
id: child.id,
|
|
parentId: child.parentId,
|
|
label: child.id,
|
|
spaceId: child.spaceId,
|
|
type: paren.childType || 'child',
|
|
p3: [paren.p3[0], paren.p3[1] + 1, y + height * i],
|
|
s3: [width, 5, height - 3],
|
|
segments: [1, 1, 1],
|
|
state: child.state || 0,
|
|
color: stateDictionary.color
|
|
})
|
|
childNodes.push(freightSpace)
|
|
this.property.layer.freightSpace.push(freightSpace)
|
|
if (this.property.config.mutiLayer) {
|
|
if (!this.property.layer.levels.hasOwnProperty(child.level)) {
|
|
this.property.layer.levels[child.level] = []
|
|
}
|
|
this.property.layer.levels[child.level].push(freightSpace)
|
|
if (child.level > 1) {
|
|
freightSpace.visible = false
|
|
}
|
|
}
|
|
}
|
|
return childNodes
|
|
}
|
|
|
|
/**
|
|
* 绘制内部矩形(带高度)
|
|
* @param paren
|
|
* @returns {[]}
|
|
*/
|
|
//四向车当前使用函数 3.15改
|
|
drawRactangleInParentWithLevel (paren, children, dictionary) {
|
|
let childNodes = []
|
|
// console.log('我被调用了一次');
|
|
for (let i = 0; i < children.length; i++) {
|
|
let child = children[i]
|
|
let stateDictionary = dictionary[dictionary.hasOwnProperty(child.state) ? child.state : '0']
|
|
// console.log('我被调用了');
|
|
let freightSpace = this.drawRectangle({
|
|
id: child.id,
|
|
parentId: child.parentId,
|
|
label: child.id,
|
|
spaceId: child.spaceId,
|
|
type: 'child',
|
|
p3: [paren.p3[0], paren.p3[1] + 5, paren.p3[2]],
|
|
s3: [paren.s3[0], 5, paren.s3[2]],
|
|
segments: [1, 1, 1],
|
|
state: child.state || 0,
|
|
color: stateDictionary.color
|
|
})
|
|
childNodes.push(freightSpace)
|
|
this.property.layer.freightSpace.push(freightSpace)
|
|
if (this.property.config.mutiLayer) {
|
|
if (!this.property.layer.levels.hasOwnProperty(child.level)) {
|
|
this.property.layer.levels[child.level] = []
|
|
}
|
|
this.property.layer.levels[child.level].push(freightSpace)
|
|
if (child.level > 1) {
|
|
freightSpace.visible = false
|
|
}
|
|
}
|
|
}
|
|
return childNodes
|
|
}
|
|
|
|
/**
|
|
* 绘制仓库
|
|
*/
|
|
drawWarehouses () {
|
|
// console.log('我不知道我调用了几次');
|
|
//调用一次
|
|
let self = this
|
|
Object.keys(this.property.warehouseInfo.storageRack).map(storageRackId => {
|
|
let storageRack = this.property.warehouseInfo.storageRack[storageRackId]
|
|
let stateDictionary = this.property.config.tileStateDictionary[this.property.config.tileStateDictionary.hasOwnProperty(storageRack.state) ? storageRack.state : '0']
|
|
//疑似仓库地图 点位`1
|
|
console.log(this.property.layer.levels.nowLevel);
|
|
|
|
let storageRackInfo = {
|
|
id: storageRack.id,
|
|
type: 'storageRack',
|
|
label: storageRack.id,
|
|
p3: [storageRack.x, 0, storageRack.y],
|
|
// p3: [120, 0, 120],
|
|
s3: [storageRack.xLength, 2, storageRack.yLength],
|
|
segments: [1, 1, 1],
|
|
capacity: storageRack.capacity,
|
|
color: stateDictionary.color,
|
|
freightSpace: storageRack.freightSpace,
|
|
childType: 'freightSpace',
|
|
state: storageRack.state,
|
|
isStorageRack: true
|
|
}
|
|
storageRackInfo.node = storageRack.node = this.drawRectangle(storageRackInfo)
|
|
self.property.layer.storageRack.push(storageRackInfo.node)
|
|
let freightSpaces = Object.values(storageRackInfo.freightSpace)
|
|
let dictionary = this.property.config.freightSpaceStateDictionary
|
|
if (self.property.config.mutiLayer) {
|
|
// console.log('你好,Mr.sun',storageRackInfo);
|
|
storageRack.childNodes = self.drawRactangleInParentWithLevel(storageRackInfo, freightSpaces, dictionary)
|
|
} else {
|
|
console.log(2222);
|
|
storageRack.childNodes = self.drawRactangleInParent(storageRackInfo, freightSpaces, dictionary)
|
|
}
|
|
})
|
|
}
|
|
|
|
filterFreightSpace (rowIndex, columnIndex, levelIndex) {
|
|
this.property.layer.freightSpace.map(freightSpace => {
|
|
let suc = false
|
|
freightSpace.children.forEach((child, i) => {
|
|
if (child.nodeInfo.spaceId) {
|
|
let nums = child.nodeInfo.spaceId.split('-')
|
|
if ((isNaN(parseInt(rowIndex)) || rowIndex === nums[0]) &&
|
|
(isNaN(parseInt(columnIndex)) || columnIndex === nums[1]) &&
|
|
(isNaN(parseInt(levelIndex)) || levelIndex === nums[2])) {
|
|
suc = true
|
|
}
|
|
}
|
|
})
|
|
freightSpace.visible = suc
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 绘制四向车通路
|
|
*/
|
|
drawRoad () {
|
|
let self = this
|
|
Object.keys(this.property.warehouseInfo.fourwayCarRoad).map(fourwayCarRoadId => {
|
|
let fourwayCarRoad = this.property.warehouseInfo.fourwayCarRoad[fourwayCarRoadId]
|
|
let stateDictionary = this.property.config.roadStateDictionary[this.property.config.roadStateDictionary.hasOwnProperty(fourwayCarRoad.state) ? fourwayCarRoad.state : '0']
|
|
|
|
//p3
|
|
let fourwayCarRoadInfo = {
|
|
id: fourwayCarRoad.id,
|
|
type: 'fourwayCarRoad',
|
|
label: fourwayCarRoad.id,
|
|
p3: [fourwayCarRoad.x, 0, fourwayCarRoad.y],
|
|
// p3: [1, 0, 2],
|
|
s3: [fourwayCarRoad.xLength, 2, fourwayCarRoad.yLength],
|
|
segments: [1, 1, 1],
|
|
color: stateDictionary.color,
|
|
level: fourwayCarRoad.level,
|
|
childType: 'roadTile',
|
|
isRoad: true
|
|
}
|
|
fourwayCarRoadInfo.node = fourwayCarRoad.node = this.drawRectangle(fourwayCarRoadInfo)
|
|
self.property.layer.fourwayCarRoad.push(fourwayCarRoadInfo.node)
|
|
let children = Object.values(fourwayCarRoadInfo.level)
|
|
let dictionary = this.property.config.roadStateDictionary
|
|
if (self.property.config.mutiLayer) {
|
|
fourwayCarRoad.childNodes = self.drawRactangleInParentWithLevel(fourwayCarRoadInfo, children, dictionary)
|
|
} else {
|
|
fourwayCarRoad.childNodes = self.drawRactangleInParent(fourwayCarRoadInfo, children, dictionary)
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 绘制特殊点位
|
|
*/
|
|
drawOtherPoint () {
|
|
let self = this
|
|
Object.keys(this.property.warehouseInfo.otherPoint).map(otherPointId => {
|
|
let otherPoint = this.property.warehouseInfo.otherPoint[otherPointId]
|
|
let stateDictionary = this.property.config.otherPointStateDictionary[this.property.config.otherPointStateDictionary.hasOwnProperty(otherPoint.state) ? otherPoint.state : '0']
|
|
let otherPointInfo = {
|
|
id: otherPoint.id,
|
|
type: 'otherPoint',
|
|
label: otherPoint.id,
|
|
p3: [otherPoint.x, 0, otherPoint.y],
|
|
s3: [otherPoint.xLength, 2, otherPoint.yLength],
|
|
segments: [1, 1, 1],
|
|
color: stateDictionary.color,
|
|
childType: 'otherPointTile',
|
|
isOtherPoint: true
|
|
}
|
|
otherPointInfo.node = otherPoint.node = this.drawRectangle(otherPointInfo)
|
|
self.property.layer.otherPoint.push(otherPointInfo.node)
|
|
let children = Object.values(otherPointInfo.level)
|
|
let dictionary = this.property.config.otherPointStateDictionary
|
|
if (self.property.config.mutiLayer) {
|
|
otherPoint.childNodes = self.drawRactangleInParentWithLevel(otherPointInfo, children, dictionary)
|
|
} else {
|
|
otherPoint.childNodes = self.drawRactangleInParent(otherPointInfo, children, dictionary)
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 绘制堆垛机
|
|
*/
|
|
drawPilers () {
|
|
if (this.property.warehouseInfo.piler) {
|
|
Object.keys(this.property.warehouseInfo.piler).map(pilerId => {
|
|
let piler = this.property.warehouseInfo.piler[pilerId]
|
|
piler.type = 'piler'
|
|
piler.p3 = piler.pathPoints[0]
|
|
piler.segments = [1, 1, 1]
|
|
let stateDictionary = this.property.config.pilerStateDictionary[this.property.config.pilerStateDictionary.hasOwnProperty(piler.state) ? piler.state : '0']
|
|
piler.color = stateDictionary.color
|
|
piler.node = this.drawRectangle(piler)
|
|
piler.pathModel = this.drawPipeline(piler.pathPoints, 'arrow', piler)
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制小车
|
|
*/
|
|
drawCars () {
|
|
if (this.property.warehouseInfo.carModel.cars) {
|
|
Object.keys(this.property.warehouseInfo.carModel.cars).map(carId => {
|
|
let car = this.property.warehouseInfo.carModel.cars[carId]
|
|
if (car.pathPoints) {
|
|
car.type = 'car'
|
|
car.label = car.id
|
|
car.p3 = car.pathPoints[0]
|
|
car.segments = [1, 1, 1]
|
|
let stateDictionary = this.property.config.carStateDictionary[this.property.config.carStateDictionary.hasOwnProperty(car.state) ? car.state : '0']
|
|
car.color = stateDictionary.color
|
|
car.node = this.drawRectangle(car)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制小车
|
|
*/
|
|
drawCar (carInfo) {
|
|
if (!this.property.warehouseInfo.carModel) {
|
|
this.property.warehouseInfo.carModel = {
|
|
cars: {}
|
|
}
|
|
}
|
|
if (!this.property.warehouseInfo.carModel.cars.hasOwnProperty(carInfo.carId)) {
|
|
let car = this.property.warehouseInfo.carModel.cars[carInfo.carId] = JSON.parse(JSON.stringify(carInfo))
|
|
car.id = car.carId
|
|
car.type = 'car'
|
|
car.label = car.carId
|
|
car.p3 = car.pathPoints[0]
|
|
car.segments = [1, 1, 1]
|
|
let stateDictionary = this.property.config.carStateDictionary[this.property.config.carStateDictionary.hasOwnProperty(car.state) ? car.state : '0']
|
|
car.color = stateDictionary.color
|
|
car.node = this.drawRectangle(car)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 绘制传送带
|
|
*/
|
|
drawBelt () {
|
|
if (this.property.warehouseInfo.beltModel) {
|
|
let beltModel = this.property.warehouseInfo.beltModel
|
|
if (beltModel.pathPoints) {
|
|
beltModel.pathModel = this.drawPipeline(beltModel.pathPoints, 'arrow-g', beltModel)
|
|
Object.keys(this.property.warehouseInfo.beltModel.trays).map(trayId => {
|
|
let tray = this.property.warehouseInfo.beltModel.trays[trayId]
|
|
tray.type = 'tray'
|
|
tray.label = tray.id
|
|
tray.p3 = beltModel.pathPoints[0]
|
|
tray.segments = [1, 1, 1]
|
|
let stateDictionary = this.property.config.trayStateDictionary[this.property.config.trayStateDictionary.hasOwnProperty(tray.state) ? tray.state : '0']
|
|
tray.color = stateDictionary.color
|
|
tray.node = this.drawRectangle(tray)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
getS3 (mesh) {
|
|
let box = new THREE.Box3().setFromObject(mesh)
|
|
let size = box.size()
|
|
return size
|
|
}
|
|
|
|
/**
|
|
* 物体是否在场景中显示
|
|
* @param {THREE.Object3D} obj 3D物体
|
|
* @return {Boolean} 是否在场景中显示
|
|
* */
|
|
activeInHierarchy (obj) {
|
|
if (!obj.visible) {
|
|
return false
|
|
}
|
|
// 遍历父对象
|
|
let parent = obj.parent
|
|
while (parent) {
|
|
if (!parent.visible) {
|
|
return 0
|
|
}
|
|
parent = parent.parent
|
|
}
|
|
return 1
|
|
}
|
|
|
|
/**
|
|
* 获取点击到的所有物体
|
|
* @param {THREE.Event} event 点击事件
|
|
* @param {THREE.Group} group group
|
|
* @param {Boolean} visibleType 物体隐藏还是显示 0只看隐藏的1只看显示的2看全部的
|
|
* @return {Array} 射线下的所有物体
|
|
* */
|
|
getIntersects (event, visibleType = 1) {
|
|
let canvas = event.path ? event.path[0] : event.currentTarget.children[0]
|
|
event.preventDefault()
|
|
/**
|
|
* THREE.Raycaster 对象从屏幕上的点击位置向场景发射的一束光线
|
|
* THREE.Raycaster,THREE.Vector2():声明raycaster 和mouse变量
|
|
* */
|
|
var raycaster = new THREE.Raycaster()
|
|
// 处理画布与电脑屏幕存在偏移量问题
|
|
var divObj = canvas.getBoundingClientRect()
|
|
// 获取鼠标点击位置
|
|
var Sx = event.clientX - divObj.left
|
|
var Sy = event.clientY - divObj.top
|
|
// 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
|
|
var x = (Sx / canvas.width) * 2 - 1
|
|
var y = -(Sy / canvas.height) * 2 + 1
|
|
// 通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线的位置
|
|
raycaster.setFromCamera(new THREE.Vector2(x, y), this.property.camera)
|
|
// 获取与raycaster射线相交的数组集合,其中的元素按照距离排序,越靠近的越靠前
|
|
var intersects = raycaster.intersectObjects(this.property.scene.children, true)
|
|
let objs = []
|
|
intersects.forEach(object => {
|
|
if (visibleType === 2 || this.activeInHierarchy(object.object) === visibleType) {
|
|
objs.push(object)
|
|
}
|
|
})
|
|
// 返回选中的对象数组query
|
|
return objs
|
|
}
|
|
|
|
/**
|
|
* 点击事件
|
|
**/
|
|
mouseClick (event) {
|
|
const intersects = this.getIntersects(event)
|
|
let data = { all: [] }
|
|
if (intersects.length > 0) {
|
|
intersects.map(intersect => {
|
|
let selectObj = intersect.object
|
|
if (selectObj.hasOwnProperty('customNodeType')) {
|
|
if (!data[selectObj.customNodeType]) {
|
|
data[selectObj.customNodeType] = []
|
|
}
|
|
data[selectObj.customNodeType].push(selectObj)
|
|
data['all'].push(selectObj)
|
|
}
|
|
})
|
|
}
|
|
// this.property.event.onClick(data)
|
|
this.property.event.ondblclick(data)
|
|
}
|
|
|
|
/**
|
|
* 动画处理
|
|
*/
|
|
animate (from) {
|
|
// console.log(1122222);
|
|
let self = from || this
|
|
requestAnimationFrame(() => {
|
|
// console.log(111);
|
|
self.animate(self)
|
|
})
|
|
// if (!this.interValKey) {
|
|
// this.interValKey = setInterval(() => {
|
|
// self.animate(self)
|
|
// }, 100)
|
|
// }
|
|
// 执行时的时间
|
|
var now = new Date().getTime()
|
|
var elapsed = now - this.animateControlInfo.last
|
|
// console.log(elapsed,this.animateControlInfo.fpsInterval);
|
|
// 经过了足够的时间
|
|
if (elapsed > this.animateControlInfo.fpsInterval) {
|
|
this.animateControlInfo.last = now - (elapsed % this.animateControlInfo.fpsInterval) // 校正当前时间
|
|
if (self.property.warehouseInfo) {
|
|
self.computePipeLineFlow()
|
|
if (self.property.warehouseInfo.piler) {
|
|
self.computePilerState()
|
|
}
|
|
if (self.property.warehouseInfo.carModel) {
|
|
self.computeCarState()
|
|
}
|
|
if (self.property.warehouseInfo.beltModel) {
|
|
self.computeTrayState()
|
|
}
|
|
}
|
|
self.render()
|
|
}
|
|
// if (this.property.videoGround && this.property.videoGround.video.played) {
|
|
// }
|
|
}
|
|
|
|
moveTo (nodeId, fuzzyQuery = false) {
|
|
let position = this.getPositionByText(nodeId, fuzzyQuery)
|
|
if (position) {
|
|
let nowEyePoint = this.property.camera.position
|
|
let newEyePoint = [
|
|
nowEyePoint.x + position.x - this.property.controls.target.x,
|
|
nowEyePoint.y + position.y - this.property.controls.target.y,
|
|
nowEyePoint.z + position.z - this.property.controls.target.z
|
|
]
|
|
let distance = this.getDistanceByPoints(newEyePoint, position)
|
|
let rate = 500 / distance
|
|
newEyePoint = this.getNewPointByRate(position, newEyePoint, rate)
|
|
this.property.camera.position.set(newEyePoint[0], newEyePoint[1], newEyePoint[2])
|
|
this.property.controls.target.set(position.x, position.y, position.z)
|
|
}
|
|
}
|
|
|
|
getNewPointByRate (p1, p2, rate = 1) {
|
|
if (p1.x) {
|
|
if (p1.e) {
|
|
p1.y = p1.e
|
|
}
|
|
p1 = [p1.x, p1.y, p1.z]
|
|
}
|
|
if (p2.x) {
|
|
if (p2.e) {
|
|
p2.y = p2.e
|
|
}
|
|
p2 = [p2.x, p2.y, p2.z]
|
|
}
|
|
let newPoint = [
|
|
p1[0] + (p2[0] - p1[0]) * rate,
|
|
p1[1] + (p2[1] - p1[1]) * rate,
|
|
p1[2] + (p2[2] - p1[2]) * rate
|
|
]
|
|
return newPoint
|
|
}
|
|
|
|
getDistanceByPoints (p1, p2) {
|
|
if (p1.x) {
|
|
if (p1.e) {
|
|
p1.y = p1.e
|
|
}
|
|
p1 = [p1.x, p1.y, p1.z]
|
|
}
|
|
if (p2.x) {
|
|
if (p2.e) {
|
|
p2.y = p2.e
|
|
}
|
|
p2 = [p2.x, p2.y, p2.z]
|
|
}
|
|
let distance = Math.sqrt(Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2) + Math.pow(p2[2] - p1[2], 2))
|
|
return distance
|
|
}
|
|
|
|
getPositionByText (text, fuzzyQuery = false) {
|
|
let position = null
|
|
this.property.scene.traverse(obj => {
|
|
if (obj.type === 'GridHelper') {
|
|
} else if (obj.type === 'Mesh') {
|
|
if (obj.nodeId !== undefined && obj.customDataType === 'freightSpace') {
|
|
if ((!fuzzyQuery && obj.nodeId.toString() === text) || (fuzzyQuery && obj.nodeId.toString().indexOf(text) > -1)) {
|
|
if (!position || obj.position.x || obj.position.y || obj.position.z) { position = obj.position }
|
|
}
|
|
}
|
|
} else if (obj.type === 'Group') {
|
|
let exist = false
|
|
obj.children.forEach((child, i) => {
|
|
if (child.nodeId !== undefined) {
|
|
if ((!fuzzyQuery && child.nodeId.toString() === text) || (fuzzyQuery && child.nodeId.toString().indexOf(text) > -1)) {
|
|
exist = true
|
|
}
|
|
}
|
|
})
|
|
if (exist) {
|
|
position = obj.position
|
|
}
|
|
}
|
|
})
|
|
return position
|
|
}
|
|
|
|
/**
|
|
* 管线流动
|
|
*/
|
|
computePipeLineFlow () {
|
|
let self = this
|
|
Object.keys(self.property.textures).map(textureID => {
|
|
if (self.property.textures[textureID].direction === -1) {
|
|
self.property.textures[textureID].texture.offset.x += 0.005
|
|
} else {
|
|
self.property.textures[textureID].texture.offset.x -= 0.005
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 计算堆垛机当前状态
|
|
*/
|
|
computePilerState () {
|
|
let self = this
|
|
Object.keys(this.property.warehouseInfo.piler).map((pilerId, index) => {
|
|
let piler = this.property.warehouseInfo.piler[pilerId]
|
|
let trajectory = piler.pathModel
|
|
if (piler.noAnim) {
|
|
let position = trajectory.curve.getPoint(piler.targetRate / 100)
|
|
piler.node.position.set(position.x, position.y, position.z)
|
|
piler.noAnim = false
|
|
} else {
|
|
let step = piler.step || 0.1
|
|
if (trajectory && piler.targetRate !== null && piler.targetRate !== undefined && piler.rate !== null && piler.rate !== undefined) {
|
|
if (Math.abs(piler.rate - piler.targetRate) < 0.001) {
|
|
if (!piler.stopFlg) {
|
|
piler.stopFlg = true
|
|
self.stopPiler(piler.id)
|
|
}
|
|
} else {
|
|
if (piler.rate < piler.targetRate) {
|
|
piler.rate += piler.targetRate - piler.rate > step ? step : piler.targetRate - piler.rate
|
|
} else if (piler.rate > piler.targetRate) {
|
|
piler.rate -= piler.rate - piler.targetRate > step ? step : piler.rate - piler.targetRate
|
|
}
|
|
let position = trajectory.curve.getPoint(piler.rate / 100)
|
|
if (position) {
|
|
piler.node.position.set(position.x, position.y, position.z)
|
|
position = trajectory.curve.getPoint(piler.rate / 100 + 0.001)
|
|
if (position) {
|
|
piler.node.up = new THREE.Vector3(0, 1, 0)
|
|
piler.node.lookAt(position.x, position.y, position.z)
|
|
piler.node.rotation.y = piler.node.rotation.y + Math.PI / 2
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (piler.state === '3') {
|
|
if (!piler.scale || piler.scale > 1.5) {
|
|
piler.scale = 1
|
|
}
|
|
piler.scale = piler.scale + 0.01
|
|
piler.node.scale.set(piler.scale, 1, piler.scale)
|
|
}
|
|
let stateDictionary = self.property.config.pilerStateDictionary[piler.state]
|
|
if (stateDictionary) {
|
|
piler.node.children[0].material.color.set(self.loadColor(stateDictionary.color))
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 计算小车当前状态
|
|
*/
|
|
computeCarState () {
|
|
let self = this
|
|
Object.keys(this.property.warehouseInfo.carModel.cars).map((carId, index) => {
|
|
let car = this.property.warehouseInfo.carModel.cars[carId]
|
|
let trajectory = car.pathModel
|
|
if (car.noAnim) {
|
|
let position = trajectory.curve.getPoint(car.targetRate / 100)
|
|
car.node.position.set(position.x, position.y, position.z)
|
|
car.noAnim = false
|
|
} else {
|
|
let step = car.step || 0.2
|
|
if (trajectory && car.targetRate !== null && car.targetRate !== undefined && car.rate !== null && car.rate !== undefined) {
|
|
if (Math.abs(car.rate - car.targetRate) < 0.001) {
|
|
if (!car.stopFlg) {
|
|
car.stopFlg = true
|
|
self.stopCar(car.id)
|
|
}
|
|
} else {
|
|
if (car.rate < car.targetRate) {
|
|
car.rate += car.targetRate - car.rate > step ? step : car.targetRate - car.rate
|
|
} else if (car.rate > car.targetRate) {
|
|
car.rate -= car.rate - car.targetRate > step ? step : car.rate - car.targetRate
|
|
}
|
|
let position = trajectory.curve.getPoint(car.rate / 100)
|
|
if (position) {
|
|
car.node.position.set(position.x, position.y, position.z)
|
|
position = trajectory.curve.getPoint(car.rate / 100 + 0.001)
|
|
if (position) {
|
|
car.node.up = new THREE.Vector3(0, 1, 0)
|
|
car.node.lookAt(position.x, position.y, position.z)
|
|
car.node.rotation.y = car.node.rotation.y - Math.PI / 2
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let stateDictionary = self.property.config.carStateDictionary[car.state]
|
|
if (stateDictionary) {
|
|
car.node.children[0].material.color.set(self.loadColor(stateDictionary.color))
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 计算托盘当前状态
|
|
*/
|
|
computeTrayState () {
|
|
let self = this
|
|
let trajectory = this.property.warehouseInfo.beltModel.pathModel
|
|
Object.keys(this.property.warehouseInfo.beltModel.trays).map((trayId, index) => {
|
|
let tray = this.property.warehouseInfo.beltModel.trays[trayId]
|
|
if (tray.noAnim) {
|
|
let position = trajectory.curve.getPoint(tray.targetRate / 100)
|
|
tray.node.position.set(position.x, position.y, position.z)
|
|
tray.noAnim = false
|
|
} else {
|
|
let step = tray.step || 0.2
|
|
if (trajectory && tray.targetRate !== null && tray.targetRate !== undefined && tray.rate !== null && tray.rate !== undefined) {
|
|
if (Math.abs(tray.rate - tray.targetRate) < 0.001) {
|
|
if (!tray.stopFlg) {
|
|
tray.stopFlg = true
|
|
self.stopTray(tray.id)
|
|
}
|
|
} else {
|
|
if (tray.rate < tray.targetRate) {
|
|
tray.rate += tray.targetRate - tray.rate > step ? step : tray.targetRate - tray.rate
|
|
} else if (tray.rate > tray.targetRate) {
|
|
tray.rate -= tray.rate - tray.targetRate > step ? step : tray.rate - tray.targetRate
|
|
}
|
|
let position = trajectory.curve.getPoint(tray.rate / 100)
|
|
if (position) {
|
|
tray.node.position.set(position.x, position.y, position.z)
|
|
position = trajectory.curve.getPoint(tray.rate / 100 + 0.001)
|
|
if (position) {
|
|
tray.node.up = new THREE.Vector3(0, 1, 0)
|
|
tray.node.lookAt(position.x, position.y, position.z)
|
|
tray.node.rotation.y = tray.node.rotation.y + Math.PI / 2
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let stateDictionary = self.property.config.trayStateDictionary[tray.state]
|
|
if (stateDictionary) {
|
|
tray.node.children[0].material.color.set(self.loadColor(stateDictionary.color))
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 更新堆垛机状态
|
|
* @param pilerInfo
|
|
*/
|
|
updatePiler (pilerInfo) {
|
|
let self = this
|
|
setTimeout(() => {
|
|
// 因为是事件回调中更新,所以需要setTimeout,不然持续更新错误状态
|
|
self.doUpdatePiler(pilerInfo)
|
|
}, 100)
|
|
}
|
|
|
|
doUpdatePiler (pilerInfo) {
|
|
let piler = this.property.warehouseInfo.piler[pilerInfo.pilerId]
|
|
if (piler) {
|
|
switch (pilerInfo.carryCargo) {
|
|
default:
|
|
case false:
|
|
if (piler.node.children.length > 2) {
|
|
piler.node.remove(piler.node.children[2])
|
|
}
|
|
break
|
|
case true:
|
|
if (piler.node.children.length <= 2) {
|
|
let parentS3 = piler.s3
|
|
let reckInfo = {
|
|
id: pilerInfo.cargoId,
|
|
type: 'cargo',
|
|
p3: [0, parentS3[1] / 2 + 1, 0],
|
|
s3: [parentS3[0] / 2 - 2, 1, parentS3[2] - 4],
|
|
segments: [1, 1, 1],
|
|
color: 0x333300
|
|
}
|
|
let rect = this.createRectangle(reckInfo)
|
|
piler.node.add(rect)
|
|
}
|
|
break
|
|
}
|
|
if (!piler.rate) {
|
|
piler.rate = 0
|
|
}
|
|
if (pilerInfo.state === '3') {
|
|
this.property.errorNodes[pilerInfo.pilerId] = pilerInfo.pilerId
|
|
piler.state = '3'
|
|
} else {
|
|
if (this.property.errorNodes.hasOwnProperty(pilerInfo.pilerId)) {
|
|
delete this.property.errorNodes[pilerInfo.pilerId]
|
|
}
|
|
if (pilerInfo.noAnim) {
|
|
piler.noAnim = pilerInfo.noAnim
|
|
piler.rate = pilerInfo.rate
|
|
} else {
|
|
piler.stopFlg = false
|
|
}
|
|
if (pilerInfo.hasOwnProperty('state')) {
|
|
piler.state = pilerInfo.state
|
|
} else {
|
|
if (piler.rate < pilerInfo.rate) {
|
|
piler.state = '1'
|
|
} else if (piler.rate > pilerInfo.rate) {
|
|
piler.state = '2'
|
|
} else if (piler.rate === pilerInfo.rate) {
|
|
piler.state = '0'
|
|
}
|
|
}
|
|
}
|
|
piler.targetRate = pilerInfo.rate
|
|
|
|
let textureLoader = new THREE.TextureLoader()
|
|
let texture
|
|
switch (piler.state) {
|
|
default:
|
|
case '0':
|
|
texture = textureLoader.load(require('../../assets/image/arrow.png'))
|
|
this.property.textures[pilerInfo.pilerId].direction = 1
|
|
break
|
|
case '1':
|
|
texture = textureLoader.load(require('../../assets/image/arrow-g.png'))
|
|
this.property.textures[pilerInfo.pilerId].direction = 1
|
|
break
|
|
case '2':
|
|
texture = textureLoader.load(require('../../assets/image/arrow-r.png'))
|
|
this.property.textures[pilerInfo.pilerId].direction = -1
|
|
break
|
|
case '3':
|
|
texture = textureLoader.load(require('../../assets/image/arrow.png'))
|
|
this.property.textures[pilerInfo.pilerId].direction = 1
|
|
break
|
|
}
|
|
texture.wrapS = THREE.RepeatWrapping
|
|
texture.wrapT = THREE.RepeatWrapping
|
|
texture.repeat.x = 10
|
|
texture.repeat.y = 4
|
|
texture.offset.y = 0.5
|
|
piler.pathModel.tube.material.map = texture
|
|
this.property.textures[pilerInfo.pilerId].texture = texture
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止堆垛机动画触发
|
|
* @param pilerId
|
|
*/
|
|
stopPiler (pilerId) {
|
|
let piler = this.property.warehouseInfo.piler[pilerId]
|
|
if (piler) {
|
|
if (piler.state !== '3') {
|
|
piler.state = '0'
|
|
}
|
|
let textureLoader = new THREE.TextureLoader()
|
|
let texture = textureLoader.load(require('../../assets/image/arrow.png'))
|
|
texture.wrapS = THREE.RepeatWrapping
|
|
texture.wrapT = THREE.RepeatWrapping
|
|
texture.repeat.x = 10
|
|
texture.repeat.y = 4
|
|
texture.offset.y = 0.5
|
|
piler.pathModel.tube.material.map = texture
|
|
this.property.textures[pilerId].texture = texture
|
|
try {
|
|
this.property.event.onAnimateStop('piler', piler)
|
|
} catch (e) {
|
|
// console.log(e.toString())
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新小车位置
|
|
* @param carInfo
|
|
*/
|
|
updateCar (carInfo) {
|
|
let self = this
|
|
setTimeout(() => {
|
|
// 因为是事件回调中更新,所以需要setTimeout,不然持续更新错误状态
|
|
self.doUpdateCar(carInfo)
|
|
}, 100)
|
|
}
|
|
|
|
doUpdateCar (carInfo) {
|
|
let car = this.property.warehouseInfo.carModel.cars[carInfo.carId]
|
|
if (car) {
|
|
if (carInfo.pathPoints) {
|
|
car.rate = 0
|
|
car.pathModel = this.drawPipeline(carInfo.pathPoints, 'arrow-g', car)
|
|
}
|
|
switch (carInfo.carryCargo) {
|
|
default:
|
|
case false:
|
|
if (car.node.children.length > 2) {
|
|
car.node.remove(car.node.children[2])
|
|
}
|
|
break
|
|
case true:
|
|
if (car.node.children.length <= 2) {
|
|
let parentS3 = car.s3
|
|
let reckInfo = {
|
|
id: carInfo.cargoId,
|
|
type: 'cargo',
|
|
p3: [0, 2, 0],
|
|
s3: [parentS3[0] - 4, 20, parentS3[2] - 4],
|
|
segments: [1, 1, 1],
|
|
color: 0x333300
|
|
}
|
|
let rect = this.createRectangle(reckInfo)
|
|
car.node.add(rect)
|
|
}
|
|
break
|
|
}
|
|
if (!car.rate) {
|
|
car.rate = 0
|
|
}
|
|
car.targetRate = carInfo.rate
|
|
if (carInfo.noAnim) {
|
|
car.noAnim = carInfo.noAnim
|
|
car.rate = car.targetRate
|
|
} else {
|
|
car.stopFlg = false
|
|
}
|
|
if (carInfo.hasOwnProperty('state')) {
|
|
car.state = carInfo.state
|
|
} else {
|
|
car.state = car.rate !== carInfo.rate ? '1' : '0'
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止小车动画触发
|
|
* @param carId
|
|
*/
|
|
stopCar (carId) {
|
|
let car = this.property.warehouseInfo.carModel.cars[carId]
|
|
if (car) {
|
|
if (car.pathModel && car.pathModel.tube) {
|
|
this.property.scene.remove(car.pathModel.tube)
|
|
}
|
|
car.state = '0'
|
|
try {
|
|
this.property.event.onAnimateStop('car', car)
|
|
} catch (e) {
|
|
// console.log(e.toString())
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 修改托盘位置
|
|
* @param trayInfo
|
|
*/
|
|
updateTray (trayInfo) {
|
|
let self = this
|
|
setTimeout(() => {
|
|
// 因为是事件回调中更新,所以需要setTimeout,不然持续更新错误状态
|
|
self.doUpdateTray(trayInfo)
|
|
}, 100)
|
|
}
|
|
|
|
doUpdateTray (trayInfo) {
|
|
let tray = this.property.warehouseInfo.beltModel.trays[trayInfo.trayId]
|
|
if (tray) {
|
|
switch (trayInfo.carryCargo) {
|
|
default:
|
|
case false:
|
|
if (tray.node.children.length > 2) {
|
|
tray.node.remove(tray.node.children[2])
|
|
}
|
|
break
|
|
case true:
|
|
if (tray.node.children.length <= 2) {
|
|
let parentS3 = tray.s3
|
|
let reckInfo = {
|
|
id: trayInfo.cargoId,
|
|
type: 'cargo',
|
|
p3: [0, 2, 0],
|
|
s3: [parentS3[0] - 4, 20, parentS3[2] - 4],
|
|
segments: [1, 1, 1],
|
|
color: 0x333300
|
|
}
|
|
let rect = this.createRectangle(reckInfo)
|
|
tray.node.add(rect)
|
|
}
|
|
break
|
|
}
|
|
if (!tray.rate) {
|
|
tray.rate = 0
|
|
}
|
|
tray.targetRate = trayInfo.rate
|
|
if (trayInfo.noAnim) {
|
|
tray.noAnim = trayInfo.noAnim
|
|
tray.rate = tray.targetRate
|
|
} else {
|
|
tray.stopFlg = false
|
|
}
|
|
if (trayInfo.hasOwnProperty('state')) {
|
|
tray.state = trayInfo.state
|
|
} else {
|
|
tray.state = tray.rate !== trayInfo.rate ? '1' : '0'
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 停止托盘动画触发
|
|
* @param trayId
|
|
*/
|
|
stopTray (trayId) {
|
|
let tray = this.property.warehouseInfo.beltModel.trays[trayId]
|
|
if (tray) {
|
|
tray.state = '0'
|
|
try {
|
|
this.property.event.onAnimateStop('tray', tray)
|
|
} catch (e) {
|
|
// console.log(e.toString())
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 更新仓库库存状态
|
|
*/
|
|
updateStorage (warehouseInfo) {
|
|
let self = this
|
|
let warehouseData = JSON.parse(JSON.stringify(warehouseInfo))
|
|
let freightSpaceStateDictionary = JSON.parse(JSON.stringify(this.property.config.freightSpaceStateDictionary))
|
|
Object.keys(warehouseData.storageRack).map(storageRackId => {
|
|
Object.keys(warehouseData.storageRack[storageRackId].freightSpace).map(freightSpaceId => {
|
|
let freightSpace = warehouseData.storageRack[storageRackId].freightSpace[freightSpaceId]
|
|
Object.keys(freightSpaceStateDictionary).map(tileStateKey => {
|
|
if (freightSpace.state&&freightSpace.state.toString() === tileStateKey) {
|
|
if (!freightSpaceStateDictionary[tileStateKey].nodeIds) {
|
|
freightSpaceStateDictionary[tileStateKey].nodeIds = []
|
|
}
|
|
freightSpaceStateDictionary[tileStateKey].nodeIds.push(freightSpace.id)
|
|
}
|
|
})
|
|
})
|
|
})
|
|
this.property.scene.traverse(obj => {
|
|
if (obj.type === 'GridHelper') {
|
|
} else if (obj.type === 'Mesh') {
|
|
if (obj.customDataType === 'freightSpace') {
|
|
Object.keys(freightSpaceStateDictionary).map(tileStateKey => {
|
|
if (freightSpaceStateDictionary[tileStateKey].nodeIds && freightSpaceStateDictionary[tileStateKey].nodeIds.indexOf(obj.nodeId) > -1) {
|
|
obj.material.color.set(self.loadColor(freightSpaceStateDictionary[tileStateKey].color))
|
|
}
|
|
})
|
|
}
|
|
} else if (obj.type === 'Group') {
|
|
obj.children.forEach((child, i) => {
|
|
Object.keys(freightSpaceStateDictionary).map(tileStateKey => {
|
|
if (freightSpaceStateDictionary[tileStateKey].nodeIds && freightSpaceStateDictionary[tileStateKey].nodeIds.indexOf(child.nodeId) > -1) {
|
|
// debugger
|
|
child.material.color.set(self.loadColor(freightSpaceStateDictionary[tileStateKey].color))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 更新仓库库存状态
|
|
*/
|
|
updateStorageByKey (freightSpaceId, state) {
|
|
let self = this
|
|
let tileState = JSON.parse(JSON.stringify(this.property.config.freightSpaceStateDictionary[state]))
|
|
this.property.scene.traverse(obj => {
|
|
if (obj.type === 'GridHelper') {
|
|
|
|
} else if (obj.type === 'Mesh') {
|
|
if (obj.nodeId !== undefined && obj.nodeId.toString() === freightSpaceId) {
|
|
obj.material.color.set(self.loadColor(tileState.color))
|
|
}
|
|
} else if (obj.type === 'Group') {
|
|
obj.children.forEach((child, i) => {
|
|
if (child.nodeId !== undefined && child.nodeId.toString() === freightSpaceId) {
|
|
child.material.color.set(self.loadColor(tileState.color))
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
loadColor (colorText) {
|
|
if (typeof colorText === 'string') {
|
|
let color = parseInt(colorText.replace(/#/g, '0x'))
|
|
// let color = parseInt(colorText.replace(/#/g, '0x'))
|
|
// console.log(colorText);
|
|
return color
|
|
} else {
|
|
return colorText
|
|
}
|
|
}
|
|
|
|
loadFbxModel (url) {
|
|
let self = this
|
|
// 实例化一个FBXLoader对象
|
|
const loader = new FBXLoader()
|
|
// 加载fbx文件
|
|
loader.load(url, (model) => {
|
|
model.traverse(childModel => {
|
|
self.loadColorForModel(childModel)
|
|
})
|
|
// 将模型添加到场景中
|
|
self.property.scene.add(model)
|
|
}, (event) => {
|
|
// 控制台打印加载进度
|
|
// console.log((event.loaded / event.total * 100) + '% loaded')
|
|
}, (error) => {
|
|
// 控制台打印加载失败
|
|
console.error(error)
|
|
})
|
|
}
|
|
|
|
loadColorForModel (model) {
|
|
let self = this
|
|
if (model.material) {
|
|
model.castShadow = true
|
|
model.material.emissive = model.material.color
|
|
model.material.emissiveMap = model.material.map
|
|
} else if (model.children.length > 0) {
|
|
model.children.map(childModel => {
|
|
self.loadColorForModel(childModel)
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 初始化地图
|
|
*/
|
|
initMap () {
|
|
if (WEBGL.isWebGLAvailable() === false) {
|
|
document.body.appendChild(WEBGL.getWebGLErrorMessage())
|
|
} else {
|
|
this.initScene()
|
|
this.initCamera()
|
|
this.initRender()
|
|
this.initMaterials()
|
|
this.initControls()
|
|
// this.initVideoGround()
|
|
}
|
|
}
|
|
|
|
initWarehouse () {
|
|
if (this.property.warehouseInfo) {
|
|
this.drawWarehouses()
|
|
if (this.property.config.vehicleType === 1) {
|
|
// this.drawPilers()
|
|
// this.drawBelt()
|
|
} else {
|
|
this.drawRoad()
|
|
}
|
|
// this.drawCars()
|
|
} else {
|
|
this.initLight()
|
|
}
|
|
this.animate()
|
|
this.initEvent()
|
|
// if (this.property.grid) {
|
|
// this.initGrid(this.property.grid.width, this.property.grid.height, this.property.grid.singleWidth, this.property.grid.singleHeight)
|
|
// }
|
|
}
|
|
}
|
|
|
|
export default MapDrawing
|
|
|