47 lines
2.5 KiB
C++
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_;
|
|
};
|
|
}
|