surface_embedding.cpp serif/geometry/deformation/surface_embedding.hpp algorithm cmath limits utility serif/geometry/deformation/detail/harmonics.hpp serif/geometry/deformation/detail/action_storage.hpp serif serif::geometry serif::geometry::deformation #include"serif/geometry/deformation/surface_embedding.hpp" #include<algorithm> #include<cmath> #include<limits> #include<utility> #include"serif/geometry/deformation/detail/harmonics.hpp" #include"serif/geometry/deformation/detail/action_storage.hpp" namespaceserif::geometry::deformation { namespace { [[nodiscard]]std::unexpected<DeformationFailure>action_overflow() { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Surfaceactionproducedanon-finiteresult;reducethesuppliedparameterorforcemagnitude."}); } [[nodiscard]]longdoubleradial_work(conststd::array<double,3>&direction,std::span<constdouble>force) { longdoubleresult=0.0L; for(std::size_tcomponent=0;component<3;++component) { result+=static_cast<longdouble>(direction[component])*force[component]; } returnresult; } }//namespace std::size_tSurfaceEmbedding::input_size()constnoexcept { returnhas_nodal_parameters()?surface_node_count():modes_.size(); } std::size_tSurfaceEmbedding::output_size()constnoexcept { return3*surface_node_count(); } std::size_tSurfaceEmbedding::surface_node_count()constnoexcept { returnradial_directions_.size(); } boolSurfaceEmbedding::has_nodal_parameters()constnoexcept { returnmodes_.empty(); } std::span<conststd::array<double,3>>SurfaceEmbedding::radial_directions()constnoexcept { returnradial_directions_; } std::span<constdouble>SurfaceEmbedding::reference_radii()constnoexcept { returnreference_radii_; } std::span<conststd::array<double,3>>SurfaceEmbedding::reference_positions()constnoexcept { returnreference_positions_; } std::span<constSphericalHarmonicMode>SurfaceEmbedding::modes()constnoexcept { returnmodes_; } std::span<constdouble>SurfaceEmbedding::nodal_basis()constnoexcept { returnnodal_basis_; } DeformationStatusSurfaceEmbedding::apply(std::span<constdouble>parameters,std::span<double>surface_displacement,Workspace)const { if(constDeformationStatusstatus=detail::validate_action_arguments(surface_displacement,output_size(),{{parameters,input_size()}});!status) { returnstatus; } for(std::size_tnode=0;node<surface_node_count();++node) { longdoubleamplitude=0.0L; if(has_nodal_parameters()) { amplitude=parameters[node]; } else { for(std::size_tmode=0;mode<input_size();++mode) { amplitude+=static_cast<longdouble>(nodal_basis_[node*input_size()+mode])*parameters[mode]; } } constdoubleradial_amplitude=static_cast<double>(amplitude); if(!std::isfinite(radial_amplitude)) { returnaction_overflow(); } for(std::size_tcomponent=0;component<3;++component) { constdoubledisplacement=static_cast<double>(static_cast<longdouble>(radial_amplitude)*radial_directions_[node][component]); if(!std::isfinite(displacement)) { returnaction_overflow(); } surface_displacement[3*node+component]=displacement; } } return{}; } DeformationStatusSurfaceEmbedding::apply_transpose(std::span<constdouble>surface_force,std::span<double>parameter_force,Workspace)const { if(constDeformationStatusstatus=detail::validate_action_arguments(parameter_force,input_size(),{{surface_force,output_size()}});!status) { returnstatus; } for(std::size_tparameter=0;parameter<input_size();++parameter) { longdoublework=0.0L; if(has_nodal_parameters()) { work=radial_work(radial_directions_[parameter],surface_force.subspan(3*parameter,3)); } else { for(std::size_tnode=0;node<surface_node_count();++node) { work+=static_cast<longdouble>(nodal_basis_[node*input_size()+parameter])*radial_work(radial_directions_[node],surface_force.subspan(3*node,3)); } } constdoubleparameter_work=static_cast<double>(work); if(!std::isfinite(parameter_work)) { returnaction_overflow(); } parameter_force[parameter]=parameter_work; } return{}; } std::expected<SurfaceEmbedding,DeformationFailure>build_surface_embedding(constNodalRadialSurface&model,std::span<conststd::array<double,3>>positions) { if(positions.empty()||positions.size()>std::numeric_limits<std::size_t>::max()/3) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_size,.message="Aradialsurfacerequiresanonempty,representablelistofreferencenodes."}); } if(!std::ranges::all_of(model.reference_center,[](doublevalue) { returnstd::isfinite(value); })) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::non_finite,.message="Thereferencecentermustcontainthreefinitecoordinates."}); } SurfaceEmbeddingresult; result.reference_positions_.assign(positions.begin(),positions.end()); result.radial_directions_.reserve(positions.size()); result.reference_radii_.reserve(positions.size()); for(conststd::array<double,3>&position:positions) { std::array<double,3>direction{}; for(std::size_tcomponent=0;component<3;++component) { direction[component]=position[component]-model.reference_center[component]; } constdoubleradius=std::hypot(direction[0],direction[1],direction[2]); if(!std::isfinite(radius)||radius<=0.0) { returnstd::unexpected(DeformationFailure{.reason=DeformationFailureReason::invalid_geometry,.message="Everyreferencesurfacenodemusthaveafinitepositivedistancefromthereferencecenter."}); } for(double&component:direction) { component/=radius; } result.radial_directions_.push_back(direction); result.reference_radii_.push_back(radius); } returnresult; } std::expected<SurfaceEmbedding,DeformationFailure>build_surface_embedding(constSphericalHarmonicSurface&model,std::span<conststd::array<double,3>>positions) { automodes=detail::make_harmonic_modes(model,positions.size()); if(!modes) { returnstd::unexpected(modes.error()); } autoresult=build_surface_embedding(NodalRadialSurface{.reference_center=model.reference_center},positions); if(!result) { returnresult; } autobasis=detail::sample_harmonic_basis(model,*modes,result->radial_directions()); if(!basis) { returnstd::unexpected(basis.error()); } if(constDeformationStatusstatus=detail::check_independent_harmonics(*basis,positions.size(),modes->size());!status) { returnstd::unexpected(status.error()); } result->modes_=std::move(*modes); result->nodal_basis_=std::move(*basis); returnresult; } }//namespaceserif::geometry::deformation