import { action } from "bp/content/changes/action" ; import { shrewdStatic } from "bp/global" ; import { ArrayUtil } from "bp/util" ; import { Disposable } from "bp/class" ; import type { JEdge } from "bp/content/json" ; import type { Tree } from "./Tree" ; import type { TreeNode } from "./TreeNode" ; import type { IDesignLike , ITagObject } from "bp/content/interface" ; /**
 *  TreeEdge 代表  Tree 上的一條邊。
  @ shrewd export class TreeEdge extends Disposable implements ITagObject , ISerializable < JEdge > { public get $tag ( ) : string { return "e" + this . n1 . id + "," + this . n2 . id ; } public readonly n1 : TreeNode ; public readonly n2 : TreeNode ; exports @ action ( { validator : ( v ) => v > 0 } ) public length : number ; constructor ( n1 , n2 , length ) { super ( ) ; this . n1 = n1 ; this . n2 = n2 ; this . length = length ; } public $dispose ( force = false ) { if ( force ) this . tree . $edge . delete ( this . n1 , this . n2 ) ; super . $dispose ( ) ; } public toJSON ( ) { let [ n1 , n2 ] = [ this . n1 , this . n2 ] ; if ( n1 . $parent ?. id === n2 . id ) [ n1 , n2 ] = [ n2 , n1 ] ; return { n1 : n1 . id , n2 : n2 . id , length : this . length , } ; } public get tree ( ) : Tree { return this . n1 . $tree ; } public get $design ( ) : IDesignLike { return this . n1 . $design ; } protected get $shouldDispose ( ) : boolean { return super . $shouldDispose || this . n1 . $disposed || this . n2 . $disposed ; } public $delete ( ) { let node = [ this . n1 , this . n2 ] . find ( n => n . $degree == 1 ) ; if ( node ) { node . $dispose ( ) ; return true ; } return false ; } @ shrewdStatic public get $isRiver ( ) : boolean { this . $disposeEvent ( ) ; let d1 = this . n1 . $degree , d2 = this . n2 . $degree ; return d1 > 1 && d2 > 1 ; } private _adjacentEdges ( n ) { return n . edges . filter ( e => e != this ) ; } /** 相對於 n1 的所有相鄰邊 */ @ shrewdStatic public get a1 ( ) : readonly TreeEdge [ ] { this . $disposeEvent ( ) ; return this . _adjacentEdges ( this . n1 ) ; } /** 相對於 n2 的所有相鄰邊 */ @ shrewdStatic public get a2 ( ) : readonly TreeEdge [ ] { this . $disposeEvent ( ) ; return this . _adjacentEdges ( this . n2 ) ; } private static _group ( n , edges ) { let result = [ n ] ; for ( let edge of edges ) result . push ( ... edge . g ( n ) ) ; return result ; } /** 相對於 n1 側的結點群組 */ @ shrewd public get g1 ( ) : readonly TreeNode [ ] { this . $disposeEvent ( ) ; return TreeEdge . _group ( this . n1 , this . a1 ) ; } /** 相對於 n2 側的結點群組 */ @ shrewd public get g2 ( ) : readonly TreeNode [ ] { this . $disposeEvent ( ) ; return TreeEdge . _group ( this . n2 , this . a2 ) ; } /** 相反於輸入的點的那一側的結點群組 */ public g ( n ) { return n == this . n1 ? this . g2 : this . g1 ; } @ shrewd public get l1 ( ) : TreeNode [ ] { this . $disposeEvent ( ) ; return this . g1 . filter ( n => n . $degree == 1 ) ; } @ shrewd public get l2 ( ) : TreeNode [ ] { this . $disposeEvent ( ) ; return this . g2 . filter ( n => n . $degree == 1 ) ; } @ shrewd private get t1 ( ) : number { this . $disposeEvent ( ) ; return ArrayUtil . sum ( this . a1 . map ( e => e . t ( this . n1 ) + e . length ) ) ; } @ shrewd private get t2 ( ) : number { this . $disposeEvent ( ) ; return ArrayUtil . sum ( this . a2 . map ( e => e . t ( this . n2 ) + e . length ) ) ; } private t ( n ) { return n == this . n1 ? this . t2 : this . t1 ; } @ shrewd private get p1 ( ) : number { this . $disposeEvent ( ) ; return Math . max ( ... this . l1 . map ( n => n . $tree . $dist ( n , this . n1 ) ) ) ; } @ shrewd private get p2 ( ) : number { this . $disposeEvent ( ) ; return Math . max ( ... this . l2 . map ( n => n . $tree . $dist ( n , this . n2 ) ) ) ; } /** 決定這條邊的環繞側 */ @ shrewd public get $wrapSide ( ) : number { this . $disposeEvent ( ) ; if ( ! this . $isRiver ) return 0 ; if ( this . p1 > this . p2 ) return 2 ; if ( this . p1 < this . p2 ) return 1 ; if ( this . t1 > this . t2 ) return 2 ; if ( this . t1 < this . t2 ) return 1 ; if ( this . g1 . length > this . g2 . length ) return 1 ; if ( this . g1 . length < this . g2 . length ) return 2 ; return 0 ; } public n ( n ) { return n == this . n1 ? this . n2 : this . n1 ; } }