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.

221 lines
5.7KB

  1. #include "app/WireWidget.hpp"
  2. #include "app/Scene.hpp"
  3. #include "engine/Engine.hpp"
  4. #include "componentlibrary.hpp"
  5. #include "window.hpp"
  6. #include "event.hpp"
  7. #include "context.hpp"
  8. #include "settings.hpp"
  9. namespace rack {
  10. static void drawPlug(NVGcontext *vg, math::Vec pos, NVGcolor color) {
  11. NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5);
  12. // Plug solid
  13. nvgBeginPath(vg);
  14. nvgCircle(vg, pos.x, pos.y, 9);
  15. nvgFillColor(vg, color);
  16. nvgFill(vg);
  17. // Border
  18. nvgStrokeWidth(vg, 1.0);
  19. nvgStrokeColor(vg, colorOutline);
  20. nvgStroke(vg);
  21. // Hole
  22. nvgBeginPath(vg);
  23. nvgCircle(vg, pos.x, pos.y, 5);
  24. nvgFillColor(vg, nvgRGBf(0.0, 0.0, 0.0));
  25. nvgFill(vg);
  26. }
  27. static void drawWire(NVGcontext *vg, math::Vec pos1, math::Vec pos2, NVGcolor color, float thickness, float tension, float opacity) {
  28. NVGcolor colorShadow = nvgRGBAf(0, 0, 0, 0.10);
  29. NVGcolor colorOutline = nvgLerpRGBA(color, nvgRGBf(0.0, 0.0, 0.0), 0.5);
  30. // Wire
  31. if (opacity > 0.0) {
  32. nvgSave(vg);
  33. // This power scaling looks more linear than actual linear scaling
  34. nvgGlobalAlpha(vg, std::pow(opacity, 1.5));
  35. float dist = pos1.minus(pos2).norm();
  36. math::Vec slump;
  37. slump.y = (1.0 - tension) * (150.0 + 1.0*dist);
  38. math::Vec pos3 = pos1.plus(pos2).div(2).plus(slump);
  39. nvgLineJoin(vg, NVG_ROUND);
  40. // Shadow
  41. math::Vec pos4 = pos3.plus(slump.mult(0.08));
  42. nvgBeginPath(vg);
  43. nvgMoveTo(vg, pos1.x, pos1.y);
  44. nvgQuadTo(vg, pos4.x, pos4.y, pos2.x, pos2.y);
  45. nvgStrokeColor(vg, colorShadow);
  46. nvgStrokeWidth(vg, thickness);
  47. nvgStroke(vg);
  48. // Wire outline
  49. nvgBeginPath(vg);
  50. nvgMoveTo(vg, pos1.x, pos1.y);
  51. nvgQuadTo(vg, pos3.x, pos3.y, pos2.x, pos2.y);
  52. nvgStrokeColor(vg, colorOutline);
  53. nvgStrokeWidth(vg, thickness);
  54. nvgStroke(vg);
  55. // Wire solid
  56. nvgStrokeColor(vg, color);
  57. nvgStrokeWidth(vg, thickness - 2);
  58. nvgStroke(vg);
  59. nvgRestore(vg);
  60. }
  61. }
  62. static const NVGcolor wireColors[] = {
  63. nvgRGB(0xc9, 0xb7, 0x0e), // yellow
  64. nvgRGB(0xc9, 0x18, 0x47), // red
  65. nvgRGB(0x0c, 0x8e, 0x15), // green
  66. nvgRGB(0x09, 0x86, 0xad), // blue
  67. // nvgRGB(0x44, 0x44, 0x44), // black
  68. // nvgRGB(0x66, 0x66, 0x66), // gray
  69. // nvgRGB(0x88, 0x88, 0x88), // light gray
  70. // nvgRGB(0xaa, 0xaa, 0xaa), // white
  71. };
  72. static int lastWireColorId = -1;
  73. WireWidget::WireWidget() {
  74. lastWireColorId = (lastWireColorId + 1) % LENGTHOF(wireColors);
  75. color = wireColors[lastWireColorId];
  76. }
  77. WireWidget::~WireWidget() {
  78. outputPort = NULL;
  79. inputPort = NULL;
  80. updateWire();
  81. }
  82. void WireWidget::updateWire() {
  83. if (inputPort && outputPort) {
  84. // Check correct types
  85. assert(inputPort->type == PortWidget::INPUT);
  86. assert(outputPort->type == PortWidget::OUTPUT);
  87. if (!wire) {
  88. wire = new Wire;
  89. wire->outputModule = outputPort->module;
  90. wire->outputId = outputPort->portId;
  91. wire->inputModule = inputPort->module;
  92. wire->inputId = inputPort->portId;
  93. context()->engine->addWire(wire);
  94. }
  95. }
  96. else {
  97. if (wire) {
  98. context()->engine->removeWire(wire);
  99. delete wire;
  100. wire = NULL;
  101. }
  102. }
  103. }
  104. math::Vec WireWidget::getOutputPos() {
  105. if (outputPort) {
  106. return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), context()->scene->rackWidget);
  107. }
  108. else if (hoveredOutputPort) {
  109. return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), context()->scene->rackWidget);
  110. }
  111. else {
  112. return context()->scene->rackWidget->lastMousePos;
  113. }
  114. }
  115. math::Vec WireWidget::getInputPos() {
  116. if (inputPort) {
  117. return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), context()->scene->rackWidget);
  118. }
  119. else if (hoveredInputPort) {
  120. return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), context()->scene->rackWidget);
  121. }
  122. else {
  123. return context()->scene->rackWidget->lastMousePos;
  124. }
  125. }
  126. json_t *WireWidget::toJson() {
  127. json_t *rootJ = json_object();
  128. std::string s = color::toHexString(color);
  129. json_object_set_new(rootJ, "color", json_string(s.c_str()));
  130. return rootJ;
  131. }
  132. void WireWidget::fromJson(json_t *rootJ) {
  133. json_t *colorJ = json_object_get(rootJ, "color");
  134. if (colorJ) {
  135. // v0.6.0 and earlier patches use JSON objects. Just ignore them if so and use the existing wire color.
  136. if (json_is_string(colorJ))
  137. color = color::fromHexString(json_string_value(colorJ));
  138. }
  139. }
  140. void WireWidget::draw(NVGcontext *vg) {
  141. float opacity = settings::wireOpacity;
  142. float tension = settings::wireTension;
  143. WireWidget *activeWire = context()->scene->rackWidget->wireContainer->activeWire;
  144. if (activeWire) {
  145. // Draw as opaque if the wire is active
  146. if (activeWire == this)
  147. opacity = 1.0;
  148. }
  149. else {
  150. PortWidget *hoveredPort = dynamic_cast<PortWidget*>(context()->event->hoveredWidget);
  151. if (hoveredPort && (hoveredPort == outputPort || hoveredPort == inputPort))
  152. opacity = 1.0;
  153. }
  154. float thickness = 5;
  155. if (wire && wire->outputModule) {
  156. Output *output = &wire->outputModule->outputs[wire->outputId];
  157. if (output->numChannels != 1) {
  158. thickness = 9;
  159. }
  160. }
  161. math::Vec outputPos = getOutputPos();
  162. math::Vec inputPos = getInputPos();
  163. drawWire(vg, outputPos, inputPos, color, thickness, tension, opacity);
  164. }
  165. void WireWidget::drawPlugs(NVGcontext *vg) {
  166. // TODO Figure out a way to draw plugs first and wires last, and cut the plug portion of the wire off.
  167. math::Vec outputPos = getOutputPos();
  168. math::Vec inputPos = getInputPos();
  169. drawPlug(vg, outputPos, color);
  170. drawPlug(vg, inputPos, color);
  171. // Draw plug light
  172. // TODO
  173. // Only draw this when light is on top of the plug stack
  174. if (outputPort) {
  175. nvgSave(vg);
  176. nvgTranslate(vg, outputPos.x - 4, outputPos.y - 4);
  177. outputPort->plugLight->draw(vg);
  178. nvgRestore(vg);
  179. }
  180. if (inputPort) {
  181. nvgSave(vg);
  182. nvgTranslate(vg, inputPos.x - 4, inputPos.y - 4);
  183. inputPort->plugLight->draw(vg);
  184. nvgRestore(vg);
  185. }
  186. }
  187. } // namespace rack