Commit 29f798ef authored by 王造's avatar 王造

Initial commit

parents
Pipeline #65 failed with stages
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
'extends': [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
}
}
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}
\ No newline at end of file
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}
# raytracing-front
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}
{
"name": "raytracing-front",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"axios": "^1.6.7",
"element-plus": "^2.5.5",
"element-ui": "^2.15.14",
"three": "^0.161.0",
"three-obj-mtl-loader": "^1.0.3",
"vue": "^3.4.15",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^8.0.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.3",
"vite": "^5.0.11"
}
}
# Cuboid OBJ File
v -30.00000000 0.00000000 0.00000000
v -15.00000000 0.00000000 0.00000000
v -15.00000000 15.00000000 0.00000000
v -30.00000000 15.00000000 0.00000000
v -30.00000000 0.00000000 10.00000000
v -15.00000000 0.00000000 10.00000000
v -15.00000000 15.00000000 10.00000000
v -30.00000000 15.00000000 10.00000000
f 4 3 2 1
f 5 6 7 8
f 1 2 6 5
f 2 3 7 6
f 3 4 8 7
f 4 1 5 8
v -40.00000000 -60.00000000 0.00000000
v 40.00000000 -60.00000000 0.00000000
v 40.00000000 -40.00000000 0.00000000
v -40.00000000 -40.00000000 0.00000000
v -40.00000000 -60.00000000 15.00000000
v 40.00000000 -60.00000000 15.00000000
v 40.00000000 -40.00000000 15.00000000
v -40.00000000 -40.00000000 15.00000000
f 12 11 10 9
f 13 14 15 16
f 9 10 14 13
f 10 11 15 14
f 11 12 16 15
f 12 9 13 16
v -28.00000000 32.00000000 0.00000000
v 21.00000000 32.00000000 0.00000000
v 21.00000000 80.00000000 0.00000000
v -28.00000000 80.00000000 0.00000000
v -28.00000000 32.00000000 13.00000000
v 21.00000000 32.00000000 13.00000000
v 21.00000000 80.00000000 13.00000000
v -28.00000000 80.00000000 13.00000000
f 20 19 18 17
f 21 22 23 24
f 17 18 22 21
f 18 19 23 22
f 19 20 24 23
f 20 17 21 24
v 40.00000000 40.00000000 0.00000000
v 120.00000000 40.00000000 0.00000000
v 120.00000000 60.00000000 0.00000000
v 40.00000000 60.00000000 0.00000000
v 40.00000000 40.00000000 18.00000000
v 120.00000000 40.00000000 18.00000000
v 120.00000000 60.00000000 18.00000000
v 40.00000000 60.00000000 18.00000000
f 28 27 26 25
f 29 30 31 32
f 25 26 30 29
f 26 27 31 30
f 27 28 32 31
f 28 25 29 32
v 10.00000000 0.00000000 0.00000000
v 50.00000000 0.00000000 0.00000000
v 50.00000000 20.00000000 0.00000000
v 10.00000000 20.00000000 0.00000000
v 10.00000000 0.00000000 10.00000000
v 50.00000000 0.00000000 10.00000000
v 50.00000000 20.00000000 10.00000000
v 10.00000000 20.00000000 10.00000000
f 36 35 34 33
f 37 38 39 40
f 33 34 38 37
f 34 35 39 38
f 35 36 40 39
f 36 33 37 40
v 140.00000000 0.00000000 0.00000000
v 160.00000000 0.00000000 0.00000000
v 160.00000000 80.00000000 0.00000000
v 140.00000000 80.00000000 0.00000000
v 140.00000000 0.00000000 15.00000000
v 160.00000000 0.00000000 15.00000000
v 160.00000000 80.00000000 15.00000000
v 140.00000000 80.00000000 15.00000000
f 44 43 42 41
f 45 46 47 48
f 41 42 46 45
f 42 43 47 46
f 43 44 48 47
f 44 41 45 48
v 60.00000000 -60.00000000 0.00000000
v 120.00000000 -60.00000000 0.00000000
v 120.00000000 -20.00000000 0.00000000
v 60.00000000 -20.00000000 0.00000000
v 60.00000000 -60.00000000 20.00000000
v 120.00000000 -60.00000000 20.00000000
v 120.00000000 -20.00000000 20.00000000
v 60.00000000 -20.00000000 20.00000000
f 52 51 50 49
f 53 54 55 56
f 49 50 54 53
f 50 51 55 54
f 51 52 56 55
f 52 49 53 56
<template>
<router-view></router-view>
</template>
<script>
import Main from './views/3DModelView.vue'
export default {
name: 'App',
components: [Main]
}
</script>
<style>
body{
display: flex;
width: 100%;
height: 100vh;
margin: 0;
}
#app {
max-width: none;
padding: 0;
margin: 0;
-webkit-flex: 1;
flex: 1;
display: block;
}
</style>
import request from '../axios/index'
export let targetUrl = 'http://127.0.0.1:8080'
// 文件上传地址
export var uploadUrl = targetUrl + '/obj/upload'
export function getAllRays(form) {
console.log(JSON.stringify(form))
return request({
url: '/obj/getAllSBRRays',
method: 'post',
data: form
})
}
export function getAllEmitRays(form) {
console.log(JSON.stringify(form))
return request({
url: '/obj/getAllEmitRays',
method: 'post',
data: form
})
}
\ No newline at end of file
//先导入axios
import axios from 'axios'
//创建一个axios对象,将基础配置配好
var request = axios.create({
baseURL: '/api', // 请求根路径
timeout: 2000000000, // 请求超时时间
headers: {} // 请求头
})
//请求拦截器 前端给后端参数
request.interceptors.request.use(
(config) => {
//在请求之前做些什么
// 添加loading加载
const token = localStorage.getItem('token')
if (token) {
config.headers['token'] = token //请求头添加token
}
return config // 拦截成功后最后一定要返回
//可通过return 向请求中添加数据
// return {
// ...config,
// token: token //添加token
// }
},
(err) => {
//请求错误做些什么
return Promise.reject(err)
}
)
// 响应拦截器 后端给前端参数
request.interceptors.response.use(
(response) => {
//对响应数据做些什么
//取消加载
return response.data
},
(err) => {
//请求失败也要取消加载
alert(err)
return Promise.reject(err) //抛出错误,把错误抛给谁? 谁调用它发请求就给谁
}
)
//导出请求对象
export default request
import {createApp } from 'vue'
import ElementUI from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.use(ElementUI)
app.mount('#app')
import { createRouter, createWebHashHistory } from 'vue-router'
import Main from '../views/3DModelView.vue'
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
redirect: '/index'
},
{
//信息导入界面
path:'/index',
component: Main,
},
]
})
export default router
<template>
<div class="control-box">
<el-switch class="switch-box" v-model="switch_state" inactive-text="表单展示"> </el-switch>
<el-form
v-if="switch_state"
class="form-box"
label-position="left"
:model="form"
:rules="formRule"
ref="form"
label-width="100px"
>
<el-form-item label="发射点坐标" prop="transmitterCoordinates">
<el-row :gutter="20">
<el-col :span="8"
><el-input v-model="form.transmitterCoordinates.x" placeholder="X"></el-input
></el-col>
<el-col :span="8"
><el-input v-model="form.transmitterCoordinates.y" placeholder="Y"></el-input
></el-col>
<el-col :span="8"
><el-input v-model="form.transmitterCoordinates.z" placeholder="Z"></el-input
></el-col>
</el-row>
</el-form-item>
<el-form-item label="接收点坐标" prop="receiverCoordinates">
<el-row :gutter="20">
<el-col :span="8"
><el-input v-model="form.receiverCoordinates.x" placeholder="X"></el-input
></el-col>
<el-col :span="8"
><el-input v-model="form.receiverCoordinates.y" placeholder="Y"></el-input
></el-col>
<el-col :span="8"
><el-input v-model="form.receiverCoordinates.z" placeholder="Z"></el-input
></el-col>
</el-row>
</el-form-item>
<el-form-item label="射线频率" prop="divideN">
<el-input v-model="form.divideN" placeholder="发射频率"></el-input>
</el-form-item>
<el-form-item label="场景文件">
<el-upload
:action="fileUrl"
:before-upload="beforeUpload"
:on-change="handleChange"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
>
<el-button type="primary">选择文件</el-button>
</el-upload>
</el-form-item>
<el-row :gutter="20">
<el-col :span="6"
><el-button class="custom-button" type="primary" @click="showScene('form')"
>展示场景</el-button
></el-col
>
<el-col :span="6"
><el-button class="custom-button" type="primary" @click="showEmitRays('form')"
>发射展示</el-button
></el-col
>
<el-col :span="6"
><el-button class="custom-button" type="primary" @click="clearScene()"
>清空场景</el-button
></el-col
>
<el-col :span="6"
><el-button class="custom-button" type="primary" @click="submitForm('form')"
>生成射线</el-button
></el-col
>
</el-row>
</el-form>
</div>
<div ref="sceneBox" class="container"></div>
</template>
<script>
import { uploadUrl, getAllRays, getAllEmitRays } from '../apis/index'
import {
PerspectiveCamera,
Scene,
WebGLRenderer,
Color,
Vector3,
AxesHelper,
OrthographicCamera,
PCFSoftShadowMap,
AmbientLight,
DirectionalLight,
MeshPhongMaterial,
MeshBasicMaterial,
SphereGeometry,
PlaneGeometry,
BufferGeometry,
LineBasicMaterial,
Line,
Mesh
} from 'three'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { CSM } from 'three/addons/csm/CSM.js'
import { CSMHelper } from 'three/addons/csm/CSMHelper.js'
export default {
data() {
return {
switch_state: true,
form: {
transmitterCoordinates: {
x: '',
y: '',
z: ''
},
receiverCoordinates: {
x: '',
y: '',
z: ''
},
divideN: null,
serverFileUrl: null
},
formRule: {
transmitterCoordinates: [
{
validator: this.coordinatesValidator,
trigger: 'blur'
}
],
receiverCoordinates: [
{
validator: this.coordinatesValidator,
trigger: 'blur'
}
],
divideN: {
validator: (rule, value, callback) => {
if (!value) {
callback(new Error('请输入发射频率'))
return
}
let x = parseInt(value, 10)
if (!Number.isInteger(x)) {
callback(new Error('请输入整数'))
} else if (x <= 0) {
callback(new Error('请输入正整数'))
} else {
callback()
}
},
trigger: 'blur'
}
},
fileUrl: uploadUrl, // 文件上传url
objText: null, // obj模型内容
scene: null,
transmitObj: null,
receiveObj: null,
sceneObj: null,
lines: []
}
},
mounted() {
let renderer, scene, camera, orthoCamera, controls, csm, csmHelper
const params = {
orthographic: false,
fade: false,
shadows: true,
far: 1000,
mode: 'practical',
lightX: -1,
lightY: -1,
lightZ: -1,
margin: 100,
lightFar: 5000,
lightNear: 1,
autoUpdateHelper: true,
updateHelper: function () {
csmHelper.update()
}
}
init(this)
animate()
function updateOrthoCamera() {
const size = controls.target.distanceTo(camera.position)
const aspect = camera.aspect
orthoCamera.left = (size * aspect) / -2
orthoCamera.right = (size * aspect) / 2
orthoCamera.top = size / 2
orthoCamera.bottom = size / -2
orthoCamera.position.copy(camera.position)
orthoCamera.rotation.copy(camera.rotation)
orthoCamera.updateProjectionMatrix()
}
function init(that) {
scene = new Scene()
that.scene = scene
scene.background = new Color('#454e61')
camera = new PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 5000)
camera.position.set(100, 100, 30)
camera.lookAt(0, 0, 0)
camera.up.set(0, 0, 1)
orthoCamera = new OrthographicCamera()
renderer = new WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
renderer.shadowMap.enabled = params.shadows
renderer.shadowMap.type = PCFSoftShadowMap
const axes = new AxesHelper(500)
axes.name = 'AxesHelper'
scene.add(axes)
controls = new OrbitControls(camera, renderer.domElement)
controls.maxPolarAngle = Math.PI / 2
controls.update()
const ambientLight = new AmbientLight(0xffffff, 1.5)
scene.add(ambientLight)
const additionalDirectionalLight = new DirectionalLight(0x000020, 1.5)
additionalDirectionalLight.position
.set(params.lightX, params.lightY, params.lightZ)
.normalize()
.multiplyScalar(-200)
scene.add(additionalDirectionalLight)
csm = new CSM({
maxFar: params.far,
cascades: 4,
mode: params.mode,
parent: scene,
shadowMapSize: 1024,
lightDirection: new Vector3(params.lightX, params.lightY, params.lightZ).normalize(),
camera: camera
})
csmHelper = new CSMHelper(csm)
csmHelper.visible = false
scene.add(csmHelper)
const floorMaterial = new MeshPhongMaterial({ color: '#252a34' })
csm.setupMaterial(floorMaterial)
const floor = new Mesh(new PlaneGeometry(1000, 1000, 8, 8), floorMaterial)
floor.castShadow = true
floor.receiveShadow = true
scene.add(floor)
window.addEventListener('resize', function () {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
updateOrthoCamera()
csm.updateFrustums()
renderer.setSize(window.innerWidth, window.innerHeight)
})
}
function animate() {
requestAnimationFrame(animate)
camera.updateMatrixWorld()
csm.update()
controls.update()
if (params.orthographic) {
updateOrthoCamera()
csm.updateFrustums()
if (params.autoUpdateHelper) {
csmHelper.update()
}
renderer.render(scene, orthoCamera)
} else {
if (params.autoUpdateHelper) {
csmHelper.update()
}
renderer.render(scene, camera)
}
}
},
methods: {
isAxyz(value) {
if (!value.x || !value.y || !value.z) {
return false
}
return true
},
coordinatesValidator(rule, value, callback) {
if (!this.isAxyz(value)) {
callback(new Error('请输入坐标'))
return
}
let x = parseInt(value.x, 10)
let y = parseInt(value.y, 10)
let z = parseInt(value.z, 10)
if (!Number.isInteger(x) || !Number.isInteger(y) || !Number.isInteger(z)) {
callback(new Error('请输入整数'))
} else if (z < 0) {
callback(new Error('请输入正整数Z坐标'))
} else {
callback()
}
},
createSphere(coordinate, c) {
const sphereGeometry = new SphereGeometry(1, 32, 32)
const sphereMaterial = new MeshBasicMaterial({ color: c })
const sphere = new Mesh(sphereGeometry, sphereMaterial)
sphere.position.set(coordinate.x, coordinate.y, coordinate.z)
return sphere
},
updateSphere(coordinate, type) {
let c = 0x000000
let obj = null
switch (type) {
case 'T':
c = 0xff0000
obj = this.createSphere(coordinate, c)
console.log(this.transmitObj)
console.log(!this.transmitObj)
if (this.transmitObj) {
this.scene.remove(this.transmitObj)
}
this.transmitObj = obj
break
case 'R':
c = 0x0000ff
obj = this.createSphere(coordinate, c)
if (this.receiveObj) {
this.scene.remove(this.receiveObj)
}
this.receiveObj = obj
break
default:
console.log('The color is neither red nor blue.')
return
}
this.scene.add(obj)
},
createObjModel(modelText) {
const loader = new OBJLoader()
const object = loader.parse(modelText)
return object
},
showScene(formName) {
this.$refs[formName].validate((valid) => {
if (!valid) {
this.$message.error('发射点和接受点不能为空!')
return
}
this.updateSphere(this.form.transmitterCoordinates, 'T')
this.updateSphere(this.form.receiverCoordinates, 'R')
})
// 创建OBJLoader实例,用于加载.obj文件
if (this.scene == null || this.objText == null) {
this.$message.error('场景不能为空!')
return
}
if (this.sceneObj) {
this.scene.remove(this.sceneObj)
}
this.sceneObj = this.createObjModel(this.objText)
this.scene.add(this.sceneObj)
// 创建OBJ加载器
// const objLoader = new OBJLoader()
// var that = this
// // 加载.obj模型
// objLoader.load('src\\1707901585449-cuboids.obj', function (obj) {
// that.scene.add(obj)
// })
// console.log(this.form)
// this.updateSphere(this.form.transmitterCoordinates, 'T')
// this.updateSphere(this.form.receiverCoordinates, 'R')
},
clearScene() {
if (this.transmitObj) {
this.scene.remove(this.transmitObj)
}
if (this.receiveObj) {
this.scene.remove(this.receiveObj)
}
if (this.sceneObj) {
this.scene.remove(this.sceneObj)
}
if (this.lines && this.lines.length > 0) {
for (let e of this.lines) {
this.scene.remove(e)
}
}
},
createLine(start, end, behavior) {
let cv = 0xff0000
switch(behavior) {
case 'REFLECT': {
cv = 0x0000ff
break
}
case 'DIFFRACT': {
cv = 0x00ff00
break
}
case 'TX': {
cv = 0xff0000
break
}
case 'ELECTRIC': {
cv = 0xffff00
break
}
default : {
cv = 0xff0000
}
}
const startPoint = new Vector3(start.x, start.y, start.z)
const endPoint = new Vector3(end.x, end.y, end.z)
const lineGeometry = new BufferGeometry().setFromPoints([startPoint, endPoint])
const lineMaterial = new LineBasicMaterial({ color: cv })
// 创建线段对象
const coloredLine = new Line(lineGeometry, lineMaterial)
return coloredLine
},
showEmitRays(formName) {
this.$refs[formName].validate((valid) => {
if (valid && this.form.serverFileUrl) {
console.log(this.form)
getAllEmitRays(this.form).then((res) => {
for (const e of res.data.lines) {
console.log(e)
let l = this.createLine(e.from, e.to, e.behavior)
this.lines.push(l)
this.scene.add(l)
}
})
} else {
console.log('error submit!!')
return false
}
})
},
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid && this.form.serverFileUrl) {
getAllRays(this.form).then((res) => {
for (const e of res.data.lines) {
console.log(e)
let l = this.createLine(e.from, e.to, e.behavior)
this.lines.push(l)
this.scene.add(l)
}
})
} else {
console.log('error submit!!')
return false
}
})
// var mockdata = {
// transmitterCoordinates: {
// x: 0,
// y: 0,
// z: 30
// },
// receiverCoordinates: {
// x: 80,
// y: -10,
// z: 0
// },
// serverFileUrl: 'C:\\Users\\wangz\\Desktop\\obj-files\\1707919670131-cuboids.obj'
// }
// this.form = mockdata
// getAllRays(mockdata).then((res) => {
// console.log(res.data.lines)
// for (const e of res.data.lines) {
// this.createLine(e.from, e.to)
// }
// })
},
beforeUpload(file) {
const isLt2M = file.size / 1024 / 1024 < 10 // 判断文件大小是否小于2MB
if (!isLt2M) {
this.$message.error('上传文件大小不能超过 10MB!')
}
return isLt2M
},
handleChange(file, fileLists) {
const reader = new FileReader()
const that = this
reader.onload = function (e) {
const text = e.target.result
that.objText = text
}
reader.readAsText(file.raw)
},
handleUploadSuccess(response, file) {
this.form.serverFileUrl = response.data
this.$message.info('上传成功!')
},
handleUploadError(error, file) {
console.log(error)
this.$message.error('上传失败,请重新上传')
}
}
}
</script>
<style>
.control-box {
position: absolute;
top: 10px;
left: 15px;
width: 450px;
z-index: 2;
}
.switch-box {
padding: 0px 0px 0px 15px;
}
.form-box {
background-color: rgba(169, 169, 169, 0.25);
padding: 15px;
}
.custom-button {
width: 90px;
height: 35px;
}
.container {
width: 100%;
height: 100%;
border: 2px solid aquamarine;
z-index: 1;
}
.el-form-item__label {
color: cornflowerblue; /* 这里替换为你想要的颜色值 */
}
.el-upload-list__item-file-name {
color: red;
}
</style>
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import {targetUrl} from './src/apis/index'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'/api': {
target: targetUrl, //目标url
changeOrigin: true, //支持跨域
rewrite: (path) => path.replace(/^\/api/, ""),
//重写路径,替换/api
}
}
}
})
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment