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.

326 lines
8.5KB

  1. //***********************************************************************************************
  2. //Impromptu Modular: Modules for VCV Rack by Marc Boulé
  3. //
  4. //Based on code from Valley Rack Free by Dale Johnson
  5. //See ./LICENSE.txt for all licenses
  6. //***********************************************************************************************
  7. #include "IMWidgets.hpp"
  8. // Dynamic SVGScrew
  9. ScrewCircle::ScrewCircle(float _angle) {
  10. static const float highRadius = 1.4f;// radius for 0 degrees (screw looks like a +)
  11. static const float lowRadius = 1.1f;// radius for 45 degrees (screw looks like an x)
  12. angle = _angle;
  13. _angle = fabs(angle - M_PI/4.0f);
  14. radius = ((highRadius - lowRadius)/(M_PI/4.0f)) * _angle + lowRadius;
  15. }
  16. void ScrewCircle::draw(NVGcontext *vg) {
  17. NVGcolor backgroundColor = nvgRGB(0x72, 0x72, 0x72);
  18. NVGcolor borderColor = nvgRGB(0x72, 0x72, 0x72);
  19. nvgBeginPath(vg);
  20. nvgCircle(vg, box.size.x/2.0f, box.size.y/2.0f, radius);// box, radius
  21. nvgFillColor(vg, backgroundColor);
  22. nvgFill(vg);
  23. nvgStrokeWidth(vg, 1.0);
  24. nvgStrokeColor(vg, borderColor);
  25. nvgStroke(vg);
  26. }
  27. DynamicSVGScrew::DynamicSVGScrew() {
  28. mode = nullptr;
  29. oldMode = -1;
  30. // for random rotated screw used in primary mode (code copied from ImpromptuModular.cpp ScrewSilverRandomRot::ScrewSilverRandomRot())
  31. // **********
  32. float angle0_90 = randomUniform()*M_PI/2.0f;
  33. //float angle0_90 = randomUniform() > 0.5f ? M_PI/4.0f : 0.0f;// for testing
  34. tw = new TransformWidget();
  35. addChild(tw);
  36. sw = new SVGWidget();
  37. tw->addChild(sw);
  38. //sw->setSVG(SVG::load(assetPlugin(plugin, "res/Screw.svg")));
  39. sw->setSVG(SVG::load(assetGlobal("res/ComponentLibrary/ScrewSilver.svg")));
  40. sc = new ScrewCircle(angle0_90);
  41. sc->box.size = sw->box.size;
  42. tw->addChild(sc);
  43. box.size = sw->box.size;
  44. tw->box.size = sw->box.size;
  45. tw->identity();
  46. // Rotate SVG
  47. Vec center = sw->box.getCenter();
  48. tw->translate(center);
  49. tw->rotate(angle0_90);
  50. tw->translate(center.neg());
  51. // for fixed svg screw used in alternate mode
  52. // **********
  53. swAlt = new SVGWidget();
  54. swAlt->visible = false;
  55. addChild(swAlt);
  56. }
  57. void DynamicSVGScrew::addSVGalt(std::shared_ptr<SVG> svg) {
  58. if(!swAlt->svg) {
  59. swAlt->setSVG(svg);
  60. }
  61. }
  62. void DynamicSVGScrew::step() { // all code except middle if() from SVGPanel::step() in SVGPanel.cpp
  63. if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
  64. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  65. oversample = 2.f;
  66. }
  67. if(mode != nullptr && *mode != oldMode) {
  68. if ((*mode) == 0) {
  69. sw->visible = true;
  70. swAlt->visible = false;
  71. }
  72. else {
  73. sw->visible = false;
  74. swAlt->visible = true;
  75. }
  76. oldMode = *mode;
  77. dirty = true;
  78. }
  79. FramebufferWidget::step();
  80. }
  81. // Dynamic SVGPanel
  82. void PanelBorderWidget_Impromptu::draw(NVGcontext *vg) { // carbon copy from SVGPanel.cpp
  83. NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
  84. nvgBeginPath(vg);
  85. nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);// full rect of module (including expansion area if a module has one)
  86. nvgStrokeColor(vg, borderColor);
  87. nvgStrokeWidth(vg, 1.0);
  88. nvgStroke(vg);
  89. if (expWidth != nullptr && *expWidth != nullptr) {// add expansion division when pannel uses expansion area
  90. int expW = **expWidth;
  91. nvgBeginPath(vg);
  92. nvgMoveTo(vg, box.size.x - expW, 1);
  93. nvgLineTo(vg, box.size.x - expW, box.size.y - 1.0);
  94. nvgStrokeWidth(vg, 2.0);
  95. nvgStroke(vg);
  96. }
  97. }
  98. DynamicSVGPanel::DynamicSVGPanel() {
  99. mode = nullptr;
  100. oldMode = -1;
  101. expWidth = nullptr;
  102. visiblePanel = new SVGWidget();
  103. addChild(visiblePanel);
  104. border = new PanelBorderWidget_Impromptu();
  105. border->expWidth = &expWidth;
  106. addChild(border);
  107. }
  108. void DynamicSVGPanel::addPanel(std::shared_ptr<SVG> svg) {
  109. panels.push_back(svg);
  110. if(!visiblePanel->svg) {
  111. visiblePanel->setSVG(svg);
  112. box.size = visiblePanel->box.size.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);
  113. border->box.size = box.size;
  114. }
  115. }
  116. void DynamicSVGPanel::step() { // all code except middle if() from SVGPanel::step() in SVGPanel.cpp
  117. if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
  118. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  119. oversample = 2.f;
  120. }
  121. if(mode != nullptr && *mode != oldMode) {
  122. visiblePanel->setSVG(panels[*mode]);
  123. oldMode = *mode;
  124. dirty = true;
  125. }
  126. FramebufferWidget::step();
  127. }
  128. // Dynamic SVGPort
  129. DynamicSVGPort::DynamicSVGPort() {
  130. mode = nullptr;
  131. oldMode = -1;
  132. //SVGPort constructor automatically called
  133. }
  134. void DynamicSVGPort::addFrame(std::shared_ptr<SVG> svg) {
  135. frames.push_back(svg);
  136. if(!background->svg)
  137. SVGPort::setSVG(svg);
  138. }
  139. void DynamicSVGPort::step() {
  140. if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
  141. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  142. oversample = 2.f;
  143. }
  144. if(mode != nullptr && *mode != oldMode) {
  145. background->setSVG(frames[*mode]);
  146. oldMode = *mode;
  147. dirty = true;
  148. }
  149. Port::step();
  150. }
  151. // Dynamic SVGSwitch
  152. DynamicSVGSwitch::DynamicSVGSwitch() {
  153. mode = nullptr;
  154. oldMode = -1;
  155. //SVGSwitch constructor automatically called
  156. }
  157. void DynamicSVGSwitch::addFrameAll(std::shared_ptr<SVG> svg) {
  158. framesAll.push_back(svg);
  159. if (framesAll.size() == 2) {
  160. addFrame(framesAll[0]);
  161. addFrame(framesAll[1]);
  162. }
  163. }
  164. void DynamicSVGSwitch::step() {
  165. if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
  166. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  167. oversample = 2.f;
  168. }
  169. if(mode != nullptr && *mode != oldMode) {
  170. if ((*mode) == 0) {
  171. frames[0]=framesAll[0];
  172. frames[1]=framesAll[1];
  173. }
  174. else {
  175. frames[0]=framesAll[2];
  176. frames[1]=framesAll[3];
  177. }
  178. oldMode = *mode;
  179. onChange(*(new EventChange()));// required because of the way SVGSwitch changes images, we only change the frames above.
  180. //dirty = true;// dirty is not sufficient when changing via frames assignments above (i.e. onChange() is required)
  181. }
  182. }
  183. // Dynamic SVGKnob
  184. DynamicSVGKnob::DynamicSVGKnob() {
  185. mode = nullptr;
  186. oldMode = -1;
  187. effect = new SVGWidget();
  188. //SVGKnob constructor automatically called
  189. }
  190. void DynamicSVGKnob::addFrameAll(std::shared_ptr<SVG> svg) {
  191. framesAll.push_back(svg);
  192. if (framesAll.size() == 1) {
  193. setSVG(svg);
  194. }
  195. }
  196. void DynamicSVGKnob::addEffect(std::shared_ptr<SVG> svg) {
  197. effect->setSVG(svg);
  198. addChild(effect);
  199. }
  200. void DynamicSVGKnob::step() {
  201. if (isNear(rack::global_ui->window.gPixelRatio, 1.0)) {
  202. // Small details draw poorly at low DPI, so oversample when drawing to the framebuffer
  203. oversample = 2.f;
  204. }
  205. if(mode != nullptr && *mode != oldMode) {
  206. if ((*mode) == 0) {
  207. setSVG(framesAll[0]);
  208. effect->visible = false;
  209. }
  210. else {
  211. setSVG(framesAll[1]);
  212. effect->visible = true;
  213. }
  214. oldMode = *mode;
  215. dirty = true;
  216. }
  217. SVGKnob::step();
  218. }
  219. // Dynamic IMTactile
  220. DynamicIMTactile::DynamicIMTactile() {
  221. snap = false;
  222. smooth = false;// must be false or else DynamicIMTactile::changeValue() call from module will crash Rack
  223. wider = nullptr;
  224. paramReadRequest = nullptr;
  225. oldWider = -1.0f;
  226. box.size = Vec(padWidth, padHeight);
  227. }
  228. void DynamicIMTactile::step() {
  229. if(wider != nullptr && *wider != oldWider) {
  230. if ((*wider) > 0.5f) {
  231. box.size = Vec(padWidthWide, padHeight);
  232. }
  233. else {
  234. box.size = Vec(padWidth, padHeight);
  235. }
  236. oldWider = *wider;
  237. }
  238. if (paramReadRequest != nullptr) {
  239. float readVal = *paramReadRequest;
  240. if (readVal != -10.0f) {
  241. setValue(readVal);
  242. *paramReadRequest = -10.0f;
  243. }
  244. }
  245. FramebufferWidget::step();
  246. }
  247. void DynamicIMTactile::onDragStart(EventDragStart &e) {
  248. dragValue = value;
  249. dragY = rack::global_ui->app.gRackWidget->lastMousePos.y;
  250. }
  251. void DynamicIMTactile::onDragMove(EventDragMove &e) {
  252. float rangeValue = maxValue - minValue;// infinite not supported (not relevant)
  253. float newDragY = rack::global_ui->app.gRackWidget->lastMousePos.y;
  254. float delta = -(newDragY - dragY) * rangeValue / box.size.y;
  255. dragY = newDragY;
  256. dragValue += delta;
  257. float dragValueClamped = clamp2(dragValue, minValue, maxValue);
  258. if (snap)
  259. dragValueClamped = roundf(dragValueClamped);
  260. setValue(dragValueClamped);
  261. }
  262. void DynamicIMTactile::onMouseDown(EventMouseDown &e) {
  263. float val = rescale(e.pos.y, box.size.y, 0.0f , minValue, maxValue);
  264. if (snap)
  265. val = roundf(val);
  266. setValue(val);
  267. ParamWidget::onMouseDown(e);
  268. }
  269. //void DynamicIMTactile::changeValue(float newVal) {
  270. // setValue(newVal);
  271. //}