import Displayable, { DisplayableProps, 
 | 
    CommonStyleProps, 
 | 
    DEFAULT_COMMON_STYLE, 
 | 
    DisplayableStatePropNames, 
 | 
    DEFAULT_COMMON_ANIMATION_PROPS 
 | 
} from './Displayable'; 
 | 
import Element, { ElementAnimateConfig } from '../Element'; 
 | 
import PathProxy from '../core/PathProxy'; 
 | 
import * as pathContain from '../contain/path'; 
 | 
import { PatternObject } from './Pattern'; 
 | 
import { Dictionary, PropType, MapToType } from '../core/types'; 
 | 
import BoundingRect from '../core/BoundingRect'; 
 | 
import { LinearGradientObject } from './LinearGradient'; 
 | 
import { RadialGradientObject } from './RadialGradient'; 
 | 
import { defaults, keys, extend, clone, isString, createObject } from '../core/util'; 
 | 
import Animator from '../animation/Animator'; 
 | 
import { lum } from '../tool/color'; 
 | 
import { DARK_LABEL_COLOR, LIGHT_LABEL_COLOR, DARK_MODE_THRESHOLD, LIGHTER_LABEL_COLOR } from '../config'; 
 | 
import { REDRAW_BIT, SHAPE_CHANGED_BIT, STYLE_CHANGED_BIT } from './constants'; 
 | 
import { TRANSFORMABLE_PROPS } from '../core/Transformable'; 
 | 
  
 | 
  
 | 
export interface PathStyleProps extends CommonStyleProps { 
 | 
    fill?: string | PatternObject | LinearGradientObject | RadialGradientObject 
 | 
    stroke?: string | PatternObject | LinearGradientObject | RadialGradientObject 
 | 
    decal?: PatternObject 
 | 
  
 | 
    /** 
 | 
     * Still experimental, not works weel on arc with edge cases(large angle). 
 | 
     */ 
 | 
    strokePercent?: number 
 | 
    strokeNoScale?: boolean 
 | 
    fillOpacity?: number 
 | 
    strokeOpacity?: number 
 | 
  
 | 
    /** 
 | 
     * `true` is not supported. 
 | 
     * `false`/`null`/`undefined` are the same. 
 | 
     * `false` is used to remove lineDash in some 
 | 
     * case that `null`/`undefined` can not be set. 
 | 
     * (e.g., emphasis.lineStyle in echarts) 
 | 
     */ 
 | 
    lineDash?: false | number[] | 'solid' | 'dashed' | 'dotted' 
 | 
    lineDashOffset?: number 
 | 
  
 | 
    lineWidth?: number 
 | 
    lineCap?: CanvasLineCap 
 | 
    lineJoin?: CanvasLineJoin 
 | 
  
 | 
    miterLimit?: number 
 | 
    /** 
 | 
     * Paint order, if do stroke first. Similar to SVG paint-order 
 | 
     * https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/paint-order 
 | 
     */ 
 | 
    strokeFirst?: boolean 
 | 
} 
 | 
  
 | 
export const DEFAULT_PATH_STYLE: PathStyleProps = defaults({ 
 | 
    fill: '#000', 
 | 
    stroke: null, 
 | 
    strokePercent: 1, 
 | 
    fillOpacity: 1, 
 | 
    strokeOpacity: 1, 
 | 
  
 | 
    lineDashOffset: 0, 
 | 
    lineWidth: 1, 
 | 
    lineCap: 'butt', 
 | 
    miterLimit: 10, 
 | 
  
 | 
    strokeNoScale: false, 
 | 
    strokeFirst: false 
 | 
} as PathStyleProps, DEFAULT_COMMON_STYLE); 
 | 
  
 | 
  
 | 
