Skip to content

Commit

Permalink
added UpdateManager to better know when to redraw the graphic
Browse files Browse the repository at this point in the history
  • Loading branch information
daign committed Apr 13, 2023
1 parent 1ad46fb commit 9866492
Show file tree
Hide file tree
Showing 15 changed files with 289 additions and 130 deletions.
22 changes: 8 additions & 14 deletions lib/application/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ControlLayer } from './controlLayer';
import { InteractiveViewport } from './interactiveViewport';
import { SelectionManager } from './selectionManager';
import { Viewport } from './viewport';
import { UpdateManager } from './updateManager';

/**
* Application consisting of a drawing layer and a control layer.
Expand All @@ -13,6 +14,9 @@ export class Application extends Group {
// The selection manager.
public selectionManager: SelectionManager;

// The update manager.
public updateManager: UpdateManager;

// The drawing layer and interactive viewport.
public drawingLayer: Viewport;

Expand All @@ -37,16 +41,15 @@ export class Application extends Group {
this.controlLayer = new ControlLayer( this );
this.appendChild( this.controlLayer );

// Build controls when selection changes.
this.selectionManager.subscribeToChanges( (): void => {
this.createControls();
this.drawingLayer.redrawObservable.notify();
} );

} else {
// If not interactive then use the normal Viewport and no ControlLayer.
this.drawingLayer = new Viewport( context, this );
this.appendChild( this.drawingLayer );
}

this.updateManager = new UpdateManager( this.selectionManager, this.controlLayer,
this.drawingLayer );
}

/**
Expand All @@ -56,13 +59,4 @@ export class Application extends Group {
public fitToContent( margin?: number ): void {
this.drawingLayer.fitToContent( margin );
}

/**
* Create the control elements for the active control object.
*/
public createControls(): void {
if ( this.controlLayer ) {
this.controlLayer.createControls();
}
}
}
1 change: 1 addition & 0 deletions lib/application/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export { ApplicationView } from './applicationView';
export { ControlLayer } from './controlLayer';
export { InteractiveViewport } from './interactiveViewport';
export { SelectionManager } from './selectionManager';
export { UpdateManager } from './updateManager';
export { Viewport } from './viewport';
5 changes: 0 additions & 5 deletions lib/application/interactiveViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,11 @@ export class InteractiveViewport extends Viewport {
}

this.updateViewport();
this.application.createControls();
this.redrawObservable.notify();
};

// Define action to deactivate element.
handle.clicked = (): void => {
this.application.selectionManager.setSelection( null, null );
this.redrawObservable.notify();
};

// Define zoom action.
Expand All @@ -158,8 +155,6 @@ export class InteractiveViewport extends Viewport {
this.viewCenter.sub( mousePosition ).multiplyScalar( 1 / factor ).add( mousePosition );

this.updateViewport();
this.application.createControls();
this.redrawObservable.notify();
};
}
}
Expand Down
73 changes: 73 additions & 0 deletions lib/application/updateManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Observable } from '@daign/observable';

import { SelectionManager } from './selectionManager';
import { ControlLayer } from './controlLayer';
import { Viewport } from './viewport';

// Observable implementation with a public invoke method.
class EventSource extends Observable {
public constructor() {
super();
}
public invoke(): void {
this.notifyObservers();
}
}

/**
* Class that manages update and change notifications between components.
*/
export class UpdateManager {
// Subscribe to know when the graphic data has changes.
public dataChangeEvent: EventSource = new EventSource();

// Subscribe to know when the selection changes.
public selectionChangeEvent: EventSource = new EventSource();

// Subscribe to know when the graphic needs to be redrawn.
public redrawEvent: EventSource = new EventSource();

// Subscribe to know when the viewport has changed.
public viewportInputEvent: EventSource = new EventSource();

/**
* Constructor.
* @param selectionManager - The selection manager.
* @param controlLayer - The control layer.
* @param drawingLayer - The drawing layer.
*/
public constructor(
private selectionManager: SelectionManager,
private controlLayer: ControlLayer | null,
private drawingLayer: Viewport
) {
// Actions to take when the selection manager has changes.
this.selectionManager.subscribeToChanges( (): void => {
this.selectionChangeEvent.invoke();
this.createControls();
this.redrawEvent.invoke();
} );

// Actions to take when the drawing layer has changes.
this.drawingLayer.subscribeToChanges( (): void => {
this.dataChangeEvent.invoke();
this.createControls();
this.redrawEvent.invoke();
} );

// Actions to take when the viewport has changed.
this.viewportInputEvent.subscribeToChanges( (): void => {
this.createControls();
this.redrawEvent.invoke();
} );
}

/**
* Let the control layer create the controls for the currently selected object.
*/
public createControls(): void {
if ( this.controlLayer ) {
this.controlLayer.createControls();
}
}
}
16 changes: 2 additions & 14 deletions lib/application/viewport.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import { Vector2 } from '@daign/math';
import { NativeScaleTransform, NativeTranslateTransform } from '@daign/2d-pipeline';
import { Observable } from '@daign/observable';

import { Group } from '../basic-elements/group';
import { ITargetContext } from '../iTargetContext';

import { Application } from './application';

// Observable implementation with a public notify method.
class ObservableObject extends Observable {
public constructor() {
super();
}
public notify(): void {
this.notifyObservers();
}
}

