import { Draggable , IndependentDraggable } from "bp/design/class" ; import { Point } from "bp/math" ; import { ViewService } from "bp/env/service" ; import type { Memento , JVertex } from "bp/content/json" ; import type { Sheet } from "./Sheet" ; import type { TreeNode } from "bp/content/context" ; import type { Control } from "bp/design/class" ; import type { IPoint , Vector } from "bp/math" ; /**
 *  Vertex 是對應於樹狀結構中的  TreeNode 的元件。
  @ shrewd export class Vertex extends IndependentDraggable implements ISerializable < JVertex > { public get $type ( ) : string { return "Vertex" ; } public get $tag ( ) : string { return "v" + this . $node . id ; } public readonly $node : TreeNode ; constructor ( sheet , node ) { super ( sheet ) ; this . $node = node ; let option = sheet . $design . $options . get ( this ) ; if ( option ) { if ( option . name != undefined ) this . $node . name = option . name ; this . $location . x = option . x ; this . $location . y = option . y ; this . $isNew = Boolean ( option . isNew ) ; this . $selected = Boolean ( option . selected ) ; } ViewService . $createView ( this ) ; sheet . $design . $history ?. $construct ( this . $toMemento ( ) ) ; } protected get $shouldDispose ( ) : boolean { return super . $shouldDispose || this . $node . $disposed ; } protected $onDispose ( ) { this . $design ?. $history ?. $destruct ( this . $toMemento ( ) ) ; super . $onDispose ( ) ; } exports public readonly height = 0 ; exports public readonly width = 0 ; exports public get name ( ) : string { return this . $node . name ; } public set name ( n ) { this . $node . name = n ; } exports public get degree ( ) : number { return this . $node . $degree ; } public $selectableWith ( c ) { return c instanceof Vertex ; } public get $dragSelectAnchor ( ) : IPoint { return this . $location ; } protected $onDragged ( ) { if ( this . $isNew ) Draggable . $relocate ( this , this . $design . $flaps . get ( this . $node ) ! ) ; } exports public addLeaf ( length = 1 ) { let v = [ ... this . $design . $vertices . values ( ) ] ; let node = this . $node . $addLeaf ( length ) ; let p = this . $findClosestEmptyPoint ( v ) ; this . $design . $options . set ( "v" + node . id , { id : node . id , name : node . name , x : p . x , y : p . y , isNew : true , } ) ; } /** 根據所有的頂點集，找出自身附近最近的空白處 */ public $findClosestEmptyPoint ( vertices ) { let { x , y } = this . $location ; let ref = new Point ( x + 0.125 , y + 0.0625 ) ; let arr = [ ] ; let occupied = new Set < string > ( ) ; for ( let v of vertices ) occupied . add ( v . $location . x + "," + v . $location . y ) ; let r = 5 ; for ( let i = x - r ; i <= x + r ; i ++ ) { for ( let j = y - r ; j <= y + r ; j ++ ) { if ( ! occupied . has ( i + "," + j ) ) { let p = new Point ( i , j ) ; arr . push ( [ p , p . $dist ( ref ) ] ) ; } } } arr . sort ( ( a , b ) => a [ 1 ] - b [ 1 ] ) ; return arr [ 0 ] [ 0 ] ; } exports public delete ( ) { this . $node . $delete ( ) ; } exports public deleteAndJoin ( ) { if ( this . $node . $degree != 2 ) return ; let edge = this . $node . $dispose ( ) ! ; let json = edge . toJSON ( ) ; json . selected = true ; this . $design . $options . set ( edge . $tag , json ) ; } public $toMemento ( ) { return [ this . $tag , this . toJSON ( ) ] ; } public toJSON ( ) { return { id : this . $node . id , name : this . name , x : this . $location . x , y : this . $location . y , } ; } protected $constraint ( v , location ) { this . $sheet . $constraint ( v , location ) ; return v ; } }