/** 
 | 
 * Base class of all displayable graphic objects 
 | 
 */ 
 | 
  
 | 
import Element, {ElementProps, ElementStatePropNames, ElementAnimateConfig, ElementCommonState} from '../Element'; 
 | 
import BoundingRect from '../core/BoundingRect'; 
 | 
import { PropType, Dictionary, MapToType } from '../core/types'; 
 | 
import Path from './Path'; 
 | 
import { keys, extend, createObject } from '../core/util'; 
 | 
import Animator from '../animation/Animator'; 
 | 
import { REDRAW_BIT, STYLE_CHANGED_BIT } from './constants'; 
 | 
  
 | 
// type CalculateTextPositionResult = ReturnType<typeof calculateTextPosition> 
 | 
  
 | 
const STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10)); 
 | 
  
 | 
export interface CommonStyleProps { 
 | 
    shadowBlur?: number 
 | 
    shadowOffsetX?: number 
 | 
    shadowOffsetY?: number 
 | 
    shadowColor?: string 
 | 
  
 | 
    opacity?: number 
 | 
    /** 
 | 
     * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation 
 | 
     */ 
 | 
    blend?: string 
 | 
} 
 | 
  
 | 
export const DEFAULT_COMMON_STYLE: CommonStyleProps = { 
 | 
    shadowBlur: 0, 
 | 
    shadowOffsetX: 0, 
 | 
    shadowOffsetY: 0, 
 | 
    shadowColor: '#000', 
 | 
    opacity: 1, 
 | 
    blend: 'source-over' 
 | 
}; 
 | 
  
 | 
export const DEFAULT_COMMON_ANIMATION_PROPS: MapToType<DisplayableProps, boolean> = { 
 | 
    style: { 
 | 
        shadowBlur: true, 
 | 
        shadowOffsetX: true, 
 | 
        shadowOffsetY: true, 
 | 
        shadowColor: true, 
 | 
        opacity: true 
 | 
    } 
 | 
 }; 
 | 
  
 | 
(DEFAULT_COMMON_STYLE as any)[STYLE_MAGIC_KEY] = true; 
 | 
  
 | 
export interface DisplayableProps extends ElementProps { 
 | 
    style?: Dictionary<any> 
 | 
  
 | 
    zlevel?: number 
 | 
    z?: number 
 | 
    z2?: number 
 | 
  
 | 
    culling?: boolean 
 | 
  
 | 
    // TODO list all cursors 
 | 
    cursor?: string 
 | 
  
 | 
    rectHover?: boolean 
 | 
  
 | 
    progressive?: boolean 
 | 
  
 | 
    incremental?: boolean 
 | 
  
 | 
    ignoreCoarsePointer?: boolean 
 | 
  
 | 
    batch?: boolean 
 | 
    invisible?: boolean 
 | 
} 
 | 
  
 | 
type DisplayableKey = keyof DisplayableProps 
 | 
type DisplayablePropertyType = PropType<DisplayableProps, DisplayableKey> 
 | 
  
 | 
export type DisplayableStatePropNames = ElementStatePropNames | 'style' | 'z' | 'z2' | 'invisible'; 
 | 
export type DisplayableState = Pick<DisplayableProps, DisplayableStatePropNames> & ElementCommonState; 
 | 
  
 | 
const PRIMARY_STATES_KEYS = ['z', 'z2', 'invisible'] as const; 
 | 
const PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible'] as const; 
 | 
  
 | 
// eslint-disable-next-line @typescript-eslint/no-unused-vars 
 | 
interface Displayable<Props extends DisplayableProps = DisplayableProps> { 
 | 
    animate(key?: '', loop?: boolean): Animator<this> 
 | 
    animate(key: 'style', loop?: boolean): Animator<this['style']> 
 | 
  
 | 
    getState(stateName: string): DisplayableState 
 | 
    ensureState(stateName: string): DisplayableState 
 | 
  
 | 
    states: Dictionary<DisplayableState> 
 | 
    stateProxy: (stateName: string) => DisplayableState 
 | 
} 
 | 
  
 | 
