UNPKG

@equinor/esv-intersection

Version:

Intersection component package with testing and automatic documentation.

184 lines (155 loc) 5.07 kB
import { SurfaceArea, SurfaceData, SurfaceLine } from '../datautils'; import { OnUpdateEvent, OnRescaleEvent } from '../interfaces'; import { colorToCSSColor } from '../utils/color'; import { LayerOptions } from './base'; import { CanvasLayer } from './base/CanvasLayer'; const DEFAULT_MAX_DEPTH = 10000; type SurfacePaths = { color: string; path: Path2D; }; export class GeomodelCanvasLayer<T extends SurfaceData> extends CanvasLayer<T> { rescaleEvent: OnRescaleEvent | undefined; surfaceAreasPaths: SurfacePaths[] = []; surfaceLinesPaths: SurfacePaths[] = []; maxDepth: number = DEFAULT_MAX_DEPTH; constructor(id?: string, options?: LayerOptions<T>) { super(id, options); this.render = this.render.bind(this); this.generateSurfaceAreasPaths = this.generateSurfaceAreasPaths.bind(this); this.generateSurfaceLinesPaths = this.generateSurfaceLinesPaths.bind(this); this.drawPolygonPath = this.drawPolygonPath.bind(this); this.drawLinePath = this.drawLinePath.bind(this); this.updatePaths = this.updatePaths.bind(this); } override onUpdate(event: OnUpdateEvent<T>): void { super.onUpdate(event); this.updatePaths(); this.render(); } override onRescale(event: OnRescaleEvent): void { this.rescaleEvent = event; this.setTransform(this.rescaleEvent); this.render(); } updatePaths(): void { if (!this.data) { this.surfaceAreasPaths = []; this.surfaceLinesPaths = []; } else { this.generateSurfaceAreasPaths(); this.generateSurfaceLinesPaths(); } } render(): void { if (!this.ctx || !this.rescaleEvent) { return; } requestAnimationFrame(() => { this.clearCanvas(); this.surfaceAreasPaths.forEach((p: SurfacePaths) => this.drawPolygonPath(p.color, p.path)); this.surfaceLinesPaths.forEach((l: SurfacePaths) => this.drawLinePath(l.color, l.path)); }); } colorToCSSColor(color: number | string): string { return colorToCSSColor(color); } generateSurfaceAreasPaths(): void { this.surfaceAreasPaths = this.data?.areas.reduce((acc: SurfacePaths[], s: SurfaceArea) => { const polygons = this.createPolygons(s.data); const mapped: SurfacePaths[] = polygons.map((polygon: number[]) => ({ color: this.colorToCSSColor(s.color), path: this.generatePolygonPath(polygon), })); acc.push(...mapped); return acc; }, []) ?? []; } generateSurfaceLinesPaths(): void { this.surfaceLinesPaths = this.data?.lines.reduce((acc: SurfacePaths[], l: SurfaceLine) => { const lines = this.generateLinePaths(l); const mapped: SurfacePaths[] = lines.map((path: Path2D) => ({ color: this.colorToCSSColor(l.color), path })); acc.push(...mapped); return acc; }, []) ?? []; } drawPolygonPath = (color: string, path: Path2D): void => { const { ctx } = this; if (ctx != null) { ctx.fillStyle = color; ctx.fill(path); } }; drawLinePath = (color: string, path: Path2D): void => { const { ctx } = this; if (ctx != null) { ctx.strokeStyle = color; ctx.stroke(path); } }; createPolygons = (data: number[][]): number[][] => { const polygons: number[][] = []; let polygon: number[] = []; // Start generating polygons for (let i = 0; i < data.length; i++) { // Generate top of polygon as long as we have valid values const topIsValid = !!data[i]?.[1]; if (topIsValid) { if (polygon === null) { polygon = []; } polygon.push(data[i]?.[0]!, data[i]?.[1]!); } const endIsReached = i === data.length - 1; if (!topIsValid || endIsReached) { if (polygon.length > 0) { // Generate bottom of polygon for (let j: number = !topIsValid ? i - 1 : i; j >= 0; j--) { if (!data[j]?.[1]) { break; } polygon.push(data[j]?.[0]!, data[j]?.[2] || this.maxDepth); } polygons.push(polygon); polygon = []; } } } return polygons; }; generatePolygonPath = (polygon: number[]): Path2D => { const path = new Path2D(); path.moveTo(polygon[0]!, polygon[1]!); for (let i = 2; i < polygon.length; i += 2) { path.lineTo(polygon[i]!, polygon[i + 1]!); } path.closePath(); return path; }; generateLinePaths = (s: SurfaceLine): Path2D[] => { const paths: Path2D[] = []; const { data: d } = s; let penDown = false; let path: Path2D | undefined; for (let i = 0; i < d.length; i++) { if (d[i]?.[1]) { if (penDown && path) { path.lineTo(d[i]?.[0]!, d[i]?.[1]!); } else { path = new Path2D(); path.moveTo(d[i]?.[0]!, d[i]?.[1]!); penDown = true; } } else if (penDown && path) { paths.push(path); penDown = false; } } if (penDown && path) { paths.push(path); } return paths; }; }