#include <xtd/drawing/color_converter>
#include <xtd/forms/application>
#include <xtd/forms/user_control>
#include <xtd/random>
#include <xtd/startup>
 
 
class signal_generated_event_args : 
public event_args {
 
public:
  signal_generated_event_args(
float sinus, 
const date_time& time) : signal_(sinus), time_(time) {}
 
  
  float signal()
 const {
return signal_;}
 
  const date_time& time()
 const {
return time_;}
 
  
private:
  float signal_ = .0f;
};
 
 
public:
  ~signal_generator_base() {enabled(false);}
  
  bool enabled() const noexcept {return enabled_;}
  signal_generator_base& enabled(bool value) noexcept {
    if (enabled_ == value) return *this;
    enabled_ = value;
    if (enabled_) generator_result_ = generator_.begin_invoke();
    else generator_.end_invoke(generator_result_);
    return *this;
  }
  
  const time_span& interval() const noexcept {
return interval_;}
 
  signal_generator_base& interval(
const time_span& value) 
noexcept {
 
    interval_ = value;
    return *this;
  }
  
  
  void start() noexcept {enabled(
true);}
 
  void stop() noexcept {enabled(
false);}
 
  
protected:
  void on_signal_generated(const signal_generated_event_args& e) {signal_generated(*this, e);}
  
  virtual float next_signal() noexcept = 0;
  
private:
  bool enabled_ = false;
  delegate<void()> generator_ {[&] {
    while(enabled_) {
      threading::thread::sleep(interval_);
      on_signal_generated({next_signal(), date_time::now()});
    }
  }};
};
 
class random_signal_generator : public signal_generator_base {
protected:
  float next_signal() noexcept override {return rand_.next(-1.0f, 1.0f);}
 
private:
};
 
class sine_wave_signal_generator : public signal_generator_base {
protected:
  float next_signal() noexcept override {
    auto result = sinuses_[index_++];
    if (index_ == sinuses_.size()) index_ = 0;
    return result;
  }
  
private:
  const list<float> sinuses_ = {.0f, .38f, .71f, .92f, 1.0f, .92f, .71f, .38f, .0f, -.38f, -.71f, -.92f, -1.0f, -.92f, -.71f, -.38f};
 
  size_t index_ = 0;
};
 
class max_min_signal_generator : public signal_generator_base {
protected:
  float next_signal() noexcept override {
    auto result = sinuses_[index_++];
    if (index_ == sinuses_.size()) index_ = 0;
    return result;
  }
  
private:
  const list<float> sinuses_ = {1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f};
 
  size_t index_ = 0;
};
 
