You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

834 lines
26KB

  1. #ifndef MODULE_MULTISCOPE_OLD_HPP
  2. #define MODULE_MULTISCOPE_OLD_HPP
  3. #include "Features.hpp"
  4. #if !USE_NEW_SCOPE
  5. #include <string.h>
  6. #include "trowaSoft.hpp"
  7. #include "trowaSoftComponents.hpp"
  8. #include "trowaSoftUtilities.hpp"
  9. #include "dsp/digital.hpp"
  10. #define TROWA_SCOPE_NUM_WAVEFORMS 3
  11. // Laying out controls
  12. #define TROWA_SCOPE_CONTROL_START_X 47 // 47
  13. #define TROWA_SCOPE_CONTROL_START_Y 40 // 94
  14. #define TROWA_SCOPE_CONTROL_DX 35 // 35
  15. #define TROWA_SCOPE_CONTROL_DY 30 // 26
  16. #define TROWA_SCOPE_CONTROL_SHAPE_SPACING 16 // 8
  17. // Labels for each waveform / shape
  18. #define TROWA_SCOPE_SHAPE_FORMAT_STRING "Shp %d"
  19. #define BUFFER_SIZE 512
  20. #define TROWA_SCOPE_USE_COLOR_LIGHTS 0
  21. // X and Y Knobs:
  22. #define TROWA_SCOPE_POS_KNOB_MIN -30.0
  23. #define TROWA_SCOPE_POS_KNOB_MAX 30.0
  24. #define TROWA_SCOPE_POS_X_KNOB_DEF 0.0 // -10
  25. #define TROWA_SCOPE_POS_Y_KNOB_DEF 0.0 // 10
  26. #define TROWA_SCOPE_SCALE_KNOB_MIN -10.0
  27. #define TROWA_SCOPE_SCALE_KNOB_MAX 10.0
  28. // Time Knob:
  29. #define TROWA_SCOPE_TIME_KNOB_MIN -6.0
  30. #define TROWA_SCOPE_TIME_KNOB_MAX -16.0
  31. #define TROWA_SCOPE_TIME_KNOB_DEF -14.0 // Default value
  32. // Hue Knob:
  33. #define TROWA_SCOPE_HUE_KNOB_MIN -10
  34. #define TROWA_SCOPE_HUE_KNOB_MAX 10
  35. #define TROWA_SCOPE_HUE_INPUT_MIN_V 0
  36. #define TROWA_SCOPE_HUE_INPUT_MAX_V 5
  37. #define TROWA_SCOPE_COLOR_KNOB_Y_OFFSET 6
  38. // Opacity:
  39. #define TROWA_SCOPE_MIN_OPACITY 0.0
  40. #define TROWA_SCOPE_MAX_OPACITY 1.0
  41. #define TROWA_SCOPE_OPACITY_INPUT_MIN 0.0 // Min Voltage in
  42. #define TROWA_SCOPE_OPACITY_INPUT_MAX 5.0 // Max Voltage in
  43. // Rotation Knob:
  44. #define TROWA_SCOPE_ROT_KNOB_MIN -10
  45. #define TROWA_SCOPE_ROT_KNOB_MAX 10
  46. #define TROWA_SCOPE_ROUND_FORMAT "%.2f" // Output string format
  47. #define TROWA_SCOPE_ROUND_VALUE 100 // Rounding
  48. #define TROWA_SCOPE_ABS_ROT_ON_COLOR COLOR_TS_BLUE // Color to signal Absolution Rotation mode is on.
  49. #define TROWA_SCOPE_LINK_XY_SCALE_ON_COLOR COLOR_MAGENTA
  50. #define TROWA_SCOPE_INFO_DISPLAY_ON_COLOR COLOR_TS_ORANGE
  51. #define TROWA_SCOPE_LISSAJOUS_ON_COLOR COLOR_YELLOW
  52. // multiScope model.
  53. extern Model *modelMultiScope;
  54. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  55. // TSWaveform
  56. // Store data about a waveform.
  57. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  58. struct TSWaveform
  59. {
  60. float bufferX[BUFFER_SIZE] = {};
  61. float bufferY[BUFFER_SIZE] = {};
  62. bool bufferPenOn[BUFFER_SIZE] = {};
  63. int bufferIndex;
  64. float frameIndex;
  65. bool lissajous = true;
  66. SchmittTrigger lissajousTrigger;
  67. // Link X and Y scale ::::::::::::::::::::::::::::::::::::::::::::::
  68. bool linkXYScales; // Just forces scaleX = scaleY (only 1:1 aspect ratio).
  69. SchmittTrigger linkXYScalesTrigger;
  70. float lastXYScaleValue; // Last value when they are synched
  71. // Rotation ::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  72. SchmittTrigger rotModeTrigger;
  73. // True for absolute angular position, false if constant angular change
  74. bool rotMode;
  75. float rotKnobValue; // Value from rotation knob
  76. float rotAbsValue; // Translated to ABS position [radians]
  77. float rotDiffValue; // Translated to differential position [radians] (rate)
  78. // Colors ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  79. NVGcolor waveColor;
  80. float waveHue;
  81. #if TROWA_SCOPE_USE_COLOR_LIGHTS
  82. // References to our lights (typed)
  83. ColorValueLight* waveLight;
  84. #endif
  85. bool colorChanged;
  86. float waveOpacity;
  87. TSWaveform()
  88. {
  89. bufferIndex = 0;
  90. frameIndex = 0;
  91. memset(bufferPenOn, true, BUFFER_SIZE);
  92. colorChanged = true;
  93. rotMode = false;
  94. rotKnobValue = 0;
  95. rotAbsValue = 0;
  96. rotDiffValue = 0;
  97. linkXYScales = false;
  98. waveOpacity = TROWA_SCOPE_MAX_OPACITY;
  99. #if TROWA_SCOPE_USE_COLOR_LIGHTS
  100. waveLight = NULL;
  101. #endif
  102. return;
  103. }
  104. void setHue(float hue)
  105. {
  106. waveHue = hue;
  107. waveColor = HueToColor(waveHue);
  108. }
  109. void setHueFromKnob(float hueKnobValue)
  110. {
  111. setHue(rescale(hueKnobValue, TROWA_SCOPE_HUE_KNOB_MIN, TROWA_SCOPE_HUE_KNOB_MAX, 0.0, 1.0));
  112. return;
  113. }
  114. };
  115. //===============================================================================
  116. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  117. // multiScope
  118. // Scope module that draws multiple waveforms.
  119. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  120. //===============================================================================
  121. struct multiScope : Module {
  122. // Should probably make this linearly growing in case of course, we add more freaken waveforms...
  123. // TODO: Refactor to make variable num waveforms.
  124. enum ParamIds {
  125. COLOR_PARAM,
  126. EXTERNAL_PARAM = COLOR_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  127. ROTATION_PARAM = EXTERNAL_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  128. ROTATION_MODE_PARAM = ROTATION_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  129. TIME_PARAM = ROTATION_MODE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  130. TRIG_PARAM = TIME_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  131. X_POS_PARAM = TRIG_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  132. X_SCALE_PARAM = X_POS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  133. Y_POS_PARAM = X_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  134. Y_SCALE_PARAM = Y_POS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  135. LINK_XY_SCALE_PARAM = Y_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS, // Force Scale X = Scale Y.
  136. OPACITY_PARAM = LINK_XY_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS, // Alpha channel
  137. LISSAJOUS_PARAM = OPACITY_PARAM + TROWA_SCOPE_NUM_WAVEFORMS, // For now always true
  138. INFO_DISPLAY_TOGGLE_PARAM = LISSAJOUS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  139. NUM_PARAMS = INFO_DISPLAY_TOGGLE_PARAM + 1
  140. };
  141. enum InputIds {
  142. COLOR_INPUT,
  143. ROTATION_INPUT = COLOR_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  144. TIME_INPUT = ROTATION_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  145. X_INPUT = TIME_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  146. Y_INPUT = X_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  147. OPACITY_INPUT = Y_INPUT + TROWA_SCOPE_NUM_WAVEFORMS, // Opacity/Alpha channel
  148. PEN_ON_INPUT = OPACITY_INPUT + TROWA_SCOPE_NUM_WAVEFORMS, // Turn on/off drawing lines in between points.
  149. NUM_INPUTS = PEN_ON_INPUT + TROWA_SCOPE_NUM_WAVEFORMS
  150. };
  151. enum LightIds {
  152. COLOR_LED,
  153. ROT_LED = COLOR_LED+TROWA_SCOPE_NUM_WAVEFORMS,
  154. LINK_XY_SCALE_LED = ROT_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  155. LISSAJOUS_LED = LINK_XY_SCALE_LED + TROWA_SCOPE_NUM_WAVEFORMS, // For now always true
  156. INFO_DISPLAY_TOGGLE_LED = LISSAJOUS_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  157. NUM_LIGHTS = INFO_DISPLAY_TOGGLE_LED + 1
  158. };
  159. enum OutputIds {
  160. NUM_OUTPUTS
  161. };
  162. bool initialized = false;
  163. bool firstLoad = true;
  164. SchmittTrigger infoDisplayOnTrigger;
  165. // Information about what we are plotting. In future may become dynamically allocated.
  166. TSWaveform* waveForms[TROWA_SCOPE_NUM_WAVEFORMS];
  167. multiScope();
  168. ~multiScope();
  169. void step() override;
  170. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  171. // toJson(void)
  172. // Save to json.
  173. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  174. json_t *toJson() override {
  175. json_t *rootJ = json_object();
  176. json_t *huesJ = json_array();
  177. json_t *linkXYScalesJ = json_array();
  178. json_t* lissajousJ = json_array();
  179. json_t *rotModeJ = json_array();
  180. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  181. {
  182. json_t* itemJ = json_real(waveForms[wIx]->waveHue);
  183. json_array_append_new(huesJ, itemJ);
  184. itemJ = json_integer((int)waveForms[wIx]->linkXYScales);
  185. json_array_append_new(linkXYScalesJ, itemJ);
  186. itemJ = json_integer((int)waveForms[wIx]->lissajous);
  187. json_array_append_new(lissajousJ, itemJ);
  188. itemJ = json_integer(waveForms[wIx]->rotMode);
  189. json_array_append_new(rotModeJ, itemJ);
  190. }
  191. json_object_set_new(rootJ, "hues", huesJ);
  192. json_object_set_new(rootJ, "linkXYScales", linkXYScalesJ);
  193. json_object_set_new(rootJ, "lissajous", lissajousJ);
  194. json_object_set_new(rootJ, "rotMode", rotModeJ);
  195. return rootJ;
  196. }
  197. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  198. // fromJson(void)
  199. // Load settings.
  200. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  201. void fromJson(json_t *rootJ) override {
  202. json_t *huesJ = json_object_get(rootJ, "hues");
  203. json_t *rotModeJ = json_object_get(rootJ, "rotMode");
  204. json_t *linkXYScalesJ = json_object_get(rootJ, "linkXYScales");
  205. json_t* lissajousJ = json_object_get(rootJ, "lissajous");
  206. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  207. {
  208. json_t* itemJ = json_array_get(huesJ, wIx);
  209. if (itemJ)
  210. waveForms[wIx]->setHue((float)json_real_value(itemJ));
  211. itemJ = NULL;
  212. itemJ = json_array_get(rotModeJ, wIx);
  213. if (itemJ)
  214. waveForms[wIx]->rotMode = json_integer_value(itemJ);
  215. itemJ = NULL;
  216. itemJ = json_array_get(linkXYScalesJ, wIx);
  217. if (itemJ)
  218. waveForms[wIx]->linkXYScales = (bool)json_integer_value(itemJ);
  219. itemJ = NULL;
  220. itemJ = json_array_get(lissajousJ, wIx);
  221. if (itemJ)
  222. waveForms[wIx]->lissajous = (bool)json_integer_value(itemJ);
  223. else
  224. waveForms[wIx]->lissajous = true;
  225. itemJ = NULL;
  226. }
  227. firstLoad = true;
  228. return;
  229. }
  230. void reset() override {
  231. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  232. {
  233. waveForms[wIx]->lissajous = true;
  234. }
  235. }
  236. };
  237. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  238. // TSScopeDisplay
  239. // A top digital display for trowaSoft scope.
  240. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  241. struct TSScopeDisplay : TransparentWidget {
  242. multiScope *module;
  243. std::shared_ptr<Font> font;
  244. std::shared_ptr<Font> labelFont;
  245. int fontSize;
  246. char messageStr[TROWA_DISP_MSG_SIZE];
  247. bool visible = true;
  248. int originalWidth = 240;
  249. TSScopeDisplay() {
  250. visible = true;
  251. font = Font::load(assetPlugin(plugin, TROWA_DIGITAL_FONT));
  252. labelFont = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  253. fontSize = 12;
  254. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  255. messageStr[i] = '\0';
  256. }
  257. void draw(NVGcontext *vg) override {
  258. if (!visible)
  259. return; // Don't draw anything if we are not visible.
  260. nvgSave(vg);
  261. Rect b = Rect(Vec(0, 0), box.size);
  262. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  263. // Default Font:
  264. nvgFontSize(vg, fontSize);
  265. nvgTextLetterSpacing(vg, 1);
  266. // Background Colors:
  267. NVGcolor backgroundColor = nvgRGBA(0x20, 0x20, 0x20, 0x80);
  268. NVGcolor borderColor = nvgRGBA(0x10, 0x10, 0x10, 0x80);
  269. // Screen:
  270. nvgBeginPath(vg);
  271. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  272. nvgFillColor(vg, backgroundColor);
  273. nvgFill(vg);
  274. nvgStrokeWidth(vg, 1.0);
  275. nvgStrokeColor(vg, borderColor);
  276. nvgStroke(vg);
  277. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  278. ////////////// Labels /////////////////
  279. const int yStart = 10;
  280. int xStart = 10;
  281. const int dxRotation = 6; // 10
  282. int y = yStart;
  283. int x = xStart;
  284. int dx = 59; //41 // 37; // 35
  285. int dy = 16; //14
  286. //nvgFontSize(vg, fontSize); // Small font
  287. nvgFontFaceId(vg, labelFont->handle);
  288. nvgFillColor(vg, textColor);
  289. // Row Labels
  290. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  291. y = yStart + 5;
  292. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  293. {
  294. NVGcolor currColor = module->waveForms[wIx]->waveColor;
  295. nvgFillColor(vg, currColor);
  296. sprintf(messageStr, "S%d", wIx + 1);
  297. nvgText(vg, 5, y, messageStr, NULL);
  298. y += dy;
  299. }
  300. // Column Labels:
  301. nvgTextAlign(vg, NVG_ALIGN_RIGHT);
  302. nvgFillColor(vg, textColor);
  303. // Column 1 (Labels)
  304. y = yStart;
  305. xStart = 35; // 40
  306. x = xStart + dx / 2.0;
  307. nvgText(vg, x, y, "X Offset", NULL);
  308. x += dx;
  309. nvgFontFaceId(vg, labelFont->handle);
  310. nvgText(vg, x, y, "X Scale", NULL);
  311. x += dx;
  312. nvgFontFaceId(vg, labelFont->handle);
  313. nvgText(vg, x, y, "Y Offset", NULL);
  314. x += dx;
  315. nvgFontFaceId(vg, labelFont->handle);
  316. nvgText(vg, x, y, "Y Scale", NULL);
  317. // Rotation (wider)
  318. x += dx + dxRotation;
  319. nvgFontFaceId(vg, labelFont->handle);
  320. nvgText(vg, x, y, "Rotate", NULL);
  321. // Values:
  322. y = yStart + 5;
  323. nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
  324. NVGcolor absRotColor = TROWA_SCOPE_ABS_ROT_ON_COLOR;
  325. absRotColor.a = 0.50;
  326. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  327. {
  328. // NVGcolor currColor = module->waveForms[wIx]->waveColor;
  329. nvgFillColor(vg, textColor);
  330. // X Offset
  331. x = xStart + dx / 2.0;
  332. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, module->params[multiScope::X_POS_PARAM + wIx].value);
  333. nvgText(vg, x, y, messageStr, NULL);
  334. // X Gain
  335. x += dx;
  336. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, module->params[multiScope::X_SCALE_PARAM + wIx].value);
  337. nvgText(vg, x, y, messageStr, NULL);
  338. // Y Offset
  339. x += dx;
  340. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, module->params[multiScope::Y_POS_PARAM + wIx].value);
  341. nvgText(vg, x, y, messageStr, NULL);
  342. // Y Gain
  343. x += dx;
  344. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, module->params[multiScope::Y_SCALE_PARAM + wIx].value);
  345. nvgText(vg, x, y, messageStr, NULL);
  346. // Rotation
  347. x += dx + dxRotation;
  348. float v = 0.0;
  349. if (module->waveForms[wIx]->rotMode)
  350. {
  351. // Absolute
  352. v = module->waveForms[wIx]->rotAbsValue;
  353. // Background:
  354. nvgBeginPath(vg);
  355. nvgRoundedRect(vg, x - dx - 3, y - 2, dx + 5, fontSize + 2, 2);
  356. nvgFillColor(vg, absRotColor);
  357. nvgFill(vg);
  358. // Text will be black for this
  359. nvgFillColor(vg, COLOR_BLACK);
  360. sprintf(messageStr, "%.1f", v * 180.0 / NVG_PI);
  361. }
  362. else
  363. {
  364. // Differential
  365. v = module->waveForms[wIx]->rotDiffValue;
  366. sprintf(messageStr, "%+.1f", v * 180.0 / NVG_PI);
  367. }
  368. nvgText(vg, x, y, messageStr, NULL);
  369. // Advance y to next
  370. y += dy;
  371. } // end loop through wave forms
  372. nvgResetScissor(vg);
  373. nvgRestore(vg);
  374. return;
  375. }
  376. }; // end TSScopeDisplay
  377. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  378. // multiScopeDisplay
  379. // Draws a waveform.
  380. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  381. struct multiScopeDisplay : TransparentWidget {
  382. multiScope *module;
  383. int frame = 0;
  384. float rot = 0;
  385. std::shared_ptr<Font> font;
  386. int wIx = 0; // Waveform index
  387. multiScopeDisplay() {
  388. //spoutInitSpout();
  389. return;
  390. }
  391. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  392. // drawWaveform()
  393. // @vg : (IN) NVGcontext
  394. // @valX: (IN) Pointer to x values.
  395. // @valY: (IN) Pointer to y values.
  396. // @rotRate: (IN) Rotation rate in radians
  397. // @flipX: (IN) Flip along x (at x=0)
  398. // @flipY: (IN) Flip along y
  399. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  400. void drawWaveform(NVGcontext *vg, float *valX, float *valY, bool* penOn, float rotRate, bool flipX, bool flipY) {
  401. if (!valX)
  402. return;
  403. nvgSave(vg);
  404. Rect b = Rect(Vec(0, 0), box.size);
  405. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  406. nvgTranslate(vg, box.size.x / 2.0, box.size.y / 2.0);
  407. nvgRotate(vg, rot += rotRate);
  408. if (flipX || flipY)
  409. {
  410. // Sets the transform to scale matrix.
  411. // void nvgTransformScale(float* dst, float sx, float sy);
  412. nvgScale(vg, ((flipX) ? -1 : 1), (flipY) ? -1 : 1); // flip
  413. }
  414. // Draw maximum display left to right
  415. nvgBeginPath(vg);
  416. nvgLineCap(vg, NVG_ROUND);
  417. nvgMiterLimit(vg, 2.0);
  418. nvgStrokeWidth(vg, 3.0);
  419. bool lastPointStarted = false;
  420. float xOffset = -box.size.x / 2.0; // Fill our screen
  421. float yOffset = -box.size.y / 2.0;
  422. for (int i = 0; i < BUFFER_SIZE; i++) {
  423. if (penOn[i])
  424. {
  425. float x, y;
  426. if (valY) {
  427. x = valX[i] / 2.0 + 0.5;
  428. y = valY[i] / 2.0 + 0.5;
  429. }
  430. else {
  431. x = (float)i / (BUFFER_SIZE - 1);
  432. y = valX[i] / 2.0 + 0.5;
  433. }
  434. Vec p;
  435. p.x = b.pos.x + xOffset + b.size.x * x;
  436. p.y = b.pos.y + yOffset + b.size.y * (1.0 - y);
  437. if (!lastPointStarted)
  438. {
  439. nvgMoveTo(vg, p.x, p.y);
  440. }
  441. else
  442. {
  443. nvgLineTo(vg, p.x, p.y);
  444. }
  445. lastPointStarted = true;
  446. //if (firstIx < 0 || (i > 0 && !penOn[i - 1]))
  447. // firstIx = i;
  448. //if (i == firstIx)
  449. // nvgMoveTo(vg, p.x, p.y);
  450. //else
  451. // nvgLineTo(vg, p.x, p.y);
  452. }
  453. else
  454. {
  455. lastPointStarted = false;
  456. }
  457. } // end loop through buffer
  458. nvgLineCap(vg, NVG_ROUND);
  459. nvgMiterLimit(vg, 2.0);
  460. nvgStrokeWidth(vg, 3.0);
  461. //nvgGlobalCompositeOperation(vg, NVG_LIGHTER);
  462. nvgStroke(vg);
  463. nvgResetScissor(vg);
  464. nvgRestore(vg);
  465. } // end drawWaveform()
  466. void draw(NVGcontext *vg) override {
  467. if (!module->initialized)
  468. return;
  469. float gainX = ((int)(module->params[multiScope::X_SCALE_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  470. float gainY = ((int)(module->params[multiScope::Y_SCALE_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  471. float offsetX = ((int)(module->params[multiScope::X_POS_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  472. float offsetY = ((int)(module->params[multiScope::Y_POS_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  473. TSWaveform* waveForm = module->waveForms[wIx];
  474. float valuesX[BUFFER_SIZE];
  475. float valuesY[BUFFER_SIZE];
  476. bool penOn[BUFFER_SIZE];
  477. float multX = gainX / 10.0;
  478. float multY = gainY / 10.0;
  479. for (int i = 0; i < BUFFER_SIZE; i++) {
  480. int j = i;
  481. // Lock display to buffer if buffer update deltaTime <= 2^-11
  482. if (waveForm->lissajous)
  483. j = (i + waveForm->bufferIndex) % BUFFER_SIZE;
  484. valuesX[i] = (waveForm->bufferX[j] + offsetX) * multX;
  485. valuesY[i] = (waveForm->bufferY[j] + offsetY) * multY;
  486. penOn[i] = waveForm->bufferPenOn[j];
  487. }
  488. // Draw waveforms
  489. NVGcolor waveColor = waveForm->waveColor;
  490. float rotRate = 0;
  491. waveColor.a = waveForm->waveOpacity;
  492. nvgStrokeColor(vg, waveColor); // Color has already been calculated by main module
  493. if (waveForm->rotMode)
  494. {
  495. // Absolute position:
  496. rot = waveForm->rotAbsValue;
  497. }
  498. else
  499. {
  500. // Differential rotation
  501. rotRate = waveForm->rotDiffValue;
  502. }
  503. if (waveForm->lissajous) {
  504. // X x Y
  505. if (module->inputs[multiScope::X_INPUT + wIx].active || module->inputs[multiScope::Y_INPUT + wIx].active) {
  506. module->lights[multiScope::COLOR_LED + wIx].value = 1.0; // Actively drawing
  507. drawWaveform(vg, valuesX, valuesY, penOn, rotRate, false, false);// module->mirrorX[wIx], module->mirrorY[wIx]);
  508. }
  509. else
  510. {
  511. module->lights[multiScope::COLOR_LED + wIx].value = 0.7; // Not really drawing anything, dim the light
  512. }
  513. }
  514. else {
  515. // Y
  516. if (module->inputs[multiScope::Y_INPUT + wIx].active) {
  517. drawWaveform(vg, valuesY, NULL, penOn, rotRate, false, false);
  518. }
  519. // X
  520. if (module->inputs[multiScope::X_INPUT + wIx].active) {
  521. drawWaveform(vg, valuesX, NULL, penOn, rotRate, false, false);
  522. }
  523. }
  524. return;
  525. } // end draw()
  526. }; // end multiScopeDisplay
  527. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  528. // TSScopeLabelArea
  529. // Draw labels on our scope.
  530. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  531. struct TSScopeLabelArea : TransparentWidget {
  532. multiScope *module;
  533. std::shared_ptr<Font> font;
  534. int fontSize;
  535. char messageStr[TROWA_DISP_MSG_SIZE];
  536. TSScopeLabelArea() {
  537. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  538. fontSize = 10;
  539. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  540. messageStr[i] = '\0';
  541. }
  542. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  543. // drawColorGrandientArc()
  544. // @cx : (IN) x-coordinate for center of arc.
  545. // @cy : (IN) y-coordinate for center of arc.
  546. // @radius: (IN) radius of arc.
  547. // @thickness: (IN) Border thickness of arc.
  548. // @start_radians : (IN) Arc start angle [radians].
  549. // @end_radians : (IN) Arc end angle [radians].
  550. // @startHue: (IN) Starting hue.
  551. // @endHue: (IN) Ending hue.
  552. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  553. void drawColorGradientArc(NVGcontext* vg, float cx, float cy, float radius, float thickness, float start_radians, float end_radians, float startHue, float endHue)
  554. {
  555. NVGcolor startColor = HueToColorGradient(startHue);
  556. NVGcolor endColor = HueToColorGradient(endHue);
  557. nvgBeginPath(vg);
  558. nvgArc(vg, /*cx*/ cx, /*cy*/ cy, radius - thickness/2.0,
  559. /*a0*/ start_radians, /*a1*/ end_radians, /*dir*/ NVG_CW);
  560. // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates
  561. // of the linear gradient, icol specifies the start color and ocol the end color.
  562. // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
  563. //float circum = radius * (end_radians - start_radians);
  564. float sx = cx + radius * cos(start_radians);
  565. float sy = cy + radius * sin(start_radians);
  566. float ex = cx + radius * cos(end_radians) + 1;
  567. float ey = cy + radius * sin(end_radians) + 1;
  568. NVGpaint paint = nvgLinearGradient(vg, sx, sy, ex, ey, startColor, endColor);
  569. nvgStrokeWidth(vg, thickness);
  570. nvgStrokePaint(vg, paint);
  571. nvgStroke(vg);
  572. return;
  573. } // end drawColorGradientArc()
  574. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  575. // drawColorGrandientArc()
  576. // @cx : (IN) x-coordinate for center of arc.
  577. // @cy : (IN) y-coordinate for center of arc.
  578. // @radius: (IN) radius of arc.
  579. // @thickness: (IN) Border thickness of arc.
  580. // @start_radians : (IN) Arc start angle [radians].
  581. // @end_radians : (IN) Arc end angle [radians].
  582. // @startHue: (IN) Starting hue.
  583. // @endHue: (IN) Ending hue.
  584. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  585. void drawColorGradientArc(NVGcontext* vg, float cx, float cy, float radius, float thickness)
  586. {
  587. const float startAngle = 0.67*NVG_PI;
  588. const float endAngle = 2.33*NVG_PI;
  589. const int numStops = 20;
  590. float dH = 1.0 / numStops;
  591. float dA = (endAngle - startAngle) / numStops;
  592. float hue = 0;
  593. float start_radians = startAngle;
  594. while (hue < 1.0)
  595. {
  596. drawColorGradientArc(vg, cx, cy, radius, thickness,
  597. start_radians, start_radians + dA*1.2,
  598. hue, hue + dH);
  599. start_radians += dA;
  600. hue += dH;
  601. }
  602. return;
  603. } // end drawColorGradientArc()
  604. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  605. // draw()
  606. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  607. void draw(NVGcontext *vg) override {
  608. // Default Font:
  609. nvgFontSize(vg, fontSize);
  610. nvgFontFaceId(vg, font->handle);
  611. nvgTextLetterSpacing(vg, 1);
  612. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  613. nvgFillColor(vg, textColor);
  614. nvgFontSize(vg, fontSize);
  615. const int xStart = 17; // 23
  616. const int yStart = 6; // 10
  617. int knobOffset = 5;
  618. int dx = TROWA_SCOPE_CONTROL_DX; // 35
  619. int dy = TROWA_SCOPE_CONTROL_DY; // 26
  620. int shapeSpacingY = TROWA_SCOPE_CONTROL_SHAPE_SPACING; // 8 An extra amount between shapes
  621. int x, y;
  622. int shapeDy = dy * 3 + knobOffset + shapeSpacingY + 3;
  623. x = xStart;
  624. y = yStart;
  625. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  626. {
  627. nvgFontSize(vg, fontSize);
  628. x = xStart;
  629. int waveY = y;
  630. // Shape Label:
  631. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  632. // BG Box
  633. nvgBeginPath(vg);
  634. nvgRect(vg, x - 3, y - 2, TROWA_SCOPE_CONTROL_START_X - x + 3, fontSize + 3);
  635. nvgFillColor(vg, textColor);
  636. nvgFill(vg);
  637. nvgFillColor(vg, COLOR_BLACK);
  638. sprintf(messageStr, TROWA_SCOPE_SHAPE_FORMAT_STRING, (wIx + 1));
  639. nvgText(vg, x, y, messageStr, NULL);
  640. nvgFillColor(vg, textColor);
  641. // Line down lhs:
  642. nvgBeginPath(vg);
  643. nvgRect(vg, x - 3, y - 2, TROWA_SCOPE_CONTROL_START_X - x + 3, fontSize + 3);
  644. nvgMoveTo(vg, /*x*/ x - 3, /*y*/ y - 2);
  645. nvgLineTo(vg, /*x*/ x - 3, /*y*/ y + shapeDy - 10); // Go Left (to right of the text "Edit")
  646. nvgStrokeWidth(vg, 1.0);
  647. nvgStrokeColor(vg, textColor);
  648. nvgStroke(vg);
  649. // Row Labels:
  650. x = TROWA_SCOPE_CONTROL_START_X - 5;
  651. y += fontSize + 8;
  652. nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
  653. nvgText(vg, x, y, "IN", NULL);
  654. y += dy;
  655. nvgText(vg, x, y, "OFF", NULL);
  656. y += dy;
  657. nvgText(vg, x, y, "SCL", NULL);
  658. const char* colLabels[] = { "X", "Y", "C", "A", "R", "T" };
  659. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
  660. x = TROWA_SCOPE_CONTROL_START_X + 15;
  661. y = waveY;
  662. for (int i = 0; i < 6; i++)
  663. {
  664. nvgText(vg, x, y, colLabels[i], NULL);
  665. x += dx;
  666. }
  667. int x1 = TROWA_SCOPE_CONTROL_START_X + 15 + 0.5*dx;
  668. int y1 = waveY + dy * 2.3 + 1;
  669. nvgFontSize(vg, fontSize*0.8);
  670. nvgText(vg, x1, y1, "LNK", NULL);
  671. x1 += 2*dx;
  672. int y2 = y1 + 6;
  673. nvgText(vg, x1 - 8, y2, "BLANK", NULL);
  674. y2 += fontSize * 0.8 + 0.5;
  675. nvgFontSize(vg, fontSize*1.05);
  676. nvgText(vg, x1 - 8, y2, "<= 0", NULL);
  677. nvgFontSize(vg, fontSize*0.8);
  678. x1 += 1.5*dx;
  679. nvgText(vg, x1, y1, "ABS", NULL);
  680. x1 += dx;
  681. nvgText(vg, x1, y1, "X*Y", NULL);
  682. // Color Knob gradient:
  683. //x += dx * 2;
  684. //y += 30;
  685. drawColorGradientArc(vg,
  686. /*cx*/ TROWA_SCOPE_CONTROL_START_X + 15 + 2*dx,
  687. /*cy*/ y + dy + 23 + TROWA_SCOPE_COLOR_KNOB_Y_OFFSET,
  688. /*radius*/ 14,
  689. /*thickness*/ 4.0);
  690. y += shapeDy - 3;
  691. } // end loop through shapes/waveforms
  692. return;
  693. } // end draw()
  694. }; // end TSScopeLabelArea
  695. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  696. // TSScopeSideBarArea
  697. // Draw labels on the RHS bar of our scope.
  698. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  699. struct TSScopeSideBarLabelArea : TransparentWidget {
  700. //multiScope *module;
  701. std::shared_ptr<Font> font;
  702. int fontSize;
  703. //char messageStr[TROWA_DISP_MSG_SIZE];
  704. TSScopeSideBarLabelArea() {
  705. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  706. fontSize = 10;
  707. //for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  708. // messageStr[i] = '\0';
  709. }
  710. TSScopeSideBarLabelArea(Vec bsize) : TSScopeSideBarLabelArea() {
  711. this->box.size = bsize;
  712. return;
  713. }
  714. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  715. // draw()
  716. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  717. void draw(NVGcontext *vg) override {
  718. nvgSave(vg);
  719. //nvgTranslate(vg, -box.size.y / 2.0, -box.size.x / 2.0);
  720. nvgRotate(vg, NVG_PI*0.5);
  721. // Default Font:
  722. nvgFontSize(vg, fontSize);
  723. nvgFontFaceId(vg, font->handle);
  724. nvgTextLetterSpacing(vg, 1);
  725. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  726. nvgFillColor(vg, textColor);
  727. nvgFontSize(vg, fontSize);
  728. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
  729. float x, y;
  730. x = 34;// box.size.x / 2.0;
  731. y = -box.size.x / 2.0; // 0;// 32;
  732. nvgText(vg, x, y, "INFO", NULL); // Info display btn toggle
  733. nvgRestore(vg);
  734. return;
  735. } // end draw()
  736. }; // end TSScopeSideBarLabelArea
  737. #endif // end if use old scope
  738. #endif // end if not defined