UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

145 lines (129 loc) 4.79 kB
/** * Algorithm: * Input Circles * -> filter out fully contained circles * -> build outer containing circles (given by detect radius) * -> find radical lines * * This implementation currently does not detect any holes. * * @author Ikaros Kappler * @version 1.0.0 * @date 2024-03-01 */ import { Circle } from "../../Circle"; import { Vertex } from "../../Vertex"; import { CircleIntersections } from "./CircleIntersections"; type Pair<T> = [T, T]; interface InverseCirclePair { baseCircleIndexA: number; baseCircleIndexB: number; baseCircleA: Circle; baseCircleB: Circle; circleA: Circle; circleB: Circle; inverseCircleA: Circle; inverseCircleB: Circle; doIntersect: boolean; circlePointsA: Pair<Vertex>; circlePointsB: Pair<Vertex>; } interface MetaballsOptions { metaRadiusAddon: number; } export class Metaballs { private circleIsFullyContained: Array<boolean> = []; // Array<boolean> private inputCircles: Array<Circle> = []; private circlesOfInterest: Array<Circle> = []; private containingCircles: Array<Circle> = []; // Array< { baseCircleIndexA, baseCircleIndexB, // baseCircleA, baseCircleB, // circleA, circleB, // inverseCircleA, inverseCircleB, // doIntersect, circlePointsA:[], circlePointsB:[] } > private inverseCirclesPairs: Array<InverseCirclePair> = []; constructor(inputCircles: Array<Circle>) { this.inputCircles = inputCircles; } public rebuild(options: MetaballsOptions) { this.detectContainmentStatus(); this.rebuildContainingCircles(options); this.inverseCirclesPairs = []; const radicalLineMatrix = CircleIntersections.buildRadicalLineMatrix(this.containingCircles); for (var i = 0; i < this.containingCircles.length; i++) { var circleA = this.containingCircles[i]; var centerCircleA = this.circlesOfInterest[i]; for (var j = i + 1; j < this.containingCircles.length; j++) { var radicalLine = radicalLineMatrix[i][j]; if (radicalLine == null) { // The two circles do not have an intersection. // console.log("Circles", i, j, "do not have any intersections"); continue; } var circleB = this.containingCircles[j]; var centerCircleB = this.circlesOfInterest[j]; // But if they have -> compute. outer circle(s). // They are symmetrical. var inverseCircle1 = new Circle(radicalLine.a, options.metaRadiusAddon); var inverseCircle2 = new Circle(radicalLine.b, options.metaRadiusAddon); var doIntersect = inverseCircle1.circleIntersection(inverseCircle2) != null; // console.log("doIntersect", doIntersect); // Now find the intersection points between inner and outer circles. // We will need them later. var circlePointsA: Pair<Vertex> = [ centerCircleA.closestPoint(inverseCircle1.center), centerCircleA.closestPoint(inverseCircle2.center) ]; var circlePointsB: Pair<Vertex> = [ centerCircleB.closestPoint(inverseCircle1.center), centerCircleB.closestPoint(inverseCircle2.center) ]; this.inverseCirclesPairs.push({ baseCircleIndexA: i, baseCircleIndexB: j, baseCircleA: this.circlesOfInterest[i], baseCircleB: this.circlesOfInterest[j], circleA: circleA, inverseCircleA: inverseCircle1, circleB: circleB, inverseCircleB: inverseCircle2, doIntersect: doIntersect, circlePointsA: circlePointsA, circlePointsB: circlePointsB }); } // END for j } // END for i } // public getInnerPath; private checkCircleFullyContained(circleIndex: number): boolean { for (var i = 0; i < this.inputCircles.length; i++) { if (circleIndex == i) { continue; } if (this.inputCircles[i].containsCircle(this.inputCircles[circleIndex])) { return true; } } return false; } private detectContainmentStatus() { this.circleIsFullyContained = []; this.circlesOfInterest = []; for (var i = 0; i < this.inputCircles.length; i++) { this.circleIsFullyContained[i] = this.checkCircleFullyContained(i); if (!this.circleIsFullyContained[i]) { this.circlesOfInterest.push(this.inputCircles[i]); } } } private rebuildContainingCircles(options: MetaballsOptions) { this.containingCircles = []; for (var i = 0; i < this.circlesOfInterest.length; i++) { var containingCircle = new Circle( this.circlesOfInterest[i].center, this.circlesOfInterest[i].radius + options.metaRadiusAddon ); this.containingCircles.push(containingCircle); } } }