Skip to content

Commit 0b50800

Browse files
committed
connection handling + connection hooks
1 parent 56d237e commit 0b50800

File tree

4 files changed

+182
-29
lines changed

4 files changed

+182
-29
lines changed

src/Editor.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,13 @@ import { IOverlayRenderer } from "./layout/IOverlayRenderer";
1313
import { LayoutElement } from "./layout/LayoutElement";
1414
import { onDoubleClick } from "./util/onDoubleClick";
1515
import { createNewNode } from "./ui/NodeSelectionModal";
16+
import { Connection, ConnectionsArray, OuletCandidate } from "./core/Connection";
1617

1718
type MouseInfo = {
1819
clientX:number, clientY:number
1920
}
2021

21-
type Connection = {
22-
from:IOutlet
23-
to:IOutlet|Vector2Like
24-
}
2522

26-
type OuletCandidate = {
27-
outlet:IOutlet
28-
alignmentScore:number
29-
}
3023

3124
/**
3225
* I'm the editor. I show the nodes, and run the ThreeJs background scene.
@@ -42,7 +35,7 @@ export class Editor {
4235

4336
protected objs:Node[] = [];
4437
protected zSortedObjs:Node[] = [];
45-
readonly connections:Connection[] = [];
38+
readonly connections:ConnectionsArray = new ConnectionsArray();
4639

4740
/**
4841
* Available outlets to connect the current "open" connection to...
@@ -176,7 +169,8 @@ export class Editor {
176169
//
177170
// update tail of "open" connections
178171
//
179-
this.connections.filter(c=>!( isOutlet(c.to) )).forEach( c=>c.to=mousePos);
172+
///this.connections.filter(c=>!( isOutlet(c.to) )).forEach( c=>c.to=mousePos);
173+
this.connections.setOrphansTarget( mousePos );
180174
}
181175

182176
});
@@ -223,7 +217,7 @@ export class Editor {
223217
// let's see if some canvas element wants to capture the mouse....
224218
//
225219
for (const obj of this.zSortedObjs) {
226-
console.log( obj)
220+
227221
//#region OUTLET
228222
//
229223
// check if mouse if over an outlet to create/delete a connection
@@ -362,25 +356,29 @@ export class Editor {
362356
this.chosenOutlet.sort((a,b)=>b.alignmentScore-a.alignmentScore);
363357

364358

365-
this.destroyConnectionsUsing( this.chosenOutlet[0].outlet )
359+
this.destroyConnectionsUsing( this.chosenOutlet[0].outlet );
360+
361+
this.connections.setOrphansTarget( this.chosenOutlet[0].outlet );
366362

367-
this.connections.forEach( connection => {
363+
console.log("CONNECT", this.chosenOutlet[0].outlet)
364+
// this.connections.forEach( connection => {
368365

369-
if( !("owner" in connection.to))
370-
{
371-
connection.to = this.chosenOutlet[0].outlet;
372-
}
366+
// if( !("owner" in connection.to))
367+
// {
368+
// connection.to = this.chosenOutlet[0].outlet;
369+
// }
373370

374-
});
371+
// });
375372
}
376373

377374
this.chosenOutlet.length=0;
378375
this.availableOutlets.length=0;
379376

380377
//remove connections with no end...
381-
let clean = this.connections.filter( c=>"owner" in c.to);
382-
this.connections.length=0;
383-
this.connections.push(...clean)
378+
this.connections.purge( c=>isOutlet(c.to) )
379+
// let clean = this.connections.filter( c=>"owner" in c.to);
380+
// this.connections.length=0;
381+
// this.connections.push(...clean)
384382
}
385383
});
386384
//#endregion
@@ -456,12 +454,13 @@ export class Editor {
456454

457455
protected destroyConnectionsUsing( outlet:IOutlet )
458456
{
459-
const clean = this.connections.filter( c=>( c.from!==outlet )
460-
&& ( c.to !==outlet )
461-
);
457+
this.connections.purge( connection=>connection.from!==outlet && (!isOutlet(connection.to) || connection.to!==outlet) )
458+
// const clean = this.connections.filter( c=>( c.from!==outlet )
459+
// && ( c.to !==outlet )
460+
// );
462461

463-
this.connections.length = 0;
464-
this.connections.push( ...clean );
462+
// this.connections.length = 0;
463+
// this.connections.push( ...clean );
465464
}
466465

467466
protected global2canvas( position:Vector2Like ) {

src/core/Connection.ts

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { Vector2Like } from "three"
2+
import { IOutlet } from "./IOutlet"
3+
import { isOutlet } from "./isOutlet"
4+
5+
export type Connection = {
6+
from:IOutlet
7+
to:IOutlet|Vector2Like
8+
}
9+
10+
export type OuletCandidate = {
11+
outlet:IOutlet
12+
alignmentScore:number
13+
}
14+
15+
export class ConnectionsArray<T extends Connection=Connection> {
16+
private _array: T[] = [];
17+
18+
get array(): T[] {
19+
return this._array;
20+
}
21+
22+
private connect( connection:Connection )
23+
{
24+
const other = connection.to;
25+
if( isOutlet(other) )
26+
{
27+
connection.from.connectedTo = other;
28+
other.connectedTo = connection.from;
29+
}
30+
}
31+
32+
private disconnect( connection:Connection ) {
33+
if( isOutlet( connection.to ) )
34+
{
35+
connection.to.connectedTo = undefined;
36+
connection.from.connectedTo = undefined;
37+
}
38+
}
39+
40+
push(...items: T[]): number {
41+
const result = this._array.push(...items);
42+
43+
items.forEach( connection=>this.connect(connection))
44+
45+
return result;
46+
}
47+
48+
pop(): T | undefined {
49+
const result = this._array.pop();
50+
51+
if( result )
52+
{
53+
this.disconnect( result );
54+
}
55+
56+
return result;
57+
}
58+
59+
splice(start: number, deleteCount?: number, ...items: T[]) {
60+
const result = this._array.splice(start, deleteCount ?? 0, ...items);
61+
62+
if( deleteCount )
63+
{
64+
result.forEach( connection=>this.disconnect(connection));
65+
}
66+
67+
items.forEach( connection=>this.connect(connection));
68+
69+
return this;
70+
}
71+
72+
filter( filterer:( connection:T, index:number, connections:T[])=>boolean ) {
73+
const filtered = this._array.filter( (connection, i, arr)=>{
74+
const ok = filterer( connection, i, arr );
75+
if(!ok) {
76+
this.disconnect( connection );
77+
}
78+
return ok;
79+
});
80+
return filtered;
81+
}
82+
83+
purge( judge:( connection:T )=>boolean ){
84+
this._array = this._array.filter( con=> {
85+
86+
const ok = judge( con );
87+
if(!ok) {
88+
this.disconnect( con )
89+
}
90+
return ok;
91+
92+
});
93+
return this;
94+
}
95+
96+
setOrphansTarget( target:IOutlet|Vector2Like )
97+
{
98+
this._array.filter( connection=>!isOutlet(connection.to) ).forEach( orphan=>{
99+
100+
orphan.to=target;
101+
this.connect(orphan)
102+
103+
})
104+
}
105+
106+
forEach( looper:(connection:T, index:number)=>void) {
107+
this._array.forEach( looper );
108+
}
109+
110+
*[Symbol.iterator](): Iterator<T> {
111+
yield* this._array;
112+
}
113+
114+
get length(): number {
115+
return this._array.length;
116+
}
117+
118+
}

src/properties/OutletProperty.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class OutletProperty extends LayoutElement implements IOutlet
1212
private _globalY = 0;
1313
private _isInput = true;
1414
private _size = 1;
15+
private _connectedTo:IOutlet | undefined;
1516

1617
get isInput() { return this._isInput; }
1718
get globalX() { return this._globalX; }
@@ -37,8 +38,26 @@ export class OutletProperty extends LayoutElement implements IOutlet
3738
isCompatible(other: IOutlet): boolean {
3839
return this.size == 5 || other.size==5? this.size==other.size : true
3940
}
41+
42+
get connectedTo(){ return this._connectedTo; }
43+
set connectedTo( other:IOutlet|undefined) {
44+
const oldOther = this._connectedTo;
45+
const changed = oldOther!==other;
46+
this._connectedTo = other;
47+
if( changed ) {
48+
if( other )
49+
{
50+
this.onConnected( other );
51+
}
52+
else
53+
{
54+
if( oldOther ) this.onDisconnected( oldOther )
55+
}
56+
}
57+
}
4058

41-
connectedTo?: IOutlet | undefined;
59+
protected onConnected( to:IOutlet ) {}
60+
protected onDisconnected( from:IOutlet ){}
4261

4362
get owner(): Node {
4463
return this.root as Node;

src/properties/TextureProperty.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
import { Theme } from "../colors/Theme";
33
import { Button } from "../components/Button";
44
import { ImagePreview } from "../components/ImagePreview";
5-
import { Layout } from "../layout/Layout";
5+
import { TextLabel } from "../components/TextLabel";
6+
import { IOutlet } from "../core/IOutlet";
7+
import { Layout, Row } from "../layout/Layout";
68
import { LayoutElement } from "../layout/LayoutElement";
79
import { Input } from "./Input";
810

911
export class TextureProperty extends Input {
1012

1113
private initial : Layout;
12-
private _imageDisplayLayout : Layout;
14+
private overwritten : Layout;
15+
private _imageDisplayLayout : Layout;
1316

1417
private _fileName?:string;
1518

@@ -50,6 +53,12 @@ export class TextureProperty extends Input {
5053
align:"stretch"
5154
});
5255

56+
this.overwritten = new Row([
57+
new TextLabel("Texture Data")
58+
] );
59+
60+
this.overwritten.xPadding = 10
61+
5362
this.initial.parent = this;
5463
this._imageDisplayLayout.parent = this;
5564

@@ -100,6 +109,14 @@ export class TextureProperty extends Input {
100109
reader.readAsDataURL(file);
101110
}
102111

112+
protected override onConnected(to: IOutlet): void {
113+
this.layout = this.overwritten;
114+
}
115+
116+
protected override onDisconnected(from: IOutlet): void {
117+
this.layout = this.initial;
118+
}
119+
103120
protected reset() {
104121
this.imgPreview.reset();
105122
this.layout = this.initial;

0 commit comments

Comments
 (0)