| // Copyright (C) 2019 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import {Size2D, Point2D} from './geom'; |
| import {isString} from './object_utils'; |
| |
| export function drawDoubleHeadedArrow( |
| ctx: CanvasRenderingContext2D, |
| x: number, |
| y: number, |
| length: number, |
| showArrowHeads: boolean, |
| width = 2, |
| color = 'black', |
| ) { |
| ctx.beginPath(); |
| ctx.lineWidth = width; |
| ctx.lineCap = 'round'; |
| ctx.strokeStyle = color; |
| ctx.moveTo(x, y); |
| ctx.lineTo(x + length, y); |
| ctx.stroke(); |
| ctx.closePath(); |
| // Arrowheads on the each end of the line. |
| if (showArrowHeads) { |
| ctx.beginPath(); |
| ctx.moveTo(x + length - 8, y - 4); |
| ctx.lineTo(x + length, y); |
| ctx.lineTo(x + length - 8, y + 4); |
| ctx.stroke(); |
| ctx.closePath(); |
| ctx.beginPath(); |
| ctx.moveTo(x + 8, y - 4); |
| ctx.lineTo(x, y); |
| ctx.lineTo(x + 8, y + 4); |
| ctx.stroke(); |
| ctx.closePath(); |
| } |
| } |
| |
| export function drawIncompleteSlice( |
| ctx: CanvasRenderingContext2D, |
| x: number, |
| y: number, |
| width: number, |
| height: number, |
| showGradient: boolean = true, |
| ) { |
| if (width <= 0 || height <= 0) { |
| return; |
| } |
| ctx.beginPath(); |
| const triangleSize = height / 4; |
| ctx.moveTo(x, y); |
| ctx.lineTo(x + width, y); |
| ctx.lineTo(x + width - 3, y + triangleSize * 0.5); |
| ctx.lineTo(x + width, y + triangleSize); |
| ctx.lineTo(x + width - 3, y + triangleSize * 1.5); |
| ctx.lineTo(x + width, y + 2 * triangleSize); |
| ctx.lineTo(x + width - 3, y + triangleSize * 2.5); |
| ctx.lineTo(x + width, y + 3 * triangleSize); |
| ctx.lineTo(x + width - 3, y + triangleSize * 3.5); |
| ctx.lineTo(x + width, y + 4 * triangleSize); |
| ctx.lineTo(x, y + height); |
| |
| const fillStyle = ctx.fillStyle; |
| if (isString(fillStyle)) { |
| if (showGradient) { |
| const gradient = ctx.createLinearGradient(x, y, x + width, y + height); |
| gradient.addColorStop(0.66, fillStyle); |
| gradient.addColorStop(1, '#FFFFFF'); |
| ctx.fillStyle = gradient; |
| } |
| } else { |
| throw new Error( |
| `drawIncompleteSlice() expects fillStyle to be a simple color not ${fillStyle}`, |
| ); |
| } |
| |
| ctx.fill(); |
| ctx.fillStyle = fillStyle; |
| } |
| |
| /** |
| * Clip a canvas using a rect-like object. |
| * |
| * @param ctx - The canvas context to clip. |
| * @param rect - The position and dimensions of the rect to clip. |
| */ |
| export function canvasClip( |
| ctx: CanvasRenderingContext2D, |
| rect: Point2D & Size2D, |
| ): void; |
| |
| /** |
| * Clip a canvas using a separate x, y, width, height values. |
| * |
| * @param ctx - The canvas context to clip. |
| */ |
| export function canvasClip( |
| ctx: CanvasRenderingContext2D, |
| x: number, |
| y: number, |
| w: number, |
| h: number, |
| ): void; |
| |
| // This function can either take individual x, y, w, h parameters to define the |
| // rect, or x can be a rect-like object. |
| export function canvasClip( |
| ctx: CanvasRenderingContext2D, |
| x: number | (Point2D & Size2D), |
| y?: number, |
| w?: number, |
| h?: number, |
| ): void { |
| ctx.beginPath(); |
| if (typeof x === 'number') { |
| // TypeScript ensures y, w, and h are defined here |
| ctx.rect(x, y!, w!, h!); |
| } else { |
| ctx.rect(x.x, x.y, x.width, x.height); |
| } |
| ctx.clip(); |
| } |
| |
| /** |
| * Save the state of the canvas, returning a disposable which restores the state |
| * when disposed. |
| * |
| * Allows using the |using| keyword to automatically restore the canvas state. |
| * @param ctx - The canvas context to save the state of. |
| * @returns A disposable. |
| * |
| * @example |
| * { |
| * using const _ = canvasSave(ctx); |
| * ctx.translate(123, 456); // Manipulate the canvas state |
| * } // ctx.restore() is automatically called when the _ falls out of scope |
| */ |
| export function canvasSave(ctx: CanvasRenderingContext2D): Disposable { |
| ctx.save(); |
| return { |
| [Symbol.dispose](): void { |
| ctx.restore(); |
| }, |
| }; |
| } |