UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

267 lines (257 loc) 14.2 kB
import { SvgPrimitive } from "../../../Render/Primitives/Primitive"; import { Shape } from "../Shape"; import { ImagePrimitive } from "../../../Render/Primitives/ImagePrimitive"; import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle"; import { Size } from "@devexpress/utils/lib/geometry/size"; import { Point } from "@devexpress/utils/lib/geometry/point"; import { ConnectionPoint } from "../../ConnectionPoint"; import { ConnectionPointSide } from "../../DiagramItem"; import { ShapeWithImageDescription } from "./ShapeWithImageDescription"; import { ICustomShape } from "../../../Interfaces"; import { ShapeDescription, ShapeDefaultDimension } from "./ShapeDescription"; import { ImageInfo } from "../../../Images/ImageInfo"; import { ImageCache, CacheImageInfo } from "../../../Images/ImageCache"; import { ImageLoader } from "../../../Images/ImageLoader"; import { ShapeParameters } from "../ShapeParameters"; import { ShapeParameterPoint } from "../ShapeParameterPoint"; import { SvgElementPrimitive } from "../../../Render/Primitives/SvgElementPrimitive"; import { TextAngle } from "../../../Render/Primitives/TextPrimitive"; export class CustomShapeDescription extends ShapeWithImageDescription { public title: string; public defaultText: string public defaultImageUrl: string private svgImage: ImageInfo; private svgToolboxImage: ImageInfo; private imageLoader = new ImageLoader(this.updateSvgImage.bind(this)); constructor(public properties: ICustomShape, public baseDescription?: ShapeDescription) { super( new Size( properties.defaultWidth || baseDescription && baseDescription.defaultSize.width || ShapeDefaultDimension, properties.defaultHeight || baseDescription && baseDescription.defaultSize.height || ShapeDefaultDimension ) ); this.defaultText = properties.defaultText !== undefined ? properties.defaultText : baseDescription && baseDescription.getDefaultText(); this.defaultImageUrl = properties.defaultImageUrl || baseDescription && baseDescription.getDefaultImageUrl(); this.title = properties.title || baseDescription && baseDescription.getTitle() || this.defaultText || ""; this.connectionPoints = this.createConnectionPoints(); if(properties.svgUrl) { this.svgImage = new ImageInfo(properties.svgUrl); const cachedImage = ImageCache.instance.createUnloadedInfoByShapeImageInfo(this.svgImage); this.imageLoader.load(cachedImage); } if(properties.svgToolboxUrl) { this.svgToolboxImage = new ImageInfo(properties.svgToolboxUrl); const cachedImage = ImageCache.instance.createUnloadedInfoByShapeImageInfo(this.svgToolboxImage); this.imageLoader.load(cachedImage); } } get key() { return this.properties.type; } get allowEditText() { return this.properties.allowEditText !== false; } get allowEditImage() { return this.baseDescription ? this.baseDescription.allowEditImage : this.properties.allowEditImage === true; } get enableChildren() { return this.baseDescription && this.baseDescription.enableChildren; } get hasTemplate() { return !!this.properties.createTemplate; } get minWidth() { return this.properties.minWidth || this.baseDescription && this.baseDescription.minWidth; } get minHeight() { return this.properties.minHeight || this.baseDescription && this.baseDescription.minHeight; } get maxWidth() { return this.properties.maxWidth || this.baseDescription && this.baseDescription.maxWidth; } get maxHeight() { return this.properties.maxHeight || this.baseDescription && this.baseDescription.maxHeight; } get keepRatioOnAutoSize(): boolean { return this.properties.keepRatioOnAutoSize; } get toolboxSize() { if(this.properties.toolboxWidthToHeightRatio) return new Size(this.defaultSize.width, this.defaultSize.width / this.properties.toolboxWidthToHeightRatio); return this.defaultSize; } getTextAngle(): TextAngle { return (this.baseDescription && this.baseDescription.getTextAngle()) || super.getTextAngle(); } getTitle() { return this.title !== undefined ? this.title : super.getTitle(); } getDefaultText() { return this.defaultText !== undefined ? this.defaultText : super.getDefaultText(); } getDefaultImageUrl() { return this.defaultImageUrl !== undefined ? this.defaultImageUrl : super.getDefaultImageUrl(); } allowResizeHorizontally(shape: Shape) { if(this.properties.allowResize === false) return false; if(this.baseDescription) return this.baseDescription.allowResizeHorizontally(shape); return super.allowResizeHorizontally(shape); } allowResizeVertically(shape: Shape) { if(this.properties.allowResize === false) return false; if(this.baseDescription) return this.baseDescription.allowResizeVertically(shape); return super.allowResizeVertically(shape); } protected createConnectionPoints(): ConnectionPoint[] { if(this.properties && this.properties.connectionPoints && this.properties.connectionPoints.length) return this.properties.connectionPoints.map(ptObj => { if(ptObj && typeof ptObj["x"] === "number" && typeof ptObj["y"] === "number") { const side = typeof ptObj["side"] === "number" ? ptObj["side"] : ConnectionPointSide.Undefined; return new ConnectionPoint(ptObj["x"], ptObj["y"], side); } }).filter(pt => pt); return super.createConnectionPoints(); } private getConnectionPointSides(): {[key: number]: number[]} { const result: {[key: number]: number[]} = {}; for(let i = 0; i < this.connectionPoints.length; i++) { const pointSide = ShapeDescription.getConnectionPointSideByGeometry(this.connectionPoints[i]); if(!result[pointSide]) result[pointSide] = []; result[pointSide].push(i); } return result; } getConnectionPointIndexForSide(side: ConnectionPointSide): number { const connectionPointSides = this.getConnectionPointSides(); let pointIndexes = connectionPointSides[side]; if(pointIndexes) { let index = Math.floor(pointIndexes.length / 2); if(pointIndexes.length % 2 === 0) index--; return pointIndexes[index]; } else { pointIndexes = connectionPointSides[(side + 1) % 4]; if(pointIndexes) return pointIndexes[0]; else { pointIndexes = connectionPointSides[(side + 3) % 4]; if(pointIndexes) return pointIndexes[pointIndexes.length - 1]; else { pointIndexes = connectionPointSides[(side + 2) % 4]; if(pointIndexes) return pointIndexes[0]; } } } return side; } createImagePrimitives(shape: Shape, forToolbox: boolean): SvgPrimitive<SVGGraphicsElement>[] { if(this.baseDescription) return this.baseDescription.createImagePrimitives(shape, forToolbox); return super.createImagePrimitives(shape, forToolbox); } createShapePrimitives(shape: Shape, forToolbox?: boolean): SvgPrimitive<SVGGraphicsElement>[] { let primitives: SvgPrimitive<SVGGraphicsElement>[] = []; const { x: left, y: top, width, height } = shape.rectangle; if(this.baseDescription) primitives = this.baseDescription.createShapePrimitives(shape, forToolbox); else { const svgImage = forToolbox && this.svgToolboxImage ? this.svgToolboxImage : this.svgImage; if(svgImage) { const svgLeft = left + (this.properties.svgLeft && !forToolbox ? this.properties.svgLeft * width : 0); const svgTop = top + (this.properties.svgTop && !forToolbox ? this.properties.svgTop * height : 0); const svgWidth = this.properties.svgWidth && !forToolbox ? this.properties.svgWidth * width : width; const svgHeight = this.properties.svgHeight && !forToolbox ? this.properties.svgHeight * height : height; primitives = primitives.concat([ new ImagePrimitive(svgLeft, svgTop, svgWidth, svgHeight, svgImage.exportUrl) ]); } } const createTemplate = forToolbox && this.properties.createToolboxTemplate || this.properties.createTemplate; if(createTemplate) { const templateLeft = left + (this.properties.templateLeft && !forToolbox ? this.properties.templateLeft * width : 0); const templateTop = top + (this.properties.templateTop && !forToolbox ? this.properties.templateTop * height : 0); const templateWidth = this.properties.templateWidth && !forToolbox ? this.properties.templateWidth * width : width; const templateHeight = this.properties.templateHeight && !forToolbox ? this.properties.templateHeight * height : height; const nativeShape = this.properties.apiController ? this.properties.apiController.createNativeShape(shape) : shape.toNative(); primitives = primitives.concat([ new SvgElementPrimitive( templateLeft, templateTop, templateWidth, templateHeight, createTemplate, this.properties.destroyTemplate, nativeShape ) ]); } return primitives; } createParameters(parameters: ShapeParameters) { if(this.baseDescription) return this.baseDescription.createParameters(parameters); else return super.createParameters(parameters); } normalizeParameters(shape: Shape, parameters: ShapeParameters) { if(this.baseDescription) this.baseDescription.normalizeParameters(shape, parameters); else super.normalizeParameters(shape, parameters); } modifyParameters(shape: Shape, parameters: ShapeParameters, deltaX: number, deltaY: number) { if(this.baseDescription) this.baseDescription.modifyParameters(shape, parameters, deltaX, deltaY); else super.modifyParameters(shape, parameters, deltaX, deltaY); } getParameterPoints(shape: Shape): ShapeParameterPoint[] { if(this.baseDescription) return this.baseDescription.getParameterPoints(shape); else return super.getParameterPoints(shape); } getTextRectangle(shape: Shape): Rectangle { if(this.baseDescription) return this.baseDescription.getTextRectangle(shape); else { const { x: left, y: top, width, height } = shape.rectangle; return new Rectangle( left + (this.properties.textLeft ? this.properties.textLeft * width : 0), top + (this.properties.textTop ? this.properties.textTop * height : 0), this.properties.textWidth ? this.properties.textWidth * width : width, this.properties.textHeight ? this.properties.textHeight * height : height, ); } } getSizeByText(textSize: Size, shape: Shape): Size { if(this.baseDescription) return this.baseDescription.getSizeByText(textSize, shape); else { let textWidth = this.properties.textWidth; if(!textWidth) textWidth = 1; let textHeight = this.properties.textHeight; if(!textHeight) textHeight = 1; return new Size(textSize.width / textWidth, textSize.height / textHeight); } } getImageSize(shapeSize: Size, includeMargins: boolean, forToolbox?: boolean): Size { if(this.baseDescription) { if(this.baseDescription instanceof ShapeWithImageDescription) return this.baseDescription.getImageSize(shapeSize, includeMargins, forToolbox); return Size.empty(); } return new Size( this.properties.imageWidth ? this.properties.imageWidth * shapeSize.width : shapeSize.width, this.properties.imageHeight ? this.properties.imageHeight * shapeSize.height : shapeSize.height) .nonNegativeSize(); } getImagePlacementRectangle(rect: Rectangle, forToolbox?: boolean): Rectangle { if(this.baseDescription) { if(this.baseDescription instanceof ShapeWithImageDescription) return this.baseDescription.getImagePlacementRectangle(rect, forToolbox); return Rectangle.fromGeometry(Point.zero(), Size.empty()); } const { x: left, y: top, width, height } = rect; return new Rectangle( left + (this.properties.imageLeft && !forToolbox ? this.properties.imageLeft * width : 0), top + (this.properties.imageTop && !forToolbox ? this.properties.imageTop * height : 0), this.properties.imageWidth && !forToolbox ? this.properties.imageWidth * width : width, this.properties.imageHeight && !forToolbox ? this.properties.imageHeight * height : height); } updateSvgImage(cacheImageInfo: CacheImageInfo): void { const isToolboxImage = cacheImageInfo.imageUrl && cacheImageInfo.imageUrl === this.properties.svgToolboxUrl; const svgImage = isToolboxImage ? this.svgToolboxImage : this.svgImage; if(cacheImageInfo.base64) svgImage.loadBase64Content(cacheImageInfo.base64); else svgImage.setUnableToLoadFlag(); if(!isToolboxImage) this.raiseShapeDescriptionChanged(this); } }