improved graph optimization
This commit is contained in:
parent
a4fbc4c36e
commit
059dbee24d
@ -74,7 +74,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
let resultsContainer = container.querySelector('.simulation-results');
|
let resultsContainer = container.querySelector('.simulation-results');
|
||||||
|
|
||||||
let batteryChargeGraph = new SvgDrawing.SvgElement(resultsContainer.querySelector('.battery-charge-graph svg'));
|
let batteryChargeGraph = new SvgDrawing.SvgElement(resultsContainer.querySelector('.battery-charge-graph svg'));
|
||||||
batteryChargeGraph.viewport.logical = { x: 0, y: 0, width: 365*24, height: parameters.batteryCapacity }
|
batteryChargeGraph.viewport.setLogical({ x: 0, y: 0, width: 365*24, height: parameters.batteryCapacity });
|
||||||
batteryChargeGraph.graph(simulationResult.batteryLevel);
|
batteryChargeGraph.graph(simulationResult.batteryLevel);
|
||||||
|
|
||||||
resultsContainer.classList.toggle('is-hidden', false);
|
resultsContainer.classList.toggle('is-hidden', false);
|
||||||
|
@ -6,11 +6,59 @@ namespace SvgDrawing {
|
|||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Point {
|
||||||
|
public static add(a: Point, b: Point, out_result: Point) {
|
||||||
|
out_result.x = a.x + b.x;
|
||||||
|
out_result.y = a.y + b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static subtract(a: Point, b: Point, out_result: Point) {
|
||||||
|
out_result.x = a.x - b.x;
|
||||||
|
out_result.y = a.y - b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static normalize(v: Point) {
|
||||||
|
let invN = 1.0 / Math.sqrt(v.x*v.x + v.y*v.y);
|
||||||
|
v.x *= invN;
|
||||||
|
v.y *= invN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static dot(a: Point, b: Point) {
|
||||||
|
return a.x*b.x + a.y*b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static normalizedDot(a: Point, b: Point) {
|
||||||
|
let NA = Math.sqrt(a.x*a.x + a.y*a.y);
|
||||||
|
let NB = Math.sqrt(b.x*b.x + b.y*b.y);
|
||||||
|
return (a.x*b.x + a.y*b.y) / NA / NB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Viewport {
|
export class Viewport {
|
||||||
constructor(public logical: Rect, public view: Rect) {}
|
private invLogicalW: number = 0;
|
||||||
|
private invLogicalH: number = 0;
|
||||||
|
|
||||||
|
constructor(private logical: Rect, private view: Rect) { this.update(); }
|
||||||
|
|
||||||
|
setLogical(r: Rect) { this.logical = r; this.update(); }
|
||||||
|
|
||||||
xLogicalToView(x: number) { return (x - this.logical.x) / this.logical.width * this.view.width + this.view.x; }
|
xLogicalToView(x: number) { return (x - this.logical.x) / this.logical.width * this.view.width + this.view.x; }
|
||||||
yLogicalToView(y: number) { return (y - this.logical.y) / this.logical.height * this.view.height + this.view.y; }
|
yLogicalToView(y: number) { return (y - this.logical.y) / this.logical.height * this.view.height + this.view.y; }
|
||||||
|
|
||||||
|
logicalToView(p: Point, out_point: Point) {
|
||||||
|
out_point.x = (p.x - this.logical.x) * this.invLogicalW * this.view.width + this.view.x;
|
||||||
|
out_point.y = (p.y - this.logical.y) * this.invLogicalH * this.view.height + this.view.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private update() {
|
||||||
|
this.invLogicalW = 1.0 / this.logical.width;
|
||||||
|
this.invLogicalH = 1.0 / this.logical.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SvgElement {
|
export class SvgElement {
|
||||||
@ -25,52 +73,97 @@ namespace SvgDrawing {
|
|||||||
graph(y: number[]): SVGPathElement;
|
graph(y: number[]): SVGPathElement;
|
||||||
graph(x: number[], y: number[]): SVGPathElement;
|
graph(x: number[], y: number[]): SVGPathElement;
|
||||||
graph(arg1: number[], arg2?: number[]) {
|
graph(arg1: number[], arg2?: number[]) {
|
||||||
let x: number[] | null = arg1;
|
let read = (idx: number, out_point: Point) => {
|
||||||
let y: number[] = arg2;
|
out_point.x = arg1[idx];
|
||||||
if(!y) {
|
out_point.y = arg2[idx];
|
||||||
y = arg1;
|
return true;
|
||||||
x = null;
|
};
|
||||||
|
|
||||||
|
if(!arg2) {
|
||||||
|
read = (idx: number, out_point: Point) => {
|
||||||
|
out_point.x = idx;
|
||||||
|
out_point.y = arg1[idx];
|
||||||
|
return true;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let num = y.length;
|
let num = arg1.length;
|
||||||
console.assert(!x || num == x.length);
|
console.assert(!arg2 || num == arg2.length);
|
||||||
|
|
||||||
if(num <= 1) return null;
|
if(num <= 1) return null;
|
||||||
|
|
||||||
let xStep = 6;
|
let optimizeCurveDist = 5;
|
||||||
|
let optimizeCurveAngle = 45.0;
|
||||||
let coordinates = 'M'+Math.round(this.viewport.xLogicalToView(x? x[0] : 0))+','+Math.round(this.viewport.yLogicalToView(y[0]));
|
if(optimizeCurveDist > 0 || optimizeCurveAngle < 0.0) {
|
||||||
coordinates += ' L';
|
let rawRead = read;
|
||||||
let lineStartX = x ? x[0] : 0;
|
|
||||||
let prevX = lineStartX;
|
|
||||||
let prevY = y[0];
|
|
||||||
let yDir = y[1] > y[0] ? 1 : -1;
|
|
||||||
|
|
||||||
let count = 0;
|
|
||||||
for(let idx = 0; idx < num; ++idx) {
|
|
||||||
let isLast = (idx == num - 1);
|
|
||||||
|
|
||||||
let newX = x ? x[idx] : idx;
|
let dp = Math.cos(optimizeCurveAngle/180*Math.PI);
|
||||||
let newY = y[idx];
|
|
||||||
let dir = isLast ? 0 : (y[idx+1] > newY ? 1 : -1);
|
|
||||||
|
|
||||||
if(newX >= lineStartX + xStep || dir != yDir || isLast) {
|
let lastDrawnPoint: Point = { x: 0, y: 0 };
|
||||||
coordinates += Math.round(this.viewport.xLogicalToView(newX))+','+Math.round(this.viewport.yLogicalToView(newY));
|
read(0, lastDrawnPoint);
|
||||||
if(!isLast) coordinates += ' ';
|
|
||||||
lineStartX = newX;
|
let nextPoint: Point = { x: 0, y: 0 };
|
||||||
yDir = isLast ? 0 : (y[idx+1] > newY ? 1 : -1);
|
let dir: Point = { x: 0, y: 0 };
|
||||||
++count;
|
let nextDir: Point = { x: 0, y: 0 };
|
||||||
|
let nextSegDir: Point = { x: 0, y: 0 };
|
||||||
|
let perp: Point = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
read = (idx: number, out_point: Point) => {
|
||||||
|
rawRead(idx, out_point);
|
||||||
|
if(idx == 0 || idx == num - 1) return true;
|
||||||
|
|
||||||
|
rawRead(idx + 1, nextPoint);
|
||||||
|
Point.subtract(out_point, lastDrawnPoint, dir);
|
||||||
|
Point.subtract(nextPoint, out_point, nextSegDir);
|
||||||
|
|
||||||
|
if(Point.normalizedDot(dir, nextSegDir) < dp) {
|
||||||
|
lastDrawnPoint = { ...out_point };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point.subtract(nextPoint, lastDrawnPoint, nextDir);
|
||||||
|
|
||||||
|
perp.x = -nextDir.y;
|
||||||
|
perp.y = nextDir.x;
|
||||||
|
Point.normalize(perp);
|
||||||
|
|
||||||
|
let d = Math.abs(Point.dot(perp, dir));
|
||||||
|
if(d > optimizeCurveDist) {
|
||||||
|
lastDrawnPoint = { ...out_point };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
prevY = newY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(count);
|
let startTime = performance.now();
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
let logicalPoint: Point = { x: 0, y: 0 };
|
||||||
|
let viewPoint: Point = { x: 0, y: 0 };
|
||||||
|
read(0, logicalPoint);
|
||||||
|
this.viewport.logicalToView(logicalPoint, viewPoint);
|
||||||
|
|
||||||
|
let coordinates = 'M'+Math.round(viewPoint.x)+','+Math.round(viewPoint.y);
|
||||||
|
coordinates += ' L';
|
||||||
|
|
||||||
|
for(let idx = 0; idx < num; ++idx) {
|
||||||
|
if(read(idx, logicalPoint)) {
|
||||||
|
this.viewport.logicalToView(logicalPoint, viewPoint);
|
||||||
|
coordinates += Math.round(viewPoint.x)+','+Math.round(viewPoint.y)+' ';
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let path = document.createElementNS('http://www.w3.org/2000/svg','path');
|
let path = document.createElementNS('http://www.w3.org/2000/svg','path');
|
||||||
path.setAttribute('class','graph');
|
path.setAttribute('class','graph');
|
||||||
path.setAttribute('d', coordinates);
|
path.setAttribute('d', coordinates);
|
||||||
this.htmlElement.append(path);
|
this.htmlElement.append(path);
|
||||||
|
|
||||||
|
let endTime = performance.now();
|
||||||
|
console.log("graph: " + count + " points, " + (endTime - startTime) + "ms");
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user