@mathigon/euclid
Advanced tools
Comparing version 1.0.7 to 1.0.8
import { Arc } from './arc'; | ||
import { Line } from './line'; | ||
import { Point } from './point'; | ||
import { Polygon } from './polygon'; | ||
import { GeoShape, SimplePoint, TransformMatrix } from './utilities'; | ||
@@ -30,6 +31,10 @@ /** Convert angles in radians to degrees. */ | ||
get arc(): Arc; | ||
project(): Point; | ||
/** Radius of the arc or sector representing this angle. */ | ||
get radius(): number; | ||
/** Shape object that can be used to draw this angle. */ | ||
shape(filled?: boolean, radius?: number, round?: boolean): Polygon | Arc; | ||
project(p: Point): Point; | ||
at(): Point; | ||
offset(): number; | ||
contains(): boolean; | ||
contains(p: Point): boolean; | ||
transform(m: TransformMatrix): Angle; | ||
@@ -36,0 +41,0 @@ rotate(a: number, c?: SimplePoint): Angle; |
@@ -1,2 +0,1 @@ | ||
import { Angle } from './angle'; | ||
import { Rectangle } from './rectangle'; | ||
@@ -14,3 +13,2 @@ import { GeoElement } from './utilities'; | ||
} | ||
export declare function angleSize(angle: Angle, options?: SVGDrawingOptions): number; | ||
export declare function drawSVG(obj: GeoElement, options?: SVGDrawingOptions): string; |
@@ -43,3 +43,2 @@ var __defProp = Object.defineProperty; | ||
Triangle: () => Triangle, | ||
angleSize: () => angleSize, | ||
drawCanvas: () => drawCanvas, | ||
@@ -68,3 +67,3 @@ drawSVG: () => drawSVG, | ||
// src/angle.ts | ||
var import_fermat6 = require("@mathigon/fermat"); | ||
var import_fermat9 = require("@mathigon/fermat"); | ||
@@ -577,113 +576,9 @@ // src/arc.ts | ||
// src/angle.ts | ||
var RAD_TO_DEG = 180 / Math.PI; | ||
var DEG_TO_RAD = Math.PI / 180; | ||
function toDeg(n) { | ||
return n * RAD_TO_DEG; | ||
} | ||
function toRad(n) { | ||
return n * DEG_TO_RAD; | ||
} | ||
var Angle = class { | ||
constructor(a, b, c) { | ||
this.a = a; | ||
this.b = b; | ||
this.c = c; | ||
this.type = "angle"; | ||
} | ||
static fromDegrees(val) { | ||
return Angle.fromRadians(val * (Math.PI / 180)); | ||
} | ||
static fromRadians(val) { | ||
const p1 = new Point(1, 0); | ||
const p2 = p1.rotate(val); | ||
return new Angle(p1, ORIGIN, p2); | ||
} | ||
get rad() { | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = phiC - phiA; | ||
if (phi < 0) | ||
phi += TWO_PI; | ||
return phi; | ||
} | ||
get deg() { | ||
return this.rad * 180 / Math.PI; | ||
} | ||
get isRight() { | ||
return (0, import_fermat6.nearlyEquals)(this.rad, Math.PI / 2, Math.PI / 360); | ||
} | ||
get bisector() { | ||
if (this.b.equals(this.a)) | ||
return void 0; | ||
if (this.b.equals(this.c)) | ||
return void 0; | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = (phiA + phiC) / 2; | ||
if (phiA > phiC) | ||
phi += Math.PI; | ||
const x = Math.cos(phi) + this.b.x; | ||
const y = Math.sin(phi) + this.b.y; | ||
return new Line(this.b, new Point(x, y)); | ||
} | ||
get sup() { | ||
return this.rad < Math.PI ? this : new Angle(this.c, this.b, this.a); | ||
} | ||
get arc() { | ||
return new Arc(this.b, this.a, this.rad); | ||
} | ||
project() { | ||
return this.c; | ||
} | ||
at() { | ||
return this.c; | ||
} | ||
offset() { | ||
return 0; | ||
} | ||
contains() { | ||
return false; | ||
} | ||
transform(m) { | ||
return new Angle(this.a.transform(m), this.b.transform(m), this.c.transform(m)); | ||
} | ||
rotate(a, c) { | ||
if ((0, import_fermat6.nearlyEquals)(a, 0)) | ||
return this; | ||
return new Angle(this.a.rotate(a, c), this.b.rotate(a, c), this.c.rotate(a, c)); | ||
} | ||
reflect(l) { | ||
return new Angle(this.a.reflect(l), this.b.reflect(l), this.c.reflect(l)); | ||
} | ||
scale(sx, sy = sx) { | ||
return new Angle(this.a.scale(sx, sy), this.b.scale(sx, sy), this.c.scale(sx, sy)); | ||
} | ||
shift(x, y = x) { | ||
return new Angle(this.a.shift(x, y), this.b.shift(x, y), this.c.shift(x, y)); | ||
} | ||
translate(p) { | ||
return new Angle(this.a.translate(p), this.b.translate(p), this.c.translate(p)); | ||
} | ||
equals(_a) { | ||
return false; | ||
} | ||
toString() { | ||
return `angle(${this.a},${this.b},${this.c})`; | ||
} | ||
}; | ||
// src/bounds.ts | ||
var import_fermat11 = require("@mathigon/fermat"); | ||
// src/rectangle.ts | ||
var import_fermat10 = require("@mathigon/fermat"); | ||
// src/polygon.ts | ||
var import_core4 = require("@mathigon/core"); | ||
var import_fermat9 = require("@mathigon/fermat"); | ||
var import_fermat8 = require("@mathigon/fermat"); | ||
// src/boolean.ts | ||
var import_core2 = require("@mathigon/core"); | ||
var import_fermat7 = require("@mathigon/fermat"); | ||
var import_fermat6 = require("@mathigon/fermat"); | ||
var PRECISION = 1e-6; | ||
@@ -707,4 +602,4 @@ function pointAboveOrOnLine(pt, left, right) { | ||
function pointsCompare(p1, p2) { | ||
if ((0, import_fermat7.nearlyEquals)(p1.x, p2.x)) { | ||
return (0, import_fermat7.nearlyEquals)(p1.y, p2.y) ? 0 : p1.y < p2.y ? -1 : 1; | ||
if ((0, import_fermat6.nearlyEquals)(p1.x, p2.x)) { | ||
return (0, import_fermat6.nearlyEquals)(p1.y, p2.y) ? 0 : p1.y < p2.y ? -1 : 1; | ||
} | ||
@@ -730,3 +625,3 @@ return p1.x < p2.x ? -1 : 1; | ||
const axb = adx * bdy - ady * bdx; | ||
if ((0, import_fermat7.nearlyEquals)(axb, 0)) | ||
if ((0, import_fermat6.nearlyEquals)(axb, 0)) | ||
return false; | ||
@@ -1132,3 +1027,3 @@ const dx = a0.x - b0.x; | ||
var import_core3 = require("@mathigon/core"); | ||
var import_fermat8 = require("@mathigon/fermat"); | ||
var import_fermat7 = require("@mathigon/fermat"); | ||
@@ -1181,8 +1076,8 @@ // src/types.ts | ||
function liesOnSegment(s, p) { | ||
if ((0, import_fermat8.nearlyEquals)(s.p1.x, s.p2.x)) | ||
return (0, import_fermat8.isBetween)(p.y, s.p1.y, s.p2.y); | ||
return (0, import_fermat8.isBetween)(p.x, s.p1.x, s.p2.x); | ||
if ((0, import_fermat7.nearlyEquals)(s.p1.x, s.p2.x)) | ||
return (0, import_fermat7.isBetween)(p.y, s.p1.y, s.p2.y); | ||
return (0, import_fermat7.isBetween)(p.x, s.p1.x, s.p2.x); | ||
} | ||
function liesOnRay(r, p) { | ||
if ((0, import_fermat8.nearlyEquals)(r.p1.x, r.p2.x)) | ||
if ((0, import_fermat7.nearlyEquals)(r.p1.x, r.p2.x)) | ||
return (p.y - r.p1.y) / (r.p2.y - r.p1.y) > 0; | ||
@@ -1192,3 +1087,3 @@ return (p.x - r.p1.x) / (r.p2.x - r.p1.x) > 0; | ||
function liesOnArc(a, p) { | ||
return (0, import_fermat8.isBetween)(a.offset(p), 0, 1); | ||
return (0, import_fermat7.isBetween)(a.offset(p), 0, 1); | ||
} | ||
@@ -1201,3 +1096,3 @@ function lineLineIntersection(l1, l2) { | ||
const d = d1x * d2y - d1y * d2x; | ||
if ((0, import_fermat8.nearlyEquals)(d, 0)) | ||
if ((0, import_fermat7.nearlyEquals)(d, 0)) | ||
return []; | ||
@@ -1216,8 +1111,8 @@ const q1 = l1.p1.x * l1.p2.y - l1.p1.y * l1.p2.x; | ||
return []; | ||
if ((0, import_fermat8.nearlyEquals)(d, 0) && (0, import_fermat8.nearlyEquals)(c1.r, c2.r)) | ||
if ((0, import_fermat7.nearlyEquals)(d, 0) && (0, import_fermat7.nearlyEquals)(c1.r, c2.r)) | ||
return []; | ||
if ((0, import_fermat8.nearlyEquals)(d, c1.r + c2.r)) | ||
if ((0, import_fermat7.nearlyEquals)(d, c1.r + c2.r)) | ||
return [new Line(c1.c, c2.c).midpoint]; | ||
const a = ((0, import_fermat8.square)(c1.r) - (0, import_fermat8.square)(c2.r) + (0, import_fermat8.square)(d)) / (2 * d); | ||
const b = Math.sqrt((0, import_fermat8.square)(c1.r) - (0, import_fermat8.square)(a)); | ||
const a = ((0, import_fermat7.square)(c1.r) - (0, import_fermat7.square)(c2.r) + (0, import_fermat7.square)(d)) / (2 * d); | ||
const b = Math.sqrt((0, import_fermat7.square)(c1.r) - (0, import_fermat7.square)(a)); | ||
const px = (c2.c.x - c1.c.x) * a / d + (c2.c.y - c1.c.y) * b / d + c1.c.x; | ||
@@ -1232,7 +1127,7 @@ const py = (c2.c.y - c1.c.y) * a / d - (c2.c.x - c1.c.x) * b / d + c1.c.y; | ||
const dy = l.p2.y - l.p1.y; | ||
const dr2 = (0, import_fermat8.square)(dx) + (0, import_fermat8.square)(dy); | ||
const dr2 = (0, import_fermat7.square)(dx) + (0, import_fermat7.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 = (0, import_fermat8.square)(c.r) * dr2 - (0, import_fermat8.square)(D); | ||
const disc = (0, import_fermat7.square)(c.r) * dr2 - (0, import_fermat7.square)(D); | ||
if (disc < 0) | ||
@@ -1242,3 +1137,3 @@ return []; | ||
const ya = -D * dx / dr2; | ||
if ((0, import_fermat8.nearlyEquals)(disc, 0)) | ||
if ((0, import_fermat7.nearlyEquals)(disc, 0)) | ||
return [c.c.shift(xa, ya)]; | ||
@@ -1276,5 +1171,9 @@ const xb = dx * (dy < 0 ? -1 : 1) * Math.sqrt(disc) / dr2; | ||
if (elements.length > 2) { | ||
return (0, import_core3.flatten)((0, import_fermat8.subsets)(elements, 2).map((e) => intersections(...e))); | ||
return (0, import_core3.flatten)((0, import_fermat7.subsets)(elements, 2).map((e) => intersections(...e))); | ||
} | ||
let [a, b] = elements; | ||
if (isAngle(a)) | ||
a = a.shape(true); | ||
if (isAngle(b)) | ||
b = b.shape(true); | ||
if (isPolygonLike(b)) | ||
@@ -1459,3 +1358,3 @@ [a, b] = [b, a]; | ||
rotate(a, center = ORIGIN) { | ||
if ((0, import_fermat9.nearlyEquals)(a, 0)) | ||
if ((0, import_fermat8.nearlyEquals)(a, 0)) | ||
return this; | ||
@@ -1535,2 +1434,4 @@ const points = this.points.map((p) => p.rotate(a, center)); | ||
const radius = Point.distance(center, this.points[0]); | ||
if (isNaN(radius) || radius > Number.MAX_SAFE_INTEGER) | ||
return; | ||
return new Circle(center, radius); | ||
@@ -1547,3 +1448,3 @@ } | ||
const radius = center.distanceFromLine(edges[0]); | ||
return new Circle(center, radius); | ||
return isNaN(radius) ? void 0 : new Circle(center, radius); | ||
} | ||
@@ -1558,3 +1459,126 @@ get orthocenter() { | ||
// src/angle.ts | ||
var RAD_TO_DEG = 180 / Math.PI; | ||
var DEG_TO_RAD = Math.PI / 180; | ||
function toDeg(n) { | ||
return n * RAD_TO_DEG; | ||
} | ||
function toRad(n) { | ||
return n * DEG_TO_RAD; | ||
} | ||
var Angle = class { | ||
constructor(a, b, c) { | ||
this.a = a; | ||
this.b = b; | ||
this.c = c; | ||
this.type = "angle"; | ||
} | ||
static fromDegrees(val) { | ||
return Angle.fromRadians(val * (Math.PI / 180)); | ||
} | ||
static fromRadians(val) { | ||
const p1 = new Point(1, 0); | ||
const p2 = p1.rotate(val); | ||
return new Angle(p1, ORIGIN, p2); | ||
} | ||
get rad() { | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = phiC - phiA; | ||
if (phi < 0) | ||
phi += TWO_PI; | ||
return phi; | ||
} | ||
get deg() { | ||
return this.rad * 180 / Math.PI; | ||
} | ||
get isRight() { | ||
return (0, import_fermat9.nearlyEquals)(this.rad, Math.PI / 2, Math.PI / 360); | ||
} | ||
get bisector() { | ||
if (this.b.equals(this.a)) | ||
return void 0; | ||
if (this.b.equals(this.c)) | ||
return void 0; | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = (phiA + phiC) / 2; | ||
if (phiA > phiC) | ||
phi += Math.PI; | ||
const x = Math.cos(phi) + this.b.x; | ||
const y = Math.sin(phi) + this.b.y; | ||
return new Line(this.b, new Point(x, y)); | ||
} | ||
get sup() { | ||
return this.rad < Math.PI ? this : new Angle(this.c, this.b, this.a); | ||
} | ||
get arc() { | ||
return new Arc(this.b, this.a, this.rad); | ||
} | ||
get radius() { | ||
return 24 + 20 * (1 - (0, import_fermat9.clamp)(this.rad, 0, Math.PI) / Math.PI); | ||
} | ||
shape(filled = true, radius, round) { | ||
if (this.a.equals(this.b) || this.c.equals(this.b)) | ||
return new Polygon(ORIGIN); | ||
const angled = this.isRight && !round; | ||
if (!radius) | ||
radius = angled ? 20 : this.radius; | ||
const ba = new Segment(this.b, this.a); | ||
const a = ba.at(radius / ba.length); | ||
if (angled) { | ||
const bc = Point.difference(this.c, this.b).unitVector.scale(radius); | ||
if (filled) | ||
return new Polygon(this.b, a, a.add(bc), this.b.add(bc)); | ||
return new Polyline(a, a.add(bc), this.b.add(bc)); | ||
} | ||
if (filled) | ||
return new Sector(this.b, a, this.rad); | ||
return new Arc(this.b, a, this.rad); | ||
} | ||
project(p) { | ||
return this.contains(p) ? p : this.shape(true).project(p); | ||
} | ||
at() { | ||
return this.c; | ||
} | ||
offset() { | ||
return 0; | ||
} | ||
contains(p) { | ||
return this.shape(true).contains(p); | ||
} | ||
transform(m) { | ||
return new Angle(this.a.transform(m), this.b.transform(m), this.c.transform(m)); | ||
} | ||
rotate(a, c) { | ||
if ((0, import_fermat9.nearlyEquals)(a, 0)) | ||
return this; | ||
return new Angle(this.a.rotate(a, c), this.b.rotate(a, c), this.c.rotate(a, c)); | ||
} | ||
reflect(l) { | ||
return new Angle(this.a.reflect(l), this.b.reflect(l), this.c.reflect(l)); | ||
} | ||
scale(sx, sy = sx) { | ||
return new Angle(this.a.scale(sx, sy), this.b.scale(sx, sy), this.c.scale(sx, sy)); | ||
} | ||
shift(x, y = x) { | ||
return new Angle(this.a.shift(x, y), this.b.shift(x, y), this.c.shift(x, y)); | ||
} | ||
translate(p) { | ||
return new Angle(this.a.translate(p), this.b.translate(p), this.c.translate(p)); | ||
} | ||
equals(_a) { | ||
return false; | ||
} | ||
toString() { | ||
return `angle(${this.a},${this.b},${this.c})`; | ||
} | ||
}; | ||
// src/bounds.ts | ||
var import_fermat11 = require("@mathigon/fermat"); | ||
// src/rectangle.ts | ||
var import_fermat10 = require("@mathigon/fermat"); | ||
var Rectangle = class { | ||
@@ -1701,2 +1725,4 @@ constructor(p, w = 1, h = w) { | ||
function drawCanvas(ctx, obj, options = {}) { | ||
if (isAngle(obj)) | ||
return drawCanvas(ctx, obj.shape(!!options.fill), options); | ||
if (options.fill) | ||
@@ -1720,5 +1746,6 @@ ctx.fillStyle = options.fill; | ||
ctx.arc(obj.c.x, obj.c.y, obj.r, 0, TWO_PI); | ||
} else if (isPolygon(obj)) { | ||
ctx.moveTo(obj.points[0].x, obj.points[0].y); | ||
for (const p of obj.points.slice(1)) | ||
} else if (isPolygonLike(obj)) { | ||
const points = obj.points; | ||
ctx.moveTo(points[0].x, points[0].y); | ||
for (const p of points.slice(1)) | ||
ctx.lineTo(p.x, p.y); | ||
@@ -1739,3 +1766,2 @@ ctx.closePath(); | ||
var import_core5 = require("@mathigon/core"); | ||
var import_fermat12 = require("@mathigon/fermat"); | ||
function drawArc(a, b, c) { | ||
@@ -1747,27 +1773,2 @@ const orient = b.x * (c.y - a.y) + a.x * (b.y - c.y) + c.x * (a.y - b.y); | ||
} | ||
function angleSize(angle, options = {}) { | ||
if (angle.isRight && !options.round) | ||
return 20; | ||
return 24 + 20 * (1 - (0, import_fermat12.clamp)(angle.rad, 0, Math.PI) / Math.PI); | ||
} | ||
function drawAngle(angle, options = {}) { | ||
let a = angle.a; | ||
const b = angle.b; | ||
let c = angle.c; | ||
const size = options.size || angleSize(angle, options); | ||
const ba = Point.difference(a, b).unitVector; | ||
const bc = Point.difference(c, b).unitVector; | ||
a = Point.sum(b, ba.scale(size)); | ||
c = Point.sum(b, bc.scale(size)); | ||
let p = options.fill ? `M${b.x},${b.y}L` : "M"; | ||
if (angle.isRight && !options.round) { | ||
const d = Point.sum(a, bc.scale(size)); | ||
p += `${a.x},${a.y}L${d.x},${d.y}L${c.x},${c.y}`; | ||
} else { | ||
p += drawArc(a, b, c); | ||
} | ||
if (options.fill) | ||
p += "Z"; | ||
return p; | ||
} | ||
function drawPath(...points) { | ||
@@ -1825,3 +1826,4 @@ return `M${points.map((p) => `${p.x},${p.y}`).join("L")}`; | ||
if (isAngle(obj)) { | ||
return drawAngle(obj, options); | ||
const shape = obj.shape(!!options.fill, options.size, options.round); | ||
return drawSVG(shape, options); | ||
} | ||
@@ -1880,3 +1882,3 @@ if (isSegment(obj)) { | ||
// src/ellipse.ts | ||
var import_fermat13 = require("@mathigon/fermat"); | ||
var import_fermat12 = require("@mathigon/fermat"); | ||
var Ellipse = class { | ||
@@ -1906,3 +1908,3 @@ constructor(c, a, b) { | ||
const C = (px / this.a) ** 2 + (py / this.b) ** 2 - 1; | ||
const points = (0, import_fermat13.quadratic)(A, B, C); | ||
const points = (0, import_fermat12.quadratic)(A, B, C); | ||
return points.map((t) => line.at(t)); | ||
@@ -1909,0 +1911,0 @@ } |
// src/angle.ts | ||
import { nearlyEquals as nearlyEquals5 } from "@mathigon/fermat"; | ||
import { clamp as clamp4, nearlyEquals as nearlyEquals8 } from "@mathigon/fermat"; | ||
@@ -510,113 +510,9 @@ // src/arc.ts | ||
// src/angle.ts | ||
var RAD_TO_DEG = 180 / Math.PI; | ||
var DEG_TO_RAD = Math.PI / 180; | ||
function toDeg(n) { | ||
return n * RAD_TO_DEG; | ||
} | ||
function toRad(n) { | ||
return n * DEG_TO_RAD; | ||
} | ||
var Angle = class { | ||
constructor(a, b, c) { | ||
this.a = a; | ||
this.b = b; | ||
this.c = c; | ||
this.type = "angle"; | ||
} | ||
static fromDegrees(val) { | ||
return Angle.fromRadians(val * (Math.PI / 180)); | ||
} | ||
static fromRadians(val) { | ||
const p1 = new Point(1, 0); | ||
const p2 = p1.rotate(val); | ||
return new Angle(p1, ORIGIN, p2); | ||
} | ||
get rad() { | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = phiC - phiA; | ||
if (phi < 0) | ||
phi += TWO_PI; | ||
return phi; | ||
} | ||
get deg() { | ||
return this.rad * 180 / Math.PI; | ||
} | ||
get isRight() { | ||
return nearlyEquals5(this.rad, Math.PI / 2, Math.PI / 360); | ||
} | ||
get bisector() { | ||
if (this.b.equals(this.a)) | ||
return void 0; | ||
if (this.b.equals(this.c)) | ||
return void 0; | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = (phiA + phiC) / 2; | ||
if (phiA > phiC) | ||
phi += Math.PI; | ||
const x = Math.cos(phi) + this.b.x; | ||
const y = Math.sin(phi) + this.b.y; | ||
return new Line(this.b, new Point(x, y)); | ||
} | ||
get sup() { | ||
return this.rad < Math.PI ? this : new Angle(this.c, this.b, this.a); | ||
} | ||
get arc() { | ||
return new Arc(this.b, this.a, this.rad); | ||
} | ||
project() { | ||
return this.c; | ||
} | ||
at() { | ||
return this.c; | ||
} | ||
offset() { | ||
return 0; | ||
} | ||
contains() { | ||
return false; | ||
} | ||
transform(m) { | ||
return new Angle(this.a.transform(m), this.b.transform(m), this.c.transform(m)); | ||
} | ||
rotate(a, c) { | ||
if (nearlyEquals5(a, 0)) | ||
return this; | ||
return new Angle(this.a.rotate(a, c), this.b.rotate(a, c), this.c.rotate(a, c)); | ||
} | ||
reflect(l) { | ||
return new Angle(this.a.reflect(l), this.b.reflect(l), this.c.reflect(l)); | ||
} | ||
scale(sx, sy = sx) { | ||
return new Angle(this.a.scale(sx, sy), this.b.scale(sx, sy), this.c.scale(sx, sy)); | ||
} | ||
shift(x, y = x) { | ||
return new Angle(this.a.shift(x, y), this.b.shift(x, y), this.c.shift(x, y)); | ||
} | ||
translate(p) { | ||
return new Angle(this.a.translate(p), this.b.translate(p), this.c.translate(p)); | ||
} | ||
equals(_a) { | ||
return false; | ||
} | ||
toString() { | ||
return `angle(${this.a},${this.b},${this.c})`; | ||
} | ||
}; | ||
// src/bounds.ts | ||
import { isBetween as isBetween4 } from "@mathigon/fermat"; | ||
// src/rectangle.ts | ||
import { isBetween as isBetween3, nearlyEquals as nearlyEquals9 } from "@mathigon/fermat"; | ||
// src/polygon.ts | ||
import { last as last2, tabulate } from "@mathigon/core"; | ||
import { nearlyEquals as nearlyEquals8 } from "@mathigon/fermat"; | ||
import { nearlyEquals as nearlyEquals7 } from "@mathigon/fermat"; | ||
// src/boolean.ts | ||
import { last } from "@mathigon/core"; | ||
import { nearlyEquals as nearlyEquals6 } from "@mathigon/fermat"; | ||
import { nearlyEquals as nearlyEquals5 } from "@mathigon/fermat"; | ||
var PRECISION = 1e-6; | ||
@@ -640,4 +536,4 @@ function pointAboveOrOnLine(pt, left, right) { | ||
function pointsCompare(p1, p2) { | ||
if (nearlyEquals6(p1.x, p2.x)) { | ||
return nearlyEquals6(p1.y, p2.y) ? 0 : p1.y < p2.y ? -1 : 1; | ||
if (nearlyEquals5(p1.x, p2.x)) { | ||
return nearlyEquals5(p1.y, p2.y) ? 0 : p1.y < p2.y ? -1 : 1; | ||
} | ||
@@ -663,3 +559,3 @@ return p1.x < p2.x ? -1 : 1; | ||
const axb = adx * bdy - ady * bdx; | ||
if (nearlyEquals6(axb, 0)) | ||
if (nearlyEquals5(axb, 0)) | ||
return false; | ||
@@ -1065,3 +961,3 @@ const dx = a0.x - b0.x; | ||
import { flatten } from "@mathigon/core"; | ||
import { isBetween as isBetween2, nearlyEquals as nearlyEquals7, square as square2, subsets } from "@mathigon/fermat"; | ||
import { isBetween as isBetween2, nearlyEquals as nearlyEquals6, square as square2, subsets } from "@mathigon/fermat"; | ||
@@ -1114,3 +1010,3 @@ // src/types.ts | ||
function liesOnSegment(s, p) { | ||
if (nearlyEquals7(s.p1.x, s.p2.x)) | ||
if (nearlyEquals6(s.p1.x, s.p2.x)) | ||
return isBetween2(p.y, s.p1.y, s.p2.y); | ||
@@ -1120,3 +1016,3 @@ return isBetween2(p.x, s.p1.x, s.p2.x); | ||
function liesOnRay(r, p) { | ||
if (nearlyEquals7(r.p1.x, r.p2.x)) | ||
if (nearlyEquals6(r.p1.x, r.p2.x)) | ||
return (p.y - r.p1.y) / (r.p2.y - r.p1.y) > 0; | ||
@@ -1134,3 +1030,3 @@ return (p.x - r.p1.x) / (r.p2.x - r.p1.x) > 0; | ||
const d = d1x * d2y - d1y * d2x; | ||
if (nearlyEquals7(d, 0)) | ||
if (nearlyEquals6(d, 0)) | ||
return []; | ||
@@ -1149,5 +1045,5 @@ const q1 = l1.p1.x * l1.p2.y - l1.p1.y * l1.p2.x; | ||
return []; | ||
if (nearlyEquals7(d, 0) && nearlyEquals7(c1.r, c2.r)) | ||
if (nearlyEquals6(d, 0) && nearlyEquals6(c1.r, c2.r)) | ||
return []; | ||
if (nearlyEquals7(d, c1.r + c2.r)) | ||
if (nearlyEquals6(d, c1.r + c2.r)) | ||
return [new Line(c1.c, c2.c).midpoint]; | ||
@@ -1174,3 +1070,3 @@ const a = (square2(c1.r) - square2(c2.r) + square2(d)) / (2 * d); | ||
const ya = -D * dx / dr2; | ||
if (nearlyEquals7(disc, 0)) | ||
if (nearlyEquals6(disc, 0)) | ||
return [c.c.shift(xa, ya)]; | ||
@@ -1211,2 +1107,6 @@ const xb = dx * (dy < 0 ? -1 : 1) * Math.sqrt(disc) / dr2; | ||
let [a, b] = elements; | ||
if (isAngle(a)) | ||
a = a.shape(true); | ||
if (isAngle(b)) | ||
b = b.shape(true); | ||
if (isPolygonLike(b)) | ||
@@ -1391,3 +1291,3 @@ [a, b] = [b, a]; | ||
rotate(a, center = ORIGIN) { | ||
if (nearlyEquals8(a, 0)) | ||
if (nearlyEquals7(a, 0)) | ||
return this; | ||
@@ -1467,2 +1367,4 @@ const points = this.points.map((p) => p.rotate(a, center)); | ||
const radius = Point.distance(center, this.points[0]); | ||
if (isNaN(radius) || radius > Number.MAX_SAFE_INTEGER) | ||
return; | ||
return new Circle(center, radius); | ||
@@ -1479,3 +1381,3 @@ } | ||
const radius = center.distanceFromLine(edges[0]); | ||
return new Circle(center, radius); | ||
return isNaN(radius) ? void 0 : new Circle(center, radius); | ||
} | ||
@@ -1490,3 +1392,126 @@ get orthocenter() { | ||
// src/angle.ts | ||
var RAD_TO_DEG = 180 / Math.PI; | ||
var DEG_TO_RAD = Math.PI / 180; | ||
function toDeg(n) { | ||
return n * RAD_TO_DEG; | ||
} | ||
function toRad(n) { | ||
return n * DEG_TO_RAD; | ||
} | ||
var Angle = class { | ||
constructor(a, b, c) { | ||
this.a = a; | ||
this.b = b; | ||
this.c = c; | ||
this.type = "angle"; | ||
} | ||
static fromDegrees(val) { | ||
return Angle.fromRadians(val * (Math.PI / 180)); | ||
} | ||
static fromRadians(val) { | ||
const p1 = new Point(1, 0); | ||
const p2 = p1.rotate(val); | ||
return new Angle(p1, ORIGIN, p2); | ||
} | ||
get rad() { | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = phiC - phiA; | ||
if (phi < 0) | ||
phi += TWO_PI; | ||
return phi; | ||
} | ||
get deg() { | ||
return this.rad * 180 / Math.PI; | ||
} | ||
get isRight() { | ||
return nearlyEquals8(this.rad, Math.PI / 2, Math.PI / 360); | ||
} | ||
get bisector() { | ||
if (this.b.equals(this.a)) | ||
return void 0; | ||
if (this.b.equals(this.c)) | ||
return void 0; | ||
const phiA = Math.atan2(this.a.y - this.b.y, this.a.x - this.b.x); | ||
const phiC = Math.atan2(this.c.y - this.b.y, this.c.x - this.b.x); | ||
let phi = (phiA + phiC) / 2; | ||
if (phiA > phiC) | ||
phi += Math.PI; | ||
const x = Math.cos(phi) + this.b.x; | ||
const y = Math.sin(phi) + this.b.y; | ||
return new Line(this.b, new Point(x, y)); | ||
} | ||
get sup() { | ||
return this.rad < Math.PI ? this : new Angle(this.c, this.b, this.a); | ||
} | ||
get arc() { | ||
return new Arc(this.b, this.a, this.rad); | ||
} | ||
get radius() { | ||
return 24 + 20 * (1 - clamp4(this.rad, 0, Math.PI) / Math.PI); | ||
} | ||
shape(filled = true, radius, round) { | ||
if (this.a.equals(this.b) || this.c.equals(this.b)) | ||
return new Polygon(ORIGIN); | ||
const angled = this.isRight && !round; | ||
if (!radius) | ||
radius = angled ? 20 : this.radius; | ||
const ba = new Segment(this.b, this.a); | ||
const a = ba.at(radius / ba.length); | ||
if (angled) { | ||
const bc = Point.difference(this.c, this.b).unitVector.scale(radius); | ||
if (filled) | ||
return new Polygon(this.b, a, a.add(bc), this.b.add(bc)); | ||
return new Polyline(a, a.add(bc), this.b.add(bc)); | ||
} | ||
if (filled) | ||
return new Sector(this.b, a, this.rad); | ||
return new Arc(this.b, a, this.rad); | ||
} | ||
project(p) { | ||
return this.contains(p) ? p : this.shape(true).project(p); | ||
} | ||
at() { | ||
return this.c; | ||
} | ||
offset() { | ||
return 0; | ||
} | ||
contains(p) { | ||
return this.shape(true).contains(p); | ||
} | ||
transform(m) { | ||
return new Angle(this.a.transform(m), this.b.transform(m), this.c.transform(m)); | ||
} | ||
rotate(a, c) { | ||
if (nearlyEquals8(a, 0)) | ||
return this; | ||
return new Angle(this.a.rotate(a, c), this.b.rotate(a, c), this.c.rotate(a, c)); | ||
} | ||
reflect(l) { | ||
return new Angle(this.a.reflect(l), this.b.reflect(l), this.c.reflect(l)); | ||
} | ||
scale(sx, sy = sx) { | ||
return new Angle(this.a.scale(sx, sy), this.b.scale(sx, sy), this.c.scale(sx, sy)); | ||
} | ||
shift(x, y = x) { | ||
return new Angle(this.a.shift(x, y), this.b.shift(x, y), this.c.shift(x, y)); | ||
} | ||
translate(p) { | ||
return new Angle(this.a.translate(p), this.b.translate(p), this.c.translate(p)); | ||
} | ||
equals(_a) { | ||
return false; | ||
} | ||
toString() { | ||
return `angle(${this.a},${this.b},${this.c})`; | ||
} | ||
}; | ||
// src/bounds.ts | ||
import { isBetween as isBetween4 } from "@mathigon/fermat"; | ||
// src/rectangle.ts | ||
import { isBetween as isBetween3, nearlyEquals as nearlyEquals9 } from "@mathigon/fermat"; | ||
var Rectangle = class { | ||
@@ -1633,2 +1658,4 @@ constructor(p, w = 1, h = w) { | ||
function drawCanvas(ctx, obj, options = {}) { | ||
if (isAngle(obj)) | ||
return drawCanvas(ctx, obj.shape(!!options.fill), options); | ||
if (options.fill) | ||
@@ -1652,5 +1679,6 @@ ctx.fillStyle = options.fill; | ||
ctx.arc(obj.c.x, obj.c.y, obj.r, 0, TWO_PI); | ||
} else if (isPolygon(obj)) { | ||
ctx.moveTo(obj.points[0].x, obj.points[0].y); | ||
for (const p of obj.points.slice(1)) | ||
} else if (isPolygonLike(obj)) { | ||
const points = obj.points; | ||
ctx.moveTo(points[0].x, points[0].y); | ||
for (const p of points.slice(1)) | ||
ctx.lineTo(p.x, p.y); | ||
@@ -1671,3 +1699,2 @@ ctx.closePath(); | ||
import { isOneOf } from "@mathigon/core"; | ||
import { clamp as clamp4 } from "@mathigon/fermat"; | ||
function drawArc(a, b, c) { | ||
@@ -1679,27 +1706,2 @@ const orient = b.x * (c.y - a.y) + a.x * (b.y - c.y) + c.x * (a.y - b.y); | ||
} | ||
function angleSize(angle, options = {}) { | ||
if (angle.isRight && !options.round) | ||
return 20; | ||
return 24 + 20 * (1 - clamp4(angle.rad, 0, Math.PI) / Math.PI); | ||
} | ||
function drawAngle(angle, options = {}) { | ||
let a = angle.a; | ||
const b = angle.b; | ||
let c = angle.c; | ||
const size = options.size || angleSize(angle, options); | ||
const ba = Point.difference(a, b).unitVector; | ||
const bc = Point.difference(c, b).unitVector; | ||
a = Point.sum(b, ba.scale(size)); | ||
c = Point.sum(b, bc.scale(size)); | ||
let p = options.fill ? `M${b.x},${b.y}L` : "M"; | ||
if (angle.isRight && !options.round) { | ||
const d = Point.sum(a, bc.scale(size)); | ||
p += `${a.x},${a.y}L${d.x},${d.y}L${c.x},${c.y}`; | ||
} else { | ||
p += drawArc(a, b, c); | ||
} | ||
if (options.fill) | ||
p += "Z"; | ||
return p; | ||
} | ||
function drawPath(...points) { | ||
@@ -1757,3 +1759,4 @@ return `M${points.map((p) => `${p.x},${p.y}`).join("L")}`; | ||
if (isAngle(obj)) { | ||
return drawAngle(obj, options); | ||
const shape = obj.shape(!!options.fill, options.size, options.round); | ||
return drawSVG(shape, options); | ||
} | ||
@@ -1904,3 +1907,2 @@ if (isSegment(obj)) { | ||
Triangle, | ||
angleSize, | ||
drawCanvas, | ||
@@ -1907,0 +1909,0 @@ drawSVG, |
@@ -64,5 +64,5 @@ import { Circle } from './circle'; | ||
readonly type = "triangle"; | ||
get circumcircle(): Circle; | ||
get incircle(): Circle; | ||
get circumcircle(): Circle | undefined; | ||
get incircle(): Circle | undefined; | ||
get orthocenter(): Point; | ||
} |
{ | ||
"name": "@mathigon/euclid", | ||
"version": "1.0.7", | ||
"version": "1.0.8", | ||
"license": "MIT", | ||
@@ -36,12 +36,12 @@ "homepage": "https://mathigon.io/euclid", | ||
"@types/tape": "4.13.2", | ||
"@typescript-eslint/eslint-plugin": "5.8.1", | ||
"@typescript-eslint/parser": "5.8.1", | ||
"esbuild": "0.14.10", | ||
"eslint": "8.6.0", | ||
"eslint-plugin-import": "2.25.3", | ||
"tape": "5.4.0", | ||
"@typescript-eslint/eslint-plugin": "5.10.2", | ||
"@typescript-eslint/parser": "5.10.2", | ||
"esbuild": "0.14.16", | ||
"eslint": "8.8.0", | ||
"eslint-plugin-import": "2.25.4", | ||
"tape": "5.5.0", | ||
"ts-node": "10.4.0", | ||
"tslib": "2.3.1", | ||
"typescript": "4.5.4" | ||
"typescript": "4.5.5" | ||
} | ||
} |
@@ -7,6 +7,7 @@ // ============================================================================= | ||
import {nearlyEquals} from '@mathigon/fermat'; | ||
import {Arc} from './arc'; | ||
import {Line} from './line'; | ||
import {clamp, nearlyEquals} from '@mathigon/fermat'; | ||
import {Arc, Sector} from './arc'; | ||
import {Line, Segment} from './line'; | ||
import {ORIGIN, Point} from './point'; | ||
import {Polygon, Polyline} from './polygon'; | ||
import {GeoShape, SimplePoint, TransformMatrix, TWO_PI} from './utilities'; | ||
@@ -94,6 +95,34 @@ | ||
// --------------------------------------------------------------------------- | ||
/** Radius of the arc or sector representing this angle. */ | ||
get radius() { | ||
return 24 + 20 * (1 - clamp(this.rad, 0, Math.PI) / Math.PI); | ||
} | ||
/** Shape object that can be used to draw this angle. */ | ||
shape(filled = true, radius?: number, round?: boolean) { | ||
if (this.a.equals(this.b) || this.c.equals(this.b)) return new Polygon(ORIGIN); | ||
const angled = this.isRight && !round; | ||
if (!radius) radius = angled ? 20 : this.radius; | ||
const ba = new Segment(this.b, this.a); | ||
const a = ba.at(radius / ba.length); | ||
if (angled) { | ||
const bc = Point.difference(this.c, this.b).unitVector.scale(radius); | ||
if (filled) return new Polygon(this.b, a, a.add(bc), this.b.add(bc)); | ||
return new Polyline(a, a.add(bc), this.b.add(bc)); | ||
} | ||
if (filled) return new Sector(this.b, a, this.rad); | ||
return new Arc(this.b, a, this.rad); | ||
} | ||
// --------------------------------------------------------------------------- | ||
// These functions are just included for compatibility with GeoPath | ||
project() { | ||
return this.c; | ||
project(p: Point) { | ||
return this.contains(p) ? p : this.shape(true).project(p); | ||
} | ||
@@ -109,4 +138,4 @@ | ||
contains() { | ||
return false; | ||
contains(p: Point) { | ||
return this.shape(true).contains(p); | ||
} | ||
@@ -113,0 +142,0 @@ |
@@ -7,4 +7,3 @@ // ============================================================================= | ||
// / <reference lib="dom" /> | ||
import {isCircle, isPolygon, isPolyline, isSegment} from './types'; | ||
import {isAngle, isCircle, isPolygonLike, isPolyline, isSegment} from './types'; | ||
import {GeoElement, TWO_PI} from './utilities'; | ||
@@ -26,4 +25,5 @@ | ||
export function drawCanvas(ctx: CanvasRenderingContext2D, obj: GeoElement, | ||
options: CanvasDrawingOptions = {}) { | ||
export function drawCanvas(ctx: CanvasRenderingContext2D, obj: GeoElement, options: CanvasDrawingOptions = {}): void { | ||
if (isAngle(obj)) return drawCanvas(ctx, obj.shape(!!options.fill), options); | ||
if (options.fill) ctx.fillStyle = options.fill; | ||
@@ -48,5 +48,6 @@ if (options.opacity) ctx.globalAlpha = options.opacity; | ||
} else if (isPolygon(obj)) { | ||
ctx.moveTo(obj.points[0].x, obj.points[0].y); | ||
for (const p of obj.points.slice(1)) ctx.lineTo(p.x, p.y); | ||
} else if (isPolygonLike(obj)) { | ||
const points = obj.points; | ||
ctx.moveTo(points[0].x, points[0].y); | ||
for (const p of points.slice(1)) ctx.lineTo(p.x, p.y); | ||
ctx.closePath(); | ||
@@ -59,3 +60,3 @@ | ||
// TODO Support for Line, Ray, Arc, Sector, Angle and Rectangle objects | ||
// TODO Support for Line, Ray, Arc and Sector objects | ||
@@ -62,0 +63,0 @@ if (options.fill) ctx.fill(); |
@@ -8,4 +8,2 @@ // ============================================================================= | ||
import {isOneOf} from '@mathigon/core'; | ||
import {clamp} from '@mathigon/fermat'; | ||
import {Angle} from './angle'; | ||
import {Arc} from './arc'; | ||
@@ -44,33 +42,2 @@ import {intersections} from './intersection'; | ||
export function angleSize(angle: Angle, options: SVGDrawingOptions = {}) { | ||
if (angle.isRight && !options.round) return 20; | ||
return 24 + 20 * (1 - clamp(angle.rad, 0, Math.PI) / Math.PI); | ||
} | ||
function drawAngle(angle: Angle, options: SVGDrawingOptions = {}) { | ||
let a = angle.a; | ||
const b = angle.b; | ||
let c = angle.c; | ||
const size = options.size || angleSize(angle, options); | ||
const ba = Point.difference(a, b).unitVector; | ||
const bc = Point.difference(c, b).unitVector; | ||
a = Point.sum(b, ba.scale(size)); | ||
c = Point.sum(b, bc.scale(size)); | ||
let p = options.fill ? `M${b.x},${b.y}L` : 'M'; | ||
if (angle.isRight && !options.round) { | ||
const d = Point.sum(a, bc.scale(size)); | ||
p += `${a.x},${a.y}L${d.x},${d.y}L${c.x},${c.y}`; | ||
} else { | ||
p += drawArc(a, b, c); | ||
} | ||
if (options.fill) p += 'Z'; | ||
return p; | ||
} | ||
function drawPath(...points: Point[]) { | ||
@@ -146,6 +113,7 @@ return `M${points.map(p => `${p.x},${p.y}`).join('L')}`; | ||
export function drawSVG(obj: GeoElement, options: SVGDrawingOptions = {}) { | ||
export function drawSVG(obj: GeoElement, options: SVGDrawingOptions = {}): string { | ||
if (isAngle(obj)) { | ||
return drawAngle(obj, options); | ||
const shape = obj.shape(!!options.fill, options.size, options.round); | ||
return drawSVG(shape, options); | ||
} | ||
@@ -152,0 +120,0 @@ |
@@ -13,3 +13,3 @@ // ============================================================================= | ||
import {Point} from './point'; | ||
import {isArc, isCircle, isLineLike, isPolygonLike, isRay, isSegment} from './types'; | ||
import {isAngle, isArc, isCircle, isLineLike, isPolygonLike, isRay, isSegment} from './types'; | ||
import {GeoShape} from './utilities'; | ||
@@ -143,2 +143,4 @@ | ||
if (isAngle(a)) a = a.shape(true); | ||
if (isAngle(b)) b = b.shape(true); | ||
if (isPolygonLike(b)) [a, b] = [b, a]; | ||
@@ -145,0 +147,0 @@ |
@@ -334,2 +334,3 @@ // ============================================================================= | ||
if (isNaN(radius) || radius > Number.MAX_SAFE_INTEGER) return; | ||
return new Circle(center, radius); | ||
@@ -350,3 +351,3 @@ } | ||
return new Circle(center, radius); | ||
return isNaN(radius) ? undefined : new Circle(center, radius); | ||
} | ||
@@ -353,0 +354,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
485826
6738