Socket
Socket
Sign inDemoInstall

@mathigon/euclid

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mathigon/euclid - npm Package Compare versions

Comparing version 1.0.8 to 1.0.9

test/ellipse-test.ts

27

dist/ellipse.d.ts

@@ -6,8 +6,17 @@ import { Line } from './line';

readonly c: Point;
readonly type = "ellipse";
readonly a: number;
readonly b: number;
readonly type = "ellipse";
readonly angle: number;
readonly f1: Point;
readonly f2: Point;
constructor(c: Point, a: number, b: number);
/**
* @param c Center of the ellipse
* @param a Major axis
* @param b Minor axis
* @param angle The rotation of the major axis of the ellipse.
*/
constructor(c: Point, a: number, b: number, angle?: number);
get rx(): number | undefined;
get ry(): number | undefined;
normalAt(p: Point): Line;

@@ -24,11 +33,11 @@ /** Intersection between an ellipse and a line. */

offset(p: Point): number;
contains(_p: Point): boolean;
contains(p: Point): boolean;
transform(_m: TransformMatrix): this;
rotate(_a: number, _c?: Point): this;
reflect(_l: Line): this;
scale(_sx: number, _sy?: number): this;
shift(_x: number, _y?: number): this;
translate(_p: Point): this;
equals(): boolean;
rotate(a: number, c?: Point): Ellipse;
reflect(l: Line): Ellipse;
scale(sx: number, sy?: number): Ellipse;
shift(x: number, y?: number): Ellipse;
translate(p: Point): Ellipse;
equals(other: Ellipse, tolerance?: number): boolean;
toString(): string;
}

@@ -282,3 +282,3 @@ var __defProp = Object.defineProperty;

get intercept() {
return this.p1.y + this.slope * this.p1.x;
return this.p1.y - this.slope * this.p1.x;
}

@@ -1739,2 +1739,4 @@ get angle() {

ctx.lineTo(p.x, p.y);
} else if (isEllipse(obj)) {
ctx.ellipse(obj.c.x, obj.c.y, obj.a, obj.b, obj.angle, 0, TWO_PI);
}

@@ -1864,18 +1866,27 @@ if (options.fill)