public:
  graph_control() {
    double_buffered(true);
  }
  
  color signal_trace_color() const noexcept {
return signal_trace_color_;}
 
  graph_control& signal_trace_color(
color value) 
noexcept {
 
    signal_trace_color_ = value;
    return *this;
  }
  
  float signal_trace_width() const noexcept {return signal_trace_width_;}
  graph_control& signal_trace_width(float value) {
    signal_trace_width_ = value;
    return *this;
  }
  
  color grid_horizontal_line_color() const noexcept {
return grid_horizontal_line_color_.value_or(back_color().is_dark() ? color_converter::light(back_color()) : 
color_converter::dark(back_color()));}
 
  graph_control& grid_horizontal_line_color(
color value) 
noexcept {
 
    grid_horizontal_line_color_ = value;
    return *this;
  }
  graph_control& grid_horizontal_line_color(std::nullptr_t) noexcept {
    grid_horizontal_line_color_.reset();
    return *this;
  }
  
  int grid_horizontal_line_spacing() const noexcept {return grid_horizontal_line_spacing_;}
  graph_control& grid_horizontal_line_spacing(int value) {
    grid_horizontal_line_spacing_ = value;
    return *this;
  }
  
  bool grid_horizontal_line_visible() const noexcept {return grid_horizontal_line_visible_;}
  graph_control& grid_horizontal_line_visible(bool value) {
    grid_horizontal_line_visible_ = value;
    return *this;
  }
  
  float grid_horizontal_line_width() const noexcept {return grid_horizontal_line_width_;}
  graph_control& grid_horizontal_line_width(float value) {
    grid_horizontal_line_width_ = value;
    return *this;
  }
  
  color grid_vertical_line_color() const noexcept {
return grid_vertical_line_color_.value_or(back_color().is_dark() ? color_converter::light(back_color()) : 
color_converter::dark(back_color()));}
 
  graph_control& grid_vertical_line_color(
color value) 
noexcept {
 
    grid_vertical_line_color_ = value;
    return *this;
  }
  graph_control& grid_vertical_line_color(std::nullptr_t) noexcept {
    grid_vertical_line_color_.reset();
    return *this;
  }
  
  int grid_vertical_line_spacing() const noexcept {return grid_vertical_line_spacing_;}
  graph_control& grid_vertical_line_spacing(int value) {
    grid_vertical_line_spacing_ = value;
    return *this;
  }
  
  bool grid_vertical_line_visible() const noexcept {return grid_vertical_line_visible_;}
  graph_control& grid_vertical_line_visible(bool value) {
    grid_vertical_line_visible_ = value;
    return *this;
  }
  
  float grid_vertical_line_width() const noexcept {return grid_vertical_line_width_;}
  graph_control& grid_vertical_line_width(float value) {
    grid_vertical_line_width_ = value;
    return *this;
  }
  
  color x_axis_line_color() const noexcept {
return x_axis_line_color_.value_or(fore_color().is_dark() ? color_converter::light(fore_color()) : 
color_converter::dark(fore_color()));}
 
  graph_control& x_axis_line_color(
color value) 
noexcept {
 
    x_axis_line_color_ = value;
    return *this;
  }
  graph_control& x_axis_line_color(std::nullptr_t) noexcept {
    x_axis_line_color_.reset();
    return *this;
  }
  
  bool x_axis_line_visible() const noexcept {return x_axis_line_visible_;}
  graph_control& x_axis_line_visible(bool value) {
    x_axis_line_visible_ = value;
    return *this;
  }
  
  float x_axis_line_width() const noexcept {return x_axis_line_width_;}
  graph_control& x_axis_line_width(float value) {
    x_axis_line_width_ = value;
    return *this;
  }
  
  color y_axis_line_color() const noexcept {
return y_axis_line_color_.value_or(fore_color().is_dark() ? color_converter::light(fore_color()) : 
color_converter::dark(fore_color()));}
 
  graph_control& y_axis_line_color(
color value) 
noexcept {
 
    y_axis_line_color_ = value;
    return *this;
  }
  graph_control& y_axis_line_color(std::nullptr_t) noexcept {
    y_axis_line_color_.reset();
    return *this;
  }
  
  bool y_axis_line_visible() const noexcept {return y_axis_line_visible_;}
  graph_control& y_axis_line_visible(bool value) {
    y_axis_line_visible_ = value;
    return *this;
  }
  
  float y_axis_line_width() const noexcept {return y_axis_line_width_;}
  graph_control& y_axis_line_width(float value) {
    y_axis_line_width_ = value;
    return *this;
  }
  
  size_t signals_max_size() const noexcept {return signals_max_size_;}
  graph_control& signals_max_size(size_t value) {
    signals_max_size_ = value;
    while (values_.size() > signals_max_size_)
      values_.erase(values_.begin());
    return *this;
  }
 
  void add_value(float value) {
    values_.push_back(value);
    if (values_.size() > signals_max_size_) values_.erase(values_.begin());
    begin_invoke([self=this] {self->invalidate();});
  }
  
protected:
    user_control::on_paint(e);
    e.graphics().smoothing_mode(drawing_2d::smoothing_mode::anti_alias);
 
    draw_grid(
e.clip_rectangle(), 
e.graphics());
 
    draw_axis(
e.clip_rectangle(), 
e.graphics());
 
    
    auto y = as<float>(
e.clip_rectangle().height()) / 2;
 
    auto height = as<float>(
e.clip_rectangle().height()) / 2;
 
    auto step = as<float>(
e.clip_rectangle().width()) / (signals_max_size_ -1);
 
    for (auto value : values_) {
      points.emplace_back(x, y - value * height);
 
    }
    if (
points.size() >= 2) 
e.graphics().draw_curve(
pen {signal_trace_color_, signal_trace_width_}, 
points);
 
  }
  
