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.

412 lines
11KB

  1. #include <app/CableWidget.hpp>
  2. #include <widget/SvgWidget.hpp>
  3. #include <widget/TransformWidget.hpp>
  4. #include <app/Scene.hpp>
  5. #include <app/RackWidget.hpp>
  6. #include <app/ModuleWidget.hpp>
  7. #include <context.hpp>
  8. #include <asset.hpp>
  9. #include <settings.hpp>
  10. #include <engine/Engine.hpp>
  11. #include <engine/Port.hpp>
  12. #include <app/MultiLightWidget.hpp>
  13. #include <componentlibrary.hpp>
  14. namespace rack {
  15. namespace app {
  16. struct TintWidget : widget::Widget {
  17. NVGcolor color = color::WHITE;
  18. void draw(const DrawArgs& args) override {
  19. nvgTint(args.vg, color);
  20. Widget::draw(args);
  21. }
  22. };
  23. struct PlugLight : componentlibrary::TRedGreenBlueLight<app::MultiLightWidget> {
  24. PlugLight() {
  25. box.size = math::Vec(9, 9);
  26. }
  27. };
  28. struct PlugWidget::Internal {
  29. CableWidget* cableWidget;
  30. engine::Port::Type type;
  31. /** Initially pointing upward. */
  32. float angle = 0.5f * M_PI;
  33. widget::FramebufferWidget* fb;
  34. widget::TransformWidget* plugTransform;
  35. TintWidget* plugTint;
  36. widget::SvgWidget* plug;
  37. widget::SvgWidget* plugPort;
  38. app::MultiLightWidget* plugLight;
  39. };
  40. PlugWidget::PlugWidget() {
  41. internal = new Internal;
  42. internal->fb = new widget::FramebufferWidget;
  43. addChild(internal->fb);
  44. internal->plugTransform = new widget::TransformWidget;
  45. internal->fb->addChild(internal->plugTransform);
  46. internal->plugTint = new TintWidget;
  47. internal->plugTransform->addChild(internal->plugTint);
  48. internal->plug = new widget::SvgWidget;
  49. internal->plug->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/Plug.svg")));
  50. internal->plugTint->addChild(internal->plug);
  51. internal->plugTransform->setSize(internal->plug->getSize());
  52. internal->plugTransform->setPosition(internal->plug->getSize().mult(-0.5));
  53. internal->plugTint->setSize(internal->plug->getSize());
  54. internal->plugPort = new widget::SvgWidget;
  55. internal->plugPort->setSvg(window::Svg::load(asset::system("res/ComponentLibrary/PlugPort.svg")));
  56. internal->plugPort->setPosition(internal->plugPort->getSize().mult(-0.5));
  57. internal->fb->addChild(internal->plugPort);
  58. internal->plugLight = new PlugLight;
  59. internal->plugLight->setPosition(internal->plugLight->getSize().mult(-0.5));
  60. addChild(internal->plugLight);
  61. setSize(internal->plug->getSize());
  62. }
  63. PlugWidget::~PlugWidget() {
  64. delete internal;
  65. }
  66. void PlugWidget::step() {
  67. std::vector<float> values(3);
  68. PortWidget* pw = internal->cableWidget->getPort(internal->type);
  69. if (pw && internal->plugLight->isVisible()) {
  70. engine::Port* port = pw->getPort();
  71. if (port) {
  72. for (int i = 0; i < 3; i++) {
  73. values[i] = port->plugLights[i].getBrightness();
  74. }
  75. }
  76. }
  77. internal->plugLight->setBrightnesses(values);
  78. Widget::step();
  79. }
  80. void PlugWidget::setColor(NVGcolor color) {
  81. if (color::isEqual(color, internal->plugTint->color))
  82. return;
  83. internal->plugTint->color = color;
  84. internal->fb->setDirty();
  85. }
  86. void PlugWidget::setAngle(float angle) {
  87. if (angle == internal->angle)
  88. return;
  89. internal->angle = angle;
  90. internal->plugTransform->identity();
  91. internal->plugTransform->rotate(angle - 0.5f * M_PI, internal->plug->getSize().div(2));
  92. internal->fb->setDirty();
  93. }
  94. void PlugWidget::setTop(bool top) {
  95. internal->plugLight->setVisible(top);
  96. }
  97. CableWidget* PlugWidget::getCable() {
  98. return internal->cableWidget;
  99. }
  100. engine::Port::Type PlugWidget::getType() {
  101. return internal->type;
  102. }
  103. struct CableWidget::Internal {
  104. /** For making history consistent when disconnecting and reconnecting cable. */
  105. int64_t cableId = -1;
  106. };
  107. CableWidget::CableWidget() {
  108. internal = new Internal;
  109. color = color::BLACK_TRANSPARENT;
  110. outputPlug = new PlugWidget;
  111. outputPlug->internal->cableWidget = this;
  112. outputPlug->internal->type = engine::Port::OUTPUT;
  113. inputPlug = new PlugWidget;
  114. inputPlug->internal->cableWidget = this;
  115. inputPlug->internal->type = engine::Port::INPUT;
  116. }
  117. CableWidget::~CableWidget() {
  118. delete outputPlug;
  119. delete inputPlug;
  120. setCable(NULL);
  121. delete internal;
  122. }
  123. bool CableWidget::isComplete() {
  124. return outputPort && inputPort;
  125. }
  126. void CableWidget::updateCable() {
  127. if (cable) {
  128. APP->engine->removeCable(cable);
  129. delete cable;
  130. cable = NULL;
  131. }
  132. if (inputPort && outputPort) {
  133. cable = new engine::Cable;
  134. cable->id = internal->cableId;
  135. cable->inputModule = inputPort->module;
  136. cable->inputId = inputPort->portId;
  137. cable->outputModule = outputPort->module;
  138. cable->outputId = outputPort->portId;
  139. APP->engine->addCable(cable);
  140. internal->cableId = cable->id;
  141. }
  142. }
  143. void CableWidget::setCable(engine::Cable* cable) {
  144. if (this->cable) {
  145. APP->engine->removeCable(this->cable);
  146. delete this->cable;
  147. this->cable = NULL;
  148. internal->cableId = -1;
  149. }
  150. if (cable) {
  151. app::ModuleWidget* outputMw = APP->scene->rack->getModule(cable->outputModule->id);
  152. if (!outputMw)
  153. throw Exception("Cable cannot find output ModuleWidget %lld", (long long) cable->outputModule->id);
  154. outputPort = outputMw->getOutput(cable->outputId);
  155. if (!outputPort)
  156. throw Exception("Cable cannot find output port %d", cable->outputId);
  157. app::ModuleWidget* inputMw = APP->scene->rack->getModule(cable->inputModule->id);
  158. if (!inputMw)
  159. throw Exception("Cable cannot find input ModuleWidget %lld", (long long) cable->inputModule->id);
  160. inputPort = inputMw->getInput(cable->inputId);
  161. if (!inputPort)
  162. throw Exception("Cable cannot find input port %d", cable->inputId);
  163. this->cable = cable;
  164. internal->cableId = cable->id;
  165. }
  166. else {
  167. outputPort = NULL;
  168. inputPort = NULL;
  169. }
  170. }
  171. engine::Cable* CableWidget::getCable() {
  172. return cable;
  173. }
  174. math::Vec CableWidget::getInputPos() {
  175. if (inputPort) {
  176. return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), APP->scene->rack);
  177. }
  178. else if (hoveredInputPort) {
  179. return hoveredInputPort->getRelativeOffset(hoveredInputPort->box.zeroPos().getCenter(), APP->scene->rack);
  180. }
  181. else {
  182. return APP->scene->rack->getMousePos();
  183. }
  184. }
  185. math::Vec CableWidget::getOutputPos() {
  186. if (outputPort) {
  187. return outputPort->getRelativeOffset(outputPort->box.zeroPos().getCenter(), APP->scene->rack);
  188. }
  189. else if (hoveredOutputPort) {
  190. return hoveredOutputPort->getRelativeOffset(hoveredOutputPort->box.zeroPos().getCenter(), APP->scene->rack);
  191. }
  192. else {
  193. return APP->scene->rack->getMousePos();
  194. }
  195. }
  196. void CableWidget::mergeJson(json_t* rootJ) {
  197. std::string s = color::toHexString(color);
  198. json_object_set_new(rootJ, "color", json_string(s.c_str()));
  199. }
  200. void CableWidget::fromJson(json_t* rootJ) {
  201. json_t* colorJ = json_object_get(rootJ, "color");
  202. if (colorJ && json_is_string(colorJ)) {
  203. color = color::fromHexString(json_string_value(colorJ));
  204. }
  205. else {
  206. // In <v0.6.0, cables used JSON objects instead of hex strings. Just ignore them if so and use the existing cable color.
  207. // In <=v1, cable colors were not serialized.
  208. color = APP->scene->rack->getNextCableColor();
  209. }
  210. }
  211. static math::Vec getSlumpPos(math::Vec pos1, math::Vec pos2) {
  212. float dist = pos1.minus(pos2).norm();
  213. math::Vec avg = pos1.plus(pos2).div(2);
  214. // Lower average point as distance increases
  215. avg.y += (1.0 - settings::cableTension) * (150.0 + 1.0 * dist);
  216. return avg;
  217. }
  218. void CableWidget::step() {
  219. math::Vec outputPos = getOutputPos();
  220. math::Vec inputPos = getInputPos();
  221. math::Vec slump = getSlumpPos(outputPos, inputPos);
  222. NVGcolor colorOpaque = color;
  223. colorOpaque.a = 1.f;
  224. // Draw output plug
  225. outputPlug->setPosition(outputPos);
  226. bool outputTop = outputPort && (APP->scene->rack->getTopPlug(outputPort) == outputPlug);
  227. outputPlug->setTop(outputTop);
  228. outputPlug->setAngle(slump.minus(outputPos).arg());
  229. outputPlug->setColor(colorOpaque);
  230. // Draw input plug
  231. inputPlug->setPosition(inputPos);
  232. bool inputTop = inputPort && (APP->scene->rack->getTopPlug(inputPort) == inputPlug);
  233. inputPlug->setTop(inputTop);
  234. inputPlug->setAngle(slump.minus(inputPos).arg());
  235. inputPlug->setColor(colorOpaque);
  236. Widget::step();
  237. }
  238. void CableWidget::draw(const DrawArgs& args) {
  239. CableWidget::drawLayer(args, 0);
  240. }
  241. void CableWidget::drawLayer(const DrawArgs& args, int layer) {
  242. // Cable shadow and cable
  243. float opacity = settings::cableOpacity;
  244. bool thick = false;
  245. if (isComplete()) {
  246. engine::Output* output = &cable->outputModule->outputs[cable->outputId];
  247. // Increase thickness if output port is polyphonic
  248. if (output->isPolyphonic()) {
  249. thick = true;
  250. }
  251. // Draw opaque if mouse is hovering over a connected port
  252. Widget* hoveredWidget = APP->event->hoveredWidget;
  253. if (outputPort == hoveredWidget || inputPort == hoveredWidget) {
  254. opacity = 1.0;
  255. }
  256. // Draw translucent cable if not active (i.e. 0 channels)
  257. else if (output->getChannels() == 0) {
  258. opacity *= 0.5;
  259. }
  260. }
  261. else {
  262. // Draw opaque if the cable is incomplete
  263. opacity = 1.0;
  264. }
  265. if (opacity <= 0.0)
  266. return;
  267. nvgAlpha(args.vg, std::pow(opacity, 1.5));
  268. math::Vec outputPos = getOutputPos();
  269. math::Vec inputPos = getInputPos();
  270. float thickness = thick ? 9.0 : 6.0;
  271. // The endpoints are off-center
  272. math::Vec slump = getSlumpPos(outputPos, inputPos);
  273. float dist = 14.f;
  274. outputPos = outputPos.plus(slump.minus(outputPos).normalize().mult(dist));
  275. inputPos = inputPos.plus(slump.minus(inputPos).normalize().mult(dist));
  276. nvgLineCap(args.vg, NVG_ROUND);
  277. // Avoids glitches when cable is bent
  278. nvgLineJoin(args.vg, NVG_ROUND);
  279. if (layer == -1) {
  280. // Draw cable shadow
  281. math::Vec shadowSlump = slump.plus(math::Vec(0, 30));
  282. nvgBeginPath(args.vg);
  283. nvgMoveTo(args.vg, VEC_ARGS(outputPos));
  284. nvgQuadTo(args.vg, VEC_ARGS(shadowSlump), VEC_ARGS(inputPos));
  285. NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.10);
  286. nvgStrokeColor(args.vg, shadowColor);
  287. nvgStrokeWidth(args.vg, thickness - 1.0);
  288. nvgStroke(args.vg);
  289. }
  290. else if (layer == 0) {
  291. // Draw cable outline
  292. nvgBeginPath(args.vg);
  293. nvgMoveTo(args.vg, VEC_ARGS(outputPos));
  294. nvgQuadTo(args.vg, VEC_ARGS(slump), VEC_ARGS(inputPos));
  295. // nvgStrokePaint(args.vg, nvgLinearGradient(args.vg, VEC_ARGS(outputPos), VEC_ARGS(inputPos), color::mult(color, 0.5), color));
  296. nvgStrokeColor(args.vg, color::mult(color, 0.8));
  297. nvgStrokeWidth(args.vg, thickness);
  298. nvgStroke(args.vg);
  299. // Draw cable
  300. nvgStrokeColor(args.vg, color::mult(color, 0.95));
  301. nvgStrokeWidth(args.vg, thickness - 1.0);
  302. nvgStroke(args.vg);
  303. }
  304. Widget::drawLayer(args, layer);
  305. }
  306. engine::Cable* CableWidget::releaseCable() {
  307. engine::Cable* cable = this->cable;
  308. this->cable = NULL;
  309. internal->cableId = -1;
  310. return cable;
  311. }
  312. void CableWidget::onAdd(const AddEvent& e) {
  313. Widget* plugContainer = APP->scene->rack->getPlugContainer();
  314. plugContainer->addChild(outputPlug);
  315. plugContainer->addChild(inputPlug);
  316. Widget::onAdd(e);
  317. }
  318. void CableWidget::onRemove(const RemoveEvent& e) {
  319. Widget* plugContainer = APP->scene->rack->getPlugContainer();
  320. plugContainer->removeChild(outputPlug);
  321. plugContainer->removeChild(inputPlug);
  322. Widget::onRemove(e);
  323. }
  324. } // namespace app
  325. } // namespace rack