copp 0.2.2

Convex-objective path parameterization for robotic trajectory planning.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
# COPP C++ Binding

[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](../../LICENSE) [![Website](https://img.shields.io/badge/website-copp.pro-2ff0d8)](https://copp.pro/) [![Docs](https://img.shields.io/badge/docs-docs.copp.pro-1f6feb)](https://docs.copp.pro/) [![C++](https://img.shields.io/badge/C%2B%2B-bindings-00599c)](#copp-c-binding)

## Convex-Objective Path Parameterization

This directory contains the C++ facade for COPP. The C++ API is designed to feel like a small RAII wrapper over the Rust/Python concepts rather than a thin exposure of the C ABI: public types live in `namespace copp`, objects own their resources, and normal calls throw `copp::Error`.

COPP solves Optimal Path Parameterization (OPP) problems. A geometric path is known,

$$
q = q(s), \qquad s \in [s_{\min}, s_{\max}],
$$

and COPP computes a time law

$$
s = s(t)
$$

so that the executed trajectory `q(s(t))` satisfies velocity, acceleration, torque, jerk, or user-provided constraints while optimizing the selected objective.

Second- and third-order profiles use the same notation as the Rust crate:

$$
a(s) = \dot{s}^2, \qquad b(s) = \ddot{s}, \qquad c(s) = \frac{\dddot{s}}{\dot{s}}.
$$

The algorithmic background, open-source algorithm availability, benchmark tables, citation information, and collaboration contact details live in the [COPP repository README](https://github.com/TOPP-THU/copp#readme). This page focuses on building, linking, installing, and using the C++ interface.

> **Open-source / PRO note:** this README documents the open-source C++ binding and its CMake target `copp::copp`. COPP PRO provides additional licensed solvers, support options, and commercial collaboration paths; see the repository-level PRO section or contact [hello@copp.pro](mailto:hello@copp.pro) if those capabilities are relevant to your application. Future prebuilt SDK packages should be checked from [GitHub Releases](https://github.com/TOPP-THU/copp/releases).

## API Availability

| Problem class  | C++ API                                                                                                                                            |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Core utilities | `copp::version`, `copp::Error`, `copp::Expected`, `copp::Span`, `copp::Matrix`, `copp::MatrixView`, `copp::MatrixRef`, boundary and interval types |
| Path           | waypoint splines, scalar-parametric `Jet3` paths, batch 2nd/3rd-order evaluator paths, derivative evaluation                                       |
| Robot          | station grids, path sampling, velocity/acceleration/jerk/torque limits, raw constraints, inverse-dynamics callbacks                                |
| TOPP2          | `copp::solver::topp2_ra`, `copp::solver::reach_set2`, TOPP2 interpolation helpers                                                                  |
| COPP2          | `copp::solver::copp2_socp`, objective descriptors, expert results                                                                                  |
| TOPP3          | `copp::solver::topp3_lp`, `copp::solver::topp3_socp`, TOPP3 interpolation helpers                                                                  |
| COPP3          | `copp::solver::copp3_socp`, objective descriptors, expert results                                                                                  |
| Clarabel       | raw `copp::clarabel::Settings`, `copp::clarabel::Options`, solver status, residuals, linear-solver information                                     |
| Eigen          | optional adapters in `copp/eigen.hpp` for borrowing Eigen vectors/matrices and mapping returned `copp::Matrix` values                              |

Runnable examples are in [examples](examples/). They are built as CMake targets named `example_*`.

## Quick Start

### Prerequisites

You need:

- Cargo and a Rust toolchain;
- CMake 3.16 or newer;
- a C++17 compiler toolchain;
- Eigen3 if you enable `COPP_CPP_WITH_EIGEN=ON`;
- Doxygen and Graphviz if you want to generate local C++ API documentation.

The source-tree CMake flow is Cargo-first: it imports an existing Rust/C++ native library from `target/release/` and does not rebuild Rust automatically. Run the commands below from the repository root, and rerun the Cargo command after changing Rust code, `src/ffi/cpp`, `bindings/cpp/src`, or public C++ headers; otherwise CMake may link an old native artifact.

### Build From Source

Build only the C++ facade feature. The Cargo command builds the Rust native artifact that CMake imports:

```powershell
cargo build --release --lib --features cpp
cmake -S bindings/cpp -B bindings/cpp/build `
  -DCOPP_CPP_WITH_EIGEN=OFF `
  -DCOPP_INSTALL_C_ABI_TARGET=OFF
cmake --build bindings/cpp/build --config Release
```

Build the C++ facade and export a separate C ABI target from the same CMake package. Use this when downstream consumers should also receive the C ABI target:

```powershell
cargo build --release --lib --features cpp,c
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_CPP_WITH_EIGEN=OFF
cmake --build bindings/cpp/build --config Release
```

`COPP_LINK_STATIC=ON` is the default. Set `-DCOPP_LINK_STATIC=OFF` to use the shared C++ facade library (`copp_cpp.dll`, `libcopp_cpp.so`, or `libcopp_cpp.dylib`). The installed CMake target propagates the required C++ import macro for shared builds. On Windows source-tree dynamic builds, the CMake example/test targets copy the runtime DLL next to the executable.

If Eigen is installed and discoverable by CMake, omit `-DCOPP_CPP_WITH_EIGEN=OFF` to enable `copp/eigen.hpp`.

### Install And Link

Install the headers, native library, and CMake package config:

```powershell
cmake --install bindings/cpp/build --config Release --prefix <install-prefix>
```

Downstream CMake projects can then link the installed package:

```cmake
find_package(copp CONFIG REQUIRED)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE copp::copp)
```

Configure the downstream project with the install prefix:

```powershell
cmake -S app -B app/build -DCMAKE_PREFIX_PATH=<install-prefix>
cmake --build app/build --config Release
```

When `COPP_INSTALL_C_ABI_TARGET=ON`, the same static package also exports `copp::c_abi` for downstream code that explicitly wants the C ABI target. C++ applications should normally link `copp::copp`. In static packages, `copp::c_abi` receives the same platform link libraries and C++ link-language metadata as the C++ facade because the imported Cargo artifact may contain C++ bridge objects. Shared C++ packages use a CMake-built facade library and should keep `COPP_INSTALL_C_ABI_TARGET=OFF`; use the C ABI package/workflow for shared C consumers.

For static packages, the exported target records common platform native libraries. If a platform or toolchain needs extra native libraries, configure COPP with:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_LINK_STATIC=ON -DCOPP_STATIC_LINK_LIBRARIES="lib1;lib2"
```

For future prebuilt packages, check [GitHub Releases](https://github.com/TOPP-THU/copp/releases). Source builds remain useful when you are working from a development branch or changing Rust/C++ binding code.

### Build Examples And Tests

Tests and examples are disabled by default for SDK-style consumption. Enable them explicitly when validating the binding locally:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build `
  -DCOPP_CPP_WITH_EIGEN=OFF `
  -DCOPP_INSTALL_C_ABI_TARGET=OFF `
  -DCOPP_BUILD_CPP_TESTS=ON `
  -DCOPP_BUILD_CPP_EXAMPLES=ON
cmake --build bindings/cpp/build --config Release
ctest --test-dir bindings/cpp/build --output-on-failure -C Release
```

Register examples as CTest tests:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build `
  -DCOPP_CPP_WITH_EIGEN=OFF `
  -DCOPP_INSTALL_C_ABI_TARGET=OFF `
  -DCOPP_BUILD_CPP_EXAMPLES=ON `
  -DCOPP_TEST_CPP_EXAMPLES=ON
cmake --build bindings/cpp/build --config Release
ctest --test-dir bindings/cpp/build --output-on-failure -C Release
```

Build one example target:

```powershell
cmake --build bindings/cpp/build --config Release --target example_topp2_ra
```

Keep tests or examples disabled when configuring a consumer-style build:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_BUILD_CPP_TESTS=OFF
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_BUILD_CPP_EXAMPLES=OFF
```

## General Workflow

Most C++ examples follow the same shape:

1. Build a path from waypoints, a scalar-parametric `Jet3` callback, or a batch evaluator callback.
2. Build a strictly increasing station grid `s`.
3. Create a `copp::Robot` or an owning raw `copp::Constraints` buffer.
4. Append stations and sample path derivatives into the robot when using robot-level limits or objectives.
5. Add velocity, acceleration, jerk, torque, or raw mathematical constraints.
6. Build a borrowed solver problem descriptor such as `topp2_ra::Problem`, `copp2_socp::Problem`, `topp3::Problem`, or `copp3_socp::Problem`.
7. Call the solver namespace function.
8. Convert the returned path-domain profile into timing data with `copp::interpolation`.
9. Evaluate the original path at sampled `s(t)` values to generate downstream trajectory references.

For second-order problems, solvers usually return an `a` profile sampled on the station grid, where $a(s)=\dot{s}^2$. For third-order problems, solvers return `copp::Profile3rd`, containing `a`, `b`, and stationary-boundary metadata.

## Minimal Program

```cpp
#include <copp/copp.hpp>

#include <iostream>

int main()
{
    std::cout << "COPP version: " << copp::version() << "\n";
    return 0;
}
```

## TOPP2-RA Example

The example below mirrors the Rust TOPP2-RA quick-start pattern: define a smooth path, sample it on a station grid, add velocity/acceleration limits, solve for `a(s) = dot{s}^2`, and convert the profile to time.

```cpp
#include <copp/copp.hpp>

#include <cmath>
#include <iostream>
#include <vector>

int main()
{
    constexpr std::size_t dim = 3;
    constexpr std::size_t n = 1001;

    auto path = copp::Path::from_evaluator_2nd(
        dim,
        0.0,
        1.0,
        [](copp::Span<const double> s,
           copp::MatrixRef q,
           copp::MatrixRef dq,
           copp::MatrixRef ddq)
        {
            constexpr double pi = 3.14159265358979323846;
            const double freq[3] = {2.0 * pi, 3.0 * pi, 5.0 * pi};
            const double phase[3] = {0.0, 0.3, 0.7};

            for (std::size_t j = 0; j < s.size(); ++j)
            {
                for (std::size_t axis = 0; axis < 3; ++axis)
                {
                    const double x = freq[axis] * s[j] + phase[axis];
                    q(axis, j) = std::sin(x);
                    dq(axis, j) = freq[axis] * std::cos(x);
                    ddq(axis, j) = -freq[axis] * freq[axis] * std::sin(x);
                }
            }
        });

    std::vector<double> s(n);
    for (std::size_t k = 0; k < n; ++k)
    {
        s[k] = static_cast<double>(k) / static_cast<double>(n - 1);
    }

    const std::vector<double> upper(dim, 1.0);
    const std::vector<double> lower(dim, -1.0);

    copp::Robot robot(dim, n);
    robot.append_s(s)
        .set_q_from_path_2nd(path, 0, n)
        .add_velocity_limits(upper, lower, 0, n)
        .add_acceleration_limits(upper, lower, 0, n);

    namespace topp2 = copp::solver::topp2_ra;

    topp2::Problem problem{
        robot.constraints(),
        copp::IndexInterval{0, n - 1},
        copp::Boundary2{0.0, 0.0},
    };

    auto a = topp2::solve(problem);
    auto time = copp::interpolation::s_to_t_topp2(s, a, 0.0);
    auto s_t = copp::interpolation::t_to_s_topp2_uniform(s, a, time.t_s, 1.0e-3);

    std::cout << "TOPP2-RA done.\n";
    std::cout << "t_final = " << time.t_final << " s\n";
    std::cout << "a_profile.len() = " << a.size() << "\n";
    std::cout << "s(t) samples = " << s_t.size() << "\n";

    return 0;
}
```

The checked-in source is available at [`examples/topp2_ra.cpp`](examples/topp2_ra.cpp).

## Path Construction

Waypoint paths are the shortest route when you already have discrete path points. Each inner initializer list is one waypoint vector; the facade converts it to the `(dim x n_points)` matrix shape expected internally.

```cpp
auto path = copp::Path::from_waypoints({
    {0.0, 0.0},
    {0.5, 0.25},
    {1.0, 1.0},
});

std::vector<double> s{0.0, 0.5, 1.0};
auto out = path.evaluate_up_to_2nd(s);
double q0_mid = out.q(0, 1);
```

Use `copp::SplineConfig` when you need a non-default spline range, clamping policy, order, or endpoint derivative states. Boundary derivative matrices use shape `(dim, m)` where `m = (order - 1) / 2`; column `r` stores the `(r + 1)`-th derivative at that endpoint. Leaving `start_state` or `end_state` empty means zero boundary derivatives.

{% raw %}
```cpp
copp::SplineConfig config;
config.order = 3;
config.start_state = copp::Matrix::from_columns({{2.0}});
config.end_state = copp::Matrix::from_columns({{2.0}});

auto path = copp::Path::from_waypoints({{0.0}, {1.0}}, config);
```
{% endraw %}

Scalar-parametric paths use `copp::Jet3` to propagate derivatives up to third order through ordinary arithmetic.

```cpp
auto path = copp::Path::from_parametric(
    [](copp::Jet3 s)
    {
        return std::vector<copp::Jet3>{
            copp::sin(s),
            copp::cos(2.0 * s),
        };
    },
    0.0,
    1.0);

auto out = path.evaluate_up_to_3rd(std::vector<double>{0.0, 0.5, 1.0});
```

Batch evaluator paths are useful when you already have a C++ path library and want to fill derivative matrices directly. The callback receives all query samples in one call, which avoids crossing the C++/Rust boundary once per station.

```cpp
auto path = copp::Path::from_evaluator_3rd(
    1,
    0.0,
    1.0,
    [](copp::Span<const double> s,
       copp::MatrixRef q,
       copp::MatrixRef dq,
       copp::MatrixRef ddq,
       copp::MatrixRef dddq)
    {
        for (std::size_t j = 0; j < s.size(); ++j)
        {
            const double x = s[j];
            q(0, j) = x * x * x;
            dq(0, j) = 3.0 * x * x;
            ddq(0, j) = 6.0 * x;
            dddq(0, j) = 6.0;
        }
    });
```

`evaluate_q`, `evaluate_up_to_2nd`, and `evaluate_up_to_3rd` all return `copp::PathDerivatives`. The requested derivative order determines which optional fields are populated.

## Robot And Constraints

`copp::Robot` is the high-level entry for physical limits. It stores stations, path samples, and optional inverse dynamics. If no inverse-dynamics callback is installed, torque limits and torque objectives use the point-mass fallback `tau = ddq`.

```cpp
copp::Robot robot(dim, s.size());
robot.append_s(s)
    .set_q_from_path_2nd(path, 0, s.size())
    .add_velocity_limits(upper, lower, 0, s.size())
    .add_acceleration_limits(upper, lower, 0, s.size());
```

Install inverse dynamics when torque constraints or torque-dependent objectives should use your robot model.

```cpp
robot.set_inverse_dynamics(
    [](copp::Span<const double> q,
       copp::Span<const double> dq,
       copp::Span<const double> ddq,
       copp::Span<double> tau)
    {
        for (std::size_t i = 0; i < tau.size(); ++i)
        {
            tau[i] = ddq[i] + 0.05 * dq[i] + std::sin(q[i]);
        }
    });
```

`copp::Constraints` is an owning raw mathematical constraint buffer for workflows that do not need robot-side physical-limit conversion. `Robot::constraints()` and `Constraints::ref()` return borrowed `ConstraintsRef` values used by TOPP-style problem descriptors.

```cpp
copp::Constraints constraints(1, s.size());
constraints.append_s(s)
    .add_constraint_1st(std::vector<double>{1.0, 1.0, 1.0}, 0);

auto ref = constraints.ref();
```

Raw constraints follow the Rust/Python mathematical convention:

- first-order: `a <= amax`;
- second-order: `acc_a * a + acc_b * b <= acc_max`;
- third-order: `sqrt(a) * (jerk_a*a + jerk_b*b + jerk_c*c + jerk_d) <= jerk_max`.

## Solver Namespaces

| Namespace                  | Order | Objective | Backend               | Typical use                                     |
| -------------------------- | ----: | --------- | --------------------- | ----------------------------------------------- |
| `copp::solver::topp2_ra`   |     2 | time      | reachability analysis | fast second-order time-optimal planning         |
| `copp::solver::reach_set2` |     2 | none      | reachable set         | second-order feasibility diagnostics            |
| `copp::solver::copp2_socp` |     2 | convex    | Clarabel SOCP         | conic optimization and expert diagnostics       |
| `copp::solver::topp3_lp`   |     3 | time      | Clarabel LP           | linearized third-order baseline                 |
| `copp::solver::topp3_socp` |     3 | time      | Clarabel SOCP         | iterative third-order convex refinement         |
| `copp::solver::copp3_socp` |     3 | convex    | Clarabel SOCP         | third-order convex-objective conic optimization |

`topp2_ra` solves second-order time-optimal problems and returns an accepted `std::vector<double>` profile `a`. Use `reach_set2` when reachable intervals themselves are the main output rather than the recovered profile.

`copp2_socp` solves second-order convex-objective problems through Clarabel. Use `solve` for the accepted profile and `solve_expert` when application code needs solver status, residuals, raw vectors, objective value, or per-objective terms.
`n`topp3_lp` and `topp3_socp` solve third-order time-optimal problems from the shared `copp::solver::topp3::Problem` descriptor. Both return `Profile3rd`; expert variants expose Clarabel diagnostics.
`n`copp3_socp` solves third-order convex-objective problems through Clarabel and exposes expert diagnostics.

## Third-Order Workflow

Third-order solvers operate on the state pair `(a,b)` and usually need a linearization profile `a_linearization`. A common workflow is to start from a simple feasible profile or a TOPP2 result, solve one third-order round, then rebuild the problem with the recovered `profile.a` and solve again.

```cpp
namespace topp3 = copp::solver::topp3;
namespace topp3_socp = copp::solver::topp3_socp;

copp::Boundary3 boundary{0.25, 0.25, 0.0, 0.0};
std::vector<double> a0(s.size(), 0.25);

topp3::Problem first{
    constraints.ref(),
    a0,
    0,
    boundary,
};

copp::clarabel::Options options;
options.allow_almost_solved = true;

auto profile1 = topp3_socp::solve(first, options);

topp3::Problem second{
    constraints.ref(),
    profile1.a,
    0,
    boundary,
};

auto profile2 = topp3_socp::solve(second, options);
auto time = copp::interpolation::s_to_t_topp3(s, profile2, 0.0);
```

`Profile3rd` owns `a`, `b`, and stationary-boundary metadata. Use `Profile3rdRef` or `profile.slice(i, j)` when interpolating or passing only a sub-window without copying profile storage.
`n## Objectives And Clarabel

COPP solvers accept objective descriptors:

```cpp
std::vector<copp::Objective> objectives{
    copp::objective::Time(1.0),
    copp::objective::ThermalEnergy(0.1, {1.0, 1.0}),
};
```

Built-in objective support:

| Objective | COPP2-SOCP | COPP3-SOCP |`n|---|---:|---:|`n| `Time` | yes | yes |`n| `ThermalEnergy` | yes | yes |`n| `Linear` | yes | yes |`n| `TotalVariationTorque` | yes | yes |

Clarabel-backed solvers use `copp::clarabel::Options`:

```cpp
copp::clarabel::Options options;
options.allow_almost_solved = true;
options.clarabel_settings.max_iter = 200;
```

Use `solve_expert` when you need raw Clarabel vectors, status, residuals, objective terms, or linear-solver metadata.

## Data Conventions

`copp::Span<T>` is a non-owning view over a contiguous one-dimensional array. It can be constructed from `std::vector<T>`, `std::array<T, N>`, pointer plus length, or other contiguous storage that outlives the COPP call.

```cpp
std::vector<double> values{0.0, 0.5, 1.0};
copp::Span<const double> from_vector(values);
copp::Span<const double> from_pointer(values.data(), values.size());
```

`copp::Matrix` is an owned column-major storage type. It is intentionally small: it stores matrix data and shape, but it is not a linear algebra package.

```cpp
copp::Matrix matrix(2, 3);
matrix(0, 0) = 0.0;
matrix(1, 0) = 1.0;
```

`Matrix::from_rows` treats each inner list as one row. `Matrix::from_columns` treats each inner list as one column.

```cpp
auto table = copp::Matrix::from_rows({
    {0.0, 0.5, 1.0},
    {0.0, 0.25, 1.0},
});

auto waypoints = copp::Matrix::from_columns({
    {0.0, 0.0},
    {0.5, 0.25},
    {1.0, 1.0},
});
```

{% raw %}
`Path::from_waypoints({{...}, {...}})` uses waypoint-list notation: each inner list is one waypoint vector. If you pass a `MatrixView` instead, the matrix shape is `(dim x n_points)`, where each column is one waypoint.
{% endraw %}

`copp::MatrixView` is a borrowed input view. It accepts column-major, row-major, and strided column-major layouts. Column-major input is the zero-copy fast path. Row-major input may be copied once before entering the Rust core, depending on the operation.

```cpp
auto cm = copp::MatrixView::column_major(data, rows, cols);
auto rm = copp::MatrixView::row_major(data, rows, cols);
auto scm = copp::MatrixView::strided_column_major(data, rows, cols, leading_dim);
```

`copp::MatrixRef` is a writable borrowed column-major matrix view used by evaluator callbacks. Output matrices in callbacks have shape `(dim, s.size())` and are indexed as `(row, column)`.

If `COPP_CPP_WITH_EIGEN=ON`, include `copp/eigen.hpp` to borrow Eigen storage or map returned COPP matrices:

```cpp
#include <copp/eigen.hpp>

Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor> waypoints(2, 3);
auto path = copp::Path::from_waypoints(copp::eigen::matrix_view(waypoints));

auto out = path.evaluate_q(std::vector<double>{0.0, 1.0});
auto q_eigen = copp::eigen::map(out.q);
```

## Error Handling

The default C++ API throws `copp::Error`. It carries a machine-readable `copp::Status` and an owned diagnostic message.

{% raw %}
```cpp
try
{
    auto path = copp::Path::from_waypoints({{0.0}, {1.0}});
}
catch (const copp::Error &error)
{
    std::cerr << error.what() << "\n";
}
```
{% endraw %}

Selected APIs provide no-throw overloads with `copp::no_throw` as the final argument. The function name remains the same, but the return type becomes `copp::Expected<T>`. This is a status-return convenience API implemented by catching exceptions from the normal throwing API; it is not a promise that the C++ binding can be compiled with C++ exceptions disabled.

{% raw %}
```cpp
auto result = copp::Path::from_waypoints({{0.0}, {1.0}}, copp::no_throw);
if (!result)
{
    std::cerr << result.error().message << "\n";
    return;
}

auto path = std::move(result).value();
```
{% endraw %}

Callbacks may throw `copp::Error` or another `std::exception`. The facade converts callback failures into COPP diagnostics when they cross back into Rust. For predictable diagnostics, prefer throwing `copp::Error` with a specific status when the callback detects invalid user data.

## Header Model

Use the umbrella header for application code:

```cpp
#include <copp/copp.hpp>
```

Public headers are hand-written and live under `bindings/cpp/include/copp/`. Generated Rust-C++ bridge headers are private implementation details and are not part of the installed user-facing API.

| Header                   | Purpose                                                                                 |
| ------------------------ | --------------------------------------------------------------------------------------- |
| `copp/copp.hpp`          | umbrella header                                                                         |
| `copp/core.hpp`          | `Span`, `Matrix`, `MatrixView`, `MatrixRef`, `Expected`, `Error`, boundaries, intervals |
| `copp/path.hpp`          | waypoint paths, Jet3 parametric paths, batch evaluator paths                            |
| `copp/robot.hpp`         | `Robot`, `Constraints`, `ConstraintsRef`, inverse dynamics callbacks                    |
| `copp/interpolation.hpp` | TOPP2/TOPP3 `s_to_t`, `t_to_s`, `Profile3rd`, `Profile3rdRef`                           |
| `copp/objective.hpp`     | `Objective` and `copp::objective::*` factories                                          |
| `copp/clarabel.hpp`      | raw Clarabel settings, options, statuses, solver diagnostics                            |
| `copp/eigen.hpp`         | optional Eigen adapters                                                                 |
| `copp/solver/*.hpp`      | TOPP/COPP solver namespaces                                                             |

## Package Layout

```text
bindings/cpp/
  README.md
  CMakeLists.txt
  include/copp/          # public C++ headers
  src/                   # public facade implementation
  cmake/                 # installed package config templates
  examples/              # runnable tutorial examples
  tests/                 # lightweight C++ smoke tests
  docs/                  # Doxygen source pages and generated HTML
  scripts/               # documentation-generation scripts
```

## Documentation

Generate C++ Doxygen docs:

```powershell
powershell -ExecutionPolicy Bypass -File bindings/cpp/scripts/generate_docs.ps1
```

Important generated pages:

- `bindings/cpp/docs/html/index.html`;
- `bindings/cpp/docs/html/cpp_core_types.html`;
- `bindings/cpp/docs/html/cpp_path.html`;
- `bindings/cpp/docs/html/cpp_robot_constraints.html`;
- `bindings/cpp/docs/html/cpp_interpolation.html`;
- `bindings/cpp/docs/html/cpp_objectives_clarabel.html`;
- `bindings/cpp/docs/html/cpp_solvers.html`.

The generated reference includes every public header, namespace summaries, tutorial pages, and example source pages.

## Examples

Path construction:

- [`examples/path_from_waypoints.cpp`](examples/path_from_waypoints.cpp);
- [`examples/path_from_parametric.cpp`](examples/path_from_parametric.cpp);
- [`examples/path_from_evaluator_2nd.cpp`](examples/path_from_evaluator_2nd.cpp);
- [`examples/path_from_evaluator_3rd.cpp`](examples/path_from_evaluator_3rd.cpp).

Robot and dynamics:

- [`examples/robot_inverse_dynamics.cpp`](examples/robot_inverse_dynamics.cpp).

Solvers:

- [`examples/topp2_ra.cpp`](examples/topp2_ra.cpp);
- [`examples/reach_set2.cpp`](examples/reach_set2.cpp);
- [`examples/copp2_socp.cpp`](examples/copp2_socp.cpp);
- [`examples/topp3_lp.cpp`](examples/topp3_lp.cpp);
- [`examples/topp3_socp.cpp`](examples/topp3_socp.cpp);
- [`examples/copp3_socp.cpp`](examples/copp3_socp.cpp).

## Troubleshooting

### CMake Cannot Find The COPP Library

Run the matching Cargo build before configuring CMake:

```powershell
cargo build --release --lib --features cpp
```

If `COPP_INSTALL_C_ABI_TARGET=ON`, use:

```powershell
cargo build --release --lib --features cpp,c
```

Pass `-DCOPP_TARGET_DIR=<path-to-target-release>` if you are using a non-default Cargo target directory.

### Eigen Cannot Be Found

Either install Eigen3 so `find_package(Eigen3 CONFIG REQUIRED)` succeeds, or configure with:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_CPP_WITH_EIGEN=OFF
```

Do not include `copp/eigen.hpp` when `COPP_CPP_WITH_EIGEN=OFF`.

### Downstream `find_package` Fails

Make sure the downstream project sees the install prefix:

```powershell
cmake -S app -B app/build -DCMAKE_PREFIX_PATH=<install-prefix>
```

During this private-branch stage the package name is `copp`, so downstream code should call `find_package(copp CONFIG REQUIRED)`.

### Runtime DLL Is Missing On Windows

For dynamic linking, ensure `copp_cpp.dll` is next to your executable or visible through `PATH`. The source-tree examples/tests copy the DLL automatically, but your own downstream executable may need an install step or an explicit post-build copy.

### Static Linking Fails With Missing Native Symbols

Pass additional native libraries through:

```powershell
cmake -S bindings/cpp -B bindings/cpp/build -DCOPP_LINK_STATIC=ON -DCOPP_STATIC_LINK_LIBRARIES="lib1;lib2"
```

### Doxygen Formulas Or Graphs Do Not Render

Install Doxygen and Graphviz, then regenerate the docs from the repository root:

```powershell
powershell -ExecutionPolicy Bypass -File bindings/cpp/scripts/generate_docs.ps1
```

The C++ Doxygen config uses MathJax for formulas, so generated HTML may need browser access to the MathJax CDN unless a local MathJax path is configured.