PineForge v0.13.1-379-g9b50973
Deterministic PineScript v6 backtest runtime — C ABI reference
Loading...
Searching...
No Matches
drawing.hpp
Go to the documentation of this file.
1#pragma once
2//
3// PineForge drawing-objects-as-data runtime.
4//
5// Spec: docs/drawing-objects-as-data.md §3 ("C++ Runtime").
6//
7// This is a HEADLESS backtester: drawing objects (line/box/label/linefill) are
8// pure DATA the strategy can create, mutate and read back into trading logic.
9// There is NO rendering, NO graphics — only geometry. The header is fully
10// self-contained and header-only (every function is `inline`); codegen emits
11// calls to the `pf_*` free functions below. Nothing here is exported via the C
12// ABI — these are codegen-emitted in-process calls, not `c_abi.cpp` symbols.
13//
14// The header is gated into the generated include block behind `self._uses_drawing`
15// exactly like `matrix.hpp` is gated behind `_uses_matrix`. The arenas are members
16// of the generated strategy subclass, so reset-per-run is automatic (a fresh
17// strategy instance is constructed per backtest).
18//
19#include <vector>
20#include <deque>
21#include <string>
22#include <algorithm>
23#include <cstdint>
24#include <stdexcept>
25#include <cmath>
26#include <utility>
27#include <pineforge/na.hpp>
28
29namespace pineforge {
30
31// ---- enums (spec §3.2) -----------------------------------------------------
32enum class XLoc : int { bar_index = 0, bar_time = 1 }; // default bar_index
33enum class YLoc : int { price = 0, abovebar = 1, belowbar = 2 }; // default price
34
35// ---- value-view handles (spec §3.2) ----------------------------------------
36// Stored in vars / array<T> / UDT fields. id < 0 == na.
37struct Line { int32_t id = -1; };
38struct Box { int32_t id = -1; };
39struct Label { int32_t id = -1; };
40struct Linefill { int32_t id = -1; };
41
42inline bool is_na(const Line& h) { return h.id < 0; }
43inline bool is_na(const Box& h) { return h.id < 0; }
44inline bool is_na(const Label& h) { return h.id < 0; }
45inline bool is_na(const Linefill& h) { return h.id < 0; }
46
47// ---- pure value type: chart.point (spec §3.2) ------------------------------
48// No arena, no identity, no delete. Lowered inline as aggregate literals by
49// codegen; the engine only needs the struct + its is_na overload.
50struct ChartPoint {
51 int64_t index = na<int64_t>(); // x-coord as bar index (na sentinel INT64_MIN)
52 int64_t time = na<int64_t>(); // x-coord as UNIX ms
53 double price = na<double>(); // y-coord (NaN sentinel)
54};
55inline bool is_na(const ChartPoint& p) {
56 return is_na(p.index) && is_na(p.time) && is_na(p.price);
57}
58
59// ---- arena records (spec §3.3) ---------------------------------------------
60// Geometry only — every visual field (color/style/width/...) is dropped at
61// lowering. x-coords are int64_t (xloc.bar_time stores UNIX-ms, overflows
62// int32; na sentinel INT64_MIN); y-coords/prices are double (NaN sentinel).
63// NOTE: BoxRec has NO extend and NO text fields (unlike LineRec / LabelRec).
64struct LineRec { int64_t x1, x2; double y1, y2; XLoc xloc; bool ext_l, ext_r; bool alive; };
65struct BoxRec { int64_t left, right; double top, bottom; XLoc xloc; bool alive; };
66struct LabelRec { int64_t x; double y; XLoc xloc; YLoc yloc; std::string text; bool alive; };
67struct LinefillRec { int32_t line1, line2; bool alive; };
68
69// ---- error type (spec §3.7) ------------------------------------------------
70// Derives from std::runtime_error so BacktestEngine::run()'s existing
71// `catch (const std::exception& e)` (src/engine_run.cpp) halts the backtest
72// exactly like TradingView when a drawing op touches a na handle.
73// Drawing is ALWAYS-ON and ALWAYS TV-faithful: no compile flag, no lenient mode.
74struct pine_drawing_error : std::runtime_error {
75 explicit pine_drawing_error(const std::string& msg) : std::runtime_error(msg) {}
76};
77
78// A na handle (id < 0) or an out-of-range id is ALWAYS an error — there is no
79// record to read. NOTE: a DEAD record (alive == false, after an explicit
80// line.delete() or FIFO eviction) is NOT an error — TV keeps the object's data
81// read/writable after delete (the object only leaves the chart + the *.all
82// list), so the engine must too. See corpus/validation/drawing-delete-halt,
83// where TV keeps producing trades (2504) after the strategy's line.delete().
84
85// ---- arena template (spec §3.4) --------------------------------------------
86// ids are monotonic and never reused (dead slots are tombstoned, not recycled)
87// -> no ABA, reproducible across runs. Exact-cap FIFO eviction (oldest first).
88template <class Rec>
90 std::vector<Rec> recs_; // index == id; dead slots retained so ids are stable & monotonic
91 std::deque<int32_t> order_; // live ids, oldest -> newest == the *.all order; drives FIFO eviction
92 int cap_;
93public:
94 explicit DrawingArena(int cap = 50) : cap_(std::max(1, cap)) {}
95
96 int32_t alloc(Rec r) {
97 while ((int)order_.size() >= cap_) { // exact-cap FIFO eviction (oldest first)
98 int32_t old = order_.front();
99 order_.pop_front();
100 recs_[old].alive = false;
101 }
102 int32_t id = (int32_t)recs_.size();
103 r.alive = true;
104 recs_.push_back(std::move(r));
105 order_.push_back(id);
106 return id;
107 }
108
109 void erase(int32_t id) { // delete(): silent no-op on na/dead
110 if (!alive(id)) return;
111 recs_[id].alive = false;
112 order_.erase(std::find(order_.begin(), order_.end(), id));
113 }
114
115 bool alive(int32_t id) const { return id >= 0 && id < (int)recs_.size() && recs_[id].alive; }
116
117 // Number of slots ever allocated (dead slots retained). ids in [0, size())
118 // reference a real record (live OR dead); ids outside that range or < 0 are
119 // na/invalid.
120 int32_t size() const { return (int32_t)recs_.size(); }
121
122 Rec& at(int32_t id) { return recs_[id]; }
123 const Rec& at(int32_t id) const { return recs_[id]; }
124
125 const std::deque<int32_t>& order() const { return order_; } // backs line.all/box.all if ever needed
126};
127
128// ---- shared helpers --------------------------------------------------------
129// Returns a reference to the record for a handle. Only a na handle (id < 0) or
130// an out-of-range id throws; a DEAD record (alive == false, e.g. after an
131// explicit line.delete() or FIFO eviction) is returned as-is — its geometry
132// remains read/writable, matching TV (delete leaves the chart + *.all, not the
133// data). See corpus/validation/drawing-delete-halt (2504 post-delete trades).
134template <class Rec, class Handle>
135inline Rec& pf_require_live(DrawingArena<Rec>& a, Handle h) {
136 if (h.id < 0 || h.id >= a.size()) throw pine_drawing_error("drawing access on na handle");
137 return a.at(h.id);
138}
139
140// Selects the x-coordinate a ChartPoint contributes for a given xloc.
141inline int64_t pf_point_x(const ChartPoint& p, XLoc xloc) {
142 return (xloc == XLoc::bar_time) ? p.time : p.index;
143}
144
145// ============================ line ==========================================
146inline Line pf_line_new(DrawingArena<LineRec>& a, int64_t x1, double y1, int64_t x2, double y2,
147 XLoc xloc = XLoc::bar_index, bool el = false, bool er = false) {
148 // *_new(na,na,na,na) allocates a LIVE record with na coords (NOT a na handle).
149 return Line{ a.alloc(LineRec{ x1, x2, y1, y2, xloc, el, er, true }) };
150}
152 XLoc xloc = XLoc::bar_index) {
153 return Line{ a.alloc(LineRec{ pf_point_x(p1, xloc), pf_point_x(p2, xloc),
154 p1.price, p2.price, xloc, false, false, true }) };
155}
156
157inline int64_t pf_line_get_x1(DrawingArena<LineRec>& a, Line h) { return pf_require_live(a, h).x1; }
158inline int64_t pf_line_get_x2(DrawingArena<LineRec>& a, Line h) { return pf_require_live(a, h).x2; }
159inline double pf_line_get_y1(DrawingArena<LineRec>& a, Line h) { return pf_require_live(a, h).y1; }
160inline double pf_line_get_y2(DrawingArena<LineRec>& a, Line h) { return pf_require_live(a, h).y2; }
161
162inline void pf_line_set_x1(DrawingArena<LineRec>& a, Line h, int64_t v) { pf_require_live(a, h).x1 = v; }
163inline void pf_line_set_x2(DrawingArena<LineRec>& a, Line h, int64_t v) { pf_require_live(a, h).x2 = v; }
164inline void pf_line_set_y1(DrawingArena<LineRec>& a, Line h, double v) { pf_require_live(a, h).y1 = v; }
165inline void pf_line_set_y2(DrawingArena<LineRec>& a, Line h, double v) { pf_require_live(a, h).y2 = v; }
166inline void pf_line_set_xy1(DrawingArena<LineRec>& a, Line h, int64_t x, double y) {
167 LineRec& r = pf_require_live(a, h); r.x1 = x; r.y1 = y;
168}
169inline void pf_line_set_xy2(DrawingArena<LineRec>& a, Line h, int64_t x, double y) {
170 LineRec& r = pf_require_live(a, h); r.x2 = x; r.y2 = y;
171}
173 LineRec& r = pf_require_live(a, h); r.x1 = pf_point_x(p, r.xloc); r.y1 = p.price;
174}
176 LineRec& r = pf_require_live(a, h); r.x2 = pf_point_x(p, r.xloc); r.y2 = p.price;
177}
178inline void pf_line_set_xloc(DrawingArena<LineRec>& a, Line h, int64_t x1, int64_t x2, XLoc xloc) {
179 // REINTERPRET: stores new numbers + flag; does NOT convert coord spaces (TV-faithful, spec §3.9).
180 LineRec& r = pf_require_live(a, h); r.x1 = x1; r.x2 = x2; r.xloc = xloc;
181}
182
183// real computation: infinite line, ignores extend; valid for xloc.bar_index (spec §3.6).
184inline double pf_line_get_price(DrawingArena<LineRec>& a, Line h, int64_t x) {
185 if (h.id < 0 || h.id >= a.size()) // na/out-of-range line -> throw -> halt (like TV)
186 throw pine_drawing_error("drawing access on na handle");
187 const LineRec& r = a.at(h.id); // dead record's geometry is still readable (TV-faithful)
188 if (r.xloc == XLoc::bar_time)
189 throw pine_drawing_error("line.get_price requires xloc.bar_index"); // TV errors on bar_time lines
190 if (r.x1 == r.x2) return na<double>(); // degenerate vertical -> na (matches TV)
191 return r.y1 + (r.y2 - r.y1) / (double)(r.x2 - r.x1) * (double)(x - r.x1); // infinite line, ignores extend
192}
193
195 if (!a.alive(h.id)) return Line{}; // copy of na/dead -> na handle (silent)
196 LineRec r = a.at(h.id); // deep copy = independent new arena record
197 return Line{ a.alloc(r) };
198}
199inline void pf_line_delete(DrawingArena<LineRec>& a, Line h) { a.erase(h.id); } // silent no-op on na/dead
200
201// ============================ box ===========================================
202inline Box pf_box_new(DrawingArena<BoxRec>& a, int64_t left, double top, int64_t right, double bottom,
203 XLoc xloc = XLoc::bar_index) {
204 return Box{ a.alloc(BoxRec{ left, right, top, bottom, xloc, true }) };
205}
207 XLoc xloc = XLoc::bar_index) {
208 return Box{ a.alloc(BoxRec{ pf_point_x(top_left, xloc), pf_point_x(bottom_right, xloc),
209 top_left.price, bottom_right.price, xloc, true }) };
210}
211
212inline int64_t pf_box_get_left (DrawingArena<BoxRec>& a, Box h) { return pf_require_live(a, h).left; }
213inline int64_t pf_box_get_right (DrawingArena<BoxRec>& a, Box h) { return pf_require_live(a, h).right; }
214inline double pf_box_get_top (DrawingArena<BoxRec>& a, Box h) { return pf_require_live(a, h).top; }
215inline double pf_box_get_bottom(DrawingArena<BoxRec>& a, Box h) { return pf_require_live(a, h).bottom; }
216
217inline void pf_box_set_left (DrawingArena<BoxRec>& a, Box h, int64_t v) { pf_require_live(a, h).left = v; }
218inline void pf_box_set_right (DrawingArena<BoxRec>& a, Box h, int64_t v) { pf_require_live(a, h).right = v; }
219inline void pf_box_set_top (DrawingArena<BoxRec>& a, Box h, double v) { pf_require_live(a, h).top = v; }
220inline void pf_box_set_bottom(DrawingArena<BoxRec>& a, Box h, double v) { pf_require_live(a, h).bottom = v; }
221inline void pf_box_set_lefttop(DrawingArena<BoxRec>& a, Box h, int64_t left, double top) {
222 BoxRec& r = pf_require_live(a, h); r.left = left; r.top = top;
223}
224inline void pf_box_set_rightbottom(DrawingArena<BoxRec>& a, Box h, int64_t right, double bottom) {
225 BoxRec& r = pf_require_live(a, h); r.right = right; r.bottom = bottom;
226}
228 BoxRec& r = pf_require_live(a, h); r.left = pf_point_x(p, r.xloc); r.top = p.price;
229}
231 BoxRec& r = pf_require_live(a, h); r.right = pf_point_x(p, r.xloc); r.bottom = p.price;
232}
233inline void pf_box_set_xloc(DrawingArena<BoxRec>& a, Box h, int64_t left, int64_t right, XLoc xloc) {
234 BoxRec& r = pf_require_live(a, h); r.left = left; r.right = right; r.xloc = xloc; // reinterpret only
235}
236
238 if (!a.alive(h.id)) return Box{};
239 BoxRec r = a.at(h.id);
240 return Box{ a.alloc(r) };
241}
242inline void pf_box_delete(DrawingArena<BoxRec>& a, Box h) { a.erase(h.id); }
243
244// ============================ label =========================================
245// NOTE: yloc.abovebar / yloc.belowbar should auto-place y at the bar high/low
246// (spec §3.9). The spec's pf_label_new signature is intentionally decoupled from
247// the engine and does NOT receive bar high/low, so y is stored AS-IS and yloc is
248// store-only here. yloc abovebar/belowbar auto-place needs bar high/low at the
249// call site — codegen would pass close()/high()/low() if ever required. Inert:
250// no corpus label reads get_y back (spec note 4), so no parity claim.
251inline Label pf_label_new(DrawingArena<LabelRec>& a, int64_t x, double y, std::string text,
252 XLoc xloc = XLoc::bar_index, YLoc yloc = YLoc::price) {
253 return Label{ a.alloc(LabelRec{ x, y, xloc, yloc, std::move(text), true }) };
254}
255inline Label pf_label_new_pt(DrawingArena<LabelRec>& a, ChartPoint point, std::string text,
256 YLoc yloc = YLoc::price) {
257 // xloc carried by the ChartPoint: bar_time only when index is na but time is set.
258 XLoc xloc = (is_na(point.index) && !is_na(point.time)) ? XLoc::bar_time : XLoc::bar_index;
259 return Label{ a.alloc(LabelRec{ pf_point_x(point, xloc), point.price, xloc, yloc,
260 std::move(text), true }) };
261}
262
263inline int64_t pf_label_get_x(DrawingArena<LabelRec>& a, Label h) { return pf_require_live(a, h).x; }
264inline double pf_label_get_y(DrawingArena<LabelRec>& a, Label h) { return pf_require_live(a, h).y; }
265inline std::string pf_label_get_text(DrawingArena<LabelRec>& a, Label h) { return pf_require_live(a, h).text; }
266
267inline void pf_label_set_x(DrawingArena<LabelRec>& a, Label h, int64_t v) { pf_require_live(a, h).x = v; }
268inline void pf_label_set_y(DrawingArena<LabelRec>& a, Label h, double v) { pf_require_live(a, h).y = v; }
269inline void pf_label_set_xy(DrawingArena<LabelRec>& a, Label h, int64_t x, double y) {
270 LabelRec& r = pf_require_live(a, h); r.x = x; r.y = y;
271}
273 LabelRec& r = pf_require_live(a, h); r.x = pf_point_x(p, r.xloc); r.y = p.price;
274}
275inline void pf_label_set_xloc(DrawingArena<LabelRec>& a, Label h, int64_t x, XLoc xloc) {
276 LabelRec& r = pf_require_live(a, h); r.x = x; r.xloc = xloc; // sets x AND xloc
277}
279 pf_require_live(a, h).yloc = yloc;
280}
281inline void pf_label_set_text(DrawingArena<LabelRec>& a, Label h, std::string text) {
282 pf_require_live(a, h).text = std::move(text);
283}
284
286 if (!a.alive(h.id)) return Label{};
287 LabelRec r = a.at(h.id); // deep copy (incl. std::string text)
288 return Label{ a.alloc(std::move(r)) };
289}
291
292// ============================ linefill ======================================
294 // Stores the two Line handle ids inertly (color dropped). linefill.new(na,na,na)
295 // is legal -> a LIVE record over na line ids (spec note 1).
296 return Linefill{ a.alloc(LinefillRec{ l1.id, l2.id, true }) };
297}
299 return Line{ pf_require_live(a, h).line1 };
300}
302 return Line{ pf_require_live(a, h).line2 };
303}
305
306// ============================ visual no-op sink (spec §3.6) ==================
307// Evaluates + discards args (so side effects/typechecking still happen), returns void.
308// Backs every VISUAL setter/ctor-kwarg: line/box/label set_color/set_style/...
309template <class... A> inline void pf_noop(A&&...) {}
310
311} // namespace pineforge
const std::deque< int32_t > & order() const
Definition drawing.hpp:125
int32_t alloc(Rec r)
Definition drawing.hpp:96
DrawingArena(int cap=50)
Definition drawing.hpp:94
int32_t size() const
Definition drawing.hpp:120
Rec & at(int32_t id)
Definition drawing.hpp:122
const Rec & at(int32_t id) const
Definition drawing.hpp:123
bool alive(int32_t id) const
Definition drawing.hpp:115
void erase(int32_t id)
Definition drawing.hpp:109
void pf_linefill_delete(DrawingArena< LinefillRec > &a, Linefill h)
Definition drawing.hpp:304
void pf_line_set_xy1(DrawingArena< LineRec > &a, Line h, int64_t x, double y)
Definition drawing.hpp:166
std::string pf_label_get_text(DrawingArena< LabelRec > &a, Label h)
Definition drawing.hpp:265
void pf_label_delete(DrawingArena< LabelRec > &a, Label h)
Definition drawing.hpp:290
void pf_line_set_xy2(DrawingArena< LineRec > &a, Line h, int64_t x, double y)
Definition drawing.hpp:169
double pf_line_get_y1(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:159
void pf_box_set_bottom_right_point(DrawingArena< BoxRec > &a, Box h, ChartPoint p)
Definition drawing.hpp:230
Line pf_linefill_get_line2(DrawingArena< LinefillRec > &a, Linefill h)
Definition drawing.hpp:301
int64_t pf_point_x(const ChartPoint &p, XLoc xloc)
Definition drawing.hpp:141
Line pf_line_copy(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:194
void pf_box_set_top(DrawingArena< BoxRec > &a, Box h, double v)
Definition drawing.hpp:219
void pf_noop(A &&...)
Definition drawing.hpp:309
Box pf_box_new(DrawingArena< BoxRec > &a, int64_t left, double top, int64_t right, double bottom, XLoc xloc=XLoc::bar_index)
Definition drawing.hpp:202
void pf_box_set_lefttop(DrawingArena< BoxRec > &a, Box h, int64_t left, double top)
Definition drawing.hpp:221
void pf_label_set_point(DrawingArena< LabelRec > &a, Label h, ChartPoint p)
Definition drawing.hpp:272
int64_t na< int64_t >()
Definition na.hpp:19
Box pf_box_new_pts(DrawingArena< BoxRec > &a, ChartPoint top_left, ChartPoint bottom_right, XLoc xloc=XLoc::bar_index)
Definition drawing.hpp:206
void pf_box_set_right(DrawingArena< BoxRec > &a, Box h, int64_t v)
Definition drawing.hpp:218
Line pf_line_new_pts(DrawingArena< LineRec > &a, ChartPoint p1, ChartPoint p2, XLoc xloc=XLoc::bar_index)
Definition drawing.hpp:151
void pf_box_set_xloc(DrawingArena< BoxRec > &a, Box h, int64_t left, int64_t right, XLoc xloc)
Definition drawing.hpp:233
void pf_box_set_top_left_point(DrawingArena< BoxRec > &a, Box h, ChartPoint p)
Definition drawing.hpp:227
double pf_label_get_y(DrawingArena< LabelRec > &a, Label h)
Definition drawing.hpp:264
Line pf_line_new(DrawingArena< LineRec > &a, int64_t x1, double y1, int64_t x2, double y2, XLoc xloc=XLoc::bar_index, bool el=false, bool er=false)
Definition drawing.hpp:146
void pf_label_set_xy(DrawingArena< LabelRec > &a, Label h, int64_t x, double y)
Definition drawing.hpp:269
void pf_line_set_y1(DrawingArena< LineRec > &a, Line h, double v)
Definition drawing.hpp:164
Line pf_linefill_get_line1(DrawingArena< LinefillRec > &a, Linefill h)
Definition drawing.hpp:298
Label pf_label_new(DrawingArena< LabelRec > &a, int64_t x, double y, std::string text, XLoc xloc=XLoc::bar_index, YLoc yloc=YLoc::price)
Definition drawing.hpp:251
int64_t pf_box_get_left(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:212
double na< double >()
Definition na.hpp:17
void pf_box_set_rightbottom(DrawingArena< BoxRec > &a, Box h, int64_t right, double bottom)
Definition drawing.hpp:224
int64_t pf_line_get_x2(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:158
int64_t pf_line_get_x1(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:157
void pf_line_set_x2(DrawingArena< LineRec > &a, Line h, int64_t v)
Definition drawing.hpp:163
double pf_box_get_bottom(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:215
Linefill pf_linefill_new(DrawingArena< LinefillRec > &a, Line l1, Line l2)
Definition drawing.hpp:293
void pf_label_set_x(DrawingArena< LabelRec > &a, Label h, int64_t v)
Definition drawing.hpp:267
void pf_label_set_text(DrawingArena< LabelRec > &a, Label h, std::string text)
Definition drawing.hpp:281
void pf_label_set_yloc(DrawingArena< LabelRec > &a, Label h, YLoc yloc)
Definition drawing.hpp:278
bool is_na(const Line &h)
Definition drawing.hpp:42
void pf_line_set_xloc(DrawingArena< LineRec > &a, Line h, int64_t x1, int64_t x2, XLoc xloc)
Definition drawing.hpp:178
Rec & pf_require_live(DrawingArena< Rec > &a, Handle h)
Definition drawing.hpp:135
void pf_line_set_first_point(DrawingArena< LineRec > &a, Line h, ChartPoint p)
Definition drawing.hpp:172
double pf_box_get_top(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:214
void pf_line_set_x1(DrawingArena< LineRec > &a, Line h, int64_t v)
Definition drawing.hpp:162
Label pf_label_new_pt(DrawingArena< LabelRec > &a, ChartPoint point, std::string text, YLoc yloc=YLoc::price)
Definition drawing.hpp:255
double pf_line_get_y2(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:160
int64_t pf_label_get_x(DrawingArena< LabelRec > &a, Label h)
Definition drawing.hpp:263
void pf_box_delete(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:242
void pf_line_set_second_point(DrawingArena< LineRec > &a, Line h, ChartPoint p)
Definition drawing.hpp:175
void pf_line_delete(DrawingArena< LineRec > &a, Line h)
Definition drawing.hpp:199
void pf_box_set_left(DrawingArena< BoxRec > &a, Box h, int64_t v)
Definition drawing.hpp:217
int64_t pf_box_get_right(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:213
double pf_line_get_price(DrawingArena< LineRec > &a, Line h, int64_t x)
Definition drawing.hpp:184
void pf_line_set_y2(DrawingArena< LineRec > &a, Line h, double v)
Definition drawing.hpp:165
void pf_label_set_y(DrawingArena< LabelRec > &a, Label h, double v)
Definition drawing.hpp:268
Label pf_label_copy(DrawingArena< LabelRec > &a, Label h)
Definition drawing.hpp:285
void pf_label_set_xloc(DrawingArena< LabelRec > &a, Label h, int64_t x, XLoc xloc)
Definition drawing.hpp:275
void pf_box_set_bottom(DrawingArena< BoxRec > &a, Box h, double v)
Definition drawing.hpp:220
Box pf_box_copy(DrawingArena< BoxRec > &a, Box h)
Definition drawing.hpp:237
std::string text
Definition drawing.hpp:66
pine_drawing_error(const std::string &msg)
Definition drawing.hpp:75