p5.imgui.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /**
  2. * Prototype functions to make library
  3. * method calls more like p5.js.
  4. */
  5. // Create GUI context
  6. let imgui;
  7. p5.prototype.createGui = function() {
  8. imgui = new ImGui();
  9. }
  10. // Start GUI
  11. p5.prototype.startGui = function() {
  12. imgui.start();
  13. }
  14. // End GUI
  15. p5.prototype.endGui = function() {
  16. imgui.end();
  17. }
  18. // Prototype functions for GUI elements
  19. p5.prototype.button = function(label, x, y, w=128, h=32) {
  20. return imgui.button(label, x, y, w, h);
  21. }
  22. p5.prototype.checkbox = function(label, value, x, y, w=32, h=32) {
  23. return imgui.checkbox(label, value, x, y, w, h);
  24. }
  25. p5.prototype.slider = function(label, value, x, y, w=256, h=32, min=0, max=1) {
  26. return imgui.slider(label, value, x, y, w, h, min, max);
  27. }
  28. p5.prototype.sliderV = function(label, value, x, y, w=32, h=256, min=0, max=1) {
  29. return imgui.sliderV(label, value, x, y, w, h, min, max);
  30. }
  31. /**
  32. * Generates hash code from a string.
  33. * @see http://stackoverflow.com/q/7616461/940217
  34. * @return {number}
  35. */
  36. String.prototype.hashCode = function(){
  37. if (Array.prototype.reduce){
  38. return this.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
  39. }
  40. var hash = 0;
  41. if (this.length === 0) return hash;
  42. for (var i = 0; i < this.length; i++) {
  43. var character = this.charCodeAt(i);
  44. hash = ((hash<<5)-hash)+character;
  45. hash = hash & hash; // Convert to 32bit integer
  46. }
  47. return hash;
  48. }
  49. /*******************
  50. * IMGUI
  51. *
  52. *
  53. */
  54. class ImGui {
  55. constructor() {
  56. this.hotItem = 0;
  57. this.activeItem = 0;
  58. this.style = new ImGuiStyle();
  59. // this.style.StyleColorsClassic();
  60. }
  61. // Check whether current mouse position
  62. // is within a rectangle
  63. regionHit(x, y, w, h) {
  64. if (mouseX < x ||
  65. mouseY < y ||
  66. mouseX >= x + w ||
  67. mouseY >= y + h) {
  68. return false;
  69. }
  70. return true;
  71. }
  72. setHit(id) {
  73. this.hotItem = id;
  74. if (this.activeItem == 0 && mouseIsPressed) {
  75. this.activeItem = id;
  76. }
  77. }
  78. /// Simple checkbox IMGUI widget
  79. checkbox(label, value, x, y, w=32, h=32) {
  80. // Create hashed id from label
  81. let id = label.hashCode();
  82. // let id = new Error().stack;
  83. // Check whether the button should be 'hot' or 'active'
  84. if (this.regionHit(x, y, w, h)) {
  85. this.hotItem = id;
  86. if (this.activeItem == 0 && mouseIsPressed) {
  87. this.activeItem = id;
  88. value.val = !value.val;
  89. }
  90. }
  91. // Render button
  92. let x8 = x+w/6;
  93. let y8 = y+h/6;
  94. let w16 = w-w/3;
  95. let h16 = h-h/3;
  96. let xw = x8+w16;
  97. let yh = y8+h16;
  98. let strokeMult = map(((w > h) ? w : h), 32, 1000, 2, 20);
  99. push();
  100. rectMode(CORNER);
  101. stroke(this.style.col_checkOuterStroke);
  102. strokeWeight(this.style.checkStrokeWt);
  103. fill(this.style.col_checkOuterFill);
  104. rect(x, y, w, h, this.style.rounding);
  105. if (this.hotItem == id && !value.val) {
  106. // Button is 'hot' and 'false'
  107. fill(this.style.col_checkOuterFillHover);
  108. rect(x, y, w, h, this.style.rounding);
  109. }
  110. else if (this.hotItem == id && value.val) {
  111. // Button is 'hot' and 'true'
  112. push();
  113. stroke(this.style.col_checkInnerStrokeHover);
  114. strokeWeight(this.style.checkStrokeWt*strokeMult);
  115. line(x8, y8, xw, yh);
  116. line(xw, y8, x8, yh);
  117. pop();
  118. if (this.activeItem == id) {
  119. // Button is also 'active'
  120. fill(this.style.col_checkOuterFillActive);
  121. rect(x, y, w, h, this.style.rounding);
  122. push();
  123. stroke(this.style.col_checkInnerStrokeActive);
  124. strokeWeight(this.style.checkStrokeWt*strokeMult);
  125. line(x8, y8, xw, yh);
  126. line(xw, y8, x8, yh);
  127. pop();
  128. }
  129. }
  130. else if (value.val) {
  131. // Button is 'true' but not 'hot'
  132. push();
  133. stroke(this.style.col_checkInnerStroke);
  134. strokeWeight(this.style.checkStrokeWt*strokeMult);
  135. line(x8, y8, xw, yh);
  136. line(xw, y8, x8, yh);
  137. pop();
  138. }
  139. pop();
  140. // If button is 'hot' and 'active', but mouse button
  141. // is not down, the user must have clicked the button.
  142. if (mouseIsPressed &&
  143. this.hotItem == id &&
  144. this.activeItem == id) {
  145. return true;
  146. }
  147. // Otherwise, no clicky.
  148. return false;
  149. }
  150. /// Simple button IMGUI widget
  151. button(label, x, y, w=128, h=32) {
  152. // Create hashed id from stack
  153. let id = label.hashCode();
  154. // Check for 'hot' or 'active'
  155. if (this.regionHit(x, y, w, h)) {
  156. this.setHit(id);
  157. }
  158. // Render button
  159. push();
  160. stroke(this.style.col_buttonStroke);
  161. strokeWeight(this.style.buttonStrokeWt);
  162. rectMode(CORNER);
  163. if (this.hotItem == id) {
  164. if (this.activeItem == id) {
  165. // Button is both 'hot' and 'active'
  166. fill(this.style.col_buttonFillActive);
  167. rect(x, y, w, h, this.style.rounding);
  168. }
  169. else {
  170. // Button is merely 'hot'
  171. fill(this.style.col_buttonFillHover);
  172. rect(x, y, w, h, this.style.rounding);
  173. }
  174. }
  175. else {
  176. // Button is not hot, but it may be active
  177. fill(this.style.col_buttonFill);
  178. rect(x, y, w, h, this.style.rounding);
  179. }
  180. // Label rendering.
  181. push();
  182. fill(this.style.col_buttonLabel);
  183. noStroke();
  184. textAlign(CENTER, CENTER);
  185. textFont(this.style.labelFont);
  186. let size = w/10;
  187. if (size > this.style.labelFontMaxSize) {
  188. size = this.style.labelFontMaxSize;
  189. }
  190. textSize(size);
  191. text(label, x + w/2, y + h/2);
  192. pop();
  193. pop();
  194. // If button is 'hot' and 'active', but mouse button
  195. // is not down, the user must have clicked the button.
  196. if (mouseIsPressed &&
  197. this.hotItem == id &&
  198. this.activeItem == id) {
  199. return true;
  200. }
  201. // Otherwise, no clicky.
  202. return false;
  203. }
  204. /// Simple slider IMGUI widget
  205. slider(label, value, x, y, w=256, h=32, min=0, max=1) {
  206. // Create hashed id from label
  207. let id = label.hashCode();
  208. // Calculate mouse cursor's relative x offset
  209. let xpos = map(value.val, min, max, 0, w-32);
  210. // Check for 'hot' or 'active'
  211. if (this.regionHit(x, y, w, h)) {
  212. this.setHit(id);
  213. }
  214. // Render the slider
  215. if (this.activeItem == id) {
  216. this.drawSlider(xpos, x, y, w, h,
  217. this.style.col_sliderBgFillActive,
  218. this.style.col_sliderIndctrFillActive,
  219. this.style.col_sliderGrabFillActive,
  220. this.style.col_sliderGrabStroke);
  221. }
  222. else if (this.hotItem == id) {
  223. this.drawSlider(xpos, x, y, w, h,
  224. this.style.col_sliderBgFillHover,
  225. this.style.col_sliderIndctrFillHover,
  226. this.style.col_sliderGrabFillHover,
  227. this.style.col_sliderGrabStroke);
  228. }
  229. else {
  230. this.drawSlider(xpos, x, y, w, h,
  231. this.style.col_sliderBgFill,
  232. this.style.col_sliderIndctrFill,
  233. this.style.col_sliderGrabFill,
  234. this.style.col_sliderGrabStroke);
  235. }
  236. // Update widget value
  237. if (this.activeItem == id) {
  238. let mousePos = mouseX - x;
  239. let v = map(mousePos, 0, w, min, max, true);
  240. if (v != value.val) {
  241. value.val = v;
  242. return true;
  243. }
  244. }
  245. return false;
  246. }
  247. /// Simple vertical slider IMGUI widget
  248. sliderV(label, value, x, y, w=32, h=256, min=0, max=1) {
  249. // Create hashed id from label
  250. let id = label.hashCode();
  251. // Calculate mouse cursor's relative y offset
  252. let ypos = map(value.val, min, max, 0, h-32);
  253. // Check for 'hot' or 'active'
  254. if (this.regionHit(x, y, w, h)) {
  255. this.setHit(id);
  256. }
  257. // Render the slider
  258. if (this.activeItem == id) {
  259. this.drawSliderV(ypos, x, y, w, h,
  260. this.style.col_sliderBgFillActive,
  261. this.style.col_sliderIndctrFillActive,
  262. this.style.col_sliderGrabFillActive,
  263. this.style.col_sliderGrabStroke);
  264. }
  265. else if (this.hotItem == id) {
  266. this.drawSliderV(ypos, x, y, w, h,
  267. this.style.col_sliderBgFillHover,
  268. this.style.col_sliderIndctrFillHover,
  269. this.style.col_sliderGrabFillHover,
  270. this.style.col_sliderGrabStroke);
  271. }
  272. else {
  273. this.drawSliderV(ypos, x, y, w, h,
  274. this.style.col_sliderBgFill,
  275. this.style.col_sliderIndctrFill,
  276. this.style.col_sliderGrabFill,
  277. this.style.col_sliderGrabStroke);
  278. }
  279. // Update widget value
  280. if (this.activeItem == id) {
  281. let mousePos = mouseY - y;
  282. let v = map(mousePos, 0, h, min, max, true);
  283. if (v != value.val) {
  284. value.val = v;
  285. return true;
  286. }
  287. }
  288. return false;
  289. }
  290. /// Draw function for slider
  291. drawSlider(xpos, x, y, w, h, bgFill, indctrFill, grabFill, grabStroke) {
  292. push();
  293. stroke(this.style.col_sliderStroke);
  294. strokeWeight(this.style.sliderStrokeWt);
  295. rectMode(CORNER);
  296. // Render bg
  297. fill(bgFill);
  298. rect(x, y, w, h, this.style.rounding);
  299. // Render indicator
  300. push();
  301. noStroke();
  302. fill(indctrFill);
  303. // rect(x+0.2*h, y+0.2*h, xpos+0.*h, h-0.4*h,
  304. // this.style.rounding, 0, 0, this.style.rounding);
  305. rect(x+10, y+10, xpos+12, h-20,
  306. this.style.rounding, 0, 0, this.style.rounding);
  307. pop();
  308. // Render grab
  309. push();
  310. stroke(grabStroke);
  311. fill(grabFill);
  312. rect(x+8+xpos, y+8, 16, h-16, this.style.rounding);
  313. pop();
  314. pop();
  315. }
  316. /// Draw function for vertical slider
  317. drawSliderV(ypos, x, y, w, h, bgFill, indctrFill, grabFill, grabStroke) {
  318. push();
  319. stroke(this.style.col_sliderStroke);
  320. strokeWeight(this.style.sliderStrokeWt);
  321. rectMode(CORNER);
  322. // Render bg
  323. fill(bgFill);
  324. rect(x, y, w, h, this.style.rounding);
  325. // Render indicator
  326. push();
  327. noStroke();
  328. fill(indctrFill);
  329. // rect(x+0.2*w, y+ypos+0.1*w, w-0.4*w, h-ypos-0.3*w,
  330. // 0, 0, this.style.rounding, this.style.rounding);
  331. rect(x+10, y+ypos+12, w-20, h-ypos-20,
  332. 0, 0, this.style.rounding, this.style.rounding);
  333. pop();
  334. // Render grab
  335. push();
  336. stroke(grabStroke);
  337. fill(grabFill);
  338. rect(x+8, y+8+ypos, w-16, 16, this.style.rounding);
  339. pop();
  340. pop();
  341. }
  342. /// Prepare for start IMGUI code
  343. start() {
  344. this.hotItem = 0;
  345. }
  346. /// Finish at end IMGUI code
  347. end() {
  348. if (!mouseIsPressed) {
  349. this.activeItem = 0;
  350. }
  351. else {
  352. if (this.activeItem == 0) {
  353. this.activeItem = -1;
  354. }
  355. }
  356. }
  357. }
  358. /*******************
  359. * IMGUI STYLE
  360. *
  361. *
  362. */
  363. class ImGuiStyle {
  364. constructor() {
  365. // Global pars
  366. this.rounding = 10;
  367. this.labelFont = 'Arial';
  368. this.labelFontMaxSize = 30;
  369. this.strokeWt = 2;
  370. // Button pars
  371. this.buttonStrokeWt = this.strokeWt;
  372. this.col_buttonStroke = color(0);
  373. this.col_buttonFill = color(160);
  374. this.col_buttonFillHover = color(196);
  375. this.col_buttonFillActive = color(220);
  376. this.col_buttonLabel = color(0);
  377. // Checkbox pars
  378. this.checkStrokeWt = this.strokeWt;
  379. this.col_checkOuterStroke = color(0);
  380. this.col_checkOuterStrokeHover = color(0);
  381. this.col_checkOuterStrokeActive = color(0);
  382. this.col_checkOuterFill = color(128);
  383. this.col_checkOuterFillHover = color(144);
  384. this.col_checkOuterFillActive = color(160);
  385. this.col_checkInnerFill = color(200);
  386. this.col_checkInnerStroke = color(200);
  387. this.col_checkInnerFillHover = color(220);
  388. this.col_checkInnerStrokeHover = color(220);
  389. this.col_checkInnerFillActive = color(240);
  390. this.col_checkInnerStrokeActive = color(240);
  391. // Slider pars
  392. this.sliderStrokeWt = this.strokeWt;
  393. this.col_sliderStroke = color(0);
  394. this.col_sliderBgFill = color(160);
  395. this.col_sliderBgFillHover = color(175);
  396. this.col_sliderBgFillActive = color(175);
  397. this.col_sliderBgStroke = color(0);
  398. this.col_sliderBgStrokeHover = color(0);
  399. this.col_sliderBgStrokeActive = color(0);
  400. this.col_sliderIndctrFill = color(128);
  401. this.col_sliderIndctrFillHover = color(144);
  402. this.col_sliderIndctrFillActive = color(144);
  403. this.col_sliderIndctrStroke = color(128);
  404. this.col_sliderIndctrStrokeHover = color(144);
  405. this.col_sliderIndctrStrokeActive = color(144);
  406. this.col_sliderGrabFill = color(64);
  407. this.col_sliderGrabFillHover = color(96);
  408. this.col_sliderGrabFillActive = color(240);
  409. this.col_sliderGrabStroke = color(64);
  410. this.col_sliderGrabStrokeHover = color(0);
  411. this.col_sliderGrabStrokeActive = color(0);
  412. }
  413. StyleColorsGrayscale() {
  414. this.rounding = 10;
  415. this.labelFont = 'Arial';
  416. this.labelFontMaxSize = 30;
  417. this.strokeWt = 2;
  418. // Button pars
  419. this.buttonStrokeWt = this.strokeWt;
  420. this.col_buttonStroke = color(0);
  421. this.col_buttonFill = color(160);
  422. this.col_buttonFillHover = color(196);
  423. this.col_buttonFillActive = color(220);
  424. this.col_buttonLabel = color(0);
  425. // Checkbox pars
  426. this.checkStrokeWt = this.strokeWt;
  427. this.col_checkOuterStroke = color(0);
  428. this.col_checkOuterStrokeHover = color(0);
  429. this.col_checkOuterStrokeActive = color(0);
  430. this.col_checkOuterFill = color(128);
  431. this.col_checkOuterFillHover = color(144);
  432. this.col_checkOuterFillActive = color(160);
  433. this.col_checkInnerFill = color(200);
  434. this.col_checkInnerStroke = color(200);
  435. this.col_checkInnerFillHover = color(220);
  436. this.col_checkInnerStrokeHover = color(220);
  437. this.col_checkInnerFillActive = color(240);
  438. this.col_checkInnerStrokeActive = color(240);
  439. // Slider pars
  440. this.sliderStrokeWt = this.strokeWt;
  441. this.col_sliderStroke = color(0);
  442. this.col_sliderBgFill = color(160);
  443. this.col_sliderBgFillHover = color(175);
  444. this.col_sliderBgFillActive = color(175);
  445. this.col_sliderBgStroke = color(0);
  446. this.col_sliderBgStrokeHover = color(0);
  447. this.col_sliderBgStrokeActive = color(0);
  448. this.col_sliderIndctrFill = color(128);
  449. this.col_sliderIndctrFillHover = color(144);
  450. this.col_sliderIndctrFillActive = color(144);
  451. this.col_sliderIndctrStroke = color(128);
  452. this.col_sliderIndctrStrokeHover = color(144);
  453. this.col_sliderIndctrStrokeActive = color(144);
  454. this.col_sliderGrabFill = color(64);
  455. this.col_sliderGrabFillHover = color(96);
  456. this.col_sliderGrabFillActive = color(240);
  457. this.col_sliderGrabStroke = color(64);
  458. this.col_sliderGrabStrokeHover = color(96);
  459. this.col_sliderGrabStrokeActive = color(0);
  460. }
  461. StyleColorsClassic() {
  462. this.rounding = 10;
  463. this.labelFont = 'Arial';
  464. this.labelFontMaxSize = 30;
  465. this.strokeWt = 5;
  466. // Button pars
  467. this.buttonStrokeWt = this.strokeWt;
  468. this.col_buttonStroke = color('#D5CAD6');
  469. this.col_buttonFill = color('#EFEFF0');
  470. this.col_buttonFillHover = color('#F7F7F7');
  471. this.col_buttonFillActive = color('#C9F0FF');
  472. this.col_buttonLabel = color('#6B5E62');
  473. // Checkbox pars
  474. this.checkStrokeWt = this.strokeWt;
  475. this.col_checkOuterStroke = color('#D5CAD6');
  476. this.col_checkOuterStrokeHover = color('#D5CAD6');
  477. this.col_checkOuterStrokeActive = color('#D5CAD6');
  478. this.col_checkOuterFill = color('#EFEFF0');
  479. this.col_checkOuterFillHover = color('#F7F7F7');
  480. this.col_checkOuterFillActive = color('#C9F0FF');
  481. this.col_checkInnerFill = color('#6B5E62');
  482. this.col_checkInnerStroke = color('#6B5E62');
  483. this.col_checkInnerFillHover = color('#7A6B70');
  484. this.col_checkInnerStrokeHover = color('#7A6B70');
  485. this.col_checkInnerFillActive = color('#7A6B70');
  486. this.col_checkInnerStrokeActive = color('#7A6B70');
  487. // Slider pars
  488. this.sliderStrokeWt = this.strokeWt;
  489. this.col_sliderStroke = color('#D5CAD6');
  490. this.col_sliderBgFill = color('#EFEFF0');
  491. this.col_sliderBgFillHover = color('#F7F7F7');
  492. this.col_sliderBgFillActive = color('#F7F7F7');
  493. this.col_sliderBgStroke = color(0);
  494. this.col_sliderBgStrokeHover = color(0);
  495. this.col_sliderBgStrokeActive = color(0);
  496. this.col_sliderIndctrFill = color('#D5CAD6');
  497. this.col_sliderIndctrFillHover = color('#D5CAD6');
  498. this.col_sliderIndctrFillActive = color('#D5CAD6');
  499. this.col_sliderIndctrStroke = color(128);
  500. this.col_sliderIndctrStrokeHover = color(144);
  501. this.col_sliderIndctrStrokeActive = color(144);
  502. this.col_sliderGrabFill = color('#6B5E62');
  503. this.col_sliderGrabFillHover = color('#7A6B70');
  504. this.col_sliderGrabFillActive = color('#C9F0FF');
  505. this.col_sliderGrabStroke = color('#6B5E62');
  506. this.col_sliderGrabStrokeHover = color('#7A6B70');
  507. this.col_sliderGrabStrokeActive = color('#7A6B70');
  508. }
  509. }