Browse Source

improved graph optimization

master
Youen Toupin 3 years ago
parent
commit
059dbee24d
  1. 2
      simulator/src/simulator-ui.ts
  2. 153
      simulator/src/svg-drawing.ts

2
simulator/src/simulator-ui.ts

@ -74,7 +74,7 @@ document.addEventListener('DOMContentLoaded', function() {
let resultsContainer = container.querySelector('.simulation-results');
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);
resultsContainer.classList.toggle('is-hidden', false);

153
simulator/src/svg-drawing.ts

@ -6,11 +6,59 @@ namespace SvgDrawing {
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 {
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; }
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 {
@ -25,52 +73,97 @@ namespace SvgDrawing {
graph(y: number[]): SVGPathElement;
graph(x: number[], y: number[]): SVGPathElement;
graph(arg1: number[], arg2?: number[]) {
let x: number[] | null = arg1;
let y: number[] = arg2;
if(!y) {
y = arg1;
x = null;
let read = (idx: number, out_point: Point) => {
out_point.x = arg1[idx];
out_point.y = arg2[idx];
return true;
};
if(!arg2) {
read = (idx: number, out_point: Point) => {
out_point.x = idx;
out_point.y = arg1[idx];
return true;
};
}
let num = y.length;
console.assert(!x || num == x.length);
let num = arg1.length;
console.assert(!arg2 || num == arg2.length);
if(num <= 1) return null;
let xStep = 6;
let optimizeCurveDist = 5;
let optimizeCurveAngle = 45.0;
if(optimizeCurveDist > 0 || optimizeCurveAngle < 0.0) {
let rawRead = read;
let coordinates = 'M'+Math.round(this.viewport.xLogicalToView(x? x[0] : 0))+','+Math.round(this.viewport.yLogicalToView(y[0]));
coordinates += ' L';
let lineStartX = x ? x[0] : 0;
let prevX = lineStartX;
let prevY = y[0];
let yDir = y[1] > y[0] ? 1 : -1;
let dp = Math.cos(optimizeCurveAngle/180*Math.PI);
let lastDrawnPoint: Point = { x: 0, y: 0 };
read(0, lastDrawnPoint);
let nextPoint: Point = { x: 0, y: 0 };
let dir: Point = { x: 0, y: 0 };
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;
}
}
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) {
let isLast = (idx == num - 1);
let newX = x ? x[idx] : idx;
let newY = y[idx];
let dir = isLast ? 0 : (y[idx+1] > newY ? 1 : -1);
if(newX >= lineStartX + xStep || dir != yDir || isLast) {
coordinates += Math.round(this.viewport.xLogicalToView(newX))+','+Math.round(this.viewport.yLogicalToView(newY));
if(!isLast) coordinates += ' ';
lineStartX = newX;
yDir = isLast ? 0 : (y[idx+1] > newY ? 1 : -1);
++count;
if(read(idx, logicalPoint)) {
this.viewport.logicalToView(logicalPoint, viewPoint);
coordinates += Math.round(viewPoint.x)+','+Math.round(viewPoint.y)+' ';
count += 1;
}
prevY = newY;
}
console.log(count);
let path = document.createElementNS('http://www.w3.org/2000/svg','path');
path.setAttribute('class','graph');
path.setAttribute('d', coordinates);
this.htmlElement.append(path);
let endTime = performance.now();
console.log("graph: " + count + " points, " + (endTime - startTime) + "ms");
return path;
}
}

Loading…
Cancel
Save