/**
* Group that acts as an viewport by applying a transformation to bring objects into the visible
* area.
Expand All @@ -35,9 +24,6 @@ export class Viewport extends Group {
// Scaling of the viewport content.
protected viewScale: number = 1;

// Observable to signal redraw execution.
public redrawObservable: ObservableObject = new ObservableObject();

/**
* Constructor.
* @param context - The target drawing context.
Expand Down Expand Up @@ -95,5 +81,7 @@ export class Viewport extends Group {
this.decenteringTransform.translation.copy( decentering );
this.scaleTransform.scaling.copy( scaling );
this.translateTransform.translation.copy( translation );

this.application.updateManager.viewportInputEvent.invoke();
}
}
10 changes: 1 addition & 9 deletions lib/control-elements/buttonControl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Matrix3 } from '@daign/math';
import { MatrixTransform } from '@daign/2d-pipeline';

import { Application } from '../application';
import { FixedRadiusCircle, Group } from '../basic-elements';

import { ButtonObject } from './buttonObject';
Expand All @@ -13,22 +12,17 @@ export class ButtonControl extends Group {
// The callback to call on click.
private callback: () => void;

// The corresponding application.
private application: Application;

/**
* Constructor.
* @param buttonObject - The button object to display.
* @param targetTransformation - The transformation matrix of the control object.
* @param application - The corresponding application.
*/
public constructor(
buttonObject: ButtonObject, targetTransformation: Matrix3, application: Application
buttonObject: ButtonObject, targetTransformation: Matrix3
) {
super();

this.application = application;

this.callback = buttonObject.callback;

/* The anchor position is used to calculate an offset transformation that gets applied to all
Expand Down Expand Up @@ -56,7 +50,5 @@ export class ButtonControl extends Group {
*/
public click(): void {
this.callback();
this.application.createControls();
this.application.drawingLayer.redrawObservable.notify();
}
}
2 changes: 1 addition & 1 deletion lib/control-elements/controlObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export abstract class ControlObject extends Group {

// Add the button controls.
this.buttons.forEach( ( button: ButtonObject ): void => {
const buttonControl = new ButtonControl( button, transformation, application );
const buttonControl = new ButtonControl( button, transformation );
group.appendChild( buttonControl );
} );

Expand Down
3 changes: 0 additions & 3 deletions lib/control-elements/controlPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,5 @@ export class ControlPoint extends Group {
} );

this.calculateOffset();

this.application.createControls();
this.application.drawingLayer.redrawObservable.notify();
}
}
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { ApplicationView } from './application/applicationView';
export { ControlLayer } from './application/controlLayer';
export { InteractiveViewport } from './application/interactiveViewport';
export { SelectionManager } from './application/selectionManager';
export { UpdateManager } from './application/updateManager';
export { Viewport } from './application/viewport';

export { FixedRadiusCircle } from './basic-elements/fixedRadiusCircle';
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@daign/2d-graphics",
"version": "1.1.1",
"version": "1.1.2",
"description": "Two dimensional graphics library that implements the daign-2d-pipeline.",
"keywords": [
"graphics",
Expand Down
64 changes: 1 addition & 63 deletions test/application/application.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import { expect } from 'chai';
import { spy } from 'sinon';

import { Vector2 } from '@daign/math';
import { View } from '@daign/2d-pipeline';

import { Application, ControlObject } from '../../lib';
import { Application } from '../../lib';
import { TestContext } from '../testContext';

class TestObject extends ControlObject {
public constructor() {
super();
}
}

describe( 'Application', (): void => {
describe( 'constructor', (): void => {
it( 'should append one child to the node', (): void => {
Expand All @@ -37,27 +28,6 @@ describe( 'Application', (): void => {
// Assert
expect( application.children.length ).to.equal( 2 );
} );

it( 'should call createControls when there are changes in the selection manager', (): void => {
// Arrange
const context = new TestContext();
const application = new Application( context, true );

const view = new View();
view.mountNode( application );

const targetPoint = new Vector2( 1, 2 );
const controlObject = new TestObject();
controlObject.points.push( targetPoint );
application.drawingLayer.appendChild( controlObject );
const spyCreateControls = spy( application.controlLayer!, 'createControls' );

// Act
application.selectionManager.setSelection( controlObject, null );

// Assert
expect( spyCreateControls.calledOnce ).to.be.true;
} );
} );

describe( 'fitToContent', (): void => {
Expand All @@ -75,36 +45,4 @@ describe( 'Application', (): void => {
expect( spyFitToContent.calledWith( 2 ) ).to.be.true;
} );
} );

describe( 'createControls', (): void => {
it( 'should call createControls on controlLayer', (): void => {
// Arrange
const context = new TestContext();
const application = new Application( context, true );
const spyCreateControls = spy( application.controlLayer!, 'createControls' );

const view = new View();
view.mountNode( application );

// Act
application.createControls();

// Assert
expect( spyCreateControls.calledOnce ).to.be.true;
} );

it( 'should not throw error when control layer does not exist', (): void => {
// Arrange
const context = new TestContext();
const application = new Application( context, false );

// Act
const goodFn = (): void => {
application.createControls();
};

// Assert
expect( goodFn ).to.not.throw();
} );
} );
} );

0 comments on commit 9866492

Please sign in to comment.