export const DEFAULT_PATH_ANIMATION_PROPS: MapToType<PathProps, boolean> = { 
 | 
    style: defaults<MapToType<PathStyleProps, boolean>, MapToType<PathStyleProps, boolean>>({ 
 | 
        fill: true, 
 | 
        stroke: true, 
 | 
        strokePercent: true, 
 | 
        fillOpacity: true, 
 | 
        strokeOpacity: true, 
 | 
        lineDashOffset: true, 
 | 
        lineWidth: true, 
 | 
        miterLimit: true 
 | 
    } as MapToType<PathStyleProps, boolean>, DEFAULT_COMMON_ANIMATION_PROPS.style) 
 | 
 }; 
 | 
  
 | 
export interface PathProps extends DisplayableProps { 
 | 
    strokeContainThreshold?: number 
 | 
    segmentIgnoreThreshold?: number 
 | 
    subPixelOptimize?: boolean 
 | 
  
 | 
    style?: PathStyleProps 
 | 
    shape?: Dictionary<any> 
 | 
  
 | 
    autoBatch?: boolean 
 | 
  
 | 
    __value?: (string | number)[] | (string | number) 
 | 
  
 | 
    buildPath?: ( 
 | 
        ctx: PathProxy | CanvasRenderingContext2D, 
 | 
        shapeCfg: Dictionary<any>, 
 | 
        inBatch?: boolean 
 | 
    ) => void 
 | 
} 
 | 
  
 | 
  
 | 
type PathKey = keyof PathProps 
 | 
type PathPropertyType = PropType<PathProps, PathKey> 
 | 
  
 | 
// eslint-disable-next-line @typescript-eslint/no-unused-vars 
 | 
interface Path<Props extends PathProps = PathProps> { 
 | 
    animate(key?: '', loop?: boolean): Animator<this> 
 | 
    animate(key: 'style', loop?: boolean): Animator<this['style']> 
 | 
    animate(key: 'shape', loop?: boolean): Animator<this['shape']> 
 | 
  
 | 
    getState(stateName: string): PathState 
 | 
    ensureState(stateName: string): PathState 
 | 
  
 | 
    states: Dictionary<PathState> 
 | 
    stateProxy: (stateName: string) => PathState 
 | 
} 
 | 
  
 | 
export type PathStatePropNames = DisplayableStatePropNames | 'shape'; 
 | 
export type PathState = Pick<PathProps, PathStatePropNames> & { 
 | 
    hoverLayer?: boolean 
 | 
} 
 | 
  
 | 
const pathCopyParams = (TRANSFORMABLE_PROPS as readonly string[]).concat(['invisible', 
 | 
    'culling', 'z', 'z2', 'zlevel', 'parent' 
 | 
]) as (keyof Path)[]; 
 | 
  
 | 
class Path<Props extends PathProps = PathProps> extends Displayable<Props> { 
 | 
  
 | 
    path: PathProxy 
 | 
  
 | 
    strokeContainThreshold: number 
 | 
  
 | 
    // This item default to be false. But in map series in echarts, 
 | 
    // in order to improve performance, it should be set to true, 
 | 
    // so the shorty segment won't draw. 
 | 
    segmentIgnoreThreshold: number 
 | 
  
 | 
    subPixelOptimize: boolean 
 | 
  
 | 
    style: PathStyleProps 
 | 
    /** 
 | 
     * If element can be batched automatically 
 | 
     */ 
 | 
    autoBatch: boolean 
 | 
  
 | 
    private _rectStroke: BoundingRect 
 | 
  
 | 
    protected _normalState: PathState 
 | 
  
 | 
    protected _decalEl: Path 
 | 
  
 | 
    // Must have an initial value on shape. 
 | 
    // It will be assigned by default value. 
 | 
    shape: Dictionary<any> 
 | 
  
 | 
    constructor(opts?: Props) { 
 | 
        super(opts); 
 | 
    } 
 | 
  