class Displayable<Props extends DisplayableProps = DisplayableProps> extends Element<Props> { 
 | 
  
 | 
    /** 
 | 
     * Whether the displayable object is visible. when it is true, the displayable object 
 | 
     * is not drawn, but the mouse event can still trigger the object. 
 | 
     */ 
 | 
    invisible: boolean 
 | 
  
 | 
    z: number 
 | 
  
 | 
    z2: number 
 | 
  
 | 
    /** 
 | 
     * The z level determines the displayable object can be drawn in which layer canvas. 
 | 
     */ 
 | 
    zlevel: number 
 | 
  
 | 
    /** 
 | 
     * If enable culling 
 | 
     */ 
 | 
    culling: boolean 
 | 
  
 | 
    /** 
 | 
     * Mouse cursor when hovered 
 | 
     */ 
 | 
    cursor: string 
 | 
  
 | 
    /** 
 | 
     * If hover area is bounding rect 
 | 
     */ 
 | 
    rectHover: boolean 
 | 
    /** 
 | 
     * For increamental rendering 
 | 
     */ 
 | 
    incremental: boolean 
 | 
  
 | 
    /** 
 | 
     * Never increase to target size 
 | 
     */ 
 | 
    ignoreCoarsePointer?: boolean 
 | 
  
 | 
    style: Dictionary<any> 
 | 
  
 | 
    protected _normalState: DisplayableState 
 | 
  
 | 
    protected _rect: BoundingRect 
 | 
    protected _paintRect: BoundingRect 
 | 
    protected _prevPaintRect: BoundingRect 
 | 
  
 | 
    dirtyRectTolerance: number 
 | 
  
 | 
    /************* Properties will be inejected in other modules. *******************/ 
 | 
  
 | 
    // @deprecated. 
 | 
    useHoverLayer?: boolean 
 | 
  
 | 
    __hoverStyle?: CommonStyleProps 
 | 
  
 | 
    // TODO use WeakMap? 
 | 
  
 | 
    // Shapes for cascade clipping. 
 | 
    // Can only be `null`/`undefined` or an non-empty array, MUST NOT be an empty array. 
 | 
    // because it is easy to only using null to check whether clipPaths changed. 
 | 
    __clipPaths?: Path[] 
 | 
  
 | 
    // FOR CANVAS PAINTER 
 | 
    __canvasFillGradient: CanvasGradient 
 | 
    __canvasStrokeGradient: CanvasGradient 
 | 
    __canvasFillPattern: CanvasPattern 
 | 
    __canvasStrokePattern: CanvasPattern 
 | 
  
 | 
    // FOR SVG PAINTER 
 | 
    __svgEl: SVGElement 
 | 
  
 | 
    constructor(props?: Props) { 
 | 
        super(props); 
 | 
    } 
 | 
  