private:
    auto grid_horizontal_line_color = grid_horizontal_line_color_.value_or(back_color().is_dark() ? color_converter::light(back_color()) : 
color_converter::dark(back_color()));
 
    auto grid_vertical_line_color = grid_vertical_line_color_.value_or(back_color().is_dark() ? color_converter::light(back_color()) : 
color_converter::dark(back_color()));
 
    if (grid_horizontal_line_visible_)
      for (
auto y = rect.
top(); 
y < rect.
bottom(); 
y += grid_horizontal_line_spacing_)
 
    if (grid_vertical_line_visible_)
      for (
auto x = rect.
left(); 
x < rect.
right(); 
x += grid_vertical_line_spacing_)
 
  }
  
    auto x_axis_line_color = x_axis_line_color_.value_or(fore_color().is_dark() ? color_converter::light(fore_color()) : 
color_converter::dark(fore_color()));
 
    auto y_axis_line_color = y_axis_line_color_.value_or(fore_color().is_dark() ? color_converter::light(fore_color()) : 
color_converter::dark(fore_color()));
 
    
  }
  
  color signal_trace_color_ = fore_color();
 
  float signal_trace_width_ = 4.0f;
  std::optional<color> grid_horizontal_line_color_;
  int grid_horizontal_line_spacing_ = 20;
  bool grid_horizontal_line_visible_ = true;
  float grid_horizontal_line_width_ = 1.0f;
  std::optional<color> grid_vertical_line_color_;
  int grid_vertical_line_spacing_ = 20;
  bool grid_vertical_line_visible_ = true;
  float grid_vertical_line_width_ = 1.0f;
  std::optional<color> x_axis_line_color_;
  bool x_axis_line_visible_ = true;
  float x_axis_line_width_ = 3.0f;
  std::optional<color> y_axis_line_color_;
  bool y_axis_line_visible_ = true;
  float y_axis_line_width_ = 3.0f;
  size_t signals_max_size_ = 48;
};
 
class form1 : 
public form {
 
public:
  static void main() {
    application::run(form1 {});
  }
  
  form1() {
    client_size({670, 430});
    
    generator1_.enabled(true);
    generator1_.signal_generated += {*this, &form1::on_generator1_signal_generated};
    
    generator2_.enabled(true);
    generator2_.interval(20_ms);
    generator2_.signal_generated += {*this, &form1::on_generator2_signal_generated};
    
    generator3_.enabled(true);
    generator3_.interval(200_ms);
    generator3_.signal_generated += {*this, &form1::on_generator3_signal_generated};
    
    generator4_.enabled(true);
    generator4_.interval(50_ms);
    generator4_.signal_generated += {*this, &form1::on_generator4_signal_generated};
 
    graph_control1_.grid_horizontal_line_visible(false);
    graph_control1_.grid_vertical_line_visible(false);
    graph_control1_.location({10, 10});
    graph_control1_.parent(*this);
    graph_control1_.signals_max_size(16);
    graph_control1_.signal_trace_color(color::red);
    graph_control1_.size({321, 201});
    graph_control1_.x_axis_line_visible(false);
    graph_control1_.y_axis_line_visible(false);
 
    graph_control2_.grid_horizontal_line_spacing(50);
    graph_control2_.grid_vertical_line_visible(false);
    graph_control2_.location({340, 10});
    graph_control2_.parent(*this);
    graph_control2_.signals_max_size(32);
    graph_control2_.signal_trace_color(color::lime);
    graph_control2_.size({321, 201});
    
    graph_control3_.location({10, 220});
    graph_control3_.parent(*this);
    graph_control3_.signals_max_size(8);
    graph_control3_.signal_trace_color(color::yellow);
    graph_control3_.size({321, 201});
    graph_control3_.x_axis_line_visible(false);
    graph_control3_.y_axis_line_visible(false);
 
    graph_control4_.grid_horizontal_line_visible(false);
    graph_control4_.grid_vertical_line_visible(false);
    graph_control4_.location({340, 220});
    graph_control4_.parent(*this);
    graph_control4_.signal_trace_color(color::dodger_blue);
    graph_control4_.size({321, 201});
 
    form_closed += [&] {
      generator1_.stop();
      generator2_.stop();
      generator3_.stop();
      generator4_.stop();
    };
  }
  
private:
  void on_generator1_signal_generated(object& sender, const signal_generated_event_args& e) {
    graph_control1_.add_value(
e.signal());
 
  }
  
  void on_generator2_signal_generated(object& sender, const signal_generated_event_args& e) {
    graph_control2_.add_value(
e.signal());
 
  }
  
  void on_generator3_signal_generated(object& sender, const signal_generated_event_args& e) {
    graph_control3_.add_value(
e.signal());
 
  }
  
  void on_generator4_signal_generated(object& sender, const signal_generated_event_args& e) {
    graph_control4_.add_value(
e.signal());
 
  }
 
  random_signal_generator generator1_;
  sine_wave_signal_generator generator2_;
  sine_wave_signal_generator generator3_;
  max_min_signal_generator generator4_;
 
  graph_control graph_control1_;
  graph_control graph_control2_;
  graph_control graph_control3_;
  graph_control graph_control4_;
};
 
