import { Store } from "./Store" ; import { Partition } from "../calculation/Partition" ; import { Partitioner } from "../calculation/Partitioner" ; import { Gadget , Pattern } from "../models" ; import { GeneratorUtil , clone } from "bp/util" ; import { Point } from "bp/math" ; import { Quadrant } from "bp/design/components" ; import type { IPoint } from "bp/math" ; import type { JJunction , JDevice , JGadget , JOverlap , JPattern , JConfiguration } from "bp/content/json" ; import type { GDevice , GPattern } from ".." ; import type { Repository } from "./Repository" ; /**
 * 一種  Configuration 是一套把  Junction * 群組構成的重疊區域切割成  Partition 的配置。
  @ shrewd export class Configuration extends Store < JPattern , Pattern > implements ISerializable < JConfiguration > { public get $tag ( ) : string { return this . $repository . $tag + "." + this . $repository . $indexOf ( this ) ; } public readonly $repository : Repository ; /** 依照 Overlap 的 id（負數）來檢索，得出它對應於第幾個 Partition 的第幾個 Overlap */ public readonly $overlapMap : ReadonlyMap < number , [ number , number ] > ; public readonly $partitions : readonly Partition [ ] ; protected $generator : Generator < JPattern > ; private _seed ? : JPattern ; private _seedSignature ? : string ; /** 所有的 JOverlap 陣列 */ public readonly $overlaps : readonly JOverlap [ ] ; constructor ( set , config , seed ? ) { super ( set . $sheet ) ; this . $repository = set ; this . _seed = seed ; if ( seed ) this . _seedSignature = Pattern . $getSignature ( seed ) ; let overlaps = [ ] ; let overlapMap = new Map ( ) ; let k = - 1 ; for ( let [ i , p ] of config . partitions . entries ( ) ) { for ( let [ j , o ] of p . overlaps . entries ( ) ) { overlaps . push ( o ) ; overlapMap . set ( k -- , [ i , j ] ) ; } } this . $overlaps = overlaps ; this . $overlapMap = overlapMap ; this . $partitions = config . partitions . map ( p => new Partition ( this , p ) ) ; if ( config . patterns ) { this . $restore ( config . patterns , config . index ! ) ; } else { this . $generator = this . _generate ( ) ; } } protected get $shouldDispose ( ) : boolean { return super . $shouldDispose || this . $repository . $disposed ; } shrewd @ shrewd public get _isActive ( ) : boolean { return this . $repository . _isActive && this . $repository . entry == this ; } protected $builder ( prototype ) { return new Pattern ( this , prototype ) ; } /** 在當前的  Configuration 中尋找  Pattern private * _generate ( ) { if ( this . _seed ) yield this . _seed ; let filter = ( pattern ) : boolean => ! this . _seedSignature || this . _seedSignature != Pattern . $getSignature ( pattern ) ; yield * GeneratorUtil . $filter ( this . _search ( [ ] ) , filter ) ; } private * _search ( devices , depth = 0 ) { if ( depth == this . $partitions . length ) { let p = this . _makePattern ( clone ( devices ) ) ; if ( p ) yield p ; } else { for ( let d of this . $partitions [ depth ] . $generate ( ) ) { devices . push ( d ) ; yield * this . _search ( devices , depth + 1 ) ; devices . pop ( ) ; } } } /** 安排位置並產生  Pattern ；如果結果太大，則傳回 null 表示失敗  private _makePattern ( prototypes ) { prototypes . forEach ( d => d . gadgets = d . gadgets . map ( g => Gadget . $instantiate ( g ) ) ) ; let devices = prototypes as GDevice [ ] ; let junctions = this . $repository . $structure ; if ( junctions . length == 1 ) { return this . _makeSingleRegularDevicePattern ( junctions [ 0 ] , devices ) ; } if ( junctions . length == 2 && this . $partitions . length == 1 ) { return this . _makeSingeJoinDevicePattern ( devices ) ; } if ( junctions . length == 2 && this . $partitions . length == 2 ) { return this . _makeTwoDeviceRelayPattern ( devices ) ; } return null ; } private _makeSingleRegularDevicePattern ( junction , devices ) { let sx = junction . sx ; if ( devices . length == 1 ) { devices [ 0 ] . offset = Math . floor ( ( sx - devices [ 0 ] . gadgets [ 0 ] . sx ) / 2 ) ; return { devices } ; } if ( devices . length == 2 ) { let [ g1 , g2 ] = devices . map ( d => d . gadgets [ 0 ] ) ; let c1 = this . $overlaps [ 0 ] . c [ 2 ] ; let c2 = this . $overlaps [ 1 ] . c [ 0 ] ; let tx1 = g1 . sx + g2 . rx ( c1 . q ! , 2 ) ; let tx2 = g2 . sx + g1 . rx ( c2 . q ! , 0 ) ; if ( tx1 > sx || tx2 > sx ) return null ; devices [ 1 ] . offset = sx - tx2 ; return { devices } ; } return null ; } private _makeSingeJoinDevicePattern ( devices ) { let [ o1 , o2 ] = this . $overlaps ; let [ j1 , j2 ] = [ o1 , o2 ] . map ( o => this . $repository . $structure [ o . parent ] ) ; let oriented = j1 . c [ 0 ] . e == j2 . c [ 0 ] . e ; let gadgets = devices [ 0 ] . gadgets ; if ( gadgets [ 0 ] . sx > j1 . sx || gadgets [ 1 ] . sx > j2 . sx ) return null ; if ( ! oriented ) devices [ 0 ] . offset = j1 . sx - gadgets [ 0 ] . sx ; return { devices } ; } private _makeTwoDeviceRelayPattern ( devices ) { let [ g1 , g2 ] = devices . map ( d => d . gadgets [ 0 ] ) ; let [ o1 , o2 ] = this . $overlaps ; let reversed = o1 . c [ 0 ] . e ! >= 0 && o1 . c [ 2 ] . e ! >= 0 ; if ( reversed ) { [ g1 , g2 ] = [ g2 , g1 ] ; [ o1 , o2 ] = [ o2 , o1 ] ; } let [ j1 , j2 ] = [ o1 , o2 ] . map ( o => this . $repository . $structure [ o . parent ] ) ; let oriented = o1 . c [ 0 ] . e ! < 0 ; let q = oriented ? 0 : 2 , tq = o1 . c [ q ] . q ! ; let sx = j1 . sx , tx = g1 . sx ; let s = g1 . $setupConnectionSlack ( g2 , q , tq ) ; sx -= Math . ceil ( g2 . rx ( tq , q ) ) + s ; let offsets = oriented ? [ s ?? 0 , 0 ] : [ sx - tx , j2 . sx - g2 . sx ] ; if ( reversed ) offsets . reverse ( ) ; if ( tx > sx ) return null ; let delta = this . _getRelativeDelta ( j1 , j2 , g2 ) ; if ( g2 . $intersects ( delta , oriented ? Quadrant . QV [ 0 ] : Quadrant . QV [ 2 ] ) ) return null ; devices . forEach ( ( d , i ) => d . offset = offsets [ i ] ) ; return { devices } ; } /** 取得 delta 點相對於特定 Gadget 的座標 */ private _getRelativeDelta ( j1 , j2 , g ) { let oriented = j1 . c [ 0 ] . e == j2 . c [ 0 ] . e ; let r = Partitioner . $getMaxIntersectionDistance ( this . $design . $tree , j1 , j2 , oriented ) ; if ( j2 . ox > j1 . ox ) [ j1 , j2 ] = [ j2 , j1 ] ; let p = { x : r - j2 . ox , y : r - j1 . oy } ; if ( ! oriented ) { p . x = g . sx - p . x ; p . y = g . sy - p . y ; } return new Point ( p ) ; } protected $onMove ( ) { this . $repository . $stretch . $selected = ! this . entry ! . $selected ; } /** 是否為第一次產生 Memento */ private _initMemento = true ; public $getMemento ( ) { let result = this . _initMemento ? this . _prototypes : this . $memento . map ( p => p instanceof Pattern ? p . toJSON ( ) : p ) ; this . _initMemento = false ; return result ; } public toJSON ( session = false ) { let result = { partitions : this . $partitions . map ( p => p . toJSON ( ) ) } ; if ( session ) { result . patterns = this . $getMemento ( ) ; result . index = this . index ; } return result ; } }