 | 
    protected _init(props?: Props) { 
 | 
        // Init default properties 
 | 
        const keysArr = keys(props); 
 | 
        for (let i = 0; i < keysArr.length; i++) { 
 | 
            const key = keysArr[i]; 
 | 
            if (key === 'style') { 
 | 
                this.useStyle(props[key] as Props['style']); 
 | 
            } 
 | 
            else { 
 | 
                super.attrKV(key as any, props[key]); 
 | 
            } 
 | 
        } 
 | 
        // Give a empty style 
 | 
        if (!this.style) { 
 | 
            this.useStyle({}); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    // Hook provided to developers. 
 | 
    beforeBrush() {} 
 | 
    afterBrush() {} 
 | 
  
 | 
    // Hook provided to inherited classes. 
 | 
    // Executed between beforeBrush / afterBrush 
 | 
    innerBeforeBrush() {} 
 | 
    innerAfterBrush() {} 
 | 
  
 | 
    shouldBePainted( 
 | 
        viewWidth: number, 
 | 
        viewHeight: number, 
 | 
        considerClipPath: boolean, 
 | 
        considerAncestors: boolean 
 | 
    ) { 
 | 
        const m = this.transform; 
 | 
        if ( 
 | 
            this.ignore 
 | 
            // Ignore invisible element 
 | 
            || this.invisible 
 | 
            // Ignore transparent element 
 | 
            || this.style.opacity === 0 
 | 
            // Ignore culled element 
 | 
            || (this.culling 
 | 
                && isDisplayableCulled(this, viewWidth, viewHeight) 
 | 
            ) 
 | 
            // Ignore scale 0 element, in some environment like node-canvas 
 | 
            // Draw a scale 0 element can cause all following draw wrong 
 | 
            // And setTransform with scale 0 will cause set back transform failed. 
 | 
            || (m && !m[0] && !m[3]) 
 | 
        ) { 
 | 
            return false; 
 | 
        } 
 | 
  
 | 
        if (considerClipPath && this.__clipPaths) { 
 | 
            for (let i = 0; i < this.__clipPaths.length; ++i) { 
 | 
                if (this.__clipPaths[i].isZeroArea()) { 
 | 
                    return false; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        if (considerAncestors && this.parent) { 
 | 
            let parent = this.parent; 
 | 
            while (parent) { 
 | 
                if (parent.ignore) { 
 | 
                    return false; 
 | 
                } 
 | 
                parent = parent.parent; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        return true; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * If displayable element contain coord x, y 
 | 
     */ 
 | 
    contain(x: number, y: number) { 
 | 
        return this.rectContain(x, y); 
 | 
    } 
 | 
  
 | 
    traverse<Context>( 
 | 
        cb: (this: Context, el: this) => void, 
 | 
        context?: Context 
 | 
    ) { 
 | 
        cb.call(context, this); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * If bounding rect of element contain coord x, y 
 | 
     */ 
 | 
    rectContain(x: number, y: number) { 
 | 
        const coord = this.transformCoordToLocal(x, y); 
 | 
        const rect = this.getBoundingRect(); 
 | 
        return rect.contain(coord[0], coord[1]); 
 | 
    } 
 | 
  
 | 
    getPaintRect(): BoundingRect { 
 | 
        let rect = this._paintRect; 
 | 
        if (!this._paintRect || this.__dirty) { 
 | 
            const transform = this.transform; 
 | 
            const elRect = this.getBoundingRect(); 
 | 
  
 | 
            const style = this.style; 
 | 
            const shadowSize = style.shadowBlur || 0; 
 | 
            const shadowOffsetX = style.shadowOffsetX || 0; 
 | 
            const shadowOffsetY = style.shadowOffsetY || 0; 
 | 
  
 | 
            rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0)); 
 | 
            if (transform) { 
 | 
                BoundingRect.applyTransform(rect, elRect, transform); 
 | 
            } 
 | 
            else { 
 | 
                rect.copy(elRect); 
 | 
            } 
 | 
  
 | 
            if (shadowSize || shadowOffsetX || shadowOffsetY) { 
 | 
                rect.width += shadowSize * 2 + Math.abs(shadowOffsetX); 
 | 
                rect.height += shadowSize * 2 + Math.abs(shadowOffsetY); 
 | 
                rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize); 
 | 
                rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize); 
 | 
  
 | 
            } 
 | 
  
 | 
            // For the accuracy tolerance of text height or line joint point 
 | 
            const tolerance = this.dirtyRectTolerance; 
 | 
            if (!rect.isZero()) { 
 | 
                rect.x = Math.floor(rect.x - tolerance); 
 | 
                rect.y = Math.floor(rect.y - tolerance); 
 | 
                rect.width = Math.ceil(rect.width + 1 + tolerance * 2); 
 | 
                rect.height = Math.ceil(rect.height + 1 + tolerance * 2); 
 | 
            } 
 | 
        } 
 | 
        return rect; 
 | 
    } 
 | 
  
 | 
    setPrevPaintRect(paintRect: BoundingRect) { 
 | 
        if (paintRect) { 
 | 
            this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0); 
 | 
            this._prevPaintRect.copy(paintRect); 
 | 
        } 
 | 
        else { 
 | 
            this._prevPaintRect = null; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    getPrevPaintRect(): BoundingRect { 
 | 
        return this._prevPaintRect; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Alias for animate('style') 
 | 
     * @param loop 
 | 
     */ 
 | 
    animateStyle(loop: boolean) { 
 | 
        return this.animate('style', loop); 
 | 
    } 
 | 
  
 | 
    // Override updateDuringAnimation 
 | 
    updateDuringAnimation(targetKey: string) { 
 | 
        if (targetKey === 'style') { 
 | 
            this.dirtyStyle(); 
 | 
        } 
 | 
        else { 
 | 
            this.markRedraw(); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    attrKV(key: DisplayableKey, value: DisplayablePropertyType) { 
 | 
        if (key !== 'style') { 
 | 
            super.attrKV(key as keyof DisplayableProps, value); 
 | 
        } 
 | 
        else { 
 | 
            if (!this.style) { 
 | 
                this.useStyle(value as Dictionary<any>); 
 | 
            } 
 | 
            else { 
 | 
                this.setStyle(value as Dictionary<any>); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    setStyle(obj: Props['style']): this 
 | 
    setStyle<T extends keyof Props['style']>(obj: T, value: Props['style'][T]): this 
 | 
    setStyle(keyOrObj: keyof Props['style'] | Props['style'], value?: unknown): this { 
 | 
        if (typeof keyOrObj === 'string') { 
 | 
            this.style[keyOrObj] = value; 
 | 
        } 
 | 
        else { 
 | 
            extend(this.style, keyOrObj as Props['style']); 
 | 
        } 
 | 
        this.dirtyStyle(); 
 | 
        return this; 
 | 
    } 
 | 
  
 | 
    // getDefaultStyleValue<T extends keyof Props['style']>(key: T): Props['style'][T] { 
 | 
    //     // Default value is on the prototype. 
 | 
    //     return this.style.prototype[key]; 
 | 
    // } 
 | 
  
 | 
    dirtyStyle(notRedraw?: boolean) { 
 | 
        if (!notRedraw) { 
 | 
            this.markRedraw(); 
 | 
        } 
 | 
        this.__dirty |= STYLE_CHANGED_BIT; 
 | 
        // Clear bounding rect. 
 | 
        if (this._rect) { 
 | 
            this._rect = null; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    dirty() { 
 | 
        this.dirtyStyle(); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Is style changed. Used with dirtyStyle. 
 | 
     */ 
 | 
    styleChanged() { 
 | 
        return !!(this.__dirty & STYLE_CHANGED_BIT); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Mark style updated. Only useful when style is used for caching. Like in the text. 
 | 
     */ 
 | 
    styleUpdated() { 
 | 
        this.__dirty &= ~STYLE_CHANGED_BIT; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Create a style object with default values in it's prototype. 
 | 
     */ 
 | 
    createStyle(obj?: Props['style']) { 
 | 
        return createObject(DEFAULT_COMMON_STYLE, obj); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Replace style property. 
 | 
     * It will create a new style if given obj is not a valid style object. 
 | 
     */ 
 | 
     // PENDING should not createStyle if it's an style object. 
 | 
    useStyle(obj: Props['style']) { 
 | 
        if (!obj[STYLE_MAGIC_KEY]) { 
 | 
            obj = this.createStyle(obj); 
 | 
        } 
 | 
        if (this.__inHover) { 
 | 
            this.__hoverStyle = obj;    // Not affect exists style. 
 | 
        } 
 | 
        else { 
 | 
            this.style = obj; 
 | 
        } 
 | 
        this.dirtyStyle(); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Determine if an object is a valid style object. 
 | 
     * Which means it is created by `createStyle.` 
 | 
     * 
 | 
     * A valid style object will have all default values in it's prototype. 
 | 
     * To avoid get null/undefined values. 
 | 
     */ 
 | 
    isStyleObject(obj: Props['style']) { 
 | 
        return obj[STYLE_MAGIC_KEY]; 
 | 
    } 
 | 
  
 | 
    protected _innerSaveToNormal(toState: DisplayableState) { 
 | 
        super._innerSaveToNormal(toState); 
 | 
  
 | 
        const normalState = this._normalState; 
 | 
        if (toState.style && !normalState.style) { 
 | 
            // Clone style object. 
 | 
            // TODO: Only save changed style. 
 | 
            normalState.style = this._mergeStyle(this.createStyle(), this.style); 
 | 
        } 
 | 
  
 | 
        this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS); 
 | 
    } 
 | 
  
 | 
    protected _applyStateObj( 
 | 
        stateName: string, 
 | 
        state: DisplayableState, 
 | 
        normalState: DisplayableState, 
 | 
        keepCurrentStates: boolean, 
 | 
        transition: boolean, 
 | 
        animationCfg: ElementAnimateConfig 
 | 
    ) { 
 | 
        super._applyStateObj(stateName, state, normalState, keepCurrentStates, transition, animationCfg); 
 | 
  
 | 
        const needsRestoreToNormal = !(state && keepCurrentStates); 
 | 
        let targetStyle: Props['style']; 
 | 
        if (state && state.style) { 
 | 
            // Only animate changed properties. 
 | 
            if (transition) { 
 | 
                if (keepCurrentStates) { 
 | 
                    targetStyle = state.style; 
 | 
                } 
 | 
                else { 
 | 
                    targetStyle = this._mergeStyle(this.createStyle(), normalState.style); 
 | 
                    this._mergeStyle(targetStyle, state.style); 
 | 
                } 
 | 
            } 
 | 
            else { 
 | 
                targetStyle = this._mergeStyle( 
 | 
                    this.createStyle(), 
 | 
                    keepCurrentStates ? this.style : normalState.style 
 | 
                ); 
 | 
                this._mergeStyle(targetStyle, state.style); 
 | 
            } 
 | 
        } 
 | 
        else if (needsRestoreToNormal) { 
 | 
            targetStyle = normalState.style; 
 | 
        } 
 | 
  
 | 
        if (targetStyle) { 
 | 
            if (transition) { 
 | 
                // Clone a new style. Not affect the original one. 
 | 
                const sourceStyle = this.style; 
 | 
  
 | 
                this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle); 
 | 
                // const sourceStyle = this.style = this.createStyle(this.style); 
 | 
  
 | 
                if (needsRestoreToNormal) { 
 | 
                    const changedKeys = keys(sourceStyle); 
 | 
                    for (let i = 0; i < changedKeys.length; i++) { 
 | 
                        const key = changedKeys[i]; 
 | 
                        if (key in targetStyle) {   // Not use `key == null` because == null may means no stroke/fill. 
 | 
                            // Pick out from prototype. Or the property won't be animated. 
 | 
                            (targetStyle as any)[key] = targetStyle[key]; 
 | 
                            // Omit the property has no default value. 
 | 
                            (this.style as any)[key] = sourceStyle[key]; 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
  
 | 
                // If states is switched twice in ONE FRAME, for example: 
 | 
                // one property(for example shadowBlur) changed from default value to a specifed value, 
 | 
                // then switched back in immediately. this.style may don't set this property yet when switching back. 
 | 
                // It won't treat it as an changed property when switching back. And it won't be animated. 
 | 
                // So here we make sure the properties will be animated from default value to a specifed value are set. 
 | 
                const targetKeys = keys(targetStyle); 
 | 
                for (let i = 0; i < targetKeys.length; i++) { 
 | 
                    const key = targetKeys[i]; 
 | 
                    this.style[key] = this.style[key]; 
 | 
                } 
 | 
  
 | 
                this._transitionState(stateName, { 
 | 
                    style: targetStyle 
 | 
                } as Props, animationCfg, this.getAnimationStyleProps() as MapToType<Props, boolean>); 
 | 
            } 
 | 
            else { 
 | 
                this.useStyle(targetStyle); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        // Don't change z, z2 for element moved into hover layer. 
 | 
        // It's not necessary and will cause paint list order changed. 
 | 
        const statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS; 
 | 
        for (let i = 0; i < statesKeys.length; i++) { 
 | 
            let key = statesKeys[i]; 
 | 
            if (state && state[key] != null) { 
 | 
                // Replace if it exist in target state 
 | 
                (this as any)[key] = state[key]; 
 | 
            } 
 | 
            else if (needsRestoreToNormal) { 
 | 
                // Restore to normal state 
 | 
                if (normalState[key] != null) { 
 | 
                    (this as any)[key] = normalState[key]; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    protected _mergeStates(states: DisplayableState[]) { 
 | 
        const mergedState = super._mergeStates(states) as DisplayableState; 
 | 
        let mergedStyle: Props['style']; 
 | 
        for (let i = 0; i < states.length; i++) { 
 | 
            const state = states[i]; 
 | 
            if (state.style) { 
 | 
                mergedStyle = mergedStyle || {}; 
 | 
                this._mergeStyle(mergedStyle, state.style); 
 | 
            } 
 | 
        } 
 | 
        if (mergedStyle) { 
 | 
            mergedState.style = mergedStyle; 
 | 
        } 
 | 
        return mergedState; 
 | 
    } 
 | 
  
 | 
    protected _mergeStyle( 
 | 
        targetStyle: CommonStyleProps, 
 | 
        sourceStyle: CommonStyleProps 
 | 
    ) { 
 | 
        extend(targetStyle, sourceStyle); 
 | 
        return targetStyle; 
 | 
    } 
 | 
  
 | 
    getAnimationStyleProps() { 
 | 
        return DEFAULT_COMMON_ANIMATION_PROPS; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * The string value of `textPosition` needs to be calculated to a real postion. 
 | 
     * For example, `'inside'` is calculated to `[rect.width/2, rect.height/2]` 
 | 
     * by default. See `contain/text.js#calculateTextPosition` for more details. 
 | 
     * But some coutom shapes like "pin", "flag" have center that is not exactly 
 | 
     * `[width/2, height/2]`. So we provide this hook to customize the calculation 
 | 
     * for those shapes. It will be called if the `style.textPosition` is a string. 
 | 
     * @param out Prepared out object. If not provided, this method should 
 | 
     *        be responsible for creating one. 
 | 
     * @param style 
 | 
     * @param rect {x, y, width, height} 
 | 
     * @return out The same as the input out. 
 | 
     *         { 
 | 
     *             x: number. mandatory. 
 | 
     *             y: number. mandatory. 
 | 
     *             textAlign: string. optional. use style.textAlign by default. 
 | 
     *             textVerticalAlign: string. optional. use style.textVerticalAlign by default. 
 | 
     *         } 
 | 
     */ 
 | 
    // calculateTextPosition: (out: CalculateTextPositionResult, style: Dictionary<any>, rect: RectLike) => CalculateTextPositionResult 
 | 
  
 | 
    protected static initDefaultProps = (function () { 
 | 
        const dispProto = Displayable.prototype; 
 | 
        dispProto.type = 'displayable'; 
 | 
        dispProto.invisible = false; 
 | 
        dispProto.z = 0; 
 | 
        dispProto.z2 = 0; 
 | 
        dispProto.zlevel = 0; 
 | 
        dispProto.culling = false; 
 | 
        dispProto.cursor = 'pointer'; 
 | 
        dispProto.rectHover = false; 
 | 
        dispProto.incremental = false; 
 | 
        dispProto._rect = null; 
 | 
        dispProto.dirtyRectTolerance = 0; 
 | 
  
 | 
        dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT; 
 | 
    })() 
 | 
} 
 | 
  
 | 
const tmpRect = new BoundingRect(0, 0, 0, 0); 
 | 
const viewRect = new BoundingRect(0, 0, 0, 0); 
 | 
function isDisplayableCulled(el: Displayable, width: number, height: number) { 
 | 
    tmpRect.copy(el.getBoundingRect()); 
 | 
    if (el.transform) { 
 | 
        tmpRect.applyTransform(el.transform); 
 | 
    } 
 | 
    viewRect.width = width; 
 | 
    viewRect.height = height; 
 | 
    return !tmpRect.intersect(viewRect); 
 | 
} 
 | 
  
 | 
export default Displayable; 
 |