Vector.js

Vector is an open source javascript library for creating interactive graphics. View the repository, run the tests, or meet the team.

Angle Interactive Open in Sandbox

Description

An interactive that demonstrates how an angle is defined as the amount of rotation between two rays in two-dimensional space.

Script

/**
* @title Angle Interactive
* @description An interactive that demonstrates how an angle is defined as the amount of rotation between two rays in two-dimensional space.
* @tags [math]
* @ignore true
*/
import { Interactive, getScriptName } from '../../index.js';
// Initialize the interactive
let interactive = new Interactive(getScriptName());
interactive.border = true;
interactive.width = 600;
interactive.height = 300;
interactive.originX = interactive.width / 2;
interactive.originY = interactive.height / 2;
;
// Create a circle
let circle = interactive.circle(0, 0, 100);
circle.root.style.stroke = 'none';
// Create a control
let c0 = interactive.control(0, 0);
let c1 = interactive.control(circle.r * Math.cos(0), circle.r * Math.sin(0));
let c2 = interactive.control(circle.r * Math.cos(-1), circle.r * Math.sin(-1));
// c1.constrainTo( circle);
// c2.constrainTo( circle);
// Create a path
let path = interactive.path('');
path.root.style.fill = 'rgb(236,236,236)';
path.update = function () {
    let a1 = Math.atan2(c1.y - c0.y, c1.x - c0.x);
    let a2 = Math.atan2(c2.y - c0.y, c2.x - c0.x);
    let angle = normalize(a2 - a1);
    let largeArcFlag = (angle > Math.PI) ? false : true;
    let r = circle.r / 3;
    let x1 = r * Math.cos(a1) + c0.x;
    let y1 = r * Math.sin(a1) + c0.y;
    let x2 = r * Math.cos(a2) + c0.x;
    let y2 = r * Math.sin(a2) + c0.y;
    path.d = `M ${c0.x} ${c0.y}
            L ${c1.x} ${c1.y}
            L ${x1} ${y1}
            A ${r} ${r} 0 ${+largeArcFlag} 0 ${x2} ${y2}
            L ${c2.x} ${c2.y}
            z`;
};
path.update();
path.addDependency(c0);
path.addDependency(c1);
path.addDependency(c2);
let arrow1 = interactive.path('');
arrow1.addDependency(c0);
arrow1.addDependency(c1);
arrow1.update = function () {
    let r = 8;
    let angle = Math.atan2(c1.y - c0.y, c1.x - c0.x);
    this.d = `M ${c1.x + r * Math.cos(angle)} ${c1.y + r * Math.sin(angle)}
  L ${c1.x + r * Math.cos(angle - 2 * Math.PI / 3)} ${c1.y + r * Math.sin(angle - 2 * Math.PI / 3)}
  L ${c1.x + r * Math.cos(angle + 2 * Math.PI / 3)} ${c1.y + r * Math.sin(angle + 2 * Math.PI / 3)}
            Z`;
};
arrow1.root.style.fill = '#0366EE';
arrow1.root.style.stroke = 'none';
arrow1.update();
let arrow2 = interactive.path('');
arrow2.addDependency(c0);
arrow2.addDependency(c2);
arrow2.update = function () {
    let r = 8;
    let angle = Math.atan2(c2.y - c0.y, c2.x - c0.x);
    this.d = `M ${c2.x + r * Math.cos(angle)} ${c2.y + r * Math.sin(angle)}
  L ${c2.x + r * Math.cos(angle - 2 * Math.PI / 3)} ${c2.y + r * Math.sin(angle - 2 * Math.PI / 3)}
  L ${c2.x + r * Math.cos(angle + 2 * Math.PI / 3)} ${c2.y + r * Math.sin(angle + 2 * Math.PI / 3)}
            Z`;
};
arrow2.root.style.fill = '#0366EE';
arrow2.root.style.stroke = 'none';
arrow2.update();
c1.addDependency(c0);
c1.update = function () {
    this.x += c0.dx;
    this.y += c0.dy;
};
c2.addDependency(c0);
c2.update = function () {
    this.x += c0.dx;
    this.y += c0.dy;
};
circle.addDependency(c0);
circle.update = function () {
    this.cx += c0.dx;
    this.cy += c0.dy;
};
// Create a checkbox to toggle between displaying radians and degrees
let degrees = interactive.checkBox(interactive.width / 6, 125, "degrees", false);
/**
* Normalizes the angle to be within the range [0, 2 PI].
*/
function normalize(angle) {
    if (angle > 0) {
        return angle;
    }
    else {
        return 2 * Math.PI + angle;
    }
}
// Gets the normalized angle between zero and tau. TODO: Maybe transform the
// coordinate system so that the positive y-direction is up instead of down.
// UPDATE: transform = 'scale(1,-1)' applied to the main svg  didn't quite work
// as expected: the text element was upside down, but maybe that could be
// reversed? bleh.
function getAngle() {
    let angle;
    let a1 = Math.atan2(c1.y - circle.cy, c1.x - circle.cx);
    let a2 = Math.atan2(c2.y - circle.cy, c2.x - circle.cx);
    angle = 2 * Math.PI - normalize(a2 - a1);
    if (degrees.value) {
        return (angle * 180 / Math.PI).toFixed(1) + '°';
    }
    else {
        return angle.toFixed(3) + ' rad';
    }
}
// Create text to display the current angle. TODO: add a check-box to change
// between radians and degrees
let text = interactive.text(-interactive.width / 3, 125, "test");
text.update = function () {
    text.contents = `angle = ${getAngle()}`;
};
text.addDependency(degrees);
text.addDependency(c1);
text.addDependency(c2);
text.update();
text.root.style.dominantBaseline = 'middle';
export { interactive, c1 };
//# sourceMappingURL=angle.js.map