diff --git a/example/CHANGELOG.md b/example/CHANGELOG.md index 1de6ac0..47f53bf 100644 --- a/example/CHANGELOG.md +++ b/example/CHANGELOG.md @@ -1,5 +1,28 @@ # example +## 0.7.0 + +### Minor Changes + +- feat(wind-layer): refactor lineWidth to support min-max range + + BREAKING CHANGE: lineWidth option now requires min-max range object instead of single number + + - Change lineWidth type from number to { min: number, max: number } + - Set default lineWidth range to { min: 1, max: 2 } + - Update shader to support dynamic line width based on particle speed + - Update types and documentation + - Update example to demonstrate new lineWidth configuration + - Add lineWidth range control in ControlPanel component + + This change allows for more dynamic and visually appealing particle trails by varying + the line width based on wind speed, similar to how line length works. + +### Patch Changes + +- Updated dependencies + - cesium-wind-layer@0.10.0 + ## 0.6.0 ### Minor Changes diff --git a/example/package.json b/example/package.json index 7814c6d..a7f104d 100644 --- a/example/package.json +++ b/example/package.json @@ -1,7 +1,7 @@ { "name": "example", "private": true, - "version": "0.6.0", + "version": "0.7.0", "type": "module", "scripts": { "dev": "vite", diff --git a/example/src/components/ColorTableInput.tsx b/example/src/components/ColorTableInput.tsx index c5b90b9..217c4ca 100644 --- a/example/src/components/ColorTableInput.tsx +++ b/example/src/components/ColorTableInput.tsx @@ -15,6 +15,23 @@ import { interpolateOranges, interpolateReds, interpolatePurples, + interpolateBuGn, + interpolateBuPu, + interpolateCividis, + interpolateCubehelixDefault, + interpolateGnBu, + interpolateGreys, + interpolateOrRd, + interpolatePuBu, + interpolatePuBuGn, + interpolatePuRd, + interpolateRdPu, + interpolateSinebow, + interpolateTurbo, + interpolateYlGn, + interpolateYlGnBu, + interpolateYlOrBr, + interpolateYlOrRd, } from 'd3-scale-chromatic'; import styled from 'styled-components'; @@ -114,23 +131,40 @@ const generateSingleColorTable = (color: string): string[] => { export const colorSchemes = [ { label: 'Single Color', value: 'single', interpolator: () => '#FFFFFF', isSingleColor: true }, - { label: 'Rainbow', value: 'rainbow', interpolator: interpolateRainbow, reverse: true }, + { label: 'Rainbow', value: 'rainbow', interpolator: interpolateRainbow }, + { label: 'Turbo', value: 'turbo', interpolator: interpolateTurbo }, { label: 'Viridis', value: 'viridis', interpolator: interpolateViridis }, { label: 'Cool', value: 'cool', interpolator: interpolateCool }, { label: 'Warm', value: 'warm', interpolator: interpolateWarm }, { label: 'Inferno', value: 'inferno', interpolator: interpolateInferno }, { label: 'Magma', value: 'magma', interpolator: interpolateMagma }, { label: 'Plasma', value: 'plasma', interpolator: interpolatePlasma }, + { label: 'Cividis', value: 'cividis', interpolator: interpolateCividis }, + { label: 'Cubehelix', value: 'cubehelix', interpolator: interpolateCubehelixDefault }, + { label: 'Sinebow', value: 'sinebow', interpolator: interpolateSinebow }, { label: 'Blues', value: 'blues', interpolator: interpolateBlues }, { label: 'Greens', value: 'greens', interpolator: interpolateGreens }, + { label: 'Greys', value: 'greys', interpolator: interpolateGreys }, { label: 'Oranges', value: 'oranges', interpolator: interpolateOranges }, - { label: 'Reds', value: 'reds', interpolator: interpolateReds }, { label: 'Purples', value: 'purples', interpolator: interpolatePurples }, + { label: 'Reds', value: 'reds', interpolator: interpolateReds }, + { label: 'BuGn', value: 'bugn', interpolator: interpolateBuGn }, + { label: 'BuPu', value: 'bupu', interpolator: interpolateBuPu }, + { label: 'GnBu', value: 'gnbu', interpolator: interpolateGnBu }, + { label: 'OrRd', value: 'orrd', interpolator: interpolateOrRd }, + { label: 'PuBuGn', value: 'pubugn', interpolator: interpolatePuBuGn }, + { label: 'PuBu', value: 'pubu', interpolator: interpolatePuBu }, + { label: 'PuRd', value: 'purd', interpolator: interpolatePuRd }, + { label: 'RdPu', value: 'rdpu', interpolator: interpolateRdPu }, + { label: 'YlGnBu', value: 'ylgnbu', interpolator: interpolateYlGnBu }, + { label: 'YlGn', value: 'ylgn', interpolator: interpolateYlGn }, + { label: 'YlOrBr', value: 'ylorbr', interpolator: interpolateYlOrBr }, + { label: 'YlOrRd', value: 'ylorrd', interpolator: interpolateYlOrRd } ].map((item) => ({ ...item, colors: item.isSingleColor ? generateSingleColorTable(item.interpolator()) - : generateColorTable(item.interpolator, item.reverse), + : generateColorTable(item.interpolator), })); const ColorTableInput: React.FC = ({ diff --git a/example/src/components/ControlPanel.tsx b/example/src/components/ControlPanel.tsx index b164eee..2a9c2ad 100644 --- a/example/src/components/ControlPanel.tsx +++ b/example/src/components/ControlPanel.tsx @@ -316,13 +316,53 @@ export const ControlPanel: React.FC = ({ - + +
+
+ + Min Width + + } + style={{ marginBottom: 0 }} + > + + +
+
+ + Max Width + + } + style={{ marginBottom: 0 }} + > + + +
+
+
= { + ...WindLayer.defaultOptions, particlesTextureSize: 200, - dropRate: 0.003, - particleHeight: 1000, - dropRateBump: 0.01, - lineWidth: 5.0, - lineLength: { min: 20, max: 100 }, - colors: colorSchemes[3].colors.reverse(), + colors: colorSchemes.find(item => item.value === 'cool')?.colors.reverse(), flipY: true, useViewerBounds: true, dynamic: true, - // Remove domain and speedFactor from here since they will be set dynamically }; export function Earth() { @@ -124,7 +121,7 @@ export function Earth() { viewerRef.current.scene.globe.depthTestAgainstTerrain = true; // Optional: Add exaggeration to make terrain features more visible - viewerRef.current.scene.verticalExaggeration = 2; + // viewerRef.current.scene.verticalExaggeration = 2; // viewerRef.current.sceneModePicker.viewModel.duration = 0; const initWindLayer = async () => { diff --git a/packages/cesium-wind-layer/CHANGELOG.md b/packages/cesium-wind-layer/CHANGELOG.md index 846d5e7..bda5411 100644 --- a/packages/cesium-wind-layer/CHANGELOG.md +++ b/packages/cesium-wind-layer/CHANGELOG.md @@ -1,5 +1,23 @@ # cesium-wind-layer +## 0.10.0 + +### Minor Changes + +- feat(wind-layer): refactor lineWidth to support min-max range + + BREAKING CHANGE: lineWidth option now requires min-max range object instead of single number + + - Change lineWidth type from number to { min: number, max: number } + - Set default lineWidth range to { min: 1, max: 2 } + - Update shader to support dynamic line width based on particle speed + - Update types and documentation + - Update example to demonstrate new lineWidth configuration + - Add lineWidth range control in ControlPanel component + + This change allows for more dynamic and visually appealing particle trails by varying + the line width based on wind speed, similar to how line length works. + ## 0.9.0 ### Minor Changes diff --git a/packages/cesium-wind-layer/package.json b/packages/cesium-wind-layer/package.json index 9b9b431..e41c6ee 100644 --- a/packages/cesium-wind-layer/package.json +++ b/packages/cesium-wind-layer/package.json @@ -1,6 +1,6 @@ { "name": "cesium-wind-layer", - "version": "0.9.0", + "version": "0.10.0", "publishConfig": { "access": "public" }, diff --git a/packages/cesium-wind-layer/readme.md b/packages/cesium-wind-layer/readme.md index 880a4c5..9cbaa16 100644 --- a/packages/cesium-wind-layer/readme.md +++ b/packages/cesium-wind-layer/readme.md @@ -67,17 +67,18 @@ const windData = { // Create wind layer with options const windLayer = new WindLayer(viewer, windData, { - particlesTextureSize: 100, // Size of the particle texture. Determines the maximum number of particles (size squared). - particleHeight: 1000, // Height of particles above ground - lineWidth: 10.0, // Width of particle trails - speedFactor: 1.0, // Speed multiplier - dropRate: 0.003, // Rate at which particles are dropped - dropRateBump: 0.001, // Additional drop rate for slow particles - colors: ['white'], // Colors for particles - flipY: false, // Flip Y coordinates if needed - domain: undefined, // Optional: domain for speed - displayRange: undefined, // Optional: display range for speed - dynamic: true, // Whether to enable dynamic particle animation + particlesTextureSize: 100, // Size of the particle texture. Determines the maximum number of particles (size squared). + particleHeight: 1000, // Height of particles above ground + lineWidth: { min: 1, max: 2 }, // Width of particle trails + lineLength: { min: 20, max: 100 }, // Length range of particle trails + speedFactor: 1.0, // Speed multiplier + dropRate: 0.003, // Rate at which particles are dropped + dropRateBump: 0.001, // Additional drop rate for slow particles + colors: ['white'], // Colors for particles + flipY: false, // Flip Y coordinates if needed + domain: undefined, // Optional: domain for speed + displayRange: undefined, // Optional: display range for speed + dynamic: true, // Whether to enable dynamic particle animation }); ``` @@ -91,25 +92,25 @@ Main class for wind visualization. ```typescript interface WindLayerOptions { - particlesTextureSize: number; // Size of the particle texture. Determines the maximum number of particles (size squared). Default is 100. - particleHeight: number; // Height of particles above the ground in meters. Default is 0. - lineWidth: number; // Width of particle trails in pixels. Default is 5.0. - lineLength: { min: number; max: number }; // Length range of particle trails. Default is { min: 20, max: 100 }. - speedFactor: number; // Factor to adjust the speed of particles. Default is 1.0. - dropRate: number; // Rate at which particles are dropped (reset). Default is 0.003. - dropRateBump: number; // Additional drop rate for slow-moving particles. Default is 0.001. - colors: string[]; // Array of colors for particles. Can be used to create color gradients. Default is ['white']. - flipY: boolean; // Whether to flip the Y-axis of the wind data. Default is false. - useViewerBounds: boolean; // Whether to use the viewer bounds to generate particles. Default is false. - domain?: { // Controls the speed rendering range. Default is undefined. - min?: number; // Minimum speed value for rendering - max?: number; // Maximum speed value for rendering + particlesTextureSize: number; // Size of the particle texture. Determines the maximum number of particles (size squared). Default is 100. + particleHeight: number; // Height of particles above the ground in meters. Default is 0. + lineWidth: { min: number; max: number }; // Width range of particle trails in pixels. Default is { min: 1, max: 2 }. + lineLength: { min: number; max: number }; // Length range of particle trails. Default is { min: 20, max: 100 }. + speedFactor: number; // Factor to adjust the speed of particles. Default is 1.0. + dropRate: number; // Rate at which particles are dropped (reset). Default is 0.003. + dropRateBump: number; // Additional drop rate for slow-moving particles. Default is 0.001. + colors: string[]; // Array of colors for particles. Can be used to create color gradients. Default is ['white']. + flipY: boolean; // Whether to flip the Y-axis of the wind data. Default is false. + useViewerBounds: boolean; // Whether to use the viewer bounds to generate particles. Default is false. + domain?: { // Controls the speed rendering range. Default is undefined. + min?: number; // Minimum speed value for rendering + max?: number; // Maximum speed value for rendering }; - displayRange?: { // Controls the speed display range for visualization. Default is undefined. - min?: number; // Minimum speed value for display - max?: number; // Maximum speed value for display + displayRange?: { // Controls the speed display range for visualization. Default is undefined. + min?: number; // Minimum speed value for display + max?: number; // Maximum speed value for display }; - dynamic: boolean; // Whether to enable dynamic particle animation. Default is true. + dynamic: boolean; // Whether to enable dynamic particle animation. Default is true. } ``` diff --git a/packages/cesium-wind-layer/readme.zh-CN.md b/packages/cesium-wind-layer/readme.zh-CN.md index e416c02..4c117d6 100644 --- a/packages/cesium-wind-layer/readme.zh-CN.md +++ b/packages/cesium-wind-layer/readme.zh-CN.md @@ -67,17 +67,18 @@ const windData = { // 使用配置创建风场图层 const windLayer = new WindLayer(viewer, windData, { - particlesTextureSize: 100, // 粒子系统的纹理大小 - particleHeight: 1000, // 粒子距地面高度 - lineWidth: 10.0, // 粒子轨迹宽度 - speedFactor: 1.0, // 速度倍数 - dropRate: 0.003, // 粒子消失率 - dropRateBump: 0.001, // 慢速粒子的额外消失率 - colors: ['white'], // 粒子颜色 - flipY: false, // 是否翻转 Y 坐标 - domain: undefined, // 速度渲染范围 - displayRange: undefined, // 速度显示范围 - dynamic: true // 是否启用动态粒子动画 + particlesTextureSize: 100, // 粒子系统的纹理大小 + particleHeight: 1000, // 粒子距地面高度 + lineWidth: { min: 1, max: 2 }, // 粒子轨迹宽度范围 + lineLength: { min: 20, max: 100 }, // 粒子轨迹长度范围 + speedFactor: 1.0, // 速度倍数 + dropRate: 0.003, // 粒子消失率 + dropRateBump: 0.001, // 慢速粒子的额外消失率 + colors: ['white'], // 粒子颜色 + flipY: false, // 是否翻转 Y 坐标 + domain: undefined, // 速度渲染范围 + displayRange: undefined, // 速度显示范围 + dynamic: true // 是否启用动态粒子动画 }); ``` @@ -91,25 +92,25 @@ const windLayer = new WindLayer(viewer, windData, { ```typescript interface WindLayerOptions { - particlesTextureSize: number; // 粒子纹理大小,决定粒子最大数量(size * size)(默认:100) - particleHeight: number; // 粒子距地面高度(默认:0) - lineWidth: number; // 粒子线宽(默认:5.0) - lineLength: { min: number; max: number }; // 粒子轨迹长度范围(默认:{ min: 20, max: 100 }) - speedFactor: number; // 速度倍数(默认:1.0) - dropRate: number; // 粒子消失率(默认:0.003) - dropRateBump: number; // 额外消失率(默认:0.01) - colors: string[]; // 粒子颜色数组(默认:['white']) - flipY: boolean; // 是否翻转 Y 坐标(默认:false) - useViewerBounds: boolean; // 是否使用视域范围生成粒子(默认:false) - domain?: { // 速度渲染范围(默认:undefined) - min?: number; // 最小速度值 - max?: number; // 最大速度值 + particlesTextureSize: number; // 粒子纹理大小,决定粒子最大数量(size * size)(默认:100) + particleHeight: number; // 粒子距地面高度(默认:0) + lineWidth: { min: number; max: number }; // 粒子轨迹宽度范围(默认:{ min: 1, max: 5 }) + lineLength: { min: number; max: number }; // 粒子轨迹长度范围(默认:{ min: 20, max: 100 }) + speedFactor: number; // 速度倍数(默认:1.0) + dropRate: number; // 粒子消失率(默认:0.003) + dropRateBump: number; // 额外消失率(默认:0.01) + colors: string[]; // 粒子颜色数组(默认:['white']) + flipY: boolean; // 是否翻转 Y 坐标(默认:false) + useViewerBounds: boolean; // 是否使用视域范围生成粒子(默认:false) + domain?: { // 速度渲染范围(默认:undefined) + min?: number; // 最小速度值 + max?: number; // 最大速度值 }; - displayRange?: { // 速度显示范围(默认:undefined) - min?: number; // 最小速度值 - max?: number; // 最大速度值 - }; - dynamic: boolean; // 是否启用动态粒子动画(默认:true) + displayRange?: { // 速度显示范围(默认:undefined) + min?: number; // 最小速度值 + max?: number; // 最大速度值 + }; + dynamic: boolean; // 是否启用动态粒子动画(默认:true) } ``` diff --git a/packages/cesium-wind-layer/src/index.ts b/packages/cesium-wind-layer/src/index.ts index 0f471b8..2fb4d88 100644 --- a/packages/cesium-wind-layer/src/index.ts +++ b/packages/cesium-wind-layer/src/index.ts @@ -16,6 +16,22 @@ export * from './types'; type WindLayerEventType = 'dataChange' | 'optionsChange'; type WindLayerEventCallback = (data: WindData | WindLayerOptions) => void; +export const DefaultOptions: WindLayerOptions = { + particlesTextureSize: 100, + dropRate: 0.003, + particleHeight: 1000, + dropRateBump: 0.01, + speedFactor: 1.0, + lineWidth: { min: 1, max: 2 }, + lineLength: { min: 20, max: 100 }, + colors: ['white'], + flipY: false, + useViewerBounds: false, + domain: undefined, + displayRange: undefined, + dynamic: true +} + export class WindLayer { private _show: boolean = true; private _resized: boolean = false; @@ -32,21 +48,7 @@ export class WindLayer { } } - static defaultOptions: WindLayerOptions = { - particlesTextureSize: 100, - dropRate: 0.003, - particleHeight: 1000, - dropRateBump: 0.01, - speedFactor: 1.0, - lineWidth: 5.0, - lineLength: { min: 20, max: 100 }, - colors: ['white'], - flipY: false, - useViewerBounds: false, - domain: undefined, - displayRange: undefined, - dynamic: true - } + static defaultOptions: WindLayerOptions = DefaultOptions; viewer: Viewer; scene: Scene; @@ -71,7 +73,7 @@ export class WindLayer { * @param {Partial} [options] - Optional configuration options for the wind layer. * @param {number} [options.particlesTextureSize=100] - Size of the particle texture. Determines the maximum number of particles (size squared). * @param {number} [options.particleHeight=0] - Height of particles above the ground in meters. - * @param {number} [options.lineWidth=3.0] - Width of particle trails in pixels. + * @param {Object} [options.lineWidth={ min: 1, max: 2 }] - Width range of particle trails. * @param {Object} [options.lineLength={ min: 20, max: 100 }] - Length range of particle trails. * @param {number} [options.speedFactor=1.0] - Factor to adjust the speed of particles. * @param {number} [options.dropRate=0.003] - Rate at which particles are dropped (reset). diff --git a/packages/cesium-wind-layer/src/shaders/segmentDraw.ts b/packages/cesium-wind-layer/src/shaders/segmentDraw.ts index 0bad63b..f877ddf 100644 --- a/packages/cesium-wind-layer/src/shaders/segmentDraw.ts +++ b/packages/cesium-wind-layer/src/shaders/segmentDraw.ts @@ -13,7 +13,7 @@ uniform float frameRateAdjustment; uniform float particleHeight; uniform float aspect; uniform float pixelSize; -uniform float lineWidth; +uniform vec2 lineWidth; uniform vec2 lineLength; uniform vec2 domain; uniform bool is3D; @@ -81,7 +81,7 @@ vec4 calculateOffsetOnNormalDirection(vec4 pointA, vec4 pointB, float offsetSign normalVector.x = normalVector.x / aspect; // 使用 widthFactor 调整宽度 - float offsetLength = lineWidth * widthFactor; + float offsetLength = widthFactor * lineWidth.y; normalVector = offsetLength * normalVector; vec4 offset = vec4(offsetSign * normalVector, 0.0, 0.0); @@ -119,11 +119,14 @@ void main() { vec4 offset = vec4(0.0); // 计算速度相关的宽度和长度因子 - float widthFactor = pointToUse < 0 ? 1.0 : 0.5; // 头部更宽,尾部更窄 - - // Calculate length based on speed float speedLength = clamp(speed.b, domain.x, domain.y); float normalizedSpeed = (speedLength - domain.x) / (domain.y - domain.x); + + // 根据速度计算宽度 + float widthFactor = mix(lineWidth.x, lineWidth.y, normalizedSpeed); + widthFactor *= (pointToUse < 0 ? 1.0 : 0.5); // 头部更宽,尾部更窄 + + // Calculate length based on speed float lengthFactor = mix(lineLength.x, lineLength.y, normalizedSpeed) * pixelSize; if (pointToUse == 1) { @@ -132,7 +135,7 @@ void main() { projectedCoordinates.previous, projectedCoordinates.current, offsetSign, - widthFactor * normalizedSpeed + widthFactor ); gl_Position = projectedCoordinates.previous + offset; v_segmentPosition = 0.0; // 头部 @@ -145,7 +148,7 @@ void main() { projectedCoordinates.current, extendedPosition, offsetSign, - widthFactor * normalizedSpeed + widthFactor ); gl_Position = extendedPosition + offset; v_segmentPosition = 1.0; // 尾部 diff --git a/packages/cesium-wind-layer/src/types.ts b/packages/cesium-wind-layer/src/types.ts index b1d678f..9e962d6 100644 --- a/packages/cesium-wind-layer/src/types.ts +++ b/packages/cesium-wind-layer/src/types.ts @@ -10,16 +10,21 @@ export interface WindLayerOptions { */ particleHeight: number; /** - * Width of particle trails in pixels. Default is 5.0. + * Width range of particle trails in pixels. Default is { min: 1, max: 5 }. * Controls the width of the particles. + * @property {number} min - Minimum width of particle trails + * @property {number} max - Maximum width of particle trails */ - lineWidth: number; + lineWidth: { + min: number; + max: number; + }; /** * Length range of particle trails. Default is { min: 20, max: 100 }. * @property {number} min - Minimum length of particle trails * @property {number} max - Maximum length of particle trails */ - lineLength?: { + lineLength: { min: number; max: number; }; diff --git a/packages/cesium-wind-layer/src/windParticlesRendering.ts b/packages/cesium-wind-layer/src/windParticlesRendering.ts index b65cee3..e033553 100644 --- a/packages/cesium-wind-layer/src/windParticlesRendering.ts +++ b/packages/cesium-wind-layer/src/windParticlesRendering.ts @@ -4,6 +4,7 @@ import { WindParticlesComputing } from './windParticlesComputing'; import CustomPrimitive from './customPrimitive'; import { ShaderManager } from './shaderManager'; import { deepMerge } from './utils'; +import { DefaultOptions } from '.'; export class WindParticlesRendering { private context: any; @@ -205,13 +206,16 @@ export class WindParticlesRendering { particleHeight: () => this.options.particleHeight || 0, aspect: () => this.context.drawingBufferWidth / this.context.drawingBufferHeight, pixelSize: () => this.viewerParameters.pixelSize, - lineWidth: () => this.options.lineWidth, - is3D: () => this.viewerParameters.sceneMode === SceneMode.SCENE3D, - segmentsDepthTexture: () => this.textures.segmentsDepth, + lineWidth: () => { + const width = this.options.lineWidth || DefaultOptions.lineWidth; + return new Cartesian2(width.min, width.max); + }, lineLength: () => { - const length = this.options.lineLength || { min: 50, max: 150 }; + const length = this.options.lineLength || DefaultOptions.lineLength; return new Cartesian2(length.min, length.max); }, + is3D: () => this.viewerParameters.sceneMode === SceneMode.SCENE3D, + segmentsDepthTexture: () => this.textures.segmentsDepth, }, vertexShaderSource: ShaderManager.getSegmentDrawVertexShader(), fragmentShaderSource: ShaderManager.getSegmentDrawFragmentShader(),