import * as d3 from 'd3'; import { isRectangleStyle, ShapeStyle } from '../../styles/ShapeStyle'; import { Node } from '../../types/Node'; import { Point } from '../../types/Point'; import { ShapeRenderer } from './ShapeRenderer'; const TWOPI = 2 * Math.PI; const PI3 = Math.PI / 3; class RectangleRenderer extends ShapeRenderer { static defaultWidth = 125; static defaultHeight = 60; protected _height: number; protected _width: number; constructor (style?: ShapeStyle) { super(style); this._width = RectangleRenderer.defaultWidth; this._height = RectangleRenderer.defaultHeight; if (style && isRectangleStyle(style)) { if (style.width) this._width = style.width; if (style.height) this._height = style.height; } } containsPoint (node: Node, x: number, y: number, scale?: number): boolean { scale = scale || 1; const w = (this._width / scale) / 2; const h = (this._height / scale) / 2; const t = node.y + h; const r = node.x + w; const b = node.y - h; const l = node.x - w; return r >= x && x >= l && t >= y && y >= b; } height (): number; height (height: number): this; height (height?: number): number | this { return arguments.length ? (this._height = +height!, this) : this._height; } intersect (angle: number): Point { while (angle < -Math.PI) angle += TWOPI; while (angle > Math.PI) angle -= TWOPI; const tana = Math.tan(angle); const r = region(angle, this._width, this._height); let x = (r === 1 || r === 2) ? 1 : -1; let y = (r === 3 || r === 4) ? -1 : 1; if (r === 1 || r === 3) { x *= this._width / 2; y *= tana * this._width / 2; } else { x *= this._height / (2 * tana); y *= this._height / 2; } return { x: x, y: y }; } render(context: CanvasRenderingContext2D, x: d3.ScaleLinear, y: d3.ScaleLinear): void { if (!this._nodes.length) return; context.save(); context.beginPath(); const w2 = this._width / 2; const h2 = this._height / 2; this._nodes.forEach(point => { const px = x(point.x); const py = y(point.y); context.rect( Math.round(px - w2) - 0.5, Math.round(py - h2) - 0.5, this._width + 1, this._height + 1); }); this._doStrokeAndFill(context); context.restore(); } selfLoopPoints (node: Node): Point[] { const nx = node.x; const ny = node.y; const w = Math.max(100, Math.min(this._width, 200)); const h = Math.min(this._height, 100); const w2 = this._width / 2; const h4 = h / 4; return [{ x: nx, y: ny, dx: w2, dy: -h4 }, { x: nx, y: ny, dx: w2 + w * Math.cos(-PI3), dy: h * Math.sin(-PI3) }, { x: nx, y: ny, dx: w2 + w * Math.cos(PI3), dy: h * Math.sin(PI3) }, { x: nx, y: ny, dx: w2, dy: h4 }]; } width (): number; width (width: number): this; width (width?: number): number | this { return arguments.length ? (this._width = +width!, this) : this._width; } } function region (angle: number, width: number, height: number): number { const atan = Math.atan2(height, width); if ((angle > -atan) && (angle <= atan)) return 1; else if ((angle > atan) && (angle <= Math.PI - atan)) return 2; else if ((angle > Math.PI - atan) || (angle <= -(Math.PI - atan))) return 3; else return 4; } export { RectangleRenderer }