prepared animations and added central arithmetic service
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<app-graphs [config]="config" [restoredHistory]="restoredHistory" (svgChange)="prepareSvgExport($event)"></app-graphs>
|
<app-graphs [config]="config" [restoredHistory]="restoredHistory" (svgChange)="prepareSvgExport($event)" [animationActive]="animationActive"></app-graphs>
|
||||||
|
|
||||||
<aside class="col-sm-4 col-lg-3 col-xl-3">
|
<aside class="col-sm-4 col-lg-3 col-xl-3">
|
||||||
<div class="aside-inner">
|
<div class="aside-inner">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AnimationService } from './services/animation.service';
|
||||||
/**
|
/**
|
||||||
* Copyright (C) 2018 Michael Czechowski <mail@dailysh.it>
|
* Copyright (C) 2018 Michael Czechowski <mail@dailysh.it>
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { GuillocheDirective } from './directives/guilloche.directive';
|
|||||||
import { CanvasService } from './services/canvas.service';
|
import { CanvasService } from './services/canvas.service';
|
||||||
import { HistoryService } from './services/history.service';
|
import { HistoryService } from './services/history.service';
|
||||||
import { AnimationService } from './services/animation.service';
|
import { AnimationService } from './services/animation.service';
|
||||||
|
import { ArithmeticService } from './services/arithmetic.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@@ -41,7 +42,8 @@ import { AnimationService } from './services/animation.service';
|
|||||||
providers: [
|
providers: [
|
||||||
CanvasService,
|
CanvasService,
|
||||||
HistoryService,
|
HistoryService,
|
||||||
AnimationService
|
AnimationService,
|
||||||
|
ArithmeticService,
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,16 +14,19 @@
|
|||||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ViewChild, QueryList, Component, Input, Output, SimpleChanges, OnChanges, HostListener, EventEmitter } from '@angular/core';
|
import { ViewChild, QueryList, Component, Input, Output, SimpleChanges, OnChanges, HostListener, EventEmitter, OnInit } from '@angular/core';
|
||||||
|
import { Observable, interval, Subscription } from 'rxjs';
|
||||||
import * as Selection from 'd3-selection';
|
import * as Selection from 'd3-selection';
|
||||||
import * as Shape from 'd3-shape';
|
import * as Shape from 'd3-shape';
|
||||||
import * as Random from 'd3-random';
|
import * as Random from 'd3-random';
|
||||||
import * as Drag from 'd3-drag';
|
import * as Drag from 'd3-drag';
|
||||||
|
|
||||||
import { environment as env } from '../../environments/environment';
|
import { environment as env } from '../../environments/environment';
|
||||||
import { GuillocheDirective } from './../directives/guilloche.directive';
|
|
||||||
import { CanvasService } from './../services/canvas.service';
|
import { CanvasService } from './../services/canvas.service';
|
||||||
import { HistoryService } from './../services/history.service';
|
import { HistoryService } from './../services/history.service';
|
||||||
|
import { AnimationService } from '../services/animation.service';
|
||||||
|
import { ArithmeticService } from '../services/arithmetic.service';
|
||||||
|
import { GuillocheDirective } from './../directives/guilloche.directive';
|
||||||
import { Graph } from '../models/graph.model';
|
import { Graph } from '../models/graph.model';
|
||||||
import { Point } from '../models/point.model';
|
import { Point } from '../models/point.model';
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ import { Point } from '../models/point.model';
|
|||||||
templateUrl: './graphs.component.html',
|
templateUrl: './graphs.component.html',
|
||||||
styleUrls: ['./graphs.component.scss']
|
styleUrls: ['./graphs.component.scss']
|
||||||
})
|
})
|
||||||
export class GraphsComponent implements OnChanges {
|
export class GraphsComponent implements OnChanges, OnInit {
|
||||||
|
|
||||||
public canvas: any | null;
|
public canvas: any | null;
|
||||||
public matrix: any | null;
|
public matrix: any | null;
|
||||||
@@ -41,34 +44,60 @@ export class GraphsComponent implements OnChanges {
|
|||||||
private genShiftPoint: any | null;
|
private genShiftPoint: any | null;
|
||||||
private genLoadedAllGraphs: any | null;
|
private genLoadedAllGraphs: any | null;
|
||||||
private hash: string;
|
private hash: string;
|
||||||
|
private animation: Observable<Graph[]>;
|
||||||
|
private timer: Observable<number>;
|
||||||
|
private animationSteps: Subscription;
|
||||||
|
|
||||||
@Input() config: any;
|
@Input() config: any;
|
||||||
@Input() restoredHistory: any;
|
@Input() restoredHistory: any;
|
||||||
|
@Input() animationActive: boolean;
|
||||||
@Output() svgChange = new EventEmitter();
|
@Output() svgChange = new EventEmitter();
|
||||||
@Output() graphChange = new EventEmitter();
|
@Output() graphChange = new EventEmitter();
|
||||||
@ViewChild('svg') svgElementRef;
|
@ViewChild('svg') svgElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvasService: CanvasService,
|
private canvasService: CanvasService,
|
||||||
private historyService: HistoryService
|
private historyService: HistoryService,
|
||||||
|
private animationService: AnimationService,
|
||||||
|
private arithmetics: ArithmeticService
|
||||||
) {
|
) {
|
||||||
this.genLoadedAllGraphs = this.countLoadedGraphs();
|
this.genLoadedAllGraphs = this.countLoadedGraphs();
|
||||||
|
this.timer = interval(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.updateGraphs();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges) {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
this.updateCanvas();
|
this.updateCanvas();
|
||||||
this.updateMatrix();
|
this.updateMatrix();
|
||||||
|
|
||||||
if (changes.restoredHistory) {
|
if (changes.config && !changes.config.firstChange) {
|
||||||
if (changes.restoredHistory.currentValue) {
|
this.updateGraphs();
|
||||||
if (this.restoredHistory.hash !== this.hash) {
|
return;
|
||||||
this.graphs = this.restoredHistory.graphs;
|
}
|
||||||
this.hash = this.restoredHistory.hash;
|
|
||||||
|
if (this.restoredHistory && this.restoredHistory.hash !== this.hash) {
|
||||||
|
this.graphs = this.restoredHistory.graphs;
|
||||||
|
this.hash = this.restoredHistory.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes.animationActive) {
|
||||||
|
if (this.animationActive) {
|
||||||
|
this.animationSteps = this.timer.subscribe(n => {
|
||||||
|
console.log('Animation step', n);
|
||||||
|
this.graphs = this.animationService.animate(this.graphs);
|
||||||
|
// this.graphs = this.graphs;
|
||||||
|
this.hash = this.historyService.hash(this.graphs);
|
||||||
|
this.saveHistory();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (this.animationSteps) {
|
||||||
|
this.animationSteps.unsubscribe();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.updateGraphs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private saveHistory() {
|
private saveHistory() {
|
||||||
@@ -112,7 +141,7 @@ export class GraphsComponent implements OnChanges {
|
|||||||
const generatedPoints = [];
|
const generatedPoints = [];
|
||||||
|
|
||||||
for (let i = 0; i < this.config.nodes; i++) {
|
for (let i = 0; i < this.config.nodes; i++) {
|
||||||
generatedPoints.push(this.randomPoint);
|
generatedPoints.push(this.arithmetics.randomPoint(this.matrix, this.config.overlap));
|
||||||
}
|
}
|
||||||
|
|
||||||
return generatedPoints;
|
return generatedPoints;
|
||||||
@@ -127,22 +156,15 @@ export class GraphsComponent implements OnChanges {
|
|||||||
this.canvasService.set(this.canvas);
|
this.canvasService.set(this.canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
private centerPoint(width, height): Point {
|
|
||||||
return {
|
|
||||||
x: width * 0.5,
|
|
||||||
y: height * 0.5
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private updateMatrix() {
|
private updateMatrix() {
|
||||||
const totalArea = Math.abs(this.canvas.clientWidth * this.canvas.clientHeight);
|
const totalArea = Math.abs(this.canvas.clientWidth * this.canvas.clientHeight);
|
||||||
const totalCenter = this.centerPoint(this.canvas.clientWidth, this.canvas.clientHeight);
|
const totalCenter = this.arithmetics.centerPoint(this.canvas.clientWidth, this.canvas.clientHeight);
|
||||||
|
|
||||||
const baseArea = Math.abs(this.config.width * this.config.height);
|
const baseArea = Math.abs(this.config.width * this.config.height);
|
||||||
const baseScale = Math.pow(totalArea / baseArea * this.config.scale, 0.5);
|
const baseScale = Math.pow(totalArea / baseArea * this.config.scale, 0.5);
|
||||||
const baseWidthScaled = baseScale * this.config.width;
|
const baseWidthScaled = baseScale * this.config.width;
|
||||||
const baseHeightScaled = baseScale * this.config.height;
|
const baseHeightScaled = baseScale * this.config.height;
|
||||||
const baseCenter = this.centerPoint(baseWidthScaled, baseHeightScaled);
|
const baseCenter = this.arithmetics.centerPoint(baseWidthScaled, baseHeightScaled);
|
||||||
|
|
||||||
this.matrix = {
|
this.matrix = {
|
||||||
start: {
|
start: {
|
||||||
@@ -160,7 +182,7 @@ export class GraphsComponent implements OnChanges {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private genVectorPoint(point: Point, vector: number) {
|
private genVectorPoint(point: Point, vector: number) {
|
||||||
const range = this.Δ(this.matrix.start, this.matrix.end) * this.config.vectors.range;
|
const range = this.arithmetics.Δ(this.matrix.start, this.matrix.end) * this.config.vectors.range;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: range * Math.sin(Math.PI * vector) + point.x,
|
x: range * Math.sin(Math.PI * vector) + point.x,
|
||||||
@@ -168,32 +190,6 @@ export class GraphsComponent implements OnChanges {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private get randomPoint() {
|
|
||||||
const overlap = this.config.overlap;
|
|
||||||
const x = {
|
|
||||||
min: this.matrix.center.x - this.matrix.width * overlap,
|
|
||||||
max: this.matrix.center.x + this.matrix.width * overlap
|
|
||||||
};
|
|
||||||
const y = {
|
|
||||||
min: this.matrix.center.y - this.matrix.height * overlap,
|
|
||||||
max: this.matrix.center.y + this.matrix.height * overlap
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: Random.randomUniform(x.min, x.max)(),
|
|
||||||
y: Random.randomUniform(y.min, y.max)()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate distance between to points with coordinates.
|
|
||||||
* @param a
|
|
||||||
* @param b
|
|
||||||
*/
|
|
||||||
private Δ(a: Point, b: Point) {
|
|
||||||
return Math.pow(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private *shiftPoint(point: Point, vector) {
|
private *shiftPoint(point: Point, vector) {
|
||||||
const genShiftX = this.shiftNumber(this.config.space, vector);
|
const genShiftX = this.shiftNumber(this.config.space, vector);
|
||||||
const genShiftY = this.shiftNumber(this.config.space, vector);
|
const genShiftY = this.shiftNumber(this.config.space, vector);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { Graph } from './../models/graph.model';
|
|||||||
import { Point } from './../models/point.model';
|
import { Point } from './../models/point.model';
|
||||||
import { Param } from './../models/param.model';
|
import { Param } from './../models/param.model';
|
||||||
import { CanvasService } from './../services/canvas.service';
|
import { CanvasService } from './../services/canvas.service';
|
||||||
|
import { ArithmeticService } from './../services/arithmetic.service';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: '[guilloche]'
|
selector: '[guilloche]'
|
||||||
@@ -43,7 +44,8 @@ export class GuillocheDirective implements OnChanges {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private canvasService: CanvasService,
|
private canvasService: CanvasService,
|
||||||
private el: ElementRef
|
private el: ElementRef,
|
||||||
|
private arithmetics: ArithmeticService
|
||||||
) {
|
) {
|
||||||
this.group = Selection.select(el.nativeElement);
|
this.group = Selection.select(el.nativeElement);
|
||||||
this.canvas = Selection.select(this.canvasService.get);
|
this.canvas = Selection.select(this.canvasService.get);
|
||||||
@@ -81,8 +83,8 @@ export class GuillocheDirective implements OnChanges {
|
|||||||
private spreadLines(points: Point[]) {
|
private spreadLines(points: Point[]) {
|
||||||
const indexMiddle = Math.floor(points.length * 0.5);
|
const indexMiddle = Math.floor(points.length * 0.5);
|
||||||
const pointMiddle = points[indexMiddle];
|
const pointMiddle = points[indexMiddle];
|
||||||
const closestCenter = this.getClosestCenter(pointMiddle);
|
const closestCenter = this.arithmetics.getClosestCenter(pointMiddle, this.matrix);
|
||||||
const radius = this.Δ(pointMiddle, closestCenter);
|
const radius = this.arithmetics.Δ(pointMiddle, closestCenter);
|
||||||
const spreadPoints = [];
|
const spreadPoints = [];
|
||||||
const pies = 80;
|
const pies = 80;
|
||||||
|
|
||||||
@@ -95,7 +97,7 @@ export class GuillocheDirective implements OnChanges {
|
|||||||
|
|
||||||
spreadPoints.sort((a, b) => {
|
spreadPoints.sort((a, b) => {
|
||||||
// Good possibility to align orientation points outsite
|
// Good possibility to align orientation points outsite
|
||||||
return this.Δ(b, pointMiddle) - this.Δ(a, pointMiddle);
|
return this.arithmetics.Δ(b, pointMiddle) - this.arithmetics.Δ(a, pointMiddle);
|
||||||
});
|
});
|
||||||
|
|
||||||
spreadPoints.some((point, index) => {
|
spreadPoints.some((point, index) => {
|
||||||
@@ -108,31 +110,6 @@ export class GuillocheDirective implements OnChanges {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getClosestCenter(point: Point) {
|
|
||||||
if (this.Δ(point, this.matrix.start) < this.Δ(point, this.matrix.end)) {
|
|
||||||
return this.matrix.start;
|
|
||||||
} else {
|
|
||||||
return this.matrix.end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFarestCenter(point: Point) {
|
|
||||||
if (this.Δ(point, this.matrix.start) > this.Δ(point, this.matrix.end)) {
|
|
||||||
return this.matrix.start;
|
|
||||||
} else {
|
|
||||||
return this.matrix.end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate distance between to points with coordinates.
|
|
||||||
* @param a
|
|
||||||
* @param b
|
|
||||||
*/
|
|
||||||
private Δ(a: Point, b: Point) {
|
|
||||||
return Math.pow(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2), 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private showGrid() {
|
private showGrid() {
|
||||||
this.graph.nodes.forEach(point => {
|
this.graph.nodes.forEach(point => {
|
||||||
this.group.append('circle')
|
this.group.append('circle')
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable, Optional, ViewChild } from '@angular/core';
|
import { Inject, Injectable, Optional, ViewChild } from '@angular/core';
|
||||||
|
import { interval, Observable } from 'rxjs';
|
||||||
import * as Selection from 'd3-selection';
|
import * as Selection from 'd3-selection';
|
||||||
|
|
||||||
import { Graph } from '../models/graph.model';
|
import { Graph } from '../models/graph.model';
|
||||||
@@ -26,8 +27,11 @@ export class AnimationService {
|
|||||||
public speed: number;
|
public speed: number;
|
||||||
public range: number;
|
public range: number;
|
||||||
public genAnimation: any;
|
public genAnimation: any;
|
||||||
|
private timer: Observable<number>;
|
||||||
|
private subscribtion: any;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.timer = interval(500);
|
||||||
this.resetAnimation();
|
this.resetAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,22 +39,32 @@ export class AnimationService {
|
|||||||
this.genAnimation = this.animateNextStep();
|
this.genAnimation = this.animateNextStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
private animateNextStep() {
|
private *animateNextStep() {
|
||||||
while (this.graphs) {
|
while (this.graphs) {
|
||||||
return true;
|
yield this.graphs = this.graphs.map(graph => {
|
||||||
|
console.log(graph);
|
||||||
|
return graph;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(initialGraphs: Graph[]) {
|
// public start(initialGraphs: Graph[]): Observable<Graph[]> {
|
||||||
|
// // this.graphs = initialGraphs.map(graph => {
|
||||||
|
// // console.log(graph);
|
||||||
|
// // return graph;
|
||||||
|
// // });
|
||||||
|
|
||||||
|
// // return this.timer.subscribe(n => this.graphs);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public animate(): Graph[] {
|
||||||
|
// return this.genAnimation.next().value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public animate(initialGraphs: Graph[]) {
|
||||||
this.graphs = initialGraphs;
|
this.graphs = initialGraphs;
|
||||||
}
|
|
||||||
|
|
||||||
public animate(): Graph[] {
|
|
||||||
return this.genAnimation.next().value;
|
return this.genAnimation.next().value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop() {
|
|
||||||
this.graphs = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
73
src/app/services/arithmetic.service.ts
Normal file
73
src/app/services/arithmetic.service.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (C) 2018 Michael Czechowski <mail@dailysh.it>
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
import * as Selection from 'd3-selection';
|
||||||
|
import * as Random from 'd3-random';
|
||||||
|
|
||||||
|
import { Point } from './../models/point.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ArithmeticService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate distance between to points with coordinates.
|
||||||
|
* @param a
|
||||||
|
* @param b
|
||||||
|
*/
|
||||||
|
public Δ(a: Point, b: Point) {
|
||||||
|
return Math.pow(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getClosestCenter(point: Point, matrix: any) {
|
||||||
|
if (this.Δ(point, matrix.start) < this.Δ(point, matrix.end)) {
|
||||||
|
return matrix.start;
|
||||||
|
} else {
|
||||||
|
return matrix.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFarestCenter(point: Point, matrix: any) {
|
||||||
|
if (this.Δ(point, matrix.start) > this.Δ(point, matrix.end)) {
|
||||||
|
return matrix.start;
|
||||||
|
} else {
|
||||||
|
return matrix.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public randomPoint(matrix: any, overlap: number) {
|
||||||
|
const x = {
|
||||||
|
min: matrix.center.x - matrix.width * overlap,
|
||||||
|
max: matrix.center.x + matrix.width * overlap
|
||||||
|
};
|
||||||
|
const y = {
|
||||||
|
min: matrix.center.y - matrix.height * overlap,
|
||||||
|
max: matrix.center.y + matrix.height * overlap
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: Random.randomUniform(x.min, x.max)(),
|
||||||
|
y: Random.randomUniform(y.min, y.max)()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public centerPoint(width, height): Point {
|
||||||
|
return {
|
||||||
|
x: width * 0.5,
|
||||||
|
y: height * 0.5
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user