|
|
|
@ -40,46 +40,101 @@ namespace SvgDrawing {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export class Viewport { |
|
|
|
|
private invLogicalW: number = 0; |
|
|
|
|
private invLogicalH: number = 0; |
|
|
|
|
private invDataW: number = 0; |
|
|
|
|
private invDataH: number = 0; |
|
|
|
|
|
|
|
|
|
constructor(private logical: Rect, private view: Rect) { this.update(); } |
|
|
|
|
constructor(private data: Rect, private view: Rect) { this.update(); } |
|
|
|
|
|
|
|
|
|
setLogical(r: Rect) { this.logical = r; this.update(); } |
|
|
|
|
setData(r: Rect) { this.data = r; this.update(); } |
|
|
|
|
setView(r: Rect) { this.view = 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; } |
|
|
|
|
xDataToView(x: number) { return (x - this.data.x) / this.data.width * this.view.width + this.view.x; } |
|
|
|
|
yDataToView(y: number) { return (y - this.data.y) / this.data.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; |
|
|
|
|
dataToView(p: Point, out_point: Point) { |
|
|
|
|
out_point.x = (p.x - this.data.x) * this.invDataW * this.view.width + this.view.x; |
|
|
|
|
out_point.y = (p.y - this.data.y) * this.invDataH * this.view.height + this.view.y; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private update() { |
|
|
|
|
this.invLogicalW = 1.0 / this.logical.width; |
|
|
|
|
this.invLogicalH = 1.0 / this.logical.height; |
|
|
|
|
this.invDataW = 1.0 / this.data.width; |
|
|
|
|
this.invDataH = 1.0 / this.data.height; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export interface LineStyle { |
|
|
|
|
className: string; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export class SvgElement { |
|
|
|
|
public viewport: Viewport; |
|
|
|
|
public width: number; |
|
|
|
|
public height: number; |
|
|
|
|
|
|
|
|
|
constructor(private htmlElement: HTMLElement) { |
|
|
|
|
let viewBox = htmlElement.getAttribute('viewBox').split(' '); |
|
|
|
|
let r: Rect = { x: Number(viewBox[0]), y: Number(viewBox[1]), width: Number(viewBox[2]), height: Number(viewBox[3]) }; |
|
|
|
|
this.viewport = new Viewport(r, { x: r.x, y: r.y + r.height, width: r.width, height: -r.height }); |
|
|
|
|
this.width = r.width; |
|
|
|
|
this.height = r.height; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
clear() { |
|
|
|
|
this.htmlElement.querySelectorAll('*').forEach(elt => elt.remove()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
line(start: Point, end: Point): SVGLineElement { |
|
|
|
|
let line = document.createElementNS('http://www.w3.org/2000/svg','line'); |
|
|
|
|
line.setAttribute('x1', this.viewport.xDataToView(start.x).toString()); |
|
|
|
|
line.setAttribute('y1', this.viewport.yDataToView(start.y).toString()); |
|
|
|
|
line.setAttribute('x2', this.viewport.xDataToView(end.x).toString()); |
|
|
|
|
line.setAttribute('y2', this.viewport.yDataToView(end.y).toString()); |
|
|
|
|
this.htmlElement.append(line); |
|
|
|
|
return line; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
text(position: Point, value: string, anchor?: string, verticalAlign?: number): SVGTextElement { |
|
|
|
|
let text = document.createElementNS('http://www.w3.org/2000/svg','text'); |
|
|
|
|
text.setAttribute('x', this.viewport.xDataToView(position.x).toString()); |
|
|
|
|
text.setAttribute('y', this.viewport.yDataToView(position.y).toString()); |
|
|
|
|
if(anchor !== undefined) text.setAttribute('text-anchor', anchor); |
|
|
|
|
if(verticalAlign !== undefined) text.setAttribute('dy', (1.0-verticalAlign) + 'em'); |
|
|
|
|
text.appendChild(document.createTextNode(value)); |
|
|
|
|
this.htmlElement.append(text); |
|
|
|
|
return text; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
graph(y: number[]): SVGPathElement; |
|
|
|
|
graph(x: number[], y: number[]): SVGPathElement; |
|
|
|
|
graph(arg1: number[], arg2?: number[]) { |
|
|
|
|
graph(y: number[]): SVGPathElement[]; |
|
|
|
|
graph(x: number[], y: number[]): SVGPathElement[]; |
|
|
|
|
graph(y: number[], indices: number[], styles: LineStyle[]): SVGPathElement[]; |
|
|
|
|
graph(x: number[], y: number[], indices: number[], styles: LineStyle[]): SVGPathElement[]; |
|
|
|
|
graph(arg1: number[], arg2?: number[], arg3?: number[] | LineStyle[], arg4?: LineStyle[]): SVGPathElement[] { |
|
|
|
|
let indices: number[] | null = null; |
|
|
|
|
let styles: LineStyle[] | null = null; |
|
|
|
|
if(arg3) { |
|
|
|
|
if(arg4) { |
|
|
|
|
indices = <number[]>arg3; |
|
|
|
|
styles = arg4; |
|
|
|
|
} |
|
|
|
|
else { |
|
|
|
|
indices = arg2; |
|
|
|
|
styles = <LineStyle[]>arg3; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(styles == null) { |
|
|
|
|
styles = [{className: ''}]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let read = (idx: number, out_point: Point) => { |
|
|
|
|
out_point.x = arg1[idx]; |
|
|
|
|
out_point.y = arg2[idx]; |
|
|
|
|
return true; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if(!arg2) { |
|
|
|
|
let reset = (idx: number) => {}; |
|
|
|
|
|
|
|
|
|
if(!arg2 || (arg3 && typeof(arg3[0]) !== 'number')) { |
|
|
|
|
read = (idx: number, out_point: Point) => { |
|
|
|
|
out_point.x = idx; |
|
|
|
|
out_point.y = arg1[idx]; |
|
|
|
@ -100,7 +155,6 @@ namespace SvgDrawing {
|
|
|
|
|
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 }; |
|
|
|
@ -108,6 +162,12 @@ namespace SvgDrawing {
|
|
|
|
|
let nextSegDir: Point = { x: 0, y: 0 }; |
|
|
|
|
let perp: Point = { x: 0, y: 0 }; |
|
|
|
|
|
|
|
|
|
let rawReset = reset; |
|
|
|
|
reset = (idx: number) => { |
|
|
|
|
rawReset(idx); |
|
|
|
|
read(idx, lastDrawnPoint); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
read = (idx: number, out_point: Point) => { |
|
|
|
|
rawRead(idx, out_point); |
|
|
|
|
if(idx == 0 || idx == num - 1) return true; |
|
|
|
@ -140,31 +200,48 @@ namespace SvgDrawing {
|
|
|
|
|
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'; |
|
|
|
|
let paths = []; |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
for(let styleIdx = 0; styleIdx < styles.length; ++styleIdx) { |
|
|
|
|
let dataPoint: Point = { x: 0, y: 0 }; |
|
|
|
|
let viewPoint: Point = { x: 0, y: 0 }; |
|
|
|
|
|
|
|
|
|
let coordinates = ''; |
|
|
|
|
|
|
|
|
|
let open = false; |
|
|
|
|
for(let idx = 0; idx < num; ++idx) { |
|
|
|
|
let includePoint = !indices || indices[idx] == styleIdx; |
|
|
|
|
|
|
|
|
|
if(!open && includePoint) { |
|
|
|
|
reset(idx); |
|
|
|
|
read(idx, dataPoint); |
|
|
|
|
this.viewport.dataToView(dataPoint, viewPoint); |
|
|
|
|
let space = coordinates == '' ? '' : ' '; |
|
|
|
|
coordinates += space+'M'+Math.round(viewPoint.x)+','+Math.round(viewPoint.y) + ' L'; |
|
|
|
|
count += 1; |
|
|
|
|
} |
|
|
|
|
else if(open && (read(idx, dataPoint) || !includePoint)) { |
|
|
|
|
this.viewport.dataToView(dataPoint, viewPoint); |
|
|
|
|
coordinates += ' '+Math.round(viewPoint.x)+','+Math.round(viewPoint.y); |
|
|
|
|
count += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
open = includePoint; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let style = styles[styleIdx]; |
|
|
|
|
let path = document.createElementNS('http://www.w3.org/2000/svg','path'); |
|
|
|
|
path.setAttribute('class', 'graph' + (style.className == '' ? '' : ' ' + style.className)); |
|
|
|
|
path.setAttribute('d', coordinates); |
|
|
|
|
this.htmlElement.append(path); |
|
|
|
|
|
|
|
|
|
paths.push(path); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
return paths; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|