mandelbrot.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #include <furi.h>
  2. #include <math.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <stdlib.h>
  6. typedef enum {
  7. EventTypeTick,
  8. EventTypeKey,
  9. } EventType;
  10. typedef struct {
  11. EventType type;
  12. InputEvent input;
  13. } PluginEvent;
  14. typedef struct {
  15. FuriMutex* mutex;
  16. float xZoom;
  17. float yZoom;
  18. float xOffset;
  19. float yOffset;
  20. float zoom;
  21. } PluginState;
  22. bool mandelbrot_pixel(int x, int y, float xZoom, float yZoom, float xOffset, float yOffset) {
  23. float ratio = 128.0 / 64.0;
  24. //x0 := scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.00, 0.47))
  25. float x0 = (((x / 128.0) * ratio * xZoom)) - xOffset;
  26. //y0 := scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1.12, 1.12))
  27. float y0 = ((y / 64.0) * yZoom) - yOffset;
  28. float x1 = 0.0;
  29. float y1 = 0.0;
  30. float x2 = 0.0;
  31. float y2 = 0.0;
  32. int iteration = 0;
  33. int max_iteration = 50;
  34. while(x2 + y2 <= 4.0 && iteration < max_iteration) {
  35. y1 = 2.0 * x1 * y1 + y0;
  36. x1 = x2 - y2 + x0;
  37. x2 = x1 * x1;
  38. y2 = y1 * y1;
  39. iteration++;
  40. }
  41. if(iteration > 49) {
  42. return true;
  43. }
  44. return false;
  45. }
  46. static void render_callback(Canvas* const canvas, void* ctx) {
  47. furi_assert(ctx);
  48. const PluginState* plugin_state = ctx;
  49. furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
  50. // border around the edge of the screen
  51. canvas_draw_frame(canvas, 0, 0, 128, 64);
  52. for(int y = 0; y < 64; y++) {
  53. for(int x = 0; x < 128; x++) {
  54. // did you know if you just pass the indivdiual bits of plugin_state instead of plugin_state
  55. // you dont get any compiler warnings :)
  56. if(mandelbrot_pixel(
  57. x,
  58. y,
  59. plugin_state->xZoom,
  60. plugin_state->yZoom,
  61. plugin_state->xOffset,
  62. plugin_state->yOffset)) {
  63. canvas_draw_dot(canvas, x, y);
  64. }
  65. }
  66. }
  67. furi_mutex_release(plugin_state->mutex);
  68. }
  69. static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
  70. furi_assert(event_queue);
  71. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  72. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  73. }
  74. static void mandelbrot_state_init(PluginState* const plugin_state) {
  75. plugin_state->xOffset = 3.0;
  76. plugin_state->yOffset = 1.12;
  77. plugin_state->xZoom = 2.47;
  78. plugin_state->yZoom = 2.24;
  79. plugin_state->zoom = 1; // this controls the camera when
  80. }
  81. int32_t mandelbrot_app(void* p) {
  82. UNUSED(p);
  83. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  84. PluginState* plugin_state = malloc(sizeof(PluginState));
  85. mandelbrot_state_init(plugin_state);
  86. plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  87. if(!plugin_state->mutex) {
  88. FURI_LOG_E("mandelbrot", "cannot create mutex\r\n");
  89. furi_message_queue_free(event_queue);
  90. free(plugin_state);
  91. return 255;
  92. }
  93. // Set system callbacks
  94. ViewPort* view_port = view_port_alloc();
  95. view_port_draw_callback_set(view_port, render_callback, plugin_state);
  96. view_port_input_callback_set(view_port, input_callback, event_queue);
  97. // Open GUI and register view_port
  98. Gui* gui = furi_record_open(RECORD_GUI);
  99. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  100. PluginEvent event;
  101. for(bool processing = true; processing;) {
  102. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  103. furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
  104. if(event_status == FuriStatusOk) {
  105. // press events
  106. if(event.type == EventTypeKey) {
  107. if(event.input.type == InputTypePress) {
  108. switch(event.input.key) {
  109. case InputKeyUp:
  110. plugin_state->yOffset += 0.1 / plugin_state->zoom;
  111. break;
  112. case InputKeyDown:
  113. plugin_state->yOffset += -0.1 / plugin_state->zoom;
  114. break;
  115. case InputKeyRight:
  116. plugin_state->xOffset += -0.1 / plugin_state->zoom;
  117. break;
  118. case InputKeyLeft:
  119. plugin_state->xOffset += 0.1 / plugin_state->zoom;
  120. break;
  121. case InputKeyOk:
  122. plugin_state->xZoom -= (2.47 / 10) / plugin_state->zoom;
  123. plugin_state->yZoom -= (2.24 / 10) / plugin_state->zoom;
  124. // used to make camera control finer the more zoomed you are
  125. // this needs to be some sort of curve
  126. plugin_state->zoom += 0.15;
  127. break;
  128. case InputKeyBack:
  129. processing = false;
  130. break;
  131. default:
  132. break;
  133. }
  134. }
  135. }
  136. }
  137. view_port_update(view_port);
  138. furi_mutex_release(plugin_state->mutex);
  139. }
  140. view_port_enabled_set(view_port, false);
  141. gui_remove_view_port(gui, view_port);
  142. furi_record_close(RECORD_GUI);
  143. view_port_free(view_port);
  144. furi_message_queue_free(event_queue);
  145. furi_mutex_free(plugin_state->mutex);
  146. free(plugin_state);
  147. return 0;
  148. }