Files
FluidSimCpp/include/FluidSim/Desktop/PipePath.hpp
2026-10-23 18:29:51 +02:00

47 lines
2.5 KiB
C++

#pragma once
#include "FluidSim/AudioProcessing.hpp"
#include <algorithm>
#include <cmath>
#include <stdexcept>
#include <vector>
#include <utility>
namespace fluidsim::port::desktop {
struct PipeAnchor {float distance;Vec3 position;};
// Knots are distances along the simulated pipe, in metres. Display positions
// define its schematic route; changing them never changes fluid cell lengths.
class PipePath {
public:
explicit PipePath(std::vector<PipeAnchor> 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;i<anchors_.size();++i) {
const auto& p=anchors_[i];
if(!std::isfinite(p.distance) || !std::isfinite(p.position.x) || !std::isfinite(p.position.y) || !std::isfinite(p.position.z) ||
(i && !(p.distance>anchors_[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<a.distance;});
std::size_t i=std::clamp<std::size_t>(it-anchors_.begin(),1,anchors_.size()-1)-1;
const auto& a=anchors_[i];const auto& b=anchors_[i+1];
const float span=b.distance-a.distance,t=(distance-a.distance)/span,t2=t*t,t3=t2*t;
const auto ma=tangent_at(i),mb=tangent_at(i+1);
auto coordinate=[&](float pa,float pb,float ta,float tb){return (2*t3-3*t2+1)*pa+(t3-2*t2+t)*span*ta+(-2*t3+3*t2)*pb+(t3-t2)*span*tb;};
return {coordinate(a.position.x,b.position.x,ma.x,mb.x),coordinate(a.position.y,b.position.y,ma.y,mb.y),coordinate(a.position.z,b.position.z,ma.z,mb.z)};
}
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};
}
private:
Vec3 tangent_at(std::size_t i) const {
const auto& a=anchors_[i?i-1:0];const auto& b=anchors_[std::min(i+1,anchors_.size()-1)];
const float d=b.distance-a.distance;
return {(b.position.x-a.position.x)/d,(b.position.y-a.position.y)/d,(b.position.z-a.position.z)/d};
}
std::vector<PipeAnchor> anchors_;
};
}