var Ellipse = class {
constructor(c, a, b) {
constructor(c, a, b, angle = 0) {
this.c = c;
this.type = "ellipse";
if (a < b) {
[a, b] = [b, a];
angle += Math.PI / 2;
}
this.a = a;
this.b = b;
this.type = "ellipse";
if (a < b)
throw new Error("Vertical ellipses not supported.");
this.angle = angle;
const f = Math.sqrt(a ** 2 - b ** 2);
this.f1 = c.shift(-f, 0);
this.f2 = c.shift(f, 0);
this.f1 = this.c.add(new Point(-f, 0).rotate(angle));
this.f2 = this.c.add(new Point(f, 0).rotate(angle));
}
get rx() {
return (0, import_fermat12.nearlyEquals)(this.angle, 0) ? this.a : (0, import_fermat12.nearlyEquals)(this.angle, Math.PI / 2) ? this.b : void 0;
}
get ry() {
return (0, import_fermat12.nearlyEquals)(this.angle, 0) ? this.b : (0, import_fermat12.nearlyEquals)(this.angle, Math.PI / 2) ? this.a : void 0;
}
normalAt(p) {
const a = new Angle(this.f1, p, this.f2);
return a.bisector;
return new Angle(this.f1, p, this.f2).bisector;
}
intersect(line) {
line = line.rotate(-this.angle, this.c);
const dx = line.p1.x - line.p2.x;

@@ -1889,3 +1900,3 @@ const dy = line.p1.y - line.p2.y;

const points = (0, import_fermat12.quadratic)(A, B, C);
return points.map((t) => line.at(t));
return points.map((t) => line.at(t).rotate(this.angle, this.c));
}

@@ -1896,13 +1907,13 @@ static fromFoci(f1, f2, stringLength) {

const b = Math.sqrt(a ** 2 - c ** 2);
return new Ellipse(Point.interpolate(f1, f2), a, b);
const angle = new Line(f1, f2).angle;
return new Ellipse(Point.interpolate(f1, f2), a, b, angle);
}
project(p) {
const [a, b] = [this.a, this.b];
p = p.rotate(-this.angle, this.c);
const th = p.angle(this.c);
const k = a * b / Math.sqrt((b * Math.cos(th)) ** 2 + (a * Math.sin(th)) ** 2);
return new Point(k * Math.cos(th), k * Math.sin(th));
return this.at(th / TWO_PI);
}
at(t) {
const th = TWO_PI * t;
return this.c.shift(this.a * Math.cos(th), this.b * Math.sin(th));
return this.c.shift(this.a * Math.cos(th), this.b * Math.sin(th)).rotate(this.angle, this.c);
}

@@ -1912,4 +1923,7 @@ offset(p) {

}
contains(_p) {
return false;
contains(p) {
const A = Math.cos(this.angle) ** 2 / this.a ** 2 + Math.sin(this.angle) ** 2 / this.b ** 2;
const B = 2 * Math.cos(this.angle) * Math.sin(this.angle) * (1 / this.a ** 2 - 1 / this.b ** 2);
const C = Math.sin(this.angle) ** 2 / this.a ** 2 + Math.cos(this.angle) ** 2 / this.b ** 2;
return A * p.x ** 2 + B * p.x * p.y + C * p.y ** 2 <= 1;
}

@@ -1919,22 +1933,24 @@ transform(_m) {

}
rotate(_a, _c = ORIGIN) {
return this;
rotate(a, c = ORIGIN) {
const l = new Line(this.f1, this.f2).rotate(a, c);
return Ellipse.fromFoci(l.p1, l.p2, this.a * 2);
}
reflect(_l) {
return this;
reflect(l) {
const axis = new Line(this.f1, this.f2).reflect(l);
return Ellipse.fromFoci(axis.p1, axis.p2, this.a * 2);
}
scale(_sx, _sy = _sx) {
return this;
scale(sx, sy = sx) {
return new Ellipse(this.c, this.a * sx, this.b * sy, this.angle);
}
shift(_x, _y = _x) {
return this;
shift(x, y = x) {
return new Ellipse(this.c.shift(x, y), this.a, this.b, this.angle);
}
translate(_p) {
return this;
translate(p) {
return new Ellipse(this.c.translate(p), this.a, this.b, this.angle);
}
equals() {
return false;
equals(other, tolerance) {
return (0, import_fermat12.nearlyEquals)(this.a, other.a, tolerance) && (0, import_fermat12.nearlyEquals)(this.b, other.b, tolerance) && (0, import_fermat12.nearlyEquals)(this.angle, other.angle, tolerance) && this.c.equals(other.c, tolerance);
}
toString() {
return `ellipse(${this.c},${this.a},${this.b})`;
return `ellipse(${this.c},${this.a},${this.b},${this.angle})`;
}

@@ -1941,0 +1957,0 @@ };

@@ -218,3 +218,3 @@ // src/angle.ts

get intercept() {
return this.p1.y + this.slope * this.p1.x;
return this.p1.y - this.slope * this.p1.x;
}

@@ -1675,2 +1675,4 @@ get angle() {

ctx.lineTo(p.x, p.y);
} else if (isEllipse(obj)) {
ctx.ellipse(obj.c.x, obj.c.y, obj.a, obj.b, obj.angle, 0, TWO_PI);
}

@@ -1798,20 +1800,29 @@ if (options.fill)

// src/ellipse.ts
import { quadratic } from "@mathigon/fermat";
import { nearlyEquals as nearlyEquals10, quadratic } from "@mathigon/fermat";
var Ellipse = class {
constructor(c, a, b) {
constructor(c, a, b, angle = 0) {
this.c = c;
this.type = "ellipse";
if (a < b) {
[a, b] = [b, a];
angle += Math.PI / 2;
}
this.a = a;
this.b = b;
this.type = "ellipse";
if (a < b)
throw new Error("Vertical ellipses not supported.");
this.angle = angle;
const f = Math.sqrt(a ** 2 - b ** 2);
this.f1 = c.shift(-f, 0);
this.f2 = c.shift(f, 0);
this.f1 = this.c.add(new Point(-f, 0).rotate(angle));
this.f2 = this.c.add(new Point(f, 0).rotate(angle));
}
get rx() {
return nearlyEquals10(this.angle, 0) ? this.a : nearlyEquals10(this.angle, Math.PI / 2) ? this.b : void 0;
}
get ry() {
return nearlyEquals10(this.angle, 0) ? this.b : nearlyEquals10(this.angle, Math.PI / 2) ? this.a : void 0;
}
normalAt(p) {
const a = new Angle(this.f1, p, this.f2);
return a.bisector;
return new Angle(this.f1, p, this.f2).bisector;
}
intersect(line) {
line = line.rotate(-this.angle, this.c);
const dx = line.p1.x - line.p2.x;

@@ -1825,3 +1836,3 @@ const dy = line.p1.y - line.p2.y;

const points = quadratic(A, B, C);
return points.map((t) => line.at(t));
return points.map((t) => line.at(t).rotate(this.angle, this.c));
}

@@ -1832,13 +1843,13 @@ static fromFoci(f1, f2, stringLength) {

const b = Math.sqrt(a ** 2 - c ** 2);
return new Ellipse(Point.interpolate(f1, f2), a, b);
const angle = new Line(f1, f2).angle;
return new Ellipse(Point.interpolate(f1, f2), a, b, angle);
}
project(p) {
const [a, b] = [this.a, this.b];
p = p.rotate(-this.angle, this.c);
const th = p.angle(this.c);
const k = a * b / Math.sqrt((b * Math.cos(th)) ** 2 + (a * Math.sin(th)) ** 2);
return new Point(k * Math.cos(th), k * Math.sin(th));
return this.at(th / TWO_PI);
}
at(t) {
const th = TWO_PI * t;
return this.c.shift(this.a * Math.cos(th), this.b * Math.sin(th));
return this.c.shift(this.a * Math.cos(th), this.b * Math.sin(th)).rotate(this.angle, this.c);
}

@@ -1848,4 +1859,7 @@ offset(p) {

}
contains(_p) {
return false;
contains(p) {
const A = Math.cos(this.angle) ** 2 / this.a ** 2 + Math.sin(this.angle) ** 2 / this.b ** 2;
const B = 2 * Math.cos(this.angle) * Math.sin(this.angle) * (1 / this.a ** 2 - 1 / this.b ** 2);
const C = Math.sin(this.angle) ** 2 / this.a ** 2 + Math.cos(this.angle) ** 2 / this.b ** 2;
return A * p.x ** 2 + B * p.x * p.y + C * p.y ** 2 <= 1;
}

@@ -1855,22 +1869,24 @@ transform(_m) {

}
rotate(_a, _c = ORIGIN) {
return this;
rotate(a, c = ORIGIN) {
const l = new Line(this.f1, this.f2).rotate(a, c);
return Ellipse.fromFoci(l.p1, l.p2, this.a * 2);
}
reflect(_l) {
return this;
reflect(l) {
const axis = new Line(this.f1, this.f2).reflect(l);
return Ellipse.fromFoci(axis.p1, axis.p2, this.a * 2);
}
scale(_sx, _sy = _sx) {
return this;
scale(sx, sy = sx) {
return new Ellipse(this.c, this.a * sx, this.b * sy, this.angle);
}
shift(_x, _y = _x) {
return this;
shift(x, y = x) {
return new Ellipse(this.c.shift(x, y), this.a, this.b, this.angle);
}
translate(_p) {
return this;
translate(p) {
return new Ellipse(this.c.translate(p), this.a, this.b, this.angle);
}
equals() {
return false;
equals(other, tolerance) {
return nearlyEquals10(this.a, other.a, tolerance) && nearlyEquals10(this.b, other.b, tolerance) && nearlyEquals10(this.angle, other.angle, tolerance) && this.c.equals(other.c, tolerance);
}
toString() {
return `ellipse(${this.c},${this.a},${this.b})`;
return `ellipse(${this.c},${this.a},${this.b},${this.angle})`;
}

@@ -1877,0 +1893,0 @@ };

{
"name": "@mathigon/euclid",
"version": "1.0.8",
"version": "1.0.9",
"license": "MIT",

@@ -31,17 +31,17 @@ "homepage": "https://mathigon.io/euclid",

"dependencies": {
"@mathigon/core": "1.0.7",
"@mathigon/fermat": "1.0.7"
"@mathigon/core": "1.0.8",
"@mathigon/fermat": "1.0.8"
},
"devDependencies": {
"@types/tape": "4.13.2",
"@typescript-eslint/eslint-plugin": "5.10.2",
"@typescript-eslint/parser": "5.10.2",
"esbuild": "0.14.16",
"eslint": "8.8.0",
"@typescript-eslint/eslint-plugin": "5.13.0",
"@typescript-eslint/parser": "5.13.0",
"esbuild": "0.14.23",
"eslint": "8.10.0",
"eslint-plugin-import": "2.25.4",
"tape": "5.5.0",
"ts-node": "10.4.0",
"tape": "5.5.2",
"ts-node": "10.5.0",
"tslib": "2.3.1",
"typescript": "4.5.5"
"typescript": "4.6.2"
}
}

@@ -7,3 +7,3 @@ // =============================================================================

import {isAngle, isCircle, isPolygonLike, isPolyline, isSegment} from './types';
import {isAngle, isCircle, isEllipse, isPolygonLike, isPolyline, isSegment} from './types';
import {GeoElement, TWO_PI} from './utilities';

@@ -56,2 +56,5 @@

for (const p of obj.points.slice(1)) ctx.lineTo(p.x, p.y);
} else if (isEllipse(obj)) {
ctx.ellipse(obj.c.x, obj.c.y, obj.a, obj.b, obj.angle, 0, TWO_PI);
}

@@ -58,0 +61,0 @@

@@ -7,3 +7,3 @@ // =============================================================================

import {quadratic} from '@mathigon/fermat';
import {nearlyEquals, quadratic} from '@mathigon/fermat';
import {Angle} from './angle';

@@ -17,16 +17,38 @@ import {Line} from './line';

readonly type = 'ellipse';
readonly a: number;
readonly b: number;
readonly angle: number;
readonly f1: Point;
readonly f2: Point;
constructor(readonly c: Point, readonly a: number, readonly b: number) {
// TODO Support vertical and rotated ellipses
if (a < b) throw new Error('Vertical ellipses not supported.');
/**
* @param c Center of the ellipse
* @param a Major axis
* @param b Minor axis
* @param angle The rotation of the major axis of the ellipse.
*/
constructor(readonly c: Point, a: number, b: number, angle = 0) {
if (a < b) {
[a, b] = [b, a];
angle += Math.PI / 2;
}
this.a = a;
this.b = b;
this.angle = angle;
const f = Math.sqrt(a ** 2 - b ** 2); // Distance from focus to the center.
this.f1 = c.shift(-f, 0);
this.f2 = c.shift(f, 0);
this.f1 = this.c.add(new Point(-f, 0).rotate(angle));
this.f2 = this.c.add(new Point(f, 0).rotate(angle));
}
get rx() {
return nearlyEquals(this.angle, 0) ? this.a : nearlyEquals(this.angle, Math.PI / 2) ? this.b : undefined;
}
get ry() {
return nearlyEquals(this.angle, 0) ? this.b : nearlyEquals(this.angle, Math.PI / 2) ? this.a : undefined;
}
normalAt(p: Point) {
const a = new Angle(this.f1, p, this.f2);
return a.bisector!;
return new Angle(this.f1, p, this.f2).bisector!;
}

@@ -36,2 +58,4 @@

intersect(line: Line) {
line = line.rotate(-this.angle, this.c);
const dx = line.p1.x - line.p2.x;

@@ -44,7 +68,7 @@ const dy = line.p1.y - line.p2.y;

const A = (dx / this.a) ** 2 + (dy / this.b) ** 2;
const B = 2 * px * dx / (this.a) ** 2 + 2 * py * dy / (this.b) ** 2;
const B = (2 * px * dx) / this.a ** 2 + (2 * py * dy) / this.b ** 2;
const C = (px / this.a) ** 2 + (py / this.b) ** 2 - 1;
const points = quadratic(A, B, C);
return points.map(t => line.at(t));
return points.map((t) => line.at(t).rotate(this.angle, this.c));
}

@@ -60,3 +84,4 @@

const b = Math.sqrt(a ** 2 - c ** 2);
return new Ellipse(Point.interpolate(f1, f2), a, b);
const angle = new Line(f1, f2).angle;
return new Ellipse(Point.interpolate(f1, f2), a, b, angle);
}

@@ -67,6 +92,5 @@

project(p: Point) {
const [a, b] = [this.a, this.b];
p = p.rotate(-this.angle, this.c);
const th = p.angle(this.c);
const k = a * b / Math.sqrt((b * Math.cos(th)) ** 2 + (a * Math.sin(th)) ** 2);
return new Point(k * Math.cos(th), k * Math.sin(th));
return this.at(th / TWO_PI);
}

@@ -76,3 +100,5 @@

const th = TWO_PI * t;
return this.c.shift(this.a * Math.cos(th), this.b * Math.sin(th));
return this.c
.shift(this.a * Math.cos(th), this.b * Math.sin(th))
.rotate(this.angle, this.c);
}

@@ -85,5 +111,7 @@

contains(_p: Point) {
// TODO Implement
return false;
contains(p: Point) {
const A = Math.cos(this.angle) ** 2 / this.a ** 2 + Math.sin(this.angle) ** 2 / this.b ** 2;
const B = 2 * Math.cos(this.angle) * Math.sin(this.angle) * (1 / this.a ** 2 - 1 / this.b ** 2);
const C = Math.sin(this.angle) ** 2 / this.a ** 2 + Math.cos(this.angle) ** 2 / this.b ** 2;
return A * p.x ** 2 + B * p.x * p.y + C * p.y ** 2 <= 1;
}

@@ -98,35 +126,36 @@

rotate(_a: number, _c = ORIGIN): this {
// TODO Implement
return this;
rotate(a: number, c = ORIGIN) {
const l = new Line(this.f1, this.f2).rotate(a, c);
return Ellipse.fromFoci(l.p1, l.p2, this.a * 2);
}
reflect(_l: Line): this {
// TODO Implement
return this;
reflect(l: Line) {
const axis = new Line(this.f1, this.f2).reflect(l);
return Ellipse.fromFoci(axis.p1, axis.p2, this.a * 2);
}
scale(_sx: number, _sy = _sx): this {
// TODO Implement
return this;
scale(sx: number, sy = sx) {
return new Ellipse(this.c, this.a * sx, this.b * sy, this.angle);
}
shift(_x: number, _y = _x): this {
// TODO Implement
return this;
shift(x: number, y = x) {
return new Ellipse(this.c.shift(x, y), this.a, this.b, this.angle);
}
translate(_p: Point) {
// TODO Implement
return this;
translate(p: Point) {
return new Ellipse(this.c.translate(p), this.a, this.b, this.angle);
}
equals() {
// TODO Implement
return false;
equals(other: Ellipse, tolerance?: number) {
return (
nearlyEquals(this.a, other.a, tolerance) &&
nearlyEquals(this.b, other.b, tolerance) &&
nearlyEquals(this.angle, other.angle, tolerance) &&
this.c.equals(other.c, tolerance)
);
}
toString() {
return `ellipse(${this.c},${this.a},${this.b})`;
return `ellipse(${this.c},${this.a},${this.b},${this.angle})`;
}
}

@@ -45,3 +45,3 @@ // =============================================================================

get intercept() {
return this.p1.y + this.slope * this.p1.x;
return this.p1.y - this.slope * this.p1.x;
}

@@ -48,0 +48,0 @@

@@ -35,1 +35,11 @@ // =============================================================================

});
tape('line intercepts', (test) => {
const line1 = new Line(new Point(1, 1), new Point(2, 2));
test.equals(line1.intercept, 0);
const line2 = new Line(new Point(0, 3), new Point(2, 1));
test.equals(line2.intercept, 3);
test.end();
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc