|
|
import {
|
|
|
LittleEndian,
|
|
|
BigEndian,
|
|
|
hexStringToBytes,
|
|
|
intBitsToFloat,
|
|
|
bytesToShort,
|
|
|
bytesToLong,
|
|
|
bytesToInt,
|
|
|
bytesToString,
|
|
|
fillIntToBytes,
|
|
|
fillShortToBytes,
|
|
|
fillLongToBytes,
|
|
|
fillStringToBytes,
|
|
|
bytesToHexString,
|
|
|
} from './byteConvert.js'
|
|
|
import { drawChartsContent, drawAxis, drawAxis2D, draw2DText, initPoints, pointColors } from './draw.js';
|
|
|
let renderer, scene, camera, controls, renderer2D, scene2D, camera2D, controls2D;
|
|
|
let axis = new THREE.Group();
|
|
|
let axis2D = new THREE.Group();
|
|
|
let initAxis = 80;//正值最大值
|
|
|
let box3D = document.getElementById("box3D2");
|
|
|
let box2D = document.getElementById("box2D2");
|
|
|
const lut = new THREE.Lut();
|
|
|
lut.addColorMap('axis', [[0.0, 0x00ee00], [0.25, 0xeeee00], [0.75, 0xee0000], [1.0, 0x4e0211]],);
|
|
|
lut.setColorMap('axis', 500)
|
|
|
let meshList = new THREE.Group();//图例柱子
|
|
|
const dummy = new THREE.Object3D();
|
|
|
const dummyPoint = new THREE.Object3D();
|
|
|
meshList.name = 'chartsMesh';
|
|
|
let pointsList = new THREE.Group();//图例散点
|
|
|
pointsList.name = 'chartsPoint';
|
|
|
let axisTextGroup = new THREE.Group();
|
|
|
axisTextGroup.name = 'axisText';
|
|
|
let axisTextGroup2D = new THREE.Group();
|
|
|
axisTextGroup2D.name = 'axisText';
|
|
|
// 周期,相位 柱子数
|
|
|
let period = 50, phase = 128, count = period * phase;
|
|
|
const geometry = new THREE.BoxGeometry(0.6, 1, 2.5);
|
|
|
geometry.computeVertexNormals()
|
|
|
const initInstancedMesh = new THREE.InstancedMesh(geometry, new THREE.MeshBasicMaterial(), count);
|
|
|
initInstancedMesh.name = 'initInstancedMesh';
|
|
|
/* prpd初始Instanced点 */
|
|
|
let initInstancedPoints,//prpd实例
|
|
|
pointsGeometry = new THREE.BoxGeometry(0, 0.8, (360 / initAxis) * 0.8,),// prpd位置属性数组
|
|
|
pointCount;
|
|
|
pointsGeometry.computeVertexNormals()
|
|
|
document.getElementById('folderInput').addEventListener('change', processFolder);
|
|
|
const phaseSelect = document.getElementById('phase');
|
|
|
const openModal = document.getElementById('openModal');
|
|
|
openModal.addEventListener('click', toggleFileModal);
|
|
|
init();
|
|
|
init2D();
|
|
|
render();
|
|
|
render2D();
|
|
|
const dataList = { list2d: [], list3d: [] };
|
|
|
let allList = [];
|
|
|
document.getElementById('fileModal').style.display = 'none';
|
|
|
function toggleFileModal() {
|
|
|
var fileModal = document.getElementById('fileModal');
|
|
|
var fileList = document.getElementById('fileList');
|
|
|
fileList.innerHTML = ''; // 清空文件列表
|
|
|
|
|
|
var folderInput = document.getElementById('folderInput');
|
|
|
var files = folderInput.files;
|
|
|
|
|
|
if (files.length > 0) {
|
|
|
for (var i = 0; i < files.length; i++) {
|
|
|
var li = document.createElement('li');
|
|
|
li.textContent = files[i].name;
|
|
|
fileList.appendChild(li);
|
|
|
}
|
|
|
}
|
|
|
fileModal.style.display = fileModal.style.display === 'none' ? 'block' : 'none';
|
|
|
|
|
|
}
|
|
|
async function processFolder(event) {
|
|
|
//const folderInput = event.target.files;
|
|
|
const output = document.getElementById('output');
|
|
|
const files = event.target.files;
|
|
|
if (files.length === 0) {
|
|
|
alert('请选择文件夹');
|
|
|
return;
|
|
|
}
|
|
|
await processFolderRecursive(files);
|
|
|
}
|
|
|
|
|
|
async function processFolderRecursive(folder) {
|
|
|
const promises = []; allList = [];
|
|
|
for (let id = 0; id < folder.length; id++) {
|
|
|
const fileType = folder[id].name.replace(/.+\./, "");
|
|
|
if (fileType == 'dat') {
|
|
|
const pathArr = folder[id].webkitRelativePath.split('/');
|
|
|
promises.push(readAsText(folder[id], pathArr))
|
|
|
}
|
|
|
}
|
|
|
await Promise.all(promises);
|
|
|
let valArr = [], valNumArr = [], list2d = [];
|
|
|
let dBmData = allList.map(i => i.toString());
|
|
|
for (let i of dBmData) {
|
|
|
let index = valArr.indexOf(i);
|
|
|
if (index == -1) {
|
|
|
valArr.push(i);
|
|
|
valNumArr.push(1);
|
|
|
list2d.push([...i.split(','), 1])
|
|
|
} else {
|
|
|
valNumArr[index]++;
|
|
|
list2d[index][2]++;
|
|
|
}
|
|
|
}
|
|
|
dataList.list3d = _.chunk(allList, phaseSelect.value)
|
|
|
dataList.list2d = list2d;
|
|
|
loopDraw(dataList.list3d, true)
|
|
|
drawPRPD(dataList.list2d)
|
|
|
console.log(dataList);
|
|
|
}
|
|
|
let timers = []; // 声明一个全局变量来保存定时器
|
|
|
|
|
|
function clearTimers() {
|
|
|
timers.forEach(timerId => {
|
|
|
clearTimeout(timerId);
|
|
|
});
|
|
|
timers = [];
|
|
|
}
|
|
|
|
|
|
function loopDraw(data, isNew) {
|
|
|
if (timers.length && isNew) {
|
|
|
clearTimers();
|
|
|
}
|
|
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
|
if (timers[i]) {
|
|
|
clearTimeout(timers[i]);
|
|
|
}
|
|
|
|
|
|
timers[i] = setTimeout(() => {
|
|
|
drawPRPS(data[i]);
|
|
|
|
|
|
if (i === data.length - 1) {
|
|
|
// 最后一个定时器触发时,清除所有定时器并进行下一轮循环
|
|
|
clearTimers();
|
|
|
loopDraw(data);
|
|
|
}
|
|
|
}, 20 + 20 * i);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//FileReader异步解析文件
|
|
|
function readAsText(file, pathArr) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const reader = new FileReader();
|
|
|
reader.onload = event => {
|
|
|
// 读取文件完成后,将文件内容和额外参数一起传递给 resolve 函数
|
|
|
loadImgUrl(event.target.result, pathArr);
|
|
|
resolve({ res: event.target.result, pathArr });
|
|
|
};
|
|
|
reader.onerror = event => {
|
|
|
reject(new Error("Error reading the file"));
|
|
|
};
|
|
|
reader.readAsArrayBuffer(file);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
//加载图片数据
|
|
|
function loadImgUrl(res, pathArr) {
|
|
|
var phaseNum = phaseSelect.value;
|
|
|
let dBmData = [];
|
|
|
let bytes = new Int8Array(res)
|
|
|
for (let i = 0, offset = 0; i < bytes.length / 4; i++, offset += 4) {
|
|
|
let j = parseInt(i / phaseNum) + 1; //周期
|
|
|
let phase = parseInt((i - (j - 1) * phaseNum + 1) * (360 / phaseNum));// 相位
|
|
|
let value = parseInt(intBitsToFloat(bytesToInt(LittleEndian, bytes, offset, 4))); // 幅值
|
|
|
value = value + 80;
|
|
|
dBmData[i] = [phase, value]//.toString();
|
|
|
}
|
|
|
allList.push(...dBmData)
|
|
|
}
|
|
|
let counter = 1;
|
|
|
initCharts()
|
|
|
function initCharts(type) {
|
|
|
if (type == 'open') {
|
|
|
meshList.visible = true;
|
|
|
return;
|
|
|
} else if (type == 'close') {
|
|
|
meshList.visible = false;
|
|
|
let k = 1, s = 1;
|
|
|
for (let j = 0; j < period; j++) {
|
|
|
for (let i = 0; i < 128; i++) {
|
|
|
initInstancedMesh.getMatrixAt(k, dummy.matrix);
|
|
|
dummy.position.setFromMatrixPosition(dummy.matrix);
|
|
|
dummy.scale.setFromMatrixScale(dummy.matrix);
|
|
|
dummy.position.y = 99999;
|
|
|
dummy.scale.y = Math.abs(1);
|
|
|
const color = lut.getColor(Math.abs(0) / initAxis);
|
|
|
initInstancedMesh.setColorAt(k, color);
|
|
|
dummy.updateMatrix();
|
|
|
initInstancedMesh.setMatrixAt(k, dummy.matrix);
|
|
|
k++;
|
|
|
}
|
|
|
}
|
|
|
initInstancedMesh.instanceMatrix.needsUpdate = true;
|
|
|
initInstancedMesh.instanceColor.needsUpdate = true;
|
|
|
for (let i = 0; i < pointCount; i++) {
|
|
|
dummyPoint.position.set(1, 99999, 1);
|
|
|
dummyPoint.updateMatrix();
|
|
|
initInstancedPoints.setMatrixAt(i, dummyPoint.matrix);
|
|
|
}
|
|
|
initInstancedPoints.instanceMatrix.needsUpdate = true;
|
|
|
return;
|
|
|
}
|
|
|
let k = 0, initList = [];
|
|
|
for (let i = 0; i < period; i++) {
|
|
|
initList.push(generateArray());
|
|
|
}
|
|
|
for (let j = 0; j < initList.length; j++) {
|
|
|
for (let i = 0; i < initList[j].length; i++) {
|
|
|
const [a, b, c] = initList[j][i];
|
|
|
dummy.position.set(j, 9999, b);
|
|
|
dummy.scale.y = 0;
|
|
|
dummy.updateMatrix();
|
|
|
initInstancedMesh.setMatrixAt(k, dummy.matrix);
|
|
|
|
|
|
const color = lut.getColor(Math.abs(c) / initAxis);
|
|
|
initInstancedMesh.setColorAt(k, color);
|
|
|
k++;
|
|
|
}
|
|
|
}
|
|
|
initInstancedMesh.instanceMatrix.needsUpdate = true;
|
|
|
initInstancedMesh.instanceColor.needsUpdate = true;
|
|
|
const object = meshList.getObjectByName('initInstancedMesh');
|
|
|
if (!object) {
|
|
|
meshList.add(initInstancedMesh);
|
|
|
}
|
|
|
let maxYPointsMap = [800, 10000, 10000, 7000];
|
|
|
let maxYPoints = maxYPointsMap[0];
|
|
|
pointCount = phase * maxYPoints;
|
|
|
initInstancedPoints = new THREE.InstancedMesh(pointsGeometry, new THREE.MeshBasicMaterial(), pointCount);
|
|
|
|
|
|
let j = 0;
|
|
|
for (let i = 0; i < pointCount; i++) {
|
|
|
dummyPoint.position.set(i, 9999, i)
|
|
|
dummyPoint.updateMatrix()
|
|
|
initInstancedPoints.setMatrixAt(j, dummyPoint.matrix)
|
|
|
const color = new THREE.Color(0x0082df)
|
|
|
initInstancedPoints.setColorAt(j, color)
|
|
|
j++
|
|
|
}
|
|
|
initInstancedPoints.instanceMatrix.needsUpdate = true;
|
|
|
initInstancedPoints.instanceColor.needsUpdate = true;
|
|
|
initInstancedPoints.name = 'initInstancedPoints';
|
|
|
// const object1 = meshList.getObjectByName('initInstancedMesh');
|
|
|
// if (!object1) {
|
|
|
pointsList.add(initInstancedPoints);
|
|
|
// }
|
|
|
}
|
|
|
//prps初始化
|
|
|
function init() {
|
|
|
let box = $('#box3D');
|
|
|
let width = box.width();
|
|
|
let height = width;
|
|
|
renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true, });
|
|
|
renderer.setSize(width, height);//设置渲染区域尺寸
|
|
|
renderer.setClearColor(0x000d13, 1); //设置背景颜色
|
|
|
//renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
|
|
|
renderer.setPixelRatio(window.devicePixelRatio)//设备像素比,优化渲染效果
|
|
|
box?.append(renderer.domElement); //body元素中插入canvas对象
|
|
|
scene = new THREE.Scene();
|
|
|
scene.add(meshList, axis, pointsList, axisTextGroup);
|
|
|
|
|
|
drawAxis(initAxis, 0, true)
|
|
|
|
|
|
var k = width / height; //窗口宽高比
|
|
|
var s = 360; //三维场景显示范围控制系数,系数越大,显示的范围越大
|
|
|
//创建相机对象
|
|
|
camera = new THREE.OrthographicCamera(-s * k + 20, s * k + 20, s + 100, -s + 100, 1, 1600);
|
|
|
camera.position.set(500, 200, 500); //设置相机位置
|
|
|
camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
|
|
|
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
|
|
// 设置左右方向的最大、最小角度限制为 90 度和 0 度
|
|
|
controls.minAzimuthAngle = 0;
|
|
|
controls.maxAzimuthAngle = Math.PI / 2;
|
|
|
// 设置上下方向的最大、最小角度限制为 90 度和 0 度
|
|
|
controls.minPolarAngle = 0;
|
|
|
controls.maxPolarAngle = Math.PI / 2;
|
|
|
//设置放大缩小上下限
|
|
|
controls.minZoom = 0.5;
|
|
|
controls.maxZoom = 2;
|
|
|
//controls.enablePan = false;
|
|
|
//设置控制器中心点
|
|
|
controls.target.set(25, 0, 180);
|
|
|
renderer.render(scene, camera);
|
|
|
|
|
|
//controls.addEventListener('change', render);
|
|
|
}
|
|
|
//prpd初始化
|
|
|
function init2D() {
|
|
|
let box = $('#box2D');
|
|
|
let width = box.width();
|
|
|
let height = width;
|
|
|
renderer2D = new THREE.WebGLRenderer({ preserveDrawingBuffer: true, });
|
|
|
renderer2D.setSize(width, height);//设置渲染区域尺寸
|
|
|
renderer2D.setClearColor(0x000d13, 1); //设置背景颜色
|
|
|
renderer2D.setPixelRatio(window.devicePixelRatio)//设备像素比,优化渲染效果
|
|
|
box?.append(renderer2D.domElement); //body元素中插入canvas对象
|
|
|
scene2D = new THREE.Scene();
|
|
|
const List = pointsList.clone()
|
|
|
scene2D.add(axis2D, List, axisTextGroup2D, pointsList)
|
|
|
drawAxis2D(initAxis, 0)
|
|
|
// 辅助坐标系 参数250表示坐标系大小,可以根据场景大小去设置
|
|
|
// let axisHelper = new THREE.AxesHelper(250);
|
|
|
// scene2D.add(axisHelper);
|
|
|
var k = width / height; //窗口宽高比
|
|
|
var s = 240; //三维场景显示范围控制系数,系数越大,显示的范围越大
|
|
|
//创建相机对象
|
|
|
camera2D = new THREE.OrthographicCamera(-s * k - 200, s * k - 200, s + 160, -s + 160, 1, 1200);
|
|
|
camera2D.position.set(600, 0, 0); //设置相机位置
|
|
|
camera2D.lookAt(scene2D.position); //设置相机方向(指向的场景对象)
|
|
|
controls2D = new THREE.OrbitControls(camera2D, renderer2D.domElement);
|
|
|
//controls2D.enablePan = false//右键位移禁用
|
|
|
//controls2D.enableZoom = false//放大缩小禁用
|
|
|
//设置放大缩小上下限
|
|
|
controls2D.minZoom = 0.5;
|
|
|
controls2D.maxZoom = 2;
|
|
|
controls2D.enableRotate = false
|
|
|
//renderer2D.render(scene2D, camera2D);
|
|
|
//controls2D.addEventListener('change', render2D);
|
|
|
}
|
|
|
function onWindowResize() {
|
|
|
let box = $('#box3D');
|
|
|
let width = box.width();
|
|
|
let height = width;
|
|
|
camera.updateProjectionMatrix();
|
|
|
renderer.setSize(width, height);
|
|
|
renderer.setPixelRatio(window.devicePixelRatio)//设备像素比,优化渲染效果
|
|
|
|
|
|
|
|
|
camera2D.updateProjectionMatrix();
|
|
|
renderer2D.setSize(width, height);
|
|
|
renderer2D.setPixelRatio(window.devicePixelRatio)//设备像素比,优化渲染效果
|
|
|
}
|
|
|
function render() {
|
|
|
// 获取摄像机的视锥体
|
|
|
const frustum = new THREE.Frustum();
|
|
|
const cameraViewProjectionMatrix = new THREE.Matrix4();
|
|
|
camera.updateMatrixWorld(); // 确保摄像机的世界矩阵已更新
|
|
|
cameraViewProjectionMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
|
frustum.setFromProjectionMatrix(cameraViewProjectionMatrix);
|
|
|
|
|
|
// 遍历场景中的每个对象,并根据视锥体进行剔除
|
|
|
scene.traverse((object) => {
|
|
|
if (object.isMesh) {
|
|
|
object.visible = frustum.intersectsObject(object);
|
|
|
}
|
|
|
});
|
|
|
// 更新帧率显示器
|
|
|
renderer.render(scene, camera);
|
|
|
requestAnimationFrame(render)
|
|
|
}
|
|
|
function render2D() {
|
|
|
//scene.rotateY(0.001);//每次绕y轴旋转0.01弧度
|
|
|
requestAnimationFrame(render2D)
|
|
|
renderer2D.render(scene2D, camera2D);
|
|
|
}
|
|
|
function drawPRPD(list) {
|
|
|
const colors = [
|
|
|
{
|
|
|
color: new THREE.Color(0x50A3BA),
|
|
|
num: 5,
|
|
|
},
|
|
|
{
|
|
|
color: new THREE.Color(0x9DB578),
|
|
|
num: 7,
|
|
|
},
|
|
|
{
|
|
|
color: new THREE.Color(0xEAC736),
|
|
|
num: 10,
|
|
|
},
|
|
|
{
|
|
|
color: new THREE.Color(0xE28B4A),
|
|
|
num: 14,
|
|
|
},
|
|
|
];
|
|
|
list.forEach((item, index) => {
|
|
|
// 更新颜色
|
|
|
const color = item[2] <= 3 ? new THREE.Color(0x0082df) : (item[2] >= 15 ? new THREE.Color(0xD94E5D) : colors.find(i => item[2] <= i.num).color);
|
|
|
initInstancedPoints.setColorAt(index, color);
|
|
|
// 更新位置
|
|
|
dummyPoint.position.set(1, item[1], item[0]);
|
|
|
dummyPoint.updateMatrix();
|
|
|
initInstancedPoints.setMatrixAt(index, dummyPoint.matrix);
|
|
|
})
|
|
|
for (let i = list.length; i < pointCount; i++) {
|
|
|
dummyPoint.position.set(1, 999999, 0);
|
|
|
dummyPoint.updateMatrix();
|
|
|
initInstancedPoints.setMatrixAt(i, dummyPoint.matrix);
|
|
|
}
|
|
|
initInstancedPoints.instanceMatrix.needsUpdate = true;
|
|
|
initInstancedPoints.instanceColor.needsUpdate = true;
|
|
|
}
|
|
|
function drawPRPS(list) {
|
|
|
let k = 1;
|
|
|
for (let j = 0; j < period; j++) {
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
initInstancedMesh.getMatrixAt(k, dummy.matrix);
|
|
|
dummy.position.setFromMatrixPosition(dummy.matrix);
|
|
|
dummy.scale.setFromMatrixScale(dummy.matrix);
|
|
|
if (dummy.position.x > 0) {
|
|
|
dummy.position.x -= 1;
|
|
|
} else {
|
|
|
let [y, h] = list[i];
|
|
|
const max = initAxis;
|
|
|
h = h > max ? max : h;
|
|
|
dummy.position.x = 49
|
|
|
dummy.position.y = h / 2;
|
|
|
dummy.scale.y = Math.abs(h);
|
|
|
const color = lut.getColor(Math.abs(h) / max);
|
|
|
initInstancedMesh.setColorAt(k, color);
|
|
|
}
|
|
|
dummy.updateMatrix();
|
|
|
initInstancedMesh.setMatrixAt(k, dummy.matrix);
|
|
|
k++;
|
|
|
}
|
|
|
}
|
|
|
initInstancedMesh.instanceMatrix.needsUpdate = true;
|
|
|
initInstancedMesh.instanceColor.needsUpdate = true;
|
|
|
}
|
|
|
function generateArray() {
|
|
|
const newArray = [];
|
|
|
const step = 360 / phase;
|
|
|
for (let i = 1; i <= phase; i++) {
|
|
|
newArray.push([counter, parseInt(i * step), getRandomInt(20, 35)]);
|
|
|
}
|
|
|
counter = (counter % period) + 1;
|
|
|
return newArray;
|
|
|
}
|
|
|
// 生成指定范围内的随机整数
|
|
|
function getRandomInt(min, max) {
|
|
|
const randomNumber = Math.random() * (max - min) + min;
|
|
|
const roundedNumber = randomNumber.toFixed(1);
|
|
|
return parseFloat(roundedNumber);
|
|
|
}
|
|
|
// 页面放大缩小时
|
|
|
$(window).resize(function () {
|
|
|
onWindowResize()
|
|
|
})
|
|
|
export { scene, axis, meshList, pointsList, axis2D, scene2D }
|