fitpack
Modern Fortran library for curve and surface fitting with splines
Loading...
Searching...
No Matches
fpPoint.hpp
Go to the documentation of this file.
1#ifndef FPPOINT_HPP_INCLUDED
2#define FPPOINT_HPP_INCLUDED
3
4/***************************************************************************************************
5! ____________________ ___ ________ __
6! / ____/ _/_ __/ __ \/ | / ____/ //_/
7! / /_ / / / / / /_/ / /| |/ / / ,<
8! / __/ _/ / / / / ____/ ___ / /___/ /| |
9! /_/ /___/ /_/ /_/ /_/ |_\____/_/ |_|
10!
11! A Curve Fitting Package
12!
13! fpPoint<dims>
14!> @brief Fixed-dimension point, byte-compatible with Fortran `real(FP_REAL) :: x(dims)`
15!
16! Hand-maintained (NOT emitted by the binding generator).
17!
18! A `fpPoint<dims>` is a standard-layout aggregate wrapping `std::array<FP_REAL,dims>`, so a
19! `std::vector<fpPoint<dims>>` has exactly the storage of a Fortran `x(dims,m)` array in
20! column-major order: point i occupies elements [i*dims, (i+1)*dims). `fpPointData()` hands
21! that buffer straight to the rank-2 C entry points with no copy and no scatter loop.
22!
23! This supersedes the pre-0.3.0 `typedef std::vector<FP_REAL> fpPoint`, which cost one heap
24! allocation per point and leaked C++ into the nominally-C `fitpack_core_c.h`.
25!
26! Author: (C) Federico Perini
27!> @since 26/08/2026
28!
29! **************************************************************************************************/
30
31#include "fitpack_core_c.h"
32
33#include <array>
34#include <cstddef>
35#include <type_traits>
36#include <vector>
37
43template <FP_SIZE dims>
44struct fpPoint
45{
46 static_assert(dims > 0, "fpPoint requires at least one dimension");
47 static_assert(sizeof(std::array<FP_REAL, dims>) == static_cast<std::size_t>(dims) * sizeof(FP_REAL),
48 "std::array<FP_REAL,dims> must be exactly dims contiguous reals");
49
51 std::array<FP_REAL, dims> coord;
52
53 // --- Element access ---
54 constexpr FP_REAL& operator[](FP_SIZE i) { return coord[static_cast<std::size_t>(i)]; }
55 constexpr const FP_REAL& operator[](FP_SIZE i) const { return coord[static_cast<std::size_t>(i)]; }
56
57 constexpr FP_SIZE size() const { return dims; }
58 constexpr FP_REAL* data() { return coord.data(); }
59 constexpr const FP_REAL* data() const { return coord.data(); }
60
61 // --- Iteration ---
62 constexpr FP_REAL* begin() { return coord.data(); }
63 constexpr FP_REAL* end() { return coord.data() + dims; }
64 constexpr const FP_REAL* begin() const { return coord.data(); }
65 constexpr const FP_REAL* end() const { return coord.data() + dims; }
66 constexpr const FP_REAL* cbegin() const { return coord.data(); }
67 constexpr const FP_REAL* cend() const { return coord.data() + dims; }
68
69 // --- Named accessors. Each is only instantiated when used, so z() on a 2-D point is a
70 // compile error and never a silent out-of-range read. ---
71 constexpr FP_REAL& x() { static_assert(dims >= 1, "fpPoint::x() requires dims >= 1"); return coord[0]; }
72 constexpr FP_REAL& y() { static_assert(dims >= 2, "fpPoint::y() requires dims >= 2"); return coord[1]; }
73 constexpr FP_REAL& z() { static_assert(dims >= 3, "fpPoint::z() requires dims >= 3"); return coord[2]; }
74
75 constexpr const FP_REAL& x() const { static_assert(dims >= 1, "fpPoint::x() requires dims >= 1"); return coord[0]; }
76 constexpr const FP_REAL& y() const { static_assert(dims >= 2, "fpPoint::y() requires dims >= 2"); return coord[1]; }
77 constexpr const FP_REAL& z() const { static_assert(dims >= 3, "fpPoint::z() requires dims >= 3"); return coord[2]; }
78};
79
80template <FP_SIZE dims>
81constexpr bool operator==(const fpPoint<dims>& a, const fpPoint<dims>& b) { return a.coord == b.coord; }
82
83template <FP_SIZE dims>
84constexpr bool operator!=(const fpPoint<dims>& a, const fpPoint<dims>& b) { return !(a == b); }
85
86namespace fp_detail
87{
89 template <FP_SIZE dims>
91 {
92 static_assert(sizeof(fpPoint<dims>) == static_cast<std::size_t>(dims) * sizeof(FP_REAL),
93 "fpPoint<dims> must be exactly dims contiguous FP_REALs (no padding)");
94 static_assert(std::is_standard_layout<fpPoint<dims>>::value,
95 "fpPoint<dims> must be standard-layout to alias Fortran storage");
96 static_assert(std::is_trivially_copyable<fpPoint<dims>>::value,
97 "fpPoint<dims> must be trivially copyable to alias Fortran storage");
98 static constexpr bool value = true;
99 };
100}
101
106template <FP_SIZE dims>
107inline const FP_REAL* fpPointData(const std::vector<fpPoint<dims>>& points)
108{
109 static_assert(fp_detail::fpPointLayout<dims>::value, "fpPoint layout contract");
110 return points.empty() ? nullptr : reinterpret_cast<const FP_REAL*>(points.data());
111}
112
116template <FP_SIZE dims>
117inline std::vector<FP_REAL> fpPointFlatten(const std::vector<fpPoint<dims>>& points)
118{
119 const FP_REAL* first = fpPointData<dims>(points);
120 return std::vector<FP_REAL>(first, first + points.size() * static_cast<std::size_t>(dims));
121}
122
126template <FP_SIZE dims>
127inline std::vector<fpPoint<dims>> fpPointGather(const std::vector<FP_REAL>& flat)
128{
129 static_assert(fp_detail::fpPointLayout<dims>::value, "fpPoint layout contract");
130 std::vector<fpPoint<dims>> points(flat.size() / static_cast<std::size_t>(dims));
131 if (!points.empty())
132 {
133 FP_REAL* raw = reinterpret_cast<FP_REAL*>(points.data());
134 for (std::size_t i = 0; i < points.size() * static_cast<std::size_t>(dims); ++i) raw[i] = flat[i];
135 }
136 return points;
137}
138
139// Compile-time smoke test of the layout contract for the dimensions users actually reach for.
140static_assert(fp_detail::fpPointLayout<1>::value, "fpPoint<1> layout");
141static_assert(fp_detail::fpPointLayout<2>::value, "fpPoint<2> layout");
142static_assert(fp_detail::fpPointLayout<3>::value, "fpPoint<3> layout");
143static_assert(fp_detail::fpPointLayout<4>::value, "fpPoint<4> layout");
144
145#endif // FPPOINT_HPP_INCLUDED
constexpr bool operator!=(const fpPoint< dims > &a, const fpPoint< dims > &b)
Definition fpPoint.hpp:84
constexpr bool operator==(const fpPoint< dims > &a, const fpPoint< dims > &b)
Definition fpPoint.hpp:81
const FP_REAL * fpPointData(const std::vector< fpPoint< dims > > &points)
Borrow a vector of points as a Fortran x(dims,m) buffer. No copy, no allocation.
Definition fpPoint.hpp:107
std::vector< fpPoint< dims > > fpPointGather(const std::vector< FP_REAL > &flat)
Rebuild a vector of points from a Fortran x(dims,m) buffer.
Definition fpPoint.hpp:127
std::vector< FP_REAL > fpPointFlatten(const std::vector< fpPoint< dims > > &points)
Flatten a vector of points into Fortran x(dims,m) order. One contiguous copy.
Definition fpPoint.hpp:117
Definition fpPoint.hpp:87
Instantiated at every zero-copy call site: the layout contract that makes the cast legal.
Definition fpPoint.hpp:91
static constexpr bool value
Definition fpPoint.hpp:98
A point in dims-dimensional space.
Definition fpPoint.hpp:45
constexpr const FP_REAL * end() const
Definition fpPoint.hpp:65
constexpr const FP_REAL * cbegin() const
Definition fpPoint.hpp:66
constexpr const FP_REAL & operator[](FP_SIZE i) const
Definition fpPoint.hpp:55
constexpr FP_REAL & operator[](FP_SIZE i)
Definition fpPoint.hpp:54
std::array< FP_REAL, dims > coord
Coordinates, in the same order as the Fortran leading dimension.
Definition fpPoint.hpp:51
constexpr const FP_REAL & z() const
Definition fpPoint.hpp:77
constexpr FP_REAL & y()
Definition fpPoint.hpp:72
constexpr FP_REAL * data()
Definition fpPoint.hpp:58
constexpr const FP_REAL * cend() const
Definition fpPoint.hpp:67
constexpr const FP_REAL & x() const
Definition fpPoint.hpp:75
constexpr const FP_REAL * begin() const
Definition fpPoint.hpp:64
constexpr FP_REAL * begin()
Definition fpPoint.hpp:62
constexpr FP_REAL * end()
Definition fpPoint.hpp:63
constexpr FP_REAL & x()
Definition fpPoint.hpp:71
constexpr const FP_REAL & y() const
Definition fpPoint.hpp:76
constexpr const FP_REAL * data() const
Definition fpPoint.hpp:59
constexpr FP_SIZE size() const
Definition fpPoint.hpp:57
constexpr FP_REAL & z()
Definition fpPoint.hpp:73