7 constexpr float kLoopWidth = 2.001f;
8 constexpr float kDefaultLineWidth = 7.0f;
12 static constexpr float kThreeHalves = 1.5f;
19 i = 0x5f3759df - (i >> 1);
21 y = y * (kThreeHalves - (x2 * y * y));
22 y = y * (kThreeHalves - (x2 * y * y));
26 force_inline float inverseMagnitudeOfPoint(Point<float> point) {
27 return inverseSqrt(point.x * point.x + point.y * point.y);
30 force_inline Point<float> normalize(Point<float> point) {
31 return point * inverseMagnitudeOfPoint(point);
36 num_points_(num_points), boost_(0.0f), fill_(false), fill_center_(0.0f), fit_(false),
37 boost_amount_(0.0f), fill_boost_amount_(0.0f), enable_backward_boost_(true),
38 index_(0), dirty_(false), last_drawn_left_(false), loop_(loop), any_boost_value_(false),
39 shader_(nullptr), fill_shader_(nullptr) {
50 line_width_ = kDefaultLineWidth;
52 x_ = std::make_unique<float[]>(num_points_);
53 y_ = std::make_unique<float[]>(num_points_);
54 boost_left_ = std::make_unique<float[]>(num_points_);
55 boost_right_ = std::make_unique<float[]>(num_points_);
57 line_data_ = std::make_unique<float[]>(num_line_floats_);
58 fill_data_ = std::make_unique<float[]>(num_fill_floats_);
59 indices_data_ = std::make_unique<int[]>(num_line_vertices_);
60 vertex_array_object_ = 0;
64 last_negative_boost_ =
false;
66 for (
int i = 0; i < num_line_vertices_; ++i)
70 line_data_[i + 2] = 1.0f;
72 for (
int i = 0; i < num_points_; ++i)
73 setXAt(i, 2.0f * i / (num_points_ - 1.0f) - 1.0f);
81 open_gl.
context.extensions.glGenVertexArrays(1, &vertex_array_object_);
82 open_gl.
context.extensions.glBindVertexArray(vertex_array_object_);
84 open_gl.
context.extensions.glGenBuffers(1, &line_buffer_);
85 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, line_buffer_);
87 GLsizeiptr line_vert_size =
static_cast<GLsizeiptr
>(num_line_floats_ *
sizeof(float));
88 open_gl.
context.extensions.glBufferData(GL_ARRAY_BUFFER, line_vert_size, line_data_.get(), GL_STATIC_DRAW);
90 open_gl.
context.extensions.glGenBuffers(1, &fill_buffer_);
91 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, fill_buffer_);
93 GLsizeiptr fill_vert_size =
static_cast<GLsizeiptr
>(num_fill_floats_ *
sizeof(float));
94 open_gl.
context.extensions.glBufferData(GL_ARRAY_BUFFER, fill_vert_size, fill_data_.get(), GL_STATIC_DRAW);
96 open_gl.
context.extensions.glGenBuffers(1, &indices_buffer_);
97 open_gl.
context.extensions.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_);
99 GLsizeiptr line_size =
static_cast<GLsizeiptr
>(num_line_vertices_ *
sizeof(int));
100 open_gl.
context.extensions.glBufferData(GL_ELEMENT_ARRAY_BUFFER, line_size, indices_data_.get(), GL_STATIC_DRAW);
104 color_uniform_ =
getUniform(open_gl, *shader_,
"color");
105 scale_uniform_ =
getUniform(open_gl, *shader_,
"scale");
106 boost_uniform_ =
getUniform(open_gl, *shader_,
"boost");
107 line_width_uniform_ =
getUniform(open_gl, *shader_,
"line_width");
108 position_ =
getAttribute(open_gl, *shader_,
"position");
112 fill_color_from_uniform_ =
getUniform(open_gl, *fill_shader_,
"color_from");
113 fill_color_to_uniform_ =
getUniform(open_gl, *fill_shader_,
"color_to");
114 fill_center_uniform_ =
getUniform(open_gl, *fill_shader_,
"center_position");
115 fill_boost_amount_uniform_ =
getUniform(open_gl, *fill_shader_,
"boost_amount");
116 fill_scale_uniform_ =
getUniform(open_gl, *fill_shader_,
"scale");
117 fill_position_ =
getAttribute(open_gl, *fill_shader_,
"position");
121 boostRange(boost_left_.get(), start, end, buffer_vertices, min);
125 boostRange(boost_right_.get(), start, end, buffer_vertices, min);
129 any_boost_value_ =
true;
132 int active_points = num_points_ - 2 * buffer_vertices;
133 int start_index = std::max((
int)std::ceil(start * (active_points - 1)), 0);
134 float end_position = end * (active_points - 1);
135 int end_index = std::max(std::ceil(end_position), 0.0f);
136 float progress = end_position - (int)end_position;
138 start_index %= active_points;
139 end_index %= active_points;
140 int num_points = end_index - start_index;
142 if (enable_backward_boost_) {
143 if ((num_points < 0 && num_points > -num_points_ / 2) || (num_points == 0 && last_negative_boost_)) {
144 num_points = -num_points;
147 else if (num_points > num_points_ / 2) {
148 num_points -= active_points;
149 num_points = -num_points;
154 last_negative_boost_ = direction < 0;
155 if (last_negative_boost_) {
156 start_index = std::max((
int)std::floor(start * (active_points - 1)), 0);
157 end_index = std::max(std::floor(end_position), 0.0f);
158 start_index %= active_points;
159 end_index %= active_points;
161 num_points = start_index - end_index;
162 progress = 1.0f - progress;
165 float delta = (1.0f - min) / num_points;
168 for (
int i = start_index; i != end_index; i = (i + active_points + direction) % active_points) {
170 val = std::min(1.0f, val);
171 float last_value = boosts[i + buffer_vertices];
172 boosts[i + buffer_vertices] = std::max(last_value, val);
175 float end_value = boosts[end_index + buffer_vertices];
176 boosts[end_index + buffer_vertices] = std::max(end_value, progress * progress);
186 bool any_boost =
false;
187 for (
int i = 0; i < num_points_; ++i) {
188 boost_left_[i] *= mult[0];
189 boost_right_[i] *= mult[1];
190 any_boost = any_boost || boost_left_[i] || boost_right_[i];
193 any_boost_value_ = any_boost;
197 float* boosts = left ? boost_left_.get() : boost_right_.get();
198 float x_adjust = 2.0f / getWidth();
199 float y_adjust = 2.0f / getHeight();
201 for (
int i = 0; i < num_points_; ++i) {
204 float x = x_adjust * x_[i] - 1.0f;
205 float y = 1.0f - y_adjust * y_[i];
206 fill_data_[index_top] = x;
207 fill_data_[index_top + 1] = y;
208 fill_data_[index_top + 2] = boosts[i];
209 fill_data_[index_bottom] = x;
210 fill_data_[index_bottom + 1] = fill_center_;
211 fill_data_[index_bottom + 2] = boosts[i];
217 if (loop_ && num_points_ >= 2) {
218 memcpy(fill_data_.get(), fill_data_.get() + end_copy_source, padding_copy_size);
220 memcpy(fill_data_.get() + begin_copy_dest, fill_data_.get() + begin_copy_source, padding_copy_size);
222 for (
int i = 0; i < num_padding_; ++i) {
231 memcpy(fill_data_.get() + end_copy_dest, fill_data_.get() + end_copy_source, padding_copy_size);
232 memcpy(fill_data_.get(), fill_data_.get() + begin_copy_source, padding_copy_size);
237 float* boosts = left ? boost_left_.get() : boost_right_.get();
239 Point<float> prev_normalized_delta;
240 for (
int i = 0; i < num_points_ - 1; ++i) {
241 if (x_[i] != x_[i + 1] || y_[i] != y_[i + 1]) {
242 prev_normalized_delta = normalize(Point<float>(x_[i + 1] - x_[i], y_[i + 1] - y_[i]));
247 Point<float> prev_delta_normal(-prev_normalized_delta.y, prev_normalized_delta.x);
248 float line_radius = line_width_ / 2.0f + 0.5f;
249 float prev_magnitude = line_radius;
251 float x_adjust = 2.0f / getWidth();
252 float y_adjust = 2.0f / getHeight();
254 for (
int i = 0; i < num_points_; ++i) {
255 float radius = line_radius * (1.0f + boost_amount_ * boosts[i]);
256 Point<float> point(x_[i], y_[i]);
257 int next_index = i + 1;
258 int clamped_next_index = std::min(next_index, num_points_ - 1);
260 Point<float> next_point(x_[clamped_next_index], y_[clamped_next_index]);
261 Point<float> delta = next_point - point;
262 if (point == next_point) {
263 delta = prev_normalized_delta;
264 next_point = point + delta;
267 float inverse_magnitude = inverseMagnitudeOfPoint(delta);
268 float magnitude = 1.0f / std::max(0.00001f, inverse_magnitude);
269 Point<float> normalized_delta(delta.x * inverse_magnitude, delta.y * inverse_magnitude);
270 Point<float> delta_normal = Point<float>(-normalized_delta.y, normalized_delta.x);
272 Point<float> angle_bisect_delta = normalized_delta - prev_normalized_delta;
273 Point<float> bisect_line;
274 bool straight = angle_bisect_delta.x < 0.001f && angle_bisect_delta.x > -0.001f &&
275 angle_bisect_delta.y < 0.001f && angle_bisect_delta.y > -0.001f;
277 bisect_line = delta_normal;
279 bisect_line = normalize(angle_bisect_delta);
281 float x1, x2, x3, x4, x5, x6;
282 float y1, y2, y3, y4, y5, y6;
284 float max_inner_radius = std::max(radius, 0.5f * (magnitude + prev_magnitude));
285 prev_magnitude = magnitude;
287 float bisect_normal_dot_product = bisect_line.getDotProduct(delta_normal);
288 float inner_mult = 1.0f / std::max(0.1f, std::fabs(bisect_normal_dot_product));
289 Point<float> inner_point = point + std::min(inner_mult * radius, max_inner_radius) * bisect_line;
290 Point<float> outer_point = point - bisect_line * radius;
292 if (bisect_normal_dot_product < 0.0f) {
293 Point<float> outer_point_start = outer_point;
294 Point<float> outer_point_end = outer_point;
296 outer_point_start = point + prev_delta_normal * radius;
297 outer_point_end = point + delta_normal * radius;
299 x1 = outer_point_start.x;
300 y1 = outer_point_start.y;
303 x5 = outer_point_end.x;
304 y5 = outer_point_end.y;
305 x2 = x4 = x6 = inner_point.x;
306 y2 = y4 = y6 = inner_point.y;
309 Point<float> outer_point_start = outer_point;
310 Point<float> outer_point_end = outer_point;
312 outer_point_start = point - prev_delta_normal * radius;
313 outer_point_end = point - delta_normal * radius;
315 x2 = outer_point_start.x;
316 y2 = outer_point_start.y;
319 x6 = outer_point_end.x;
320 y6 = outer_point_end.y;
321 x1 = x3 = x5 = inner_point.x;
322 y1 = y3 = y5 = inner_point.y;
332 line_data_[first] = x_adjust * x1 - 1.0f;
333 line_data_[first + 1] = 1.0f - y_adjust * y1;
335 line_data_[second] = x_adjust * x2 - 1.0f;
336 line_data_[second + 1] = 1.0f - y_adjust * y2;
338 line_data_[third] = x_adjust * x3 - 1.0f;
339 line_data_[third + 1] = 1.0f - y_adjust * y3;
341 line_data_[fourth] = x_adjust * x4 - 1.0f;
342 line_data_[fourth + 1] = 1.0f - y_adjust * y4;
344 line_data_[fifth] = x_adjust * x5 - 1.0f;
345 line_data_[fifth + 1] = 1.0f - y_adjust * y5;
347 line_data_[sixth] = x_adjust * x6 - 1.0f;
348 line_data_[sixth + 1] = 1.0f - y_adjust * y6;
350 prev_delta_normal = delta_normal;
351 prev_normalized_delta = normalized_delta;
355 if (loop_ && num_points_ >= 2) {
360 memcpy(line_data_.get(), line_data_.get() + end_copy_source, padding_copy_size);
361 memcpy(line_data_.get() + begin_copy_dest, line_data_.get() + begin_copy_source, padding_copy_size);
369 Point<float> delta_start(
xAt(0) -
xAt(1),
yAt(0) -
yAt(1));
370 Point<float> delta_start_offset = normalize(delta_start) * line_radius;
371 Point<float> delta_end(
xAt(num_points_ - 1) -
xAt(num_points_ - 2),
yAt(num_points_ - 1) -
yAt(num_points_ - 2));
372 Point<float> delta_end_offset = normalize(delta_end) * line_radius;
379 line_data_[copy_index_start] = (
xAt(num_points_ - 1) + delta_end_offset.x) * x_adjust - 1.0f;
380 line_data_[copy_index_start + 1] = 1.0f - (
yAt(num_points_ - 1) + delta_end_offset.y) * y_adjust;
381 line_data_[copy_index_start + 2] = boosts[num_points_ - 1];
390 if (fill_shader_ ==
nullptr)
393 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
395 glEnable(GL_SCISSOR_TEST);
397 open_gl.
context.extensions.glBindVertexArray(vertex_array_object_);
399 if (dirty_ || last_drawn_left_ != left) {
401 last_drawn_left_ = left;
405 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, line_buffer_);
407 GLsizeiptr line_vert_size =
static_cast<GLsizeiptr
>(num_line_floats_ *
sizeof(float));
408 open_gl.
context.extensions.glBufferData(GL_ARRAY_BUFFER, line_vert_size, line_data_.get(), GL_STATIC_DRAW);
410 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, fill_buffer_);
412 GLsizeiptr fill_vert_size =
static_cast<GLsizeiptr
>(num_fill_floats_ *
sizeof(float));
413 open_gl.
context.extensions.glBufferData(GL_ARRAY_BUFFER, fill_vert_size, fill_data_.get(), GL_STATIC_DRAW);
415 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, 0);
418 open_gl.
context.extensions.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buffer_);
420 float x_shrink = 1.0f;
421 float y_shrink = 1.0f;
423 x_shrink = 1.0f - 0.33f * line_width_ / getWidth();
424 y_shrink = 1.0f - 0.33f * line_width_ / getHeight();
428 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, fill_buffer_);
430 fill_color_from_uniform_->set(fill_color_from_.getFloatRed(), fill_color_from_.getFloatGreen(),
431 fill_color_from_.getFloatBlue(), fill_color_from_.getFloatAlpha());
432 fill_color_to_uniform_->set(fill_color_to_.getFloatRed(), fill_color_to_.getFloatGreen(),
433 fill_color_to_.getFloatBlue(), fill_color_to_.getFloatAlpha());
434 fill_center_uniform_->set(fill_center_);
435 fill_boost_amount_uniform_->set(fill_boost_amount_);
436 fill_scale_uniform_->set(x_shrink, y_shrink);
440 open_gl.
context.extensions.glEnableVertexAttribArray(fill_position_->attributeID);
441 glDrawElements(GL_TRIANGLE_STRIP, num_fill_vertices_, GL_UNSIGNED_INT,
nullptr);
444 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, line_buffer_);
448 open_gl.
context.extensions.glEnableVertexAttribArray(position_->attributeID);
449 color_uniform_->set(color_.getFloatRed(), color_.getFloatGreen(), color_.getFloatBlue(), color_.getFloatAlpha());
451 scale_uniform_->set(x_shrink, y_shrink);
452 boost_uniform_->set(boost_);
453 line_width_uniform_->set(line_width_);
455 glDrawElements(GL_TRIANGLE_STRIP, num_line_vertices_, GL_UNSIGNED_INT,
nullptr);
457 open_gl.
context.extensions.glDisableVertexAttribArray(position_->attributeID);
458 open_gl.
context.extensions.glBindBuffer(GL_ARRAY_BUFFER, 0);
459 open_gl.
context.extensions.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
461 glDisable(GL_SCISSOR_TEST);
473 color_uniform_ =
nullptr;
474 scale_uniform_ =
nullptr;
475 boost_uniform_ =
nullptr;
476 line_width_uniform_ =
nullptr;
478 fill_shader_ =
nullptr;
479 fill_color_from_uniform_ =
nullptr;
480 fill_color_to_uniform_ =
nullptr;
481 fill_center_uniform_ =
nullptr;
482 fill_boost_amount_uniform_ =
nullptr;
483 fill_scale_uniform_ =
nullptr;
484 fill_position_ =
nullptr;
486 open_gl.
context.extensions.glDeleteBuffers(1, &line_buffer_);
487 open_gl.
context.extensions.glDeleteBuffers(1, &fill_buffer_);
488 open_gl.
context.extensions.glDeleteBuffers(1, &indices_buffer_);
490 vertex_array_object_ = 0;
static bool setViewPort(Component *component, Rectangle< int > bounds, OpenGlWrapper &open_gl)
Sets the OpenGL viewport to match a specified rectangle within a component.
Definition open_gl_component.cpp:42
void addRoundedCorners()
Adds rounded corners to the component's edges.
Definition open_gl_component.cpp:138
static std::unique_ptr< OpenGLShaderProgram::Attribute > getAttribute(const OpenGlWrapper &open_gl, const OpenGLShaderProgram &program, const char *name)
Retrieves an attribute from the shader program if it exists.
Definition open_gl_component.h:79
virtual void destroy(OpenGlWrapper &open_gl)
Destroys any OpenGL-specific resources allocated by this component.
Definition open_gl_component.cpp:168
static std::unique_ptr< OpenGLShaderProgram::Uniform > getUniform(const OpenGlWrapper &open_gl, const OpenGLShaderProgram &program, const char *name)
Retrieves a uniform from the shader program if it exists.
Definition open_gl_component.h:64
virtual void init(OpenGlWrapper &open_gl)
Initializes any OpenGL-specific resources needed by the component.
Definition open_gl_component.cpp:148
virtual void init(OpenGlWrapper &open_gl) override
Initializes OpenGL resources for rendering the line.
Definition open_gl_line_renderer.cpp:78
virtual void destroy(OpenGlWrapper &open_gl) override
Destroys OpenGL resources allocated by this line renderer.
Definition open_gl_line_renderer.cpp:468
force_inline float xAt(int index) const
Gets the x-coordinate of a point at a given index.
Definition open_gl_line_renderer.h:81
virtual void render(OpenGlWrapper &open_gl, bool animate) override
Renders the line using OpenGL.
Definition open_gl_line_renderer.cpp:464
void decayBoosts(vital::poly_float mult)
Decays all boosts by a multiplicative factor, allowing animated damping.
Definition open_gl_line_renderer.cpp:185
static constexpr int kLineFloatsPerVertex
Floats per vertex in the line data (x, y, and potentially others).
Definition open_gl_line_renderer.h:19
static constexpr int kFillVerticesPerPoint
Number of vertices per point in the fill representation.
Definition open_gl_line_renderer.h:25
virtual ~OpenGlLineRenderer()
Destructor.
Definition open_gl_line_renderer.cpp:76
force_inline void setXAt(int index, float val)
Sets the x-coordinate of a point, marking data as dirty.
Definition open_gl_line_renderer.h:105
void boostRightRange(float start, float end, int buffer_vertices, float min)
Boosts right-side range of the line.
Definition open_gl_line_renderer.cpp:124
static constexpr int kFillFloatsPerVertex
Floats per vertex in the fill data (x, y, and boost value).
Definition open_gl_line_renderer.h:21
static constexpr int kLineVerticesPerPoint
Number of vertices per point in the line representation.
Definition open_gl_line_renderer.h:23
void setLineVertices(bool left)
Sets line vertices according to the current line and boost data.
Definition open_gl_line_renderer.cpp:236
void setFillVertices(bool left)
Sets fill vertices according to the current line and boost data.
Definition open_gl_line_renderer.cpp:196
static constexpr int kLineFloatsPerPoint
Floats per point in the line data (6 vertices * 3 floats each).
Definition open_gl_line_renderer.h:27
OpenGlLineRenderer(int num_points, bool loop=false)
Constructs an OpenGlLineRenderer for a given number of points.
Definition open_gl_line_renderer.cpp:35
static constexpr int kFillFloatsPerPoint
Floats per point in the fill data (2 vertices * 4 floats each).
Definition open_gl_line_renderer.h:29
force_inline float yAt(int index) const
Gets the y-coordinate of a point at a given index.
Definition open_gl_line_renderer.h:78
void boostRange(float *boosts, float start, float end, int buffer_vertices, float min)
Boosts a range for the given boost array.
Definition open_gl_line_renderer.cpp:128
void drawLines(OpenGlWrapper &open_gl, bool left)
Draws the line and optional fill using OpenGL.
Definition open_gl_line_renderer.cpp:386
void boostLeftRange(float start, float end, int buffer_vertices, float min)
Boosts left-side range of the line.
Definition open_gl_line_renderer.cpp:120
@ kFillVertex
Definition shaders.h:46
@ kLineVertex
Definition shaders.h:45
@ kLineFragment
Definition shaders.h:77
@ kFillFragment
Definition shaders.h:78
OpenGLShaderProgram * getShaderProgram(VertexShader vertex_shader, FragmentShader fragment_shader, const GLchar **varyings=nullptr)
Retrieves or creates an OpenGLShaderProgram from a given vertex and fragment shader pair.
Definition shaders.cpp:953
#define force_inline
Definition common.h:23
A helper struct containing references to OpenGL context, shaders, and display scale.
Definition shaders.h:174
OpenGLContext & context
The OpenGLContext for current rendering.
Definition shaders.h:181
Shaders * shaders
Pointer to the Shaders instance providing compiled shaders.
Definition shaders.h:182
Represents a vector of floating-point values using SIMD instructions.
Definition poly_values.h:600
Provides various utility functions, classes, and constants for audio, math, and general-purpose opera...