123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- <template>
- <SubDialog
- :modelValue="modelValue"
- @update:modelValue="$emit('update:modelValue', $event)"
- title="云图"
- width="500"
- height="600px"
- contentHeight="450px"
- @confirm="handleConfirm"
- @cancel="handleCancel"
- >
- <div>
- <el-collapse v-model="activeNames">
- <el-collapse-item name="1">
- <template #title>
- <span class="collapse-title">标量</span>
- </template>
- <el-form label-position="left">
- <el-form-item label="名称:" :label-width="formLabelWidth1">
- <el-input v-model="cloudConfig.scalarname" disabled></el-input>
- </el-form-item>
- <el-form-item label="类型:" :label-width="formLabelWidth1">
- <el-input v-model="cloudConfig.type" disabled></el-input>
- </el-form-item>
- <el-form-item label="标量名:" :label-width="formLabelWidth1">
- <el-select
- v-model="cloudConfig.scalarname"
- @change="handleScalarChange"
- >
- <el-option
- v-for="item in availableScalars"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="" :label-width="formLabelWidth1">
- <el-row>
- <el-col :span="12">
- <el-checkbox
- label="极值"
- v-model="cloudConfig.jzcheck"
- ></el-checkbox>
- </el-col>
- <el-col :span="12">
- <el-checkbox
- label="单元值离散到点"
- v-model="cloudConfig.dycheck"
- ></el-checkbox>
- </el-col>
- </el-row>
- </el-form-item>
- </el-form>
- </el-collapse-item>
- <el-collapse-item name="2">
- <template #title>
- <span class="collapse-title">云图间隔</span>
- </template>
- <el-form label-position="left">
- <el-form-item label="名称:" :label-width="formLabelWidth1">
- <el-input-number
- v-model="cloudConfig.intervalCount"
- :min="2"
- :max="20"
- @change="updateColorBarIntervals"
- />
- </el-form-item>
- <el-form-item label=" " :label-width="formLabelWidth1">
- <el-row>
- <el-col :span="24">
- <el-checkbox
- label="平滑云图"
- v-model="cloudConfig.smooth"
- ></el-checkbox>
- </el-col>
- </el-row>
- </el-form-item>
- </el-form>
- </el-collapse-item>
- <el-collapse-item name="3">
- <template #title>
- <span class="collapse-title">数据范围</span>
- </template>
- <el-form label-position="left">
- <el-form-item label="数据范围类型:" :label-width="formLabelWidth1">
- <el-select v-model="cloudConfig.dataAreaType">
- <el-option
- v-for="item in dataAreaTypeoptions"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="最大值:" :label-width="formLabelWidth1">
- <el-input v-model="cloudConfig.max"></el-input>
- </el-form-item>
- <el-form-item label="最小值:" :label-width="formLabelWidth1">
- <el-input v-model="cloudConfig.min"></el-input>
- </el-form-item>
- </el-form>
- </el-collapse-item>
- <el-collapse-item name="4">
- <template #title>
- <span class="collapse-title">色卡颜色范围</span>
- </template>
- <el-form label-position="left">
- <el-form-item label="最大值:" :label-width="formLabelWidth1">
- <el-row style="width: 100%">
- <el-col :span="22">
- <el-input v-model="cloudConfig.maxcv"></el-input>
- </el-col>
- <el-col :span="2"
- ><el-color-picker v-model="color1" @change="updateMaxValue" />
- </el-col>
- </el-row>
- </el-form-item>
- <el-form-item label="最小值:" :label-width="formLabelWidth1">
- <el-row style="width: 100%">
- <el-col :span="22">
- <el-input v-model="cloudConfig.mincv"></el-input
- ></el-col>
- <el-col :span="2"
- ><el-color-picker v-model="color2" @change="updateMinValue"
- /></el-col>
- </el-row>
- </el-form-item>
- </el-form>
- </el-collapse-item>
- </el-collapse>
- </div>
- </SubDialog>
- </template>
- <script setup>
- import { ref, computed, watch, onMounted } from "vue"
- import SubDialog from "./SubDialog.vue"
- import * as THREE from "three"
- import { debounce } from "lodash-es"
- const props = defineProps({
- modelValue: Boolean,
- initialData: {
- type: Object,
- default: () => ({})
- },
- threeSceneRef: Object,
- pltData: Object
- })
- const emit = defineEmits(["update:modelValue", "confirm", "cancel"])
- // 表单标签宽度
- const formLabelWidth1 = "120px"
- // 折叠面板当前激活项
- const activeNames = ref(["1", "2", "3", "4"])
- // 颜色选择器
- const color1 = ref("#ff0000")
- const color2 = ref("#0000ff")
- // 可用标量选项
- const availableScalars = ref([
- { label: "CoefPressure", value: "CoefPressure" },
- { label: "Mach", value: "Mach" },
- { label: "VelocityX", value: "VelocityX" },
- { label: "VelocityY", value: "VelocityY" },
- { label: "VelocityZ", value: "VelocityZ" }
- ])
- // 数据范围类型选项
- const dataAreaTypeoptions = ref([
- { label: "当前时间步", value: "当前时间步" },
- { label: "所有时间步", value: "所有时间步" },
- { label: "固定值", value: "固定值" }
- ])
- // 表单数据
- const cloudConfig = ref({
- name: "",
- type: "point scalar",
- scalarname: "CoefPressure",
- jzcheck: false,
- dycheck: false,
- intervalCount: 3, // 默认3等分
- smooth: true,
- dataAreaType: "当前时间步",
- max: "0.00",
- min: "0.00",
- maxcv: "(1.00, 0.00, 0.00)",
- mincv: "(0.00, 0.00, 1.00)"
- })
- // 计算当前可用的标量字段
- const availableScalarFields = computed(() => {
- if (!props.pltData?.zones?.length) return []
- // 从第一个zone中获取所有可用的变量
- const firstZone = props.pltData.zones[0]
- return Object.keys(firstZone.variables || {})
- })
- // 过滤可用的标量选项
- const filteredScalarOptions = computed(() => {
- return availableScalars.value.filter((item) =>
- availableScalarFields.value.includes(item.value)
- )
- })
- // 初始化时设置默认标量
- onMounted(() => {
- if (filteredScalarOptions.value.length > 0) {
- cloudConfig.value.scalarname = filteredScalarOptions.value[0].value
- updateDataRange()
- }
- })
- // 处理标量变化
- const handleScalarChange = (selectedScalar) => {
- updateDataRange()
- updateCloudMap()
- }
- // 更新数据范围
- const updateDataRange = () => {
- if (!props.pltData?.zones || !cloudConfig.value.scalarname) return
- let min = Infinity
- let max = -Infinity
- // 遍历所有zone找到最大最小值
- props.pltData.zones.forEach((zone) => {
- const scalarData = zone.variables?.[cloudConfig.value.scalarname]
- if (!scalarData) return
- const zoneMin = Math.min(...scalarData)
- const zoneMax = Math.max(...scalarData)
- min = Math.min(min, zoneMin)
- max = Math.max(max, zoneMax)
- })
- // 更新表单中的范围值
- cloudConfig.value.min = min.toFixed(4)
- cloudConfig.value.max = max.toFixed(4)
- }
- // 更新云图显示
- const updateCloudMap = debounce(() => {
- if (!props.threeSceneRef || !cloudConfig.value.scalarname) return
- // 先更新平滑设置
- props.threeSceneRef.setSmoothShading(cloudConfig.value.smooth)
- // 再更新颜色映射
- props.threeSceneRef.updateColorMapping(cloudConfig.value.scalarname, {
- colorMap: "rainbow",
- min: parseFloat(cloudConfig.value.min),
- max: parseFloat(cloudConfig.value.max),
- showExtremes: cloudConfig.value.jzcheck,
- smooth: cloudConfig.value.smooth // 传递平滑参数
- })
- }, 300)
- // 更新最大值颜色
- const updateMaxValue = () => {
- const rgb = hexToRgb(color1.value)
- cloudConfig.value.maxcv = `(${rgb.r}, ${rgb.g}, ${rgb.b})`
- updateColorBarIntervals()
- }
- // 更新最小值颜色
- const updateMinValue = () => {
- const rgb = hexToRgb(color2.value)
- cloudConfig.value.mincv = `(${rgb.r}, ${rgb.g}, ${rgb.b})`
- updateColorBarIntervals()
- }
- // 更新颜色条间隔
- const updateColorBarIntervals = () => {
- if (!props.threeSceneRef) return;
- props.threeSceneRef.updateColorBar({
- intervals: calculateIntervals(),
- colorRange: {
- min: parseFloat(cloudConfig.value.min),
- max: parseFloat(cloudConfig.value.max)
- },
- variable: cloudConfig.value.scalarname,
- title: getScalarLabel(cloudConfig.value.scalarname),
- gradientStart: parseColorString(cloudConfig.value.mincv), // 渐变起点颜色
- gradientEnd: parseColorString(cloudConfig.value.maxcv) // 渐变终点颜色
- });
- };
- // 添加辅助函数获取变量标签
- const getScalarLabel = (value) => {
- return (
- filteredScalarOptions.value.find((item) => item.value === value)?.label ||
- value
- )
- }
- // 解析颜色字符串 "(r, g, b)" 为数组 [r, g, b]
- const parseColorString = (str) => {
- const matches = str.match(/\(([^)]+)\)/);
- if (!matches) return [0, 0, 1];
- return matches[1].split(',').map(v => parseFloat(v.trim()));
- };
- // 计算等分点
- const calculateIntervals = () => {
- const count = cloudConfig.value.intervalCount
- const min = parseFloat(cloudConfig.value.min)
- const max = parseFloat(cloudConfig.value.max)
- const intervals = []
- const step = (max - min) / (count - 1)
- for (let i = 0; i < count; i++) {
- intervals.push((min + i * step).toFixed(2))
- }
- return intervals
- }
- // 16进制颜色转RGB对象
- const hexToRgb = (hex) => {
- const r = parseInt(hex.slice(1, 3), 16) / 255
- const g = parseInt(hex.slice(3, 5), 16) / 255
- const b = parseInt(hex.slice(5, 7), 16) / 255
- return { r, g, b }
- }
- // 确认操作
- const handleConfirm = () => {
- // 先更新颜色条
- updateColorBarIntervals()
- // 再提交配置
- emit("confirm", {
- ...cloudConfig.value,
- gradientStart: parseColorString(cloudConfig.value.mincv),
- gradientEnd: parseColorString(cloudConfig.value.maxcv)
- });
- emit("update:modelValue", false)
- }
- // 取消操作
- const handleCancel = () => {
- emit("cancel")
- emit("update:modelValue", false)
- }
- // 监听pltData变化
- watch(
- () => props.pltData,
- (newData) => {
- if (newData) {
- updateDataRange()
- updateCloudMap()
- }
- },
- { deep: true }
- )
- </script>
- <style scoped>
- /* 原有样式保持不变 */
- .collapse-title {
- font-weight: bold;
- font-size: 14px;
- }
- .el-collapse-item__content {
- padding-bottom: 10px;
- }
- .el-form-item {
- margin-bottom: 16px;
- }
- </style>
- <style>
- .extreme-label {
- transform: translate(-50%, 0);
- white-space: nowrap;
- pointer-events: none;
- }
- </style>
|