using System.Diagnostics;
|
using System.Windows.Threading;
|
|
namespace Yw.WpfUI.Hydro
|
{
|
internal class LogicalFlowEffect3D : ModelVisual3D
|
{
|
|
public LogicalFlowEffect3D(LogicalLink3D link, LogicalMaterialHelper materialHelper)
|
{
|
_link = link;
|
_materialHelper = materialHelper;
|
this.Content = new Model3DGroup();
|
|
InitializeParticles();
|
|
_animationTimer = new DispatcherTimer
|
{
|
Interval = TimeSpan.FromMilliseconds(16)
|
};
|
}
|
|
private readonly LogicalMaterialHelper _materialHelper = null;
|
private const int _max_particles = 500;//最大粒子数
|
private readonly LogicalLink3D _link;//管段
|
private readonly Random _random = new();//随机
|
private readonly List<LogicalParticle3D> _particles = new(_max_particles);//粒子
|
private readonly DispatcherTimer _animationTimer;//动画定时器
|
private readonly Stopwatch _stopWatch = new();//秒表
|
|
/// <summary>
|
/// 流速
|
/// </summary>
|
public double FlowRate { get; set; } = 1.0;
|
|
/// <summary>
|
/// 湍流
|
/// </summary>
|
public double Turbulence { get; set; } = 0.3;
|
|
/// <summary>
|
/// 是否运行
|
/// </summary>
|
public bool IsRunning { get; private set; }
|
|
#region 基础方法
|
|
//计算最优粒子数量
|
private int CalculateOptimalParticleCount()
|
{
|
var diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
|
var length = (_link.EndPosition - _link.StartPosition).Length;
|
double volume = Math.PI * Math.Pow(diameter / 2, 2) * length;
|
int count = (int)(volume * 0.3);
|
return Math.Clamp(count, 20, _max_particles);
|
}
|
|
//计算粒子尺寸
|
private double CalculateParticleSize()
|
{
|
var diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
|
var length = (_link.EndPosition - _link.StartPosition).Length;
|
return diameter * (_random.NextDouble() * 0.3 + 0.2);
|
}
|
|
//应用湍流
|
private Point3D ApplyTurbulence(Point3D position, Vector3D velocity)
|
{
|
double diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
|
var normal1 = Vector3D.CrossProduct(velocity, new Vector3D(0, 0, 1));
|
var normal2 = Vector3D.CrossProduct(velocity, normal1);
|
double turbulence = Turbulence * diameter;
|
//position += normal1 * (_random.NextDouble() - 0.5) * turbulence;
|
//position += normal2 * (_random.NextDouble() - 0.5) * turbulence;
|
return position;
|
}
|
|
//获取沿着管段的点
|
private Point3D GetPointAlongLink(double t)
|
{
|
return new Point3D(
|
_link.EndPosition.X + t * (_link.EndPosition.X - _link.StartPosition.X),
|
_link.StartPosition.Y + t * (_link.EndPosition.Y - _link.StartPosition.Y),
|
_link.StartPosition.Z + t * (_link.EndPosition.Z - _link.StartPosition.Z));
|
}
|
|
//获取基础速度
|
private Vector3D GetBaseVelocity()
|
{
|
var direction = _link.EndPosition - _link.StartPosition;
|
direction.Normalize();
|
return direction;
|
}
|
|
//是否管段外部
|
private bool IsOutsideLink(Point3D point)
|
{
|
var linkVec = _link.EndPosition - _link.StartPosition;
|
var pointVec = point - _link.StartPosition;
|
|
double dot = Vector3D.DotProduct(linkVec, pointVec);
|
if (dot < 0 || dot > linkVec.LengthSquared)
|
{
|
return true;
|
}
|
double diameter = Yw.Settings.HydroL3dParasHelper.HydroL3d.Logical.Link.Normal.Diameter;
|
double distance = (pointVec - linkVec * (dot / linkVec.LengthSquared)).Length;
|
return distance > diameter * 0.6;
|
}
|
|
//重置粒子
|
private void ResetParticle(LogicalParticle3D particle)
|
{
|
double t = _random.NextDouble();
|
particle.Position = GetPointAlongLink(t);
|
particle.Velocity = GetBaseVelocity() * (_random.NextDouble() * 0.4 + 0.8);
|
particle.Age = 0;
|
|
var position = particle.Position;
|
particle.Position = ApplyTurbulence(position, particle.Velocity);
|
particle.Update();
|
}
|
|
#endregion
|
|
|
//初始化粒子
|
private void InitializeParticles()
|
{
|
var modelGroup = (Model3DGroup)this.Content;
|
modelGroup.Children.Clear();
|
|
int particleCount = CalculateOptimalParticleCount();
|
|
for (int i = 0; i < particleCount; i++)
|
{
|
var particle = new LogicalParticle3D(_materialHelper);
|
particle.Position = _link.StartPosition;
|
particle.Size = CalculateParticleSize();
|
particle.Color = Colors.Green;
|
particle.Lifetime = _random.NextDouble() * 10 + 5;
|
particle.Age = _random.NextDouble() * 10;
|
ResetParticle(particle);
|
_particles.Add(particle);
|
modelGroup.Children.Add(particle.Content);
|
}
|
}
|
|
|
/// <summary>
|
/// 开始动画
|
/// </summary>
|
public void Play()
|
{
|
if (IsRunning)
|
{
|
return;
|
}
|
_animationTimer.Tick += OnAnimationFrame;
|
_animationTimer.Start();
|
_stopWatch.Start();
|
this.IsRunning = true;
|
}
|
|
/// <summary>
|
/// 停止动画
|
/// </summary>
|
public void Stop()
|
{
|
if (!IsRunning)
|
{
|
return;
|
}
|
_animationTimer.Tick -= OnAnimationFrame;
|
_animationTimer.Stop();
|
_stopWatch.Stop();
|
this.IsRunning = false;
|
}
|
|
private void OnAnimationFrame(object sender, EventArgs e)
|
{
|
double deltaTime = _stopWatch.Elapsed.TotalSeconds;
|
_stopWatch.Restart();
|
|
double speedFactor = this.FlowRate * deltaTime * 2;
|
|
foreach (var particle in _particles)
|
{
|
particle.Age += deltaTime;
|
if (particle.Age > particle.Lifetime)
|
{
|
ResetParticle(particle);
|
continue;
|
}
|
|
particle.Position += particle.Velocity * speedFactor;
|
|
if (IsOutsideLink(particle.Position))
|
{
|
ResetParticle(particle);
|
continue;
|
}
|
|
if (_random.NextDouble() < 0.1)
|
{
|
particle.Position = ApplyTurbulence(particle.Position, particle.Velocity);
|
}
|
|
particle.Update();
|
}
|
}
|
|
}
|
|
|
}
|