 | 
    update() { 
 | 
        super.update(); 
 | 
  
 | 
        const style = this.style; 
 | 
        if (style.decal) { 
 | 
            const decalEl: Path = this._decalEl = this._decalEl || new Path(); 
 | 
            if (decalEl.buildPath === Path.prototype.buildPath) { 
 | 
                decalEl.buildPath = ctx => { 
 | 
                    this.buildPath(ctx, this.shape); 
 | 
                }; 
 | 
            } 
 | 
  
 | 
            decalEl.silent = true; 
 | 
  
 | 
            const decalElStyle = decalEl.style; 
 | 
  
 | 
            for (let key in style) { 
 | 
                if ((decalElStyle as any)[key] !== (style as any)[key]) { 
 | 
                    (decalElStyle as any)[key] = (style as any)[key]; 
 | 
                } 
 | 
            } 
 | 
            decalElStyle.fill = style.fill ? style.decal : null; 
 | 
            decalElStyle.decal = null; 
 | 
            decalElStyle.shadowColor = null; 
 | 
            style.strokeFirst && (decalElStyle.stroke = null); 
 | 
  
 | 
            for (let i = 0; i < pathCopyParams.length; ++i) { 
 | 
                (decalEl as any)[pathCopyParams[i]] = this[pathCopyParams[i]]; 
 | 
            } 
 | 
  
 | 
            decalEl.__dirty |= REDRAW_BIT; 
 | 
        } 
 | 
        else if (this._decalEl) { 
 | 
            this._decalEl = null; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    getDecalElement() { 
 | 
        return this._decalEl; 
 | 
    } 
 | 
  
 | 
    protected _init(props?: Props) { 
 | 
        // Init default properties 
 | 
        const keysArr = keys(props); 
 | 
  
 | 
        this.shape = this.getDefaultShape(); 
 | 
        const defaultStyle = this.getDefaultStyle(); 
 | 
        if (defaultStyle) { 
 | 
            this.useStyle(defaultStyle); 
 | 
        } 
 | 
  
 | 
        for (let i = 0; i < keysArr.length; i++) { 
 | 
            const key = keysArr[i]; 
 | 
            const value = props[key]; 
 | 
            if (key === 'style') { 
 | 
                if (!this.style) { 
 | 
                    // PENDING Reuse style object if possible? 
 | 
                    this.useStyle(value as Props['style']); 
 | 
                } 
 | 
                else { 
 | 
                    extend(this.style, value as Props['style']); 
 | 
                } 
 | 
            } 
 | 
            else if (key === 'shape') { 
 | 
                // this.shape = value; 
 | 
                extend(this.shape, value as Props['shape']); 
 | 
            } 
 | 
            else { 
 | 
                super.attrKV(key as any, value); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        // Create an empty one if no style object exists. 
 | 
        if (!this.style) { 
 | 
            this.useStyle({}); 
 | 
        } 
 | 
        // const defaultShape = this.getDefaultShape(); 
 | 
        // if (!this.shape) { 
 | 
        //     this.shape = defaultShape; 
 | 
        // } 
 | 
        // else { 
 | 
        //     defaults(this.shape, defaultShape); 
 | 
        // } 
 | 
    } 
 | 
  
 | 
    protected getDefaultStyle(): Props['style'] { 
 | 
        return null; 
 | 
    } 
 | 
  
 | 
    // Needs to override 
 | 
    protected getDefaultShape() { 
 | 
        return {}; 
 | 
    } 
 | 
  
 | 
    protected canBeInsideText() { 
 | 
        return this.hasFill(); 
 | 
    } 
 | 
  
 | 
    protected getInsideTextFill() { 
 | 
        const pathFill = this.style.fill; 
 | 
        if (pathFill !== 'none') { 
 | 
            if (isString(pathFill)) { 
 | 
                const fillLum = lum(pathFill, 0); 
 | 
                // Determin text color based on the lum of path fill. 
 | 
                // TODO use (1 - DARK_MODE_THRESHOLD)? 
 | 
                if (fillLum > 0.5) {   // TODO Consider background lum? 
 | 
                    return DARK_LABEL_COLOR; 
 | 
                } 
 | 
                else if (fillLum > 0.2) { 
 | 
                    return LIGHTER_LABEL_COLOR; 
 | 
                } 
 | 
                return LIGHT_LABEL_COLOR; 
 | 
            } 
 | 
            else if (pathFill) { 
 | 
                return LIGHT_LABEL_COLOR; 
 | 
            } 
 | 
  
 | 
        } 
 | 
        return DARK_LABEL_COLOR; 
 | 
    } 
 | 
  
 | 
    protected getInsideTextStroke(textFill?: string) { 
 | 
        const pathFill = this.style.fill; 
 | 
        // Not stroke on none fill object or gradient object 
 | 
        if (isString(pathFill)) { 
 | 
            const zr = this.__zr; 
 | 
            const isDarkMode = !!(zr && zr.isDarkMode()); 
 | 
            const isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD; 
 | 
            // All dark or all light. 
 | 
            if (isDarkMode === isDarkLabel) { 
 | 
                return pathFill; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath 
 | 
    // Like in circle 
 | 
    buildPath( 
 | 
        ctx: PathProxy | CanvasRenderingContext2D, 
 | 
        shapeCfg: Dictionary<any>, 
 | 
        inBatch?: boolean 
 | 
    ) {} 
 | 
  
 | 
    pathUpdated() { 
 | 
        this.__dirty &= ~SHAPE_CHANGED_BIT; 
 | 
    } 
 | 
  
 | 
    getUpdatedPathProxy(inBatch?: boolean) { 
 | 
        // Update path proxy data to latest. 
 | 
        !this.path && this.createPathProxy(); 
 | 
        this.path.beginPath(); 
 | 
        this.buildPath(this.path, this.shape, inBatch); 
 | 
        return this.path; 
 | 
    } 
 | 
  
 | 
    createPathProxy() { 
 | 
        this.path = new PathProxy(false); 
 | 
    } 
 | 
  
 | 
    hasStroke() { 
 | 
        const style = this.style; 
 | 
        const stroke = style.stroke; 
 | 
        return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0)); 
 | 
    } 
 | 
  
 | 
    hasFill() { 
 | 
        const style = this.style; 
 | 
        const fill = style.fill; 
 | 
        return fill != null && fill !== 'none'; 
 | 
    } 
 | 
  
 | 
    getBoundingRect(): BoundingRect { 
 | 
        let rect = this._rect; 
 | 
        const style = this.style; 
 | 
        const needsUpdateRect = !rect; 
 | 
        if (needsUpdateRect) { 
 | 
            let firstInvoke = false; 
 | 
            if (!this.path) { 
 | 
                firstInvoke = true; 
 | 
                // Create path on demand. 
 | 
                this.createPathProxy(); 
 | 
            } 
 | 
            let path = this.path; 
 | 
            if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) { 
 | 
                path.beginPath(); 
 | 
                this.buildPath(path, this.shape, false); 
 | 
                this.pathUpdated(); 
 | 
            } 
 | 
            rect = path.getBoundingRect(); 
 | 
        } 
 | 
        this._rect = rect; 
 | 
  
 | 
        if (this.hasStroke() && this.path && this.path.len() > 0) { 
 | 
            // Needs update rect with stroke lineWidth when 
 | 
            // 1. Element changes scale or lineWidth 
 | 
            // 2. Shape is changed 
 | 
            const rectStroke = this._rectStroke || (this._rectStroke = rect.clone()); 
 | 
            if (this.__dirty || needsUpdateRect) { 
 | 
                rectStroke.copy(rect); 
 | 
                // PENDING, Min line width is needed when line is horizontal or vertical 
 | 
                const lineScale = style.strokeNoScale ? this.getLineScale() : 1; 
 | 
                // FIXME Must after updateTransform 
 | 
                let w = style.lineWidth; 
 | 
  
 | 
                // Only add extra hover lineWidth when there are no fill 
 | 
                if (!this.hasFill()) { 
 | 
                    const strokeContainThreshold = this.strokeContainThreshold; 
 | 
                    w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold); 
 | 
                } 
 | 
                // Consider line width 
 | 
                // Line scale can't be 0; 
 | 
                if (lineScale > 1e-10) { 
 | 
                    rectStroke.width += w / lineScale; 
 | 
                    rectStroke.height += w / lineScale; 
 | 
                    rectStroke.x -= w / lineScale / 2; 
 | 
                    rectStroke.y -= w / lineScale / 2; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            // Return rect with stroke 
 | 
            return rectStroke; 
 | 
        } 
 | 
  
 | 
        return rect; 
 | 
    } 
 | 
  
 | 
    contain(x: number, y: number): boolean { 
 | 
        const localPos = this.transformCoordToLocal(x, y); 
 | 
        const rect = this.getBoundingRect(); 
 | 
        const style = this.style; 
 | 
        x = localPos[0]; 
 | 
        y = localPos[1]; 
 | 
  
 | 
        if (rect.contain(x, y)) { 
 | 
            const pathProxy = this.path; 
 | 
            if (this.hasStroke()) { 
 | 
                let lineWidth = style.lineWidth; 
 | 
                let lineScale = style.strokeNoScale ? this.getLineScale() : 1; 
 | 
                // Line scale can't be 0; 
 | 
                if (lineScale > 1e-10) { 
 | 
                    // Only add extra hover lineWidth when there are no fill 
 | 
                    if (!this.hasFill()) { 
 | 
                        lineWidth = Math.max(lineWidth, this.strokeContainThreshold); 
 | 
                    } 
 | 
                    if (pathContain.containStroke( 
 | 
                        pathProxy, lineWidth / lineScale, x, y 
 | 
                    )) { 
 | 
                        return true; 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
            if (this.hasFill()) { 
 | 
                return pathContain.contain(pathProxy, x, y); 
 | 
            } 
 | 
        } 
 | 
        return false; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Shape changed 
 | 
     */ 
 | 
    dirtyShape() { 
 | 
        this.__dirty |= SHAPE_CHANGED_BIT; 
 | 
        if (this._rect) { 
 | 
            this._rect = null; 
 | 
        } 
 | 
        if (this._decalEl) { 
 | 
            this._decalEl.dirtyShape(); 
 | 
        } 
 | 
        this.markRedraw(); 
 | 
    } 
 | 
  
 | 
    dirty() { 
 | 
        this.dirtyStyle(); 
 | 
        this.dirtyShape(); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Alias for animate('shape') 
 | 
     * @param {boolean} loop 
 | 
     */ 
 | 
    animateShape(loop: boolean) { 
 | 
        return this.animate('shape', loop); 
 | 
    } 
 | 
  
 | 
    // Override updateDuringAnimation 
 | 
    updateDuringAnimation(targetKey: string) { 
 | 
        if (targetKey === 'style') { 
 | 
            this.dirtyStyle(); 
 | 
        } 
 | 
        else if (targetKey === 'shape') { 
 | 
            this.dirtyShape(); 
 | 
        } 
 | 
        else { 
 | 
            this.markRedraw(); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    // Overwrite attrKV 
 | 
    attrKV(key: PathKey, value: PathPropertyType) { 
 | 
        // FIXME 
 | 
        if (key === 'shape') { 
 | 
            this.setShape(value as Props['shape']); 
 | 
        } 
 | 
        else { 
 | 
            super.attrKV(key as keyof DisplayableProps, value); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    setShape(obj: Props['shape']): this 
 | 
    setShape<T extends keyof Props['shape']>(obj: T, value: Props['shape'][T]): this 
 | 
    setShape(keyOrObj: keyof Props['shape'] | Props['shape'], value?: unknown): this { 
 | 
        let shape = this.shape; 
 | 
        if (!shape) { 
 | 
            shape = this.shape = {}; 
 | 
        } 
 | 
        // Path from string may not have shape 
 | 
        if (typeof keyOrObj === 'string') { 
 | 
            shape[keyOrObj] = value; 
 | 
        } 
 | 
        else { 
 | 
            extend(shape, keyOrObj as Props['shape']); 
 | 
        } 
 | 
        this.dirtyShape(); 
 | 
  
 | 
        return this; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * If shape changed. used with dirtyShape 
 | 
     */ 
 | 
    shapeChanged() { 
 | 
        return !!(this.__dirty & SHAPE_CHANGED_BIT); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Create a path style object with default values in it's prototype. 
 | 
     * @override 
 | 
     */ 
 | 
    createStyle(obj?: Props['style']) { 
 | 
        return createObject(DEFAULT_PATH_STYLE, obj); 
 | 
    } 
 | 
  
 | 
    protected _innerSaveToNormal(toState: PathState) { 
 | 
        super._innerSaveToNormal(toState); 
 | 
  
 | 
        const normalState = this._normalState; 
 | 
        // Clone a new one. DON'T share object reference between states and current using. 
 | 
        // TODO: Clone array in shape?. 
 | 
        // TODO: Only save changed shape. 
 | 
        if (toState.shape && !normalState.shape) { 
 | 
            normalState.shape = extend({}, this.shape); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    protected _applyStateObj( 
 | 
        stateName: string, 
 | 
        state: PathState, 
 | 
        normalState: PathState, 
 | 
        keepCurrentStates: boolean, 
 | 
        transition: boolean, 
 | 
        animationCfg: ElementAnimateConfig 
 | 
    ) { 
 | 
        super._applyStateObj(stateName, state, normalState, keepCurrentStates, transition, animationCfg); 
 | 
        const needsRestoreToNormal = !(state && keepCurrentStates); 
 | 
        let targetShape: Props['shape']; 
 | 
        if (state && state.shape) { 
 | 
            // Only animate changed properties. 
 | 
            if (transition) { 
 | 
                if (keepCurrentStates) { 
 | 
                    targetShape = state.shape; 
 | 
                } 
 | 
                else { 
 | 
                    // Inherits from normal state. 
 | 
                    targetShape = extend({}, normalState.shape); 
 | 
                    extend(targetShape, state.shape); 
 | 
                } 
 | 
            } 
 | 
            else { 
 | 
                // Because the shape will be replaced. So inherits from current shape. 
 | 
                targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape); 
 | 
                extend(targetShape, state.shape); 
 | 
            } 
 | 
        } 
 | 
        else if (needsRestoreToNormal) { 
 | 
            targetShape = normalState.shape; 
 | 
        } 
 | 
  
 | 
        if (targetShape) { 
 | 
            if (transition) { 
 | 
                // Clone a new shape. 
 | 
                this.shape = extend({}, this.shape); 
 | 
                // Only supports transition on primary props. Because shape is not deep cloned. 
 | 
                const targetShapePrimaryProps: Props['shape'] = {}; 
 | 
                const shapeKeys = keys(targetShape); 
 | 
                for (let i = 0; i < shapeKeys.length; i++) { 
 | 
                    const key = shapeKeys[i]; 
 | 
                    if (typeof targetShape[key] === 'object') { 
 | 
                        (this.shape as Props['shape'])[key] = targetShape[key]; 
 | 
                    } 
 | 
                    else { 
 | 
                        targetShapePrimaryProps[key] = targetShape[key]; 
 | 
                    } 
 | 
                } 
 | 
                this._transitionState(stateName, { 
 | 
                    shape: targetShapePrimaryProps 
 | 
                } as Props, animationCfg); 
 | 
            } 
 | 
            else { 
 | 
                this.shape = targetShape; 
 | 
                this.dirtyShape(); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    protected _mergeStates(states: PathState[]) { 
 | 
        const mergedState = super._mergeStates(states) as PathState; 
 | 
        let mergedShape: Props['shape']; 
 | 
        for (let i = 0; i < states.length; i++) { 
 | 
            const state = states[i]; 
 | 
            if (state.shape) { 
 | 
                mergedShape = mergedShape || {}; 
 | 
                this._mergeStyle(mergedShape, state.shape); 
 | 
            } 
 | 
        } 
 | 
        if (mergedShape) { 
 | 
            mergedState.shape = mergedShape; 
 | 
        } 
 | 
        return mergedState; 
 | 
    } 
 | 
  
 | 
    getAnimationStyleProps() { 
 | 
        return DEFAULT_PATH_ANIMATION_PROPS; 
 | 
    } 
 | 
    /** 
 | 
     * If path shape is zero area 
 | 
     */ 
 | 
    isZeroArea(): boolean { 
 | 
        return false; 
 | 
    } 
 | 
    /** 
 | 
     * 扩展一个 Path element, 比如星形,圆等。 
 | 
     * Extend a path element 
 | 
     * @DEPRECATED Use class extends 
 | 
     * @param props 
 | 
     * @param props.type Path type 
 | 
     * @param props.init Initialize 
 | 
     * @param props.buildPath Overwrite buildPath method 
 | 
     * @param props.style Extended default style config 
 | 
     * @param props.shape Extended default shape config 
 | 
     */ 
 | 
    static extend<Shape extends Dictionary<any>>(defaultProps: { 
 | 
        type?: string 
 | 
        shape?: Shape 
 | 
        style?: PathStyleProps 
 | 
        beforeBrush?: Displayable['beforeBrush'] 
 | 
        afterBrush?: Displayable['afterBrush'] 
 | 
        getBoundingRect?: Displayable['getBoundingRect'] 
 | 
  
 | 
        calculateTextPosition?: Element['calculateTextPosition'] 
 | 
        buildPath(this: Path, ctx: CanvasRenderingContext2D | PathProxy, shape: Shape, inBatch?: boolean): void 
 | 
        init?(this: Path, opts: PathProps): void // TODO Should be SubPathOption 
 | 
    }): { 
 | 
        new(opts?: PathProps & {shape: Shape}): Path 
 | 
    } { 
 | 
        interface SubPathOption extends PathProps { 
 | 
            shape: Shape 
 | 
        } 
 | 
  
 | 
        class Sub extends Path { 
 | 
  
 | 
            shape: Shape 
 | 
  
 | 
            getDefaultStyle() { 
 | 
                return clone(defaultProps.style); 
 | 
            } 
 | 
  
 | 
            getDefaultShape() { 
 | 
                return clone(defaultProps.shape); 
 | 
            } 
 | 
  
 | 
            constructor(opts?: SubPathOption) { 
 | 
                super(opts); 
 | 
                defaultProps.init && defaultProps.init.call(this as any, opts); 
 | 
            } 
 | 
        } 
 | 
  
 | 
        // TODO Legacy usage. Extend functions 
 | 
        for (let key in defaultProps) { 
 | 
            if (typeof (defaultProps as any)[key] === 'function') { 
 | 
                (Sub.prototype as any)[key] = (defaultProps as any)[key]; 
 | 
            } 
 | 
        } 
 | 
        // Sub.prototype.buildPath = defaultProps.buildPath; 
 | 
        // Sub.prototype.beforeBrush = defaultProps.beforeBrush; 
 | 
        // Sub.prototype.afterBrush = defaultProps.afterBrush; 
 | 
  
 | 
        return Sub as any; 
 | 
    } 
 | 
  
 | 
    protected static initDefaultProps = (function () { 
 | 
        const pathProto = Path.prototype; 
 | 
        pathProto.type = 'path'; 
 | 
        pathProto.strokeContainThreshold = 5; 
 | 
        pathProto.segmentIgnoreThreshold = 0; 
 | 
        pathProto.subPixelOptimize = false; 
 | 
        pathProto.autoBatch = false; 
 | 
        pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT; 
 | 
    })() 
 | 
} 
 | 
  
 | 
export default Path; 
 |