UNPKG

@eclipse-scout/chart

Version:
231 lines (202 loc) 6.49 kB
/* * Copyright (c) 2010, 2023 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 * which is available at https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 */ // place venn 3 by simulation // find angle and distance (to a) and radius where "error" is minimal import {VennCircle, VennCircleHelper} from '../index'; export class VennAsync3Calculator { helper: VennCircleHelper; venn1: VennCircle; venn2: VennCircle; venn3: VennCircle; u: number; v: number; w: number; uv: number; uw: number; vw: number; uvw: number; maxD: number; dStep: number; rStep: number; alphaStep: number; alphaBest: number; dBest: number; rBest: number; errorBest: number; callback: () => void; cancelled: boolean; constructor(helper: VennCircleHelper, venn1: VennCircle, venn2: VennCircle, venn3: VennCircle, u: number, v: number, w: number, uv: number, uw: number, vw: number, uvw: number, d12: number, d13: number, d23: number) { // if circles are empty, they are drawn as small circle, so: adjust u v w to find better errors if (u === 0 && uv === 0 && uw === 0 && uvw === 0) { u = 1; } if (v === 0 && uv === 0 && vw === 0 && uvw === 0) { v = 1; } if (w === 0 && uw === 0 && vw === 0 && uvw === 0) { w = 1; } this.helper = helper; this.venn1 = venn1; this.venn2 = venn2; this.venn3 = venn3; this.u = u; this.v = v; this.w = w; this.uv = uv; this.uw = uw; this.vw = vw; this.uvw = uvw; /** step and ranges for loops */ this.maxD = this.venn1.r + 2 * this.venn2.r + 2 * this.venn1.r + this.helper.distR; this.dStep = this.maxD / 30; this.rStep = venn3.r / 4; this.alphaStep = Math.PI / 30; /** best vars (initialize with 0 so the optimizer knows that they are numbers) */ this.alphaBest = 0; this.dBest = 0; this.rBest = 0; this.errorBest = 0; this.callback = null; this.cancelled = false; } start(callback: () => void) { this.callback = callback; setTimeout(this._next.bind(this, 0)); } cancel() { this.cancelled = true; } protected _end() { // set x and y and r of venn3 this.venn3.x = this.venn1.x + this.dBest * Math.cos(this.alphaBest); this.venn3.y = this.venn1.y - this.dBest * Math.sin(this.alphaBest); this.venn3.r = this.rBest; this.callback(); } protected _next(alpha: number) { if (!this.cancelled) { // iterate this._iteration(alpha); } if (this.cancelled) { return; // stop loop if interrupted } alpha += this.alphaStep; if (alpha < Math.PI) { // schedule next loop iteration setTimeout(this._next.bind(this, alpha)); } else { // end loop this._end(); } } protected _iteration(alpha: number) { // optimize speed: no var lookup (should help the optimizer in general, and IE in particular) let maxD = this.maxD, dStep = this.dStep, minR = this.helper.minR, rStep = this.rStep, total = this.helper.total, x1 = this.venn1.x, y1 = this.venn1.y, r1 = this.venn1.r, x2 = this.venn2.x, y2 = this.venn2.y, r2 = this.venn2.r, r3 = this.venn3.r, u = this.u, v = this.v, w = this.w, uv = this.uv, uw = this.uw, vw = this.vw, uvw = this.uvw, alphaBest = this.alphaBest, dBest = this.dBest, rBest = this.rBest, errorBest = this.errorBest; for (let d = 0; d < maxD; d += dStep) { // calc x, y let x = x1 + d * Math.cos(alpha); let y = y1 - d * Math.sin(alpha); for (let r = Math.max(minR, r3 * 0.75); r <= r3 * 1.25; r += rStep) { // find areas with monte carlo, do not laugh! i tried even this: // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.507.1195&rep=rep1&type=pdf let minX = Math.min(x1 - r1, x2 - r2, x - r); let maxX = Math.max(x1 + r1, x2 + r2, x + r); let minY = Math.min(y1 - r1, y2 - r2, y - r); let maxY = Math.max(y1 + r1, y2 + r2, y + r); let stepX = (maxX - minX) / 100; let stepY = (maxY - minY) / 100; // areas of venn let a1 = 0, a2 = 0, a3 = 0, a12 = 0, a13 = 0, a23 = 0, a123 = 0; for (let testX = minX; testX < maxX; testX += stepX) { for (let testY = minY; testY < maxY; testY += stepY) { // optimize speed for ie: no function call let t1 = ((testX - x1) * (testX - x1) + (testY - y1) * (testY - y1)) < (r1 * r1); let t2 = ((testX - x2) * (testX - x2) + (testY - y2) * (testY - y2)) < (r2 * r2); let t3 = ((testX - x) * (testX - x) + (testY - y) * (testY - y)) < (r * r); // check if inside if (t1 && t2 && t3) { a123++; } else if (t1 && t2 && !t3) { a12++; } else if (t1 && !t2 && !t3) { a1++; } else if (!t1 && t2 && !t3) { a2++; } else if (t1 && !t2 && t3) { a13++; } else if (!t1 && t2 && t3) { a23++; } else if (!t1 && !t2 && t3) { a3++; } } } let aTotal = a1 + a2 + a3 + a12 + a13 + a23 + a123; // calc error let error = d / maxD; error += this._error(uvw, total, a123, aTotal); error += this._error(uv, total, a12, aTotal); error += this._error(uw, total, a13, aTotal); error += this._error(vw, total, a23, aTotal); error += this._error(u, total, a1, aTotal); error += this._error(v, total, a2, aTotal); error += this._error(w, total, a3, aTotal); // better than before? if (alpha === 0 || error < errorBest) { alphaBest = alpha; dBest = d; rBest = r; errorBest = error; } } } this.alphaBest = alphaBest; this.dBest = dBest; this.rBest = rBest; this.errorBest = errorBest; } protected _error(u: number, total: number, a: number, aTotal: number): number { // be brutal if basic error if ((u === 0 && a !== 0) || (u !== 0 && a === 0)) { return 1000; } return Math.abs(u / total - a / aTotal) * 100; } }