Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
function lineCircleIntersection(l, c) {
const dx = l.p2.x - l.p1.x;
const dy = l.p2.y - l.p1.y;
const dr2 = square(dx) + square(dy);
const cx = c.c.x;
const cy = c.c.y;
const D = (l.p1.x - cx) * (l.p2.y - cy) - (l.p2.x - cx) * (l.p1.y - cy);
const disc = square(c.r) * dr2 - square(D);
if (disc < 0) return []; // No solution
const xa = D * dy / dr2;
const ya = -D * dx / dr2;
if (nearlyEquals(disc, 0)) return [c.c.shift(xa, ya)]; // One solution
const xb = dx * (dy < 0 ? -1 : 1) * Math.sqrt(disc) / dr2;
const yb = Math.abs(dy) * Math.sqrt(disc) / dr2;
return [c.c.shift(xa + xb, ya + yb), c.c.shift(xa - xb, ya - yb)];
}
function circleCircleIntersection(c1, c2) {
const d = Point.distance(c1.c, c2.c);
if (d > c1.r + c2.r) return []; // Circles are separate.
if (d < Math.abs(c1.r - c2.r)) return []; // One circles contains the other.
if (nearlyEquals(d, 0) && nearlyEquals(c1.r,c2.r)) return []; // Circles are the same.
if (nearlyEquals(d, c1.r + c2.r)) return [new Line(c1.c, c2.c).midpoint]; // Circles touch.
const a = (square(c1.r) - square(c2.r) + square(d)) / (2 * d);
const b = Math.sqrt(square(c1.r) - square(a));
const px = (c2.c.x - c1.c.x) * a / d + (c2.c.y - c1.c.y) * b / d + c1.c.x;
const py = (c2.c.y - c1.c.y) * a / d - (c2.c.x - c1.c.x) * b / d + c1.c.y;
const qx = (c2.c.x - c1.c.x) * a / d - (c2.c.y - c1.c.y) * b / d + c1.c.x;
const qy = (c2.c.y - c1.c.y) * a / d + (c2.c.x - c1.c.x) * b / d + c1.c.y;
return [new Point(px, py), new Point(qx, qy)]
}
function circleCircleIntersection(c1, c2) {
const d = Point.distance(c1.c, c2.c);
if (d > c1.r + c2.r) return []; // Circles are separate.
if (d < Math.abs(c1.r - c2.r)) return []; // One circles contains the other.
if (nearlyEquals(d, 0) && nearlyEquals(c1.r,c2.r)) return []; // Circles are the same.
if (nearlyEquals(d, c1.r + c2.r)) return [new Line(c1.c, c2.c).midpoint]; // Circles touch.
const a = (square(c1.r) - square(c2.r) + square(d)) / (2 * d);
const b = Math.sqrt(square(c1.r) - square(a));
const px = (c2.c.x - c1.c.x) * a / d + (c2.c.y - c1.c.y) * b / d + c1.c.x;
const py = (c2.c.y - c1.c.y) * a / d - (c2.c.x - c1.c.x) * b / d + c1.c.y;
const qx = (c2.c.x - c1.c.x) * a / d - (c2.c.y - c1.c.y) * b / d + c1.c.x;
const qy = (c2.c.y - c1.c.y) * a / d + (c2.c.x - c1.c.x) * b / d + c1.c.y;
return [new Point(px, py), new Point(qx, qy)]
}
function rewrite(expr, rule) {
if (isString(expr) || isNumber(expr)) return expr;
let [fn, ...args] = expr;
args = args.map(a => rewrite(a, rule));
// For addition and multiplications we need to match subsets of arguments, e.g.
// `a + b + c` should match the rule `a + b`.
let [ruleFn, ...ruleArgs] = rule[0];
if ('+*'.includes(fn) && '+*'.includes(ruleFn) && args.length > ruleArgs.length) {
for (let p of subsets(list(args.length), ruleArgs.length)) {
let argsSubset = p.map(i => args[i]); // The subset of args that match the rule.
let argsOthers = args.filter((x, i) => !p.includes(i)); // All other args.
let placeholders = match([fn, ...argsSubset], rule[0])[0];
if (placeholders) {
let argsReplaced = substitute(rule[1], placeholders);
return flattenAssociative([fn, argsReplaced, ...argsOthers]);
}
}
return [fn, ...args];
}
let placeholders = match([fn,...args], rule[0])[0];
return placeholders ? substitute(rule[1], placeholders) : [fn, ...args];
}
export function quiz($step: Step) {
const goals = list(14).map(x => 'blank-' + x).join(' ');
$step.onScore(goals, () => {
// Don't show confetti during page load.
if ($step.isPageLoaded) $step.tools.confetti();
});
}
function match(expr, target) {
// Both expression and target are a number.
if (isNumber(target)) {
return (expr === target) ? [{}] : [];
}
if (isString(target)) {
// Matches any constants (numbers)
if (target[0] == 'c') {
return isNumber(expr) ? [{[target]: expr}] : [];
}
// Matches any variables (strings)
if (target[0] == 'v') {
return isString(expr) ? [{[target]: expr}] : [];
}
// Match any other expressions
return [{[target]: expr}];
}
// Check if functions are the same
if (!isArray(expr)) return [];
let [fn, ...args] = expr;
if (fn != target[0]) return [];
if (expr.length != target.length) return [];
// Match all arguments of a function. Addition and multiplication can
// match arguments in any order.
if ('+*'.includes(expr[0])) {
for (let a of permutations(args)) {
function mathML(expr) {
// TODO Implement MathML for all operators.
// TODO Distinguish between fractions and '÷'.
if (isNumber(expr)) return ``;
if (isString(expr)) return ``;
const [fn, ...args] = expr;
const argsStr = args.map(a => needsBrackets(a, fn) ? `` : mathML(a));
switch(fn) {
case 'sqrt': return ``;
case '/': return ``;
case '^': return ``;
case '*': return argsStr.join('');
case '+': return argsStr.join('');
case '-': return argsStr.join('');
default: return ``;
}
}
function match(expr, target) {
// Both expression and target are a number.
if (isNumber(target)) {
return (expr === target) ? [{}] : [];
}
if (isString(target)) {
// Matches any constants (numbers)
if (target[0] == 'c') {
return isNumber(expr) ? [{[target]: expr}] : [];
}
// Matches any variables (strings)
if (target[0] == 'v') {
return isString(expr) ? [{[target]: expr}] : [];
}
// Match any other expressions
return [{[target]: expr}];
}
// Check if functions are the same
if (!isArray(expr)) return [];
export function kepler($step: Step) {
const N = 100; // number of points
const a = 120; // semi-major axis (ry)
const e = 0.5; // eccentricity
// BesselJ function - Table[BesselJ[i, i*0.5], {i, 0, 10}]
const besselJ = [1, 0.242268, 0.114903, 0.060964, 0.0339957, 0.0195016,
0.0113939, 0.006743, 0.00402867, 0.00242466, 0.0014678];
const points = tabulate((n) => {
const M = 2 * Math.PI * n / N; // mean anomaly
let E = M; // eccentric anomaly
for (let i = 1; i < 10; ++i) E += 2 / i * besselJ[i] * Math.sin(i * M);
const th = 2 * Math.atan(Math.sqrt((1 + e) / (1 - e)) * Math.tan(E / 2));
const r = a * (1 - e * e) / (1 + e * Math.cos(th));
return Point.fromPolar(th, r).shift(220, 120);
}, N + 1);
const $orbit = $step.$('.orbit') as SVGView;
const $earth = $step.$('.earth') as SVGView;
const $sweep = $step.$('.sweep')!;
const $play = $step.$('x-play-btn') as PlayBtn;
$orbit.points = points;
function match(expr, target) {
// Both expression and target are a number.
if (isNumber(target)) {
return (expr === target) ? [{}] : [];
}
if (isString(target)) {
// Matches any constants (numbers)
if (target[0] == 'c') {
return isNumber(expr) ? [{[target]: expr}] : [];
}
// Matches any variables (strings)
if (target[0] == 'v') {
return isString(expr) ? [{[target]: expr}] : [];
}
// Match any other expressions
return [{[target]: expr}];
}
// Check if functions are the same
if (!isArray(expr)) return [];
let [fn, ...args] = expr;
if (fn != target[0]) return [];
if (expr.length != target.length) return [];