Lumiera 0.pre.04~rc.1
»edit your freedom«
Loading...
Searching...
No Matches
stave-bracket-widget.cpp
Go to the documentation of this file.
1/*
2 StaveBracketWidget - track body area to show overview and timecode and markers
3
4 Copyright (C)
5 2023, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12* *****************************************************************/
13
14
44#include "lib/util.hpp"
45
46#include <cmath>
47
48
49
50using util::min;
51using util::max;
52using std::ceil;
53
54
55namespace stage {
56namespace timeline {
57
58 namespace {//---------Implementation-details--Stave-Bracket-design-----------------------
59
60 const uint FALLBACK_FONT_SIZE_px = 12.5; // (assuming 96dpi and 10 Point font)
61 const uint POINT_PER_INCH = 72; // typographic point ≔ 1/72 inch
62
63 const double BASE_WIDTH_PER_EM = 0.5; // scale factor: width of double line relative to font size
64
65 const double ORG = 0.0;
66 const double PHI = (1.0 + sqrt(5)) / 2.0; // Golden Ratio Φ ≔ ½·(1+√5) ≈ 1.6180339887498948482
67 const double PHI_MAJOR = PHI - 1.0; // 1/Φ = Φ-1
68 const double PHI_MINOR = 2.0 - PHI; // 1-1/Φ = 2-Φ
69 const double PHISQUARE = 1.0 + PHI; // Φ² = Φ+1
70 const double PHI_MINSQ = 5.0 - 3*PHI; // Φ-minor of Φ-minor : (2-Φ)²= 2²-4Φ + Φ²
71
72 const double BAR_WIDTH = PHI_MINOR; // the main (bold) vertical bar line is right aligned to axis
73 const double BAR_LEFT = -BAR_WIDTH;
74 const double LIN_WIDTH = PHI_MINSQ; // thin line is Φ-minor of bold line (which itself is Φ-minor)
75 const double LIN_LEFT = PHI_MAJOR - LIN_WIDTH; // main line and thin line create a Φ-division
76
78 const double SQUARE_TIP_Y = -PHISQUARE;
79 const double SQUARE_MINOR = 1.0;
80
81 const double ARC_O_XC = -(3.0 + PHI);
82 const double ARC_O_YC = -6.8541019662496847; // +Y points downwards
83 const double ARC_O_R = 8.0574801069408135; // Radius of the arc segment
84 const double ARC_O_TIP = 0.5535743588970450; // Radians ↻ clockwise from +X
85 const double ARC_O_END = 1.0172219678978512;
86
87 const double ARC_I_XC = -2.5;
88 const double ARC_I_YC = -7.3541019662496883;
89 const double ARC_I_R = 6.6978115661011230;
90 const double ARC_I_TIP = 0.7853981633974485;
91 const double ARC_I_END = 1.2490457723982538;
92
97 double
99 {
100 Pango::FontDescription font = style->get_font (Gtk::STATE_FLAG_NORMAL);
101 auto sizeSpec = double(font.get_size()) / PANGO_SCALE;
102 // Note: size specs are given as integers with multiplier PANGO_SCALE (typically 1024)
103 if (sizeSpec <=0) return FALLBACK_FONT_SIZE_px;
104 if (not font.get_size_is_absolute())
105 {// size is given relative (in points)
106 auto screen = style->get_screen();
107 if (not screen) return FALLBACK_FONT_SIZE_px;
108 double dpi = screen->get_resolution();
109 sizeSpec *= dpi / POINT_PER_INCH;
110 } // spec{points}/point_per_inch*pixel_per_inch ⟼ pixel
111 return sizeSpec;
112 }
113
120 double
122 {
123 return BASE_WIDTH_PER_EM * getAbsoluteFontSize (style);
124 }
125
140 double
141 determineScale (StyleC style, int givenHeight)
142 {
143 auto required = 2*PHISQUARE + style->get_padding().get_top()
144 + style->get_padding().get_bottom();
145 auto maxScale = givenHeight / (required);
146 return min (maxScale, baseWidth (style));
147 }
148
154 int
155 calcRequiredWidth (StyleC style, int givenHeight)
156 {
157 return ceil (PHISQUARE * determineScale (style,givenHeight)
158 +style->get_padding().get_right()
159 +style->get_padding().get_left()
160 );
161 }
162
164 int
166 {
167 return ceil (PHISQUARE * baseWidth (style)
168 +style->get_padding().get_right()
169 +style->get_padding().get_left()
170 );
171 }
172
176 double
177 anchorLeft (StyleC style, double scale)
178 {
179 return style->get_padding().get_left()
180 + scale * BAR_WIDTH;
181 }
182
186 double
187 anchorUpper (StyleC style, double scale)
188 {
189 return style->get_padding().get_top()
190 - scale * SQUARE_TIP_Y;
191 }
192
196 double
197 anchorLower (StyleC style, double scale, int canvasHeight)
198 {
199 auto lowerAnchor
200 = canvasHeight
201 - (style->get_padding().get_bottom()
202 - scale * SQUARE_TIP_Y);
203 auto minHeight = PHISQUARE*scale + style->get_padding().get_top();
204 return max (lowerAnchor, minHeight); // Fallback: both caps back to back
205 }
206
207
216 void
217 drawCap (CairoC cox, Gdk::RGBA colour, double ox, double oy, double scale, bool upside=true)
218 {
219 cox->save();
220 cox->translate (ox,oy);
221 cox->scale (scale, upside? scale:-scale);
222 cox->set_source_rgba(colour.get_red()
223 ,colour.get_green()
224 ,colour.get_blue()
225 ,colour.get_alpha());
226 // draw the inner contour of the bracket cap,
227 // which is the outer arc from left top of the bar to the tip point
228 cox->move_to(BAR_LEFT, ORG);
229 cox->arc_negative(ARC_O_XC,ARC_O_YC,ARC_O_R, ARC_O_END, ARC_O_TIP);
230 // draw the outer contour of the bracket cap,
231 // which is the inner arc from tip point to Φ-minor of the enclosing square
233 cox->close_path();
234 //
235 cox->fill();
236 //
237 cox->restore();
238 }
239
241 void
242 drawBar (CairoC cox, Gdk::RGBA colour, double leftX, double upperY, double lowerY, double scale)
243 {
244 cox->save();
245 cox->translate (leftX, upperY);
246 cox->scale (scale, scale);
247 cox->set_source_rgba(colour.get_red()
248 ,colour.get_green()
249 ,colour.get_blue()
250 ,colour.get_alpha());
251 //
252 double height = max (0.0, (lowerY - upperY)/scale);
253 cox->rectangle(BAR_LEFT, -SQUARE_MINOR, BAR_WIDTH, height + 2*SQUARE_MINOR);
254 cox->rectangle(LIN_LEFT, ORG, LIN_WIDTH, height);
255 //
256 cox->fill();
257 //
258 cox->restore();
259 }
260
268 void
269 connect (CairoC cox, Gdk::RGBA colour
270 ,double leftX, double upperY, double lowerY, double width, double scale
271 ,std::vector<uint> connectors)
272 {
273 double limit = lowerY - upperY;
274 double line = leftX + scale*(LIN_LEFT + LIN_WIDTH/2);
275 double rad = scale * PHI_MAJOR;
276 cox->save();
277 // shift connectors to join below top cap
278 cox->translate (line, upperY);
279 // fill circle with a lightened yellow hue
280 cox->set_source_rgb(1 - 0.2*(colour.get_red())
281 ,1 - 0.2*(colour.get_green())
282 ,1 - 0.5*(1 - colour.get_blue()) );
283 // draw a circle joint on top of the small vertical line
284 for (uint off : connectors)
285 if (off <= limit)
286 {
287 cox->move_to(rad,off);
288 cox->arc ( 0,off, rad, 0, 2 * M_PI);
289 cox->close_path();
290 }
291 //
292 cox->fill_preserve();
293 cox->set_source_rgba(colour.get_red()
294 ,colour.get_green()
295 ,colour.get_blue()
296 ,colour.get_alpha());
297 cox->set_line_width(scale*LIN_WIDTH*PHI_MAJOR);
298 cox->stroke();
299 //
300 // draw connecting arrows...
301 cox->translate(rad,0);
302 // Note: arrow tip uses complete width, reaches into the padding-right
303 double len = width-line-rad-1; // -1 to create room for a sharp miter
304 ASSERT (len > 0);
305 double arr = len * PHI_MINOR;
306 double bas = scale * PHI_MINOR;
307 for (uint off : connectors)
308 if (off <= limit)
309 {
310 cox->move_to(ORG,off);
311 cox->line_to(arr,off);
312 // draw arrow head...
313 cox->move_to(arr,off-bas);
314 cox->line_to(len,off);
315 cox->line_to(arr,off+bas);
316 cox->close_path();
317 }
318 cox->set_miter_limit(20); // to create sharp arrow tip
319 cox->fill_preserve();
320 cox->stroke();
321 //
322 cox->restore();
323 }
324
325 }//(End)Implementation details (drawing design)
326
327
328
329
330
331
333
335 : _Base{}
336 , connectors_{}
337 {
338 get_style_context()->add_class (CLASS_fork_bracket);
339 this->property_expand() = false;
340 }
341
342
353 bool
355 {
356 // invoke (presumably empty) base implementation....
357 bool event_is_handled = _Base::on_draw (cox);
358
359 StyleC style = this->get_style_context();
360 auto colour = style->get_color (Gtk::STATE_FLAG_NORMAL);
361 int height = this->get_allocated_height();
362 int width = this->get_width();
363 double scale = determineScale (style, height);
364 double left = anchorLeft (style, scale);
365 double upper = anchorUpper (style,scale);
366 double lower = anchorLower (style, scale, height);
367
368 drawCap (cox, colour, left, upper, scale, true);
369 drawCap (cox, colour, left, lower, scale, false);
370 drawBar (cox, colour, left, upper, lower, scale);
371 connect (cox, colour, left, upper, lower, width, scale, connectors_);
372
373 return event_is_handled;
374 }
375
376
378 Gtk::SizeRequestMode
380 {
381 return Gtk::SizeRequestMode::SIZE_REQUEST_WIDTH_FOR_HEIGHT;
382 }
383
389 void
390 StaveBracketWidget::get_preferred_width_for_height_vfunc (int givenHeight, int& minimum_width, int& natural_width) const
391 {
392 StyleC style = this->get_style_context();
393 minimum_width = natural_width = calcRequiredWidth (style, givenHeight);
394 }
395
396 void
397 StaveBracketWidget::get_preferred_width_vfunc (int& minimum_width, int& natural_width) const
398 {
399 StyleC style = this->get_style_context();
400 minimum_width = natural_width = calcDesiredWidth (style);
401 }
402
403
404}}// namespace stage::timeline
bool on_draw(CairoC cox) override
Custom drawing: a »stave bracket« to indicate track scope.
void get_preferred_width_vfunc(int &, int &) const override
void get_preferred_width_for_height_vfunc(int, int &, int &) const override
The structural outline adapts flexible in vertical direction, but requires a proportional horizontal ...
Gtk::SizeRequestMode get_request_mode_vfunc() const final
indicate layout oriented towards vertical extension
unsigned int uint
Definition integral.hpp:29
void drawCap(CairoC cox, Gdk::RGBA colour, double ox, double oy, double scale, bool upside=true)
Draw the curved end cap of the bracket, inspired by musical notation.
double anchorUpper(StyleC style, double scale)
place top cap vertical anchor, down from canvas upside.
double getAbsoluteFontSize(StyleC style)
Use contextual CSS style information to find out about the standard font size
double determineScale(StyleC style, int givenHeight)
determine the base metric, taking into account the available canvas size.
double anchorLower(StyleC style, double scale, int canvasHeight)
place bottom cap vertical anchor, mirroring top cap
double baseWidth(StyleC style)
Setup the base metric for this bracket drawing based on CSS styling.
void drawBar(CairoC cox, Gdk::RGBA colour, double leftX, double upperY, double lowerY, double scale)
draw the double bar to fit between upper and lower cap
double anchorLeft(StyleC style, double scale)
place left anchor reference line to right side of bold bar.
PCairoContext const & CairoC
PStyleContext const & StyleC
Lumiera GTK UI implementation root.
Definition guifacade.cpp:37
cuString CLASS_fork_bracket
auto max(IT &&elms)
auto min(IT &&elms)
Widget to group tracks visually in the Timeline presentation.
Definition of access keys for uniform UI styling.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...