mandelbrot.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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, void* ctx) {
  70. furi_assert(ctx);
  71. FuriMessageQueue* event_queue = ctx;
  72. PluginEvent event = {.type = EventTypeKey, .input = *input_event};
  73. furi_message_queue_put(event_queue, &event, FuriWaitForever);
  74. }
  75. static void mandelbrot_state_init(PluginState* const plugin_state) {
  76. plugin_state->xOffset = 3.0;
  77. plugin_state->yOffset = 1.12;
  78. plugin_state->xZoom = 2.47;
  79. plugin_state->yZoom = 2.24;
  80. plugin_state->zoom = 1; // this controls the camera when
  81. }
  82. int32_t mandelbrot_app(void* p) {
  83. UNUSED(p);
  84. FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
  85. PluginState* plugin_state = malloc(sizeof(PluginState));
  86. mandelbrot_state_init(plugin_state);
  87. plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  88. if(!plugin_state->mutex) {
  89. FURI_LOG_E("mandelbrot", "cannot create mutex\r\n");
  90. furi_message_queue_free(event_queue);
  91. free(plugin_state);
  92. return 255;
  93. }
  94. // Set system callbacks
  95. ViewPort* view_port = view_port_alloc();
  96. view_port_draw_callback_set(view_port, render_callback, plugin_state);
  97. view_port_input_callback_set(view_port, input_callback, event_queue);
  98. // Open GUI and register view_port
  99. Gui* gui = furi_record_open(RECORD_GUI);
  100. gui_add_view_port(gui, view_port, GuiLayerFullscreen);
  101. PluginEvent event;
  102. for(bool processing = true; processing;) {
  103. FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
  104. furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
  105. if(event_status == FuriStatusOk) {
  106. // press events
  107. if(event.type == EventTypeKey) {
  108. if(event.input.type == InputTypePress) {
  109. switch(event.input.key) {
  110. case InputKeyUp:
  111. plugin_state->yOffset += 0.1 / plugin_state->zoom;
  112. break;
  113. case InputKeyDown:
  114. plugin_state->yOffset += -0.1 / plugin_state->zoom;
  115. break;
  116. case InputKeyRight:
  117. plugin_state->xOffset += -0.1 / plugin_state->zoom;
  118. break;
  119. case InputKeyLeft:
  120. plugin_state->xOffset += 0.1 / plugin_state->zoom;
  121. break;
  122. case InputKeyOk:
  123. plugin_state->xZoom -= (2.47 / 10) / plugin_state->zoom;
  124. plugin_state->yZoom -= (2.24 / 10) / plugin_state->zoom;
  125. // used to make camera control finer the more zoomed you are
  126. // this needs to be some sort of curve
  127. plugin_state->zoom += 0.15;
  128. break;
  129. case InputKeyBack:
  130. processing = false;
  131. break;
  132. default:
  133. break;
  134. }
  135. }
  136. }
  137. }
  138. furi_mutex_release(plugin_state->mutex);
  139. view_port_update(view_port);
  140. }
  141. view_port_enabled_set(view_port, false);
  142. gui_remove_view_port(gui, view_port);
  143. furi_record_close(RECORD_GUI);
  144. view_port_free(view_port);
  145. furi_message_queue_free(event_queue);
  146. furi_mutex_free(plugin_state->mutex);
  147. free(plugin_state);
  148. return 0;
  149. }