#pragma once #include "FluidSim/AudioProcessing.hpp" #include #include #include #include #include namespace fluidsim::port::desktop { struct PipeAnchor {float distance;Vec3 position;}; // Piecewise straight centerline; knot distances are measured from its geometry. class PipePath { public: explicit PipePath(std::vector anchors):anchors_(std::move(anchors)) { if(anchors_.size()<2 || anchors_.front().distance!=0)throw std::invalid_argument("pipe path needs origin and endpoint"); for(std::size_t i=0;ianchors_[i-1].distance))) throw std::invalid_argument("invalid distance-referenced pipe path"); } } Vec3 position(float distance) const { distance=std::clamp(distance,0.f,anchors_.back().distance); auto it=std::upper_bound(anchors_.begin(),anchors_.end(),distance,[](float s,const PipeAnchor& a){return s(it-anchors_.begin(),1,anchors_.size()-1)-1; const auto& a=anchors_[i];const auto& b=anchors_[i+1]; const float t=(distance-a.distance)/(b.distance-a.distance); return {std::lerp(a.position.x,b.position.x,t),std::lerp(a.position.y,b.position.y,t),std::lerp(a.position.z,b.position.z,t)}; } Vec3 tangent(float distance) const { const float epsilon=anchors_.back().distance*.0001f; const auto a=position(distance-epsilon),b=position(distance+epsilon); const float x=b.x-a.x,y=b.y-a.y,z=b.z-a.z,n=std::sqrt(x*x+y*y+z*z); return n>1e-8f?Vec3{x/n,y/n,z/n}:Vec3{1,0,0}; } Vec3 miter_offset(float distance,Vec3 radial) const { const auto unit=[](Vec3 v){const float n=std::sqrt(v.x*v.x+v.y*v.y+v.z*v.z);return Vec3{v.x/n,v.y/n,v.z/n};}; for(std::size_t i=1;i+1.99999f)return radial; const auto bend=unit({u.x-v.x,u.y-v.y,u.z-v.z}); const float scale=std::min(3.f,1/std::sqrt(std::max(.0001f,(1+cosine)*.5f))); const float extension=(radial.x*bend.x+radial.y*bend.y+radial.z*bend.z)*(scale-1); return {radial.x+bend.x*extension,radial.y+bend.y*extension,radial.z+bend.z*extension}; } return radial; } private: std::vector anchors_; }; }