Represents a strongly typed list of objects that can be accessed by index. Provides methods to search...
Definition list.hpp:71
 
Represents an instant in time, typically expressed as a date and time of day.
Definition date_time.hpp:85
 
Converts colors to and from xtd::drawing::color class. This class cannot be inherited.
Definition color_converter.hpp:24
 
Represents an ARGB (alpha, red, green, blue) color.
Definition color.hpp:49
 
Defines an object used to draw lines and curves. This class cannot be inherited.
Definition graphics.hpp:70
 
void draw_line(const xtd::drawing::pen &pen, const xtd::drawing::point &pt1, const point &pt2)
Draws a line connecting two xtd::drawing::point structures.
 
Defines an object used to draw lines and curves. This class cannot be inherited.
Definition pen.hpp:35
 
Represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional ...
Definition point.hpp:54
 
Stores a set of four integers that represent the location and size of a rectangle.
Definition rectangle.hpp:44
 
int32 width() const noexcept
Gets the width of this xtd::drawing::rectangle structure.
 
int32 left() const noexcept
Gets the x-coordinate of the left edge of this xtd::drawing::rectangle structure.
 
int32 top() const noexcept
Gets the y-coordinate of the top edge of this xtd::drawing::rectangle structure.
 
int32 bottom() const noexcept
Gets the y-coordinate that is the sum of the xtd::drawing::rectangle::y and xtd::drawing::rectangle::...
 
int32 height() const noexcept
Gets the height of this xtd::drawing::rectangle structure.
 
int32 right() const noexcept
Gets the x-coordinate that is the sum of xtd::drawing::rectangle::x and xtd::drawing::rectangle::widt...
 
Represents the base class for classes that contain event data, and provides a value to use for events...
Definition event_args.hpp:18
 
Represents an event.
Definition event.hpp:21
 
Represents a pseudo-random number generator, a device that produces a sequence of numbers that meet c...
Definition random.hpp:40
 
xtd::delegate< void(xtd::object &sender, event_args_t e)> generic_event_handler
Represents the method that will handle an event when the event provides data.
Definition event_handler.hpp:22
 
#define startup_(main_method)
Defines the entry point to be called when the application loads. Generally this is set either to the ...
Definition startup.hpp:175
 
#define abstract_
This keyword is used to represents an abstract class.
Definition abstract.hpp:25
 
@ points
Defines points unit (1pt = 1/72 of 1in).
 
xtd::sptr< xtd::iasync_result > async_result
Represents the status of an asynchronous operation.
Definition async_result.hpp:19
 
signal
Specifies signals that can interrupt the current process.
Definition signal.hpp:18
 
@ start
Starting of a logical operation.
 
@ stop
Stopping of a logical operation.
 
The xtd::collections::generic namespace contains interfaces and classes that define generic collectio...
Definition comparer.hpp:15
 
The xtd::drawing namespace provides access to GDI+ basic graphics functionality. More advanced functi...
Definition brush.hpp:18
 
The xtd namespace contains all fundamental classes to access Hardware, Os, System,...
Definition xtd_about_box.hpp:10
 
Represents a time interval.
Definition time_span.hpp:29