|
42 | 42 | return lerp(a, b, alpha) |
43 | 43 | } |
44 | 44 |
|
| 45 | +let rvec = (x, y, dx, dy, a) => { |
| 46 | + let ca = cos(a), sa = sin(a) |
| 47 | + return [ |
| 48 | + x + ca * dx - sa * dy, |
| 49 | + y + sa * dx + ca * dy, |
| 50 | + ] |
| 51 | +} |
| 52 | + |
| 53 | +let point_onto_line = (x, y, x1, y1, x2, y2) => { |
| 54 | + let d = dist(x1, y1, x2, y2) || 1 |
| 55 | + let dx = (x2 - x1) / d, dy = (y2 - y1) / d |
| 56 | + let p_dx = x - x1, p_dy = y - y1 |
| 57 | + let f = clamp(dx * p_dx + dy * p_dy, 0, d) |
| 58 | + return [x1 + dx * f, y1 + dy * f] |
| 59 | +} |
| 60 | + |
45 | 61 | let ease = { |
46 | 62 | outQuart: t => 1 - pow(1 - t, 4), |
47 | 63 | outBounce: t => { |
|
56 | 72 | } |
57 | 73 |
|
58 | 74 | let cursor = v => document.body.style.cursor = v |
| 75 | +let hand = () => cursor("pointer") |
59 | 76 |
|
60 | 77 | let mouse = { |
61 | 78 | x: 0, y: 0, |
|
244 | 261 | let canvas = document.querySelector("canvas") |
245 | 262 | let ctx = canvas.getContext("2d") |
246 | 263 |
|
247 | | -let line = (x1, y1, x2, y2, strokeStyle) => { |
248 | | - ctx.strokeStyle = strokeStyle || ctx.strokeStyle |
| 264 | +let line = (x1, y1, x2, y2, color) => { |
| 265 | + ctx.strokeStyle = color || ctx.strokeStyle |
249 | 266 | ctx.beginPath() |
250 | 267 | ctx.moveTo(x1, y1) |
251 | 268 | ctx.lineTo(x2, y2) |
252 | 269 | ctx.stroke() |
253 | 270 | } |
254 | 271 |
|
255 | | -let circle = (x, y, r, color = "SteelBlue", mode = "fill") => { |
256 | | - ctx.lineWidth = 4 |
257 | | - if (mode == "fill") ctx.fillStyle = color |
258 | | - else ctx.strokeStyle = color |
| 272 | +let circle = (x, y, r, mode = "fill", color) => { |
| 273 | + if (mode == "fill") { |
| 274 | + ctx.fillStyle = color || ctx.fillStyle |
| 275 | + } else { |
| 276 | + ctx.strokeStyle = color || ctx.strokeStyle |
| 277 | + ctx.lineWidth = 4 |
| 278 | + } |
259 | 279 | ctx.beginPath() |
260 | 280 | ctx.ellipse( |
261 | 281 | x, y, r, r, |
|
265 | 285 | else ctx.stroke() |
266 | 286 | } |
267 | 287 |
|
| 288 | +let polygon = (...pts) => { |
| 289 | + ctx.beginPath() |
| 290 | + ctx.moveTo(pts[0], pts[1]) |
| 291 | + for (let i = 2; i < pts.length; i += 2) |
| 292 | + ctx.lineTo(pts[i], pts[i + 1]) |
| 293 | + ctx.closePath() |
| 294 | + ctx.fill() |
| 295 | +} |
| 296 | + |
268 | 297 | let _mats = [] |
269 | 298 | let push = () => _mats.push(ctx.getTransform()) |
270 | 299 | let pop = () => ctx.setTransform(_mats.pop()) |
|
385 | 414 | if (my == undefined) my = wmy |
386 | 415 | let a = this |
387 | 416 | let r = a.r || 25 |
388 | | - if (a.fill) circle(a.x, a.y, r, a.fill) |
389 | | - else circle(a.x, a.y, r, "#122", "line") |
| 417 | + if (a.fill) circle(a.x, a.y, r, "fill", a.fill) |
| 418 | + else circle(a.x, a.y, r, "line", "#122") |
390 | 419 | if (bonding.includes(a)) { |
391 | | - circle(a.x, a.y, r * 1.3, "#122", "line") |
| 420 | + circle(a.x, a.y, r * 1.3, "line", "#122") |
392 | 421 | } |
393 | 422 | if (no_hover) return |
394 | 423 | if (dist(a.x, a.y, mx, my) < r && !touch_left()) { |
395 | | - cursor("pointer") |
| 424 | + hand() |
396 | 425 | ctx.globalAlpha = 0.5 |
397 | | - circle(a.x, a.y, r, "#444") |
| 426 | + circle(a.x, a.y, r, "fill", "#444") |
398 | 427 | ctx.globalAlpha = 1 |
399 | 428 | } |
400 | 429 | } |
|
465 | 494 | vv.atoms = [] |
466 | 495 | for (let i = 0; i < 6; i++) |
467 | 496 | vv.atoms.push(new Atom()) |
| 497 | + |
| 498 | + vv.bonds = [0, 1, 1, 0, 1, 1] |
468 | 499 | } |
469 | 500 |
|
470 | 501 | update(dt) { |
|
496 | 527 | ctx.rotate(vv.a) |
497 | 528 | ctx.scale(vv.s, vv.s) |
498 | 529 |
|
| 530 | + let slide_t = (vv.open |
| 531 | + ? ease.outBounce(vv.open_t) |
| 532 | + : 1 - ease.outBounce(1 - vv.open_t) |
| 533 | + ) |
| 534 | + let roll_t = 1 - slide_t |
499 | 535 | let btn_slide_t = (vv.open |
500 | 536 | ? ease.outQuart(vv.open_t) |
501 | 537 | : 1 - ease.outQuart(1 - vv.open_t) |
502 | 538 | ) |
503 | 539 | let btn_roll_t = 1 - btn_slide_t |
504 | 540 |
|
| 541 | + let m, mx, my |
| 542 | + if (vv.open && vv.open_t == 1) { |
| 543 | + let h = tan(pi / 3) * 200 |
| 544 | + let mid = tan(pi / 6) * 200 |
| 545 | + |
| 546 | + push() |
| 547 | + ctx.translate(300 * slide_t, 0) |
| 548 | + ctx.rotate(-pi / 2 * roll_t) |
| 549 | + ctx.translate(0, -mid) |
| 550 | + m = ctx.getTransform().inverse() |
| 551 | + ;({ x: mx, y: my } = m.transformPoint(mouse)) |
| 552 | + let in_ca = dist(mx, my, ...point_onto_line( |
| 553 | + mx, my, 200, 0, -200, 0 |
| 554 | + )) < 50 |
| 555 | + let in_ab = dist(mx, my, ...point_onto_line( |
| 556 | + mx, my, -200, 0, 0, h |
| 557 | + )) < 50 |
| 558 | + let in_bc = dist(mx, my, ...point_onto_line( |
| 559 | + mx, my, 0, h, 200, 0 |
| 560 | + )) < 50 |
| 561 | + if (in_ca && !in_ab && !in_bc) |
| 562 | + { vv.bonds[3] = 1 - vv.bonds[3] } |
| 563 | + if (in_ab && !in_ca && !in_bc) |
| 564 | + { vv.bonds[4] = 1 - vv.bonds[4] } |
| 565 | + if (in_bc && !in_ab && !in_ca) |
| 566 | + { vv.bonds[5] = 1 - vv.bonds[5] } |
| 567 | + pop() |
| 568 | + |
| 569 | + push() |
| 570 | + ctx.translate(-300 * slide_t, 0) |
| 571 | + ctx.rotate(pi / 2 * roll_t) |
| 572 | + ctx.translate(0, -mid) |
| 573 | + m = ctx.getTransform().inverse() |
| 574 | + ;({ x: mx, y: my } = m.transformPoint(mouse)) |
| 575 | + in_ca = dist(mx, my, ...point_onto_line( |
| 576 | + mx, my, 200, 0, -200, 0 |
| 577 | + )) < 50 |
| 578 | + in_ab = dist(mx, my, ...point_onto_line( |
| 579 | + mx, my, -200, 0, 0, h |
| 580 | + )) < 50 |
| 581 | + in_bc = dist(mx, my, ...point_onto_line( |
| 582 | + mx, my, 0, h, 200, 0 |
| 583 | + )) < 50 |
| 584 | + if (in_ca && !in_ab && !in_bc) |
| 585 | + { vv.bonds[0] = 1 - vv.bonds[0] } |
| 586 | + if (in_ab && !in_ca && !in_bc) |
| 587 | + { vv.bonds[1] = 1 - vv.bonds[1] } |
| 588 | + if (in_bc && !in_ab && !in_ca) |
| 589 | + { vv.bonds[2] = 1 - vv.bonds[2] } |
| 590 | + pop() |
| 591 | + } |
| 592 | + |
505 | 593 | ctx.translate(0, 120 * btn_slide_t) |
506 | 594 | ctx.rotate(-pi * btn_roll_t) |
507 | 595 | let s = lerp(1, 1.6, btn_slide_t) |
508 | 596 | ctx.scale(s, s) |
509 | 597 |
|
510 | | - let m = ctx.getTransform().inverse() |
511 | | - let { x: mx, y: my } = m.transformPoint(mouse) |
| 598 | + m = ctx.getTransform().inverse() |
| 599 | + ;({ x: mx, y: my } = m.transformPoint(mouse)) |
512 | 600 |
|
513 | 601 | for (let b of Object.values(vv.btns)) |
514 | 602 | if (dist(b.x, b.y, mx, my) < 25) b.fn() |
|
543 | 631 | ctx.translate(300 * slide_t, 0) |
544 | 632 | ctx.rotate(-pi / 2 * roll_t) |
545 | 633 | ctx.translate(0, -mid) |
| 634 | + let m = ctx.getTransform().inverse() |
| 635 | + let { x: mx, y: my } = m.transformPoint(mouse) |
| 636 | + let in_ca = dist(mx, my, ...point_onto_line( |
| 637 | + mx, my, 200, 0, -200, 0 |
| 638 | + )) < 50 |
| 639 | + let in_ab = dist(mx, my, ...point_onto_line( |
| 640 | + mx, my, -200, 0, 0, h |
| 641 | + )) < 50 |
| 642 | + let in_bc = dist(mx, my, ...point_onto_line( |
| 643 | + mx, my, 0, h, 200, 0 |
| 644 | + )) < 50 |
546 | 645 | ctx.lineWidth = 100 |
547 | | - line(-200, 0, 200, 0, "#4d3b34") |
548 | | - line(-200, 0, 0, h, "#47352f") |
549 | | - line(0, h, 200, 0) |
| 646 | + let bcol = ["#4d3b34", "#47352f"] |
| 647 | + line(200, 0, -200, 0, bcol[vv.bonds[3]]) |
| 648 | + line(-200, 0, 0, h, bcol[vv.bonds[4]]) |
| 649 | + line(0, h, 200, 0, bcol[vv.bonds[5]]) |
| 650 | + ctx.fillStyle = bcol[1] |
| 651 | + circle(-200, 0, 50) |
| 652 | + circle(0, h, 50) |
| 653 | + circle(200, 0, 50) |
| 654 | + let pt = rvec(-200, 0, 50, 0, -pi / 6) |
| 655 | + let d1 = dist(-200, 50, ...pt) |
| 656 | + let d2 = dist(-200, 0, -200 + d1, 50) |
| 657 | + polygon(-200, 50, ...pt, -200 + d1, 50) |
| 658 | + polygon( |
| 659 | + ...rvec(0, h, 50, 0, -pi / 6), |
| 660 | + ...rvec(0, h, 50, 0, -5 * pi / 6), |
| 661 | + 0, h - d2, |
| 662 | + ) |
| 663 | + polygon(200, 50, -pt[0], pt[1], 200 - d1, 50) |
| 664 | + if (vv.open && vv.open_t == 1 && !touch_left()) { |
| 665 | + ctx.globalAlpha = 0.2 |
| 666 | + ctx.strokeStyle = "#222" |
| 667 | + if (in_ca && !in_ab && !in_bc) |
| 668 | + { hand(), line(200, 0, -200, 0) } |
| 669 | + if (in_ab && !in_ca && !in_bc) |
| 670 | + { hand(), line(-200, 0, 0, h) } |
| 671 | + if (in_bc && !in_ab && !in_ca) |
| 672 | + { hand(), line(0, h, 200, 0) } |
| 673 | + ctx.globalAlpha = 1 |
| 674 | + } |
550 | 675 |
|
551 | 676 | if (vv.atoms[3].fill) |
552 | 677 | ad.call({ ...vv.atoms[3], x: -200, y: 0, r: 50 }, true) |
|
560 | 685 | ctx.translate(-300 * slide_t, 0) |
561 | 686 | ctx.rotate(pi / 2 * roll_t) |
562 | 687 | ctx.translate(0, -mid) |
| 688 | + m = ctx.getTransform().inverse() |
| 689 | + ;({ x: mx, y: my } = m.transformPoint(mouse)) |
| 690 | + in_ca = dist(mx, my, ...point_onto_line( |
| 691 | + mx, my, 200, 0, -200, 0 |
| 692 | + )) < 50 |
| 693 | + in_ab = dist(mx, my, ...point_onto_line( |
| 694 | + mx, my, -200, 0, 0, h |
| 695 | + )) < 50 |
| 696 | + in_bc = dist(mx, my, ...point_onto_line( |
| 697 | + mx, my, 0, h, 200, 0 |
| 698 | + )) < 50 |
563 | 699 | ctx.lineWidth = 100 |
564 | | - line(-200, 0, 200, 0, "#4d3b34") |
565 | | - line(-200, 0, 0, h, "#47352f") |
566 | | - line(0, h, 200, 0) |
| 700 | + line(200, 0, -200, 0, bcol[vv.bonds[0]]) |
| 701 | + line(-200, 0, 0, h, bcol[vv.bonds[1]]) |
| 702 | + line(0, h, 200, 0, bcol[vv.bonds[2]]) |
| 703 | + ctx.fillStyle = bcol[1] |
| 704 | + circle(-200, 0, 50) |
| 705 | + circle(0, h, 50) |
| 706 | + circle(200, 0, 50) |
| 707 | + polygon(-200, 50, ...pt, -200 + d1, 50) |
| 708 | + polygon( |
| 709 | + ...rvec(0, h, 50, 0, -pi / 6), |
| 710 | + ...rvec(0, h, 50, 0, -5 * pi / 6), |
| 711 | + 0, h - d2, |
| 712 | + ) |
| 713 | + polygon(200, 50, -pt[0], pt[1], 200 - d1, 50) |
| 714 | + if (vv.open && vv.open_t == 1 && !touch_left()) { |
| 715 | + ctx.globalAlpha = 0.2 |
| 716 | + ctx.strokeStyle = "#222" |
| 717 | + if (in_ca && !in_ab && !in_bc) |
| 718 | + { hand(), line(200, 0, -200, 0) } |
| 719 | + if (in_ab && !in_ca && !in_bc) |
| 720 | + { hand(), line(-200, 0, 0, h) } |
| 721 | + if (in_bc && !in_ab && !in_ca) |
| 722 | + { hand(), line(0, h, 200, 0) } |
| 723 | + ctx.globalAlpha = 1 |
| 724 | + } |
567 | 725 |
|
568 | 726 | if (vv.atoms[0].fill) |
569 | 727 | ad.call({ ...vv.atoms[0], x: -200, y: 0, r: 50 }, true) |
|
578 | 736 | let s = lerp(1, 1.6, btn_slide_t) |
579 | 737 | ctx.scale(s, s) |
580 | 738 |
|
581 | | - let m = ctx.getTransform().inverse() |
582 | | - let { x: mx, y: my } = m.transformPoint(mouse) |
| 739 | + m = ctx.getTransform().inverse() |
| 740 | + ;({ x: mx, y: my } = m.transformPoint(mouse)) |
583 | 741 |
|
584 | 742 | for (let b of Object.values(vv.btns)) |
585 | 743 | ad.call(b, false, mx, my) |
|
0 commit comments