Files
water-management-system/frontend/node_modules/@cesium/engine/Source/Scene/buildVoxelCustomShader.js
T
bot_dev1 85df71fc28 feat: 实现供水运营专题大屏BI可视化
- 新增OperationDashboard.vue全屏大屏组件
- 添加6个核心KPI指标卡片: 进水总量/出水总量/产销差率/营收额/平均水质/报警次数
- 实现6个ECharts图表: 供水趋势/水质分布/报警统计/管网空间/设备状态/营收分析
- 创建静态HTML版本operation-dashboard.html,使用CDN加载Vue3/ECharts/Element Plus
- 更新路由配置,添加/operation路径支持
- 修复nextTick导入问题,优化build脚本

🚧 开发者: bot_dev1
📝 任务: #38 [BI] 运营仪表盘 + 供水专题大屏
2026-06-15 09:06:11 +08:00

200 lines
6.6 KiB
JavaScript

// @ts-check
import Color from "../Core/Color.js";
import createColorRamp from "../Core/createColorRamp.js";
import CustomShader from "./Model/CustomShader.js";
import defined from "../Core/defined.js";
import MetadataComponentType from "./MetadataComponentType.js";
import MetadataType from "./MetadataType.js";
import TextureUniform from "./Model/TextureUniform.js";
import UniformType from "./Model/UniformType.js";
/** @import VoxelProvider from "./VoxelProvider.js"; */
/**
* Builds a custom shader for a voxel primitive based on information
* from the VoxelProvider about the metadata properties.
*
* Supports scalar float properties, or vec4 properties with float components.
*
* @function
*
* @param {VoxelProvider} provider The VoxelProvider for the primitive.
* @returns {CustomShader|undefined} A custom shader for the primitive, or undefined if a shader could not be constructed.
*
* @private
*/
function buildVoxelCustomShader(provider) {
const { names, types, componentTypes, minimumValues, maximumValues } =
provider;
// Loop over metadata properties and build a shader for the first property
// that has a supported type and component type.
for (let i = 0; i < names.length; i++) {
if (!rangeIsValid(minimumValues?.[i], maximumValues?.[i])) {
continue;
}
const shader = constructMetadataShader(
names[i],
types[i],
componentTypes[i],
minimumValues[i],
maximumValues[i],
);
if (defined(shader)) {
return shader;
}
}
}
/**
* Check if a range of values is valid for constructing a meaningful shader.
*
* @param {number[]} minimumValue The minimum value of the range.
* @param {number[]} maximumValue The maximum value of the range.
* @returns {boolean} True if the range is valid, false otherwise.
*
* @private
*/
function rangeIsValid(minimumValue, maximumValue) {
if (!Array.isArray(minimumValue) || !Array.isArray(maximumValue)) {
return false;
}
const minimumComponent = Math.min(...minimumValue);
const maximumComponent = Math.max(...maximumValue);
return minimumComponent < maximumComponent;
}
/**
* Construct a metadata shader for a single metadata property.
*
* @param {string} name The name of the metadata property.
* @param {MetadataType} type The type of the metadata property.
* @param {MetadataComponentType} componentType The component type of the metadata property.
* @param {number[]} minimumValue The minimum value of the metadata property.
* @param {number[]} maximumValue The maximum value of the metadata property.
* @returns {CustomShader|undefined} A custom shader for the metadata property, or undefined if a shader could not be constructed.
*
* @private
*/
function constructMetadataShader(
name,
type,
componentType,
minimumValue,
maximumValue,
) {
if (
type === MetadataType.VEC4 &&
componentType === MetadataComponentType.FLOAT32
) {
return buildColorShader(name, minimumValue, maximumValue);
} else if (
type === MetadataType.SCALAR &&
componentType === MetadataComponentType.FLOAT32
) {
return buildColorMapShader(name, minimumValue, maximumValue);
}
}
/**
* Build a color shader for a metadata property of type VEC4 and component type FLOAT32.
*
* @param {string} name The name of the metadata property.
* @param {number[]} minimumValue The minimum value of the metadata property.
* @param {number[]} maximumValue The maximum value of the metadata property.
* @returns {CustomShader|undefined} A custom shader for the metadata property, or undefined if a shader could not be constructed.
*
* @private
*/
function buildColorShader(name, minimumValue, maximumValue) {
// Check if the values are in a range that would be meaningful to interpret as colors.
const minComponent = Math.min(...minimumValue);
const maxComponent = Math.max(...maximumValue);
if (minComponent < 0 || maxComponent > 1) {
// The data is out of the range expected for colors, so a color shader will be meaningless.
return;
}
return new CustomShader({
fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
{
material.diffuse = fsInput.metadata.${name}.rgb;
material.alpha = fsInput.metadata.${name}.a;
}`,
});
}
/**
* Build a color map shader for a metadata property of type SCALAR and component type FLOAT32.
*
* @param {string} name The name of the metadata property.
* @param {number[]} minimumValue The minimum value of the metadata property.
* @param {number[]} maximumValue The maximum value of the metadata property.
* @returns {CustomShader} A custom shader for the metadata property.
*
* @private
*/
function buildColorMapShader(name, minimumValue, maximumValue) {
// Perceptually uniform color map similar to the "viridis" color map used in scientific visualization.
const colorMapWidth = 128;
const colorMap = createColorRamp(
[
new Color(0.267, 0.004, 0.329),
new Color(0.282, 0.14, 0.457),
new Color(0.254, 0.265, 0.53),
new Color(0.207, 0.372, 0.553),
new Color(0.164, 0.471, 0.558),
new Color(0.128, 0.567, 0.551),
new Color(0.135, 0.659, 0.518),
new Color(0.194, 0.741, 0.443),
new Color(0.282, 0.819, 0.369),
new Color(0.396, 0.898, 0.301),
new Color(0.538, 0.965, 0.236),
new Color(0.741, 0.998, 0.149),
new Color(0.993, 1.0, 0.144),
],
colorMapWidth,
);
const textureUniform = new TextureUniform({
typedArray: colorMap,
width: colorMapWidth,
height: 1,
});
return new CustomShader({
uniforms: {
u_colorMap: {
type: UniformType.SAMPLER_2D,
value: textureUniform,
},
// The starting values are also available in the shader from the metadata statistics.
// But by using uniforms, we enable later dynamic changes to the range.
u_minimumValue: {
type: UniformType.FLOAT,
value: minimumValue[0],
},
u_maximumValue: {
type: UniformType.FLOAT,
value: maximumValue[0],
},
},
fragmentShaderText: `void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
{
float value = fsInput.metadata.${name};
vec3 voxelNormal = fsInput.attributes.normalEC;
float diffuse = max(0.0, dot(voxelNormal, czm_lightDirectionEC));
float lighting = 0.5 + 0.5 * diffuse;
if (value >= u_minimumValue && value <= u_maximumValue) {
float lerp = (value - u_minimumValue) / (u_maximumValue - u_minimumValue);
material.diffuse = texture(u_colorMap, vec2(lerp, 0.5)).rgb * lighting;
material.alpha = 1.0;
}
}`,
});
}
export default buildVoxelCustomShader;