From e02cbd302752eb16f581ce43f4341201bca89b02 Mon Sep 17 00:00:00 2001 From: fingerpich <zarei.bs@gmail.com> Date: Sun, 6 Jan 2019 15:34:45 +0330 Subject: [PATCH] #4 added option to change the play speed --- package-lock.json | 14 ++++++++++ package.json | 1 + src/app/app.module.ts | 2 ++ src/app/app.service.ts | 23 +++++++++++++--- src/app/container/container.component.html | 10 +++++-- src/app/container/container.component.scss | 31 ++++++++++++++++++---- src/app/container/container.component.ts | 14 +++++++++- src/app/node-types/create.ts | 17 +++++++----- src/app/node-types/rxNode.ts | 2 +- src/app/node-types/subscribe.ts | 19 +++++++------ src/app/scene/result-animator.ts | 17 ++++++++---- src/app/scene/result-path.ts | 5 ++++ src/app/scene/result.ts | 1 + 13 files changed, 122 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4e10f0..00d0b6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3408,6 +3408,11 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", "dev": true }, + "detect-passive-events": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/detect-passive-events/-/detect-passive-events-1.0.4.tgz", + "integrity": "sha1-btR35uW863kHlzXc01d4nTf5qRo=" + }, "di": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", @@ -7536,6 +7541,15 @@ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, + "ng5-slider": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/ng5-slider/-/ng5-slider-1.1.11.tgz", + "integrity": "sha512-P8bPFvx6qC5ogvvdkSZILBkDEAQcAX3QxAi1E3Fwaw0/xe7P3JCbJzWW2Z6vCR96PN0Og4wy3cEqnW8l0VAwUg==", + "requires": { + "detect-passive-events": "^1.0.4", + "tslib": "^1.7.1" + } + }, "ngx-clipboard": { "version": "11.1.9", "resolved": "https://registry.npmjs.org/ngx-clipboard/-/ngx-clipboard-11.1.9.tgz", diff --git a/package.json b/package.json index 9911846..51ab671 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@angular/service-worker": "^7.1.1", "core-js": "^2.4.1", "d3": "^5.7.0", + "ng5-slider": "^1.1.11", "ngx-clipboard": "^11.1.9", "rxjs": "^6.3.3", "rxjs-compat": "^6.0.0-rc.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c695e92..67e0ac6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -12,6 +12,7 @@ import { SceneComponent } from './scene/scene.component'; import { FlexLayoutModule } from '@angular/flex-layout'; import {AppService} from './app.service'; import { ClipboardModule } from 'ngx-clipboard'; +import { Ng5SliderModule } from 'ng5-slider'; import { PropertyComponentComponent } from './property-inspector/property-component/property-component.component'; import { ContainerComponent } from './container/container.component'; import { HeaderComponent } from './container/header/header.component'; @@ -36,6 +37,7 @@ import { environment } from '../environments/environment'; FormsModule, HttpModule, ClipboardModule, + Ng5SliderModule, FlexLayoutModule, ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) ], diff --git a/src/app/app.service.ts b/src/app/app.service.ts index d96d01b..e9206cd 100644 --- a/src/app/app.service.ts +++ b/src/app/app.service.ts @@ -5,12 +5,17 @@ import {Operator} from './operator'; import {DiagramNode} from './scene/diagram-node'; import resultAnimator from './scene/result-animator'; import {DiagramEdge} from './scene/diagram-edge'; +import {Subscribe} from './node-types'; +import {zip} from 'rxjs'; +import {take} from 'rxjs/operators'; +import {Observable} from 'rxjs/Observable'; @Injectable() export class AppService { private selectedCreationOption: any; private selectItemSubject: Subject<Operator>; private controlSubject: Subject<string>; + private animationHasFinished: Boolean = false; private nodesList: Array<DiagramNode> = []; private edgeList: Array<DiagramEdge> = []; @@ -62,7 +67,7 @@ export class AppService { const xLoc = window.innerWidth / (window.innerWidth < 600 ? 2 : 3); const yLoc = 100; const nodes = [ - {id: 0, x: xLoc, y: yLoc, node_type: 'Create', properties: {list: [{time: 0, value: 1}]}}, + {id: 0, x: xLoc, y: yLoc, node_type: 'Create', properties: {items: [{time: 0, value: 1}]}}, {id: 1, x: xLoc, y: yLoc + 200, node_type: 'Subscribe', properties: {}} ]; const edges = [{source: 0, target: 1}]; @@ -74,13 +79,15 @@ export class AppService { } public set delay(value) { GraphCreator.animateTime = value; - this.refreshRxObjects(); + if (this.animationHasFinished) { + this.refreshRxObjects(); + } } - public refreshRxObjects() { const nodes = this.nodesList; const edges = this.edgeList; + this.animationHasFinished = false; // DISPOSE created rx objects for (const node of nodes) { @@ -115,5 +122,15 @@ export class AppService { } } } + + const finishSubjects: Array<Subject<object>> = nodes + .filter(n => n.data.title === 'Subscribe') + .map(subscribeNode => (subscribeNode.data as Subscribe).finishSubject); + + zip(...finishSubjects).pipe(take(1)).subscribe(() => { + resultAnimator.hasFinished.pipe(take(1)).subscribe(() => { + this.animationHasFinished = true; + }); + }); } } diff --git a/src/app/container/container.component.html b/src/app/container/container.component.html index 32dc1d1..c629ece 100644 --- a/src/app/container/container.component.html +++ b/src/app/container/container.component.html @@ -4,12 +4,18 @@ <div fxFlex class="sceneContainer"> <button (click)="showCreationMenuToggle()" [class.active]="showCreationMenu" title="Tools (shortcut : Shift)" class="add"> <svg id="Layer_1" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44.74 44.75"><title>Tools (shortcut : Shift)</title><path d="M682.37,362a22.38,22.38,0,1,0,22.37,22.37A22.4,22.4,0,0,0,682.37,362Zm0,40.59a18.22,18.22,0,1,1,18.22-18.22,18.24,18.24,0,0,1-18.22,18.22Zm0,0" transform="translate(-660 -362)"/><path d="M692.64,382.47h-8.37V374.1a1.9,1.9,0,1,0-3.8,0v8.37H672.1a1.9,1.9,0,0,0,0,3.8h8.37v8.37a1.9,1.9,0,0,0,3.8,0v-8.37h8.37a1.9,1.9,0,0,0,0-3.8Zm0,0" transform="translate(-660 -362)"/></svg> - <!--<i class="material-icons">add</i>--> </button> <button (click)="replay()" title="Replay(shortcut : Enter)" class="replay"> <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 226 226"><title>Replay(shortcut : Enter)</title><path d="M150.27,108.56,103.34,74.48a5.63,5.63,0,0,0-9,4.56v68.2a5.58,5.58,0,0,0,5.66,5.65,5.59,5.59,0,0,0,3.3-1.1l46.93-34.08a5.56,5.56,0,0,0,2.35-4.55,5.67,5.67,0,0,0-2.35-4.6Zm0,0" transform="translate(0)"/><path d="M113,0A113,113,0,1,0,226,113,113,113,0,0,0,113,0Zm0,207.13A94.13,94.13,0,1,1,207.12,113,94.12,94.12,0,0,1,113,207.13Zm0,0" transform="translate(0)"/></svg> - <!--<i class="material-icons">play_circle_outline</i>--> </button> + <button (click)="showSlider = true" title="Time Setting" class="showTimeSetting"> + <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 29"><title>Play speed</title><path d="M299,286a3,3,0,0,0-2,5.18,12,12,0,1,0,8.46,1.7l.51-.89.43.25.5-.86-.43-.25-.86-.5-.43-.25-.5.86.43.25-.51.88a12.09,12.09,0,0,0-3.52-1.19,3,3,0,0,0-2-5.18Zm0,1a2,2,0,0,1,.5,3.93V290h.5v-1h-2v1h.5v.93A2,2,0,0,1,299,287Zm0,5a10.93,10.93,0,0,1,5.22,1.32l.09.05A11,11,0,1,1,299,292Zm0,1a10,10,0,1,0,10,10,10,10,0,0,0-10-10Zm0,1a9,9,0,1,1-9,9,9,9,0,0,1,9-9Zm0,1a.5.5,0,1,0,.5.5.5.5,0,0,0-.5-.5Zm-3.76,1a.5.5,0,0,0-.24.06.5.5,0,0,0-.18.68.5.5,0,1,0,.42-.75Zm7.49,0a.48.48,0,0,0-.41.24.5.5,0,1,0,.86.5.5.5,0,0,0-.18-.68.43.43,0,0,0-.27-.06Zm-4.23,1v4.59a1.5,1.5,0,1,0,1,0V297Zm-6,1.75a.51.51,0,0,0-.41.25.5.5,0,0,0,.18.68.5.5,0,1,0,.5-.86.52.52,0,0,0-.27-.07Zm13,0a.39.39,0,0,0-.23.07.5.5,0,1,0,.68.18.52.52,0,0,0-.45-.25Zm-14,3.75a.5.5,0,1,0,.5.5.5.5,0,0,0-.5-.5Zm7.5,0a.5.5,0,1,1-.5.5.5.5,0,0,1,.5-.5Zm7.5,0a.5.5,0,1,0,.5.5.5.5,0,0,0-.5-.5Zm-14,3.75a.44.44,0,0,0-.24.07.5.5,0,1,0,.24-.07Zm13,0a.5.5,0,0,0-.22.93.5.5,0,1,0,.5-.86.55.55,0,0,0-.28-.07ZM295.23,309a.5.5,0,0,0-.23.93.5.5,0,0,0,.68-.18.49.49,0,0,0-.45-.75Zm7.51,0a.5.5,0,1,0,.26.93.5.5,0,0,0,.18-.68.48.48,0,0,0-.44-.25ZM299,310a.5.5,0,1,0,.5.5.5.5,0,0,0-.5-.5Zm0,0" transform="translate(-287 -286)"/></svg> + </button> + <div class="slider-container" *ngIf="showSlider"> + <span class="close" (click)="showSlider=false">×</span> + <b>Player speed</b> + <ng5-slider (userChange)="sliderChanged()" [(value)]="speed.value" [options]="speed.options"></ng5-slider> + </div> <rxstudio-scene fxFlex></rxstudio-scene> <rxstudio-creation-menu (onSelect)="showCreationMenu=false" *ngIf="showCreationMenu"></rxstudio-creation-menu> </div> diff --git a/src/app/container/container.component.scss b/src/app/container/container.component.scss index f5e94ee..013014e 100644 --- a/src/app/container/container.component.scss +++ b/src/app/container/container.component.scss @@ -4,7 +4,10 @@ height:100%; padding:0; overflow: hidden; - + .close{ + cursor: pointer; + float: right; + } .sceneContainer { position: relative; border:1px solid $borderColor; @@ -13,6 +16,14 @@ height: 100%; position: absolute; } + .slider-container{ + width: 300px; + right: 0; + position: absolute; + top: 0; + background: white; + z-index: 1; + } button { border: none; border-radius: 50%; @@ -29,10 +40,14 @@ -ms-transition: padding 0.3s; -o-transition: padding 0.3s; transition: padding 0.3s; - &:hover{ - //color:lighten($primaryColor, 10%); - fill:$primaryColor; - padding: 0rem; + &.showTimeSetting{ + right: 0; + top: 0; + padding: 4px; + height: 1.6rem; + box-sizing: border-box; + width: 1.6rem; + border-radius: 16px; } &.replay{ right: 1rem; @@ -45,6 +60,12 @@ right: 1rem; bottom: 1rem; } + &:hover{ + //color:lighten($primaryColor, 10%); + fill:$primaryColor; + padding: 0rem; + } + } } rxstudio-creation-menu{ diff --git a/src/app/container/container.component.ts b/src/app/container/container.component.ts index 193a4a1..03dff97 100644 --- a/src/app/container/container.component.ts +++ b/src/app/container/container.component.ts @@ -15,8 +15,20 @@ export class ContainerComponent implements OnInit { showColdStream; showCreationMenu = false; - + showSlider = false; + speed = { + value: 6, + options: { + floor: 1, + ceil: 10 + } + } constructor(private appService: AppService, private route: ActivatedRoute) { + this.sliderChanged(); + } + + sliderChanged () { + this.appService.delay = Math.ceil(Math.pow(1.24, this.speed.value * 3 + 10)); } @HostListener('window:keyup', ['$event']) diff --git a/src/app/node-types/create.ts b/src/app/node-types/create.ts index 04fe4ca..bb9e1b1 100644 --- a/src/app/node-types/create.ts +++ b/src/app/node-types/create.ts @@ -9,8 +9,7 @@ export class Create extends RxNode { protected static maxInput = 0; protected static minInput = 0; - // protected static propertiesType = [{name:'list',type: 'list', params:[{name:'time',type:'Number'},{name:'value',type:'Number'}]}]; - protected static propertiesType = new PropertyType('list', PropertyTypeEnum.List, + protected static propertiesType = new PropertyType('items', PropertyTypeEnum.List, new PropertyType('', PropertyTypeEnum.Object, [ new PropertyType('time', PropertyTypeEnum.Number), new PropertyType('value', PropertyTypeEnum.Number) @@ -18,26 +17,30 @@ export class Create extends RxNode { , ''); public properties = { - list: [ + items: [ {time: 0, value: 1} ] }; public graphInputs = []; public runner = () => { - const delay = (observer, delayTime, value) => { + const delay = (observer, delayTime, value, isLastOne) => { setTimeout(() => { observer.next(value); + if (isLastOne) { + observer.complete(); + } }, delayTime || 0); }; return Observable.create((observer) => { - for (const l of this.properties.list) { - delay(observer, l.time, l.value); + const maxTimeItem = this.properties.items.reduce((max, item) => max.time <= item.time ? item : max, {time: 0}); + for (const item of this.properties.items) { + delay(observer, item.time, item.value, maxTimeItem === item); } }); } public toString = () => { - const list = this.properties.list; + const list = this.properties.items; const getNext = ({value, time}) => { return time ? `setTimeout(function(){ observer.next(${value});}, ${time})` : `observer.next(${value});`; }; diff --git a/src/app/node-types/rxNode.ts b/src/app/node-types/rxNode.ts index 754093d..31882a6 100644 --- a/src/app/node-types/rxNode.ts +++ b/src/app/node-types/rxNode.ts @@ -55,7 +55,7 @@ export class RxNode implements Operator { timeoutStep = level; } } - resultAnimator.add(<Result>{node, numberInfo: xx, timeoutStep}); + resultAnimator.add(<Result>{node, numberInfo: xx, timeoutStep, }); return xx; })); this.level = level; diff --git a/src/app/node-types/subscribe.ts b/src/app/node-types/subscribe.ts index 52720e0..ecfba3c 100644 --- a/src/app/node-types/subscribe.ts +++ b/src/app/node-types/subscribe.ts @@ -1,6 +1,7 @@ import {RxNode} from './rxNode'; import {map} from 'rxjs/operators'; import {NumberInfo} from '../scene/number-info'; +import {Subject} from 'rxjs/Subject'; export class Subscribe extends RxNode { protected static title = 'Subscribe'; @@ -12,6 +13,7 @@ export class Subscribe extends RxNode { public properties = {}; public graphInputs = []; + public finishSubject = new Subject(); public runner = () => { // const thisObservable = this.graphInputs[0]; @@ -20,16 +22,13 @@ export class Subscribe extends RxNode { return x; })); setTimeout(() => { - this.rxo = this.rx.subscribe( - function (x) { - console.log('Next: %s', x); - }, /* on next*/ - function (err) { - console.log('Error: %s', err); - }, /* on error*/ - function () { - console.log('Completed'); - /* on complete*/ + this.rxo = this.rx.subscribe((x) => { + console.log('asc'); + }, + (err) => { + // TODO: show errors in properties + }, () => { + this.finishSubject.next({finished: true}); }); }); return thisObservable; diff --git a/src/app/scene/result-animator.ts b/src/app/scene/result-animator.ts index 818d8fc..f611919 100644 --- a/src/app/scene/result-animator.ts +++ b/src/app/scene/result-animator.ts @@ -1,18 +1,19 @@ import {ResultPath} from './result-path'; import {Result} from './result'; -import {timer} from 'rxjs'; import {Subject} from 'rxjs/Subject'; class ResultAnimator { resultPathArray: Array<ResultPath>; resultChanged = new Subject(); - subscription; + hasFinished = new Subject(); + timeoutRef; constructor() { this.reset(); } add (res: Result) { + res.lastTicks = 0; const matchedNumInfo = this.resultPathArray.find(resPath => (resPath.id === res.numberInfo.id)); if (matchedNumInfo) { matchedNumInfo.add(res); @@ -35,12 +36,18 @@ class ResultAnimator { .map(resultPath => resultPath.getThisClockResult()) .filter(result => !!result); this.resultChanged.next(resultArray); - this.subscription = setTimeout(() => this.tick(true, delay), delay); + this.timeoutRef = setTimeout(() => this.tick(true, delay), delay); + const hasFinished = this.resultPathArray.length && this.resultPathArray.reduce((acc, result) => { + return result.hasFinished() && acc; + }, true); + if (hasFinished) { + this.hasFinished.next({finished: true}); + } } stop() { - if (this.subscription) { - clearTimeout(this.subscription); + if (this.timeoutRef) { + clearTimeout(this.timeoutRef); } } } diff --git a/src/app/scene/result-path.ts b/src/app/scene/result-path.ts index 6253aa5..37337f5 100644 --- a/src/app/scene/result-path.ts +++ b/src/app/scene/result-path.ts @@ -13,6 +13,10 @@ export class ResultPath { this.path.push(next); } + hasFinished() { + return this.path.length === 1 && this.path[0].lastTicks > 1; + } + getThisClockResult() { if (this.path.length) { if (this.path[0].timeoutStep > 1) { @@ -21,6 +25,7 @@ export class ResultPath { if (this.path.length > 1) { return this.path.shift(); } else { + this.path[0].lastTicks++; return this.path[0]; } } diff --git a/src/app/scene/result.ts b/src/app/scene/result.ts index 51d5925..1a55e7b 100644 --- a/src/app/scene/result.ts +++ b/src/app/scene/result.ts @@ -5,4 +5,5 @@ export interface Result { numberInfo: NumberInfo; node: DiagramNode; timeoutStep: number; + lastTicks: number; }