enhanced graph shifting
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"start": "ng serve --aot -o",
|
||||
"build": "ng build --prod",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
|
||||
@@ -99,8 +99,8 @@
|
||||
Animation
|
||||
</label>
|
||||
<div class="btn-group" role="group" aria-label="Basic example">
|
||||
<button type="button" class="btn" (click)="animationActive = true" [ngClass]="{'btn-outline-primary': !animationActive, 'btn-primary': animationActive}">An</button>
|
||||
<button type="button" class="btn" (click)="animationActive = false" [ngClass]="{'btn-outline-primary': animationActive, 'btn-primary': !animationActive}">Aus</button>
|
||||
<button type="button" class="btn" (click)="startAnimation()" [ngClass]="{'btn-outline-primary': !animationActive, 'btn-primary': animationActive}">An</button>
|
||||
<button type="button" class="btn" (click)="stopAnimation()" [ngClass]="{'btn-outline-primary': animationActive, 'btn-primary': !animationActive}">Aus</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dropdown-divider mb-4"></div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { AnimationService } from './services/animation.service';
|
||||
/**
|
||||
* Copyright (C) 2018 Michael Czechowski <mail@dailysh.it>
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
@@ -29,6 +28,8 @@ import { Config } from './models/config.model';
|
||||
import { CanvasService } from './services/canvas.service';
|
||||
import { HistoryService } from './services/history.service';
|
||||
import { Graph } from './models/graph.model';
|
||||
import { GraphService } from './services/graph.service';
|
||||
import { AnimationService } from './services/animation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -49,6 +50,7 @@ export class AppComponent implements OnInit {
|
||||
constructor(
|
||||
private canvasService: CanvasService,
|
||||
private historyService: HistoryService,
|
||||
private graphService: GraphService,
|
||||
) {
|
||||
moment.locale('de');
|
||||
|
||||
@@ -100,4 +102,14 @@ export class AppComponent implements OnInit {
|
||||
this.configForm.reset({...history.config});
|
||||
this.restoredHistory = history;
|
||||
}
|
||||
|
||||
public startAnimation() {
|
||||
this.animationActive = true;
|
||||
this.graphService.startAnimation();
|
||||
}
|
||||
|
||||
public stopAnimation() {
|
||||
this.animationActive = false;
|
||||
this.graphService.stopAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ import { GuillocheDirective } from './directives/guilloche.directive';
|
||||
import { CanvasService } from './services/canvas.service';
|
||||
import { HistoryService } from './services/history.service';
|
||||
import { AnimationService } from './services/animation.service';
|
||||
import { ArithmeticService } from './services/arithmetic.service';
|
||||
import { MathService } from './services/math.service';
|
||||
import { GraphService } from './services/graph.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -43,7 +44,8 @@ import { ArithmeticService } from './services/arithmetic.service';
|
||||
CanvasService,
|
||||
HistoryService,
|
||||
AnimationService,
|
||||
ArithmeticService,
|
||||
MathService,
|
||||
GraphService,
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<svg #svg width="100%" height="100%">
|
||||
<g guilloche *ngFor="let graph of graphs" [graph]="graph" [matrix]="matrix" [config]="config" (guillocheChange)="prepareGuillocheExport($event)"></g>
|
||||
<g guilloche [graph]="graphs[0]" [matrix]="matrix" [config]="config" [animate]="true" (guillocheChange)="prepareGuillocheExport($event)"></g>
|
||||
<g guilloche [graph]="graphs[1]" [matrix]="matrix" [config]="config" [animate]="true" (guillocheChange)="prepareGuillocheExport($event)"></g>
|
||||
<!-- <g guilloche *ngFor="let graph of graphs" [graph]="graph" [matrix]="matrix" [config]="config" [animate]="true" (guillocheChange)="prepareGuillocheExport($event)"></g> -->
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 197 B After Width: | Height: | Size: 511 B |
@@ -25,10 +25,11 @@ import { environment as env } from '../../environments/environment';
|
||||
import { CanvasService } from './../services/canvas.service';
|
||||
import { HistoryService } from './../services/history.service';
|
||||
import { AnimationService } from '../services/animation.service';
|
||||
import { ArithmeticService } from '../services/arithmetic.service';
|
||||
import { MathService } from '../services/math.service';
|
||||
import { GuillocheDirective } from './../directives/guilloche.directive';
|
||||
import { Graph } from '../models/graph.model';
|
||||
import { Point } from '../models/point.model';
|
||||
import { GraphService } from '../services/graph.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-graphs',
|
||||
@@ -48,7 +49,6 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
private hash: string;
|
||||
private animation: Observable<Graph[]>;
|
||||
private timer: Observable<number>;
|
||||
private animationSteps: any;
|
||||
|
||||
@Input() config: any;
|
||||
@Input() restoredHistory: any;
|
||||
@@ -66,7 +66,8 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
private canvasService: CanvasService,
|
||||
private historyService: HistoryService,
|
||||
private animationService: AnimationService,
|
||||
private arithmetics: ArithmeticService
|
||||
private math: MathService,
|
||||
private graphService: GraphService
|
||||
) {
|
||||
this.genLoadedAllGraphs = this.countLoadedGraphs();
|
||||
this.timer = interval(500);
|
||||
@@ -90,19 +91,6 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
this.graphs = this.restoredHistory.graphs;
|
||||
this.hash = this.restoredHistory.hash;
|
||||
}
|
||||
|
||||
if (changes.animationActive) {
|
||||
if (this.animationActive) {
|
||||
this.animationSteps = setInterval(() => this.animateGraph(), 50);
|
||||
} else {
|
||||
if (this.animationSteps) {
|
||||
clearInterval(this.animationSteps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private animateGraph() {
|
||||
this.graphs = this.animationService.animate(this.graphs);
|
||||
}
|
||||
|
||||
private saveHistory() {
|
||||
@@ -110,6 +98,10 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
this.historyService.save(this.graphs, this.config);
|
||||
}
|
||||
|
||||
private saveGraph() {
|
||||
this.graphService.set(this.graphs);
|
||||
}
|
||||
|
||||
private updateGraphs(): void {
|
||||
const genShiftStart = this.shiftPoint(this.matrix.start, this.config.vectors.start);
|
||||
const genShiftEnd = this.shiftPoint(this.matrix.end, this.config.vectors.end);
|
||||
@@ -130,6 +122,7 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
this.graphs = curveList.map(curve => this.adjustGraph(curve));
|
||||
this.hash = this.historyService.hash(this.graphs);
|
||||
this.saveHistory();
|
||||
this.saveGraph();
|
||||
}
|
||||
|
||||
private adjustGraph(curve) {
|
||||
@@ -147,7 +140,7 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
const generatedPoints = [];
|
||||
|
||||
for (let i = 0; i < this.config.nodes; i++) {
|
||||
generatedPoints.push(this.arithmetics.randomPoint(this.matrix, this.config.overlap));
|
||||
generatedPoints.push(this.math.randomPoint(this.matrix, this.config.overlap));
|
||||
}
|
||||
|
||||
return generatedPoints;
|
||||
@@ -165,13 +158,13 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
|
||||
private updateMatrix() {
|
||||
const totalArea = Math.abs(this.canvas.clientWidth * this.canvas.clientHeight);
|
||||
const totalCenter = this.arithmetics.centerPoint(this.canvas.clientWidth, this.canvas.clientHeight);
|
||||
const totalCenter = this.math.centerOfArea(this.canvas.clientWidth, this.canvas.clientHeight);
|
||||
|
||||
const baseArea = Math.abs(this.config.width * this.config.height);
|
||||
const baseScale = Math.pow(totalArea / baseArea * this.config.scale, 0.5);
|
||||
const baseWidthScaled = baseScale * this.config.width;
|
||||
const baseHeightScaled = baseScale * this.config.height;
|
||||
const baseCenter = this.arithmetics.centerPoint(baseWidthScaled, baseHeightScaled);
|
||||
const baseCenter = this.math.centerOfArea(baseWidthScaled, baseHeightScaled);
|
||||
|
||||
this.matrix = {
|
||||
start: {
|
||||
@@ -189,7 +182,7 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
}
|
||||
|
||||
private genVectorPoint(point: Point, vector: number) {
|
||||
const range = this.arithmetics.Δ(this.matrix.start, this.matrix.end) * this.config.vectors.range;
|
||||
const range = this.math.Δ(this.matrix.start, this.matrix.end) * this.config.vectors.range;
|
||||
|
||||
return {
|
||||
x: range * Math.sin(Math.PI * vector) + point.x,
|
||||
@@ -215,7 +208,7 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
private *shiftNumber(space: number, vector: number) {
|
||||
let current = 0;
|
||||
let index = 0;
|
||||
const sign = this.flipSign();
|
||||
const sign = this.math.flipSign();
|
||||
|
||||
while (true) {
|
||||
yield current = sign.next().value * index * space + current;
|
||||
@@ -223,14 +216,6 @@ export class GraphsComponent implements OnChanges, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
private *flipSign() {
|
||||
let sign = 1;
|
||||
|
||||
while (true) {
|
||||
yield sign = sign * (-1);
|
||||
}
|
||||
}
|
||||
|
||||
public prepareGuillocheExport(guillocheElement) {
|
||||
if (this.genLoadedAllGraphs.next().value) {
|
||||
this.svgChange.emit(this.svgElementRef);
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
import { ElementRef, HostListener, Output, EventEmitter, Input, Directive, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { ElementRef, HostListener, Output, EventEmitter, Input, Directive, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import * as Selection from 'd3-selection';
|
||||
import * as Shape from 'd3-shape';
|
||||
import * as Random from 'd3-random';
|
||||
import * as Drag from 'd3-drag';
|
||||
import * as Ease from 'd3-ease';
|
||||
import * as Timer from 'd3-timer';
|
||||
|
||||
import { environment as env } from './../../environments/environment';
|
||||
import { Config } from './../models/config.model';
|
||||
@@ -26,33 +28,74 @@ import { Graph } from './../models/graph.model';
|
||||
import { Point } from './../models/point.model';
|
||||
import { Param } from './../models/param.model';
|
||||
import { CanvasService } from './../services/canvas.service';
|
||||
import { ArithmeticService } from './../services/arithmetic.service';
|
||||
import { MathService } from './../services/math.service';
|
||||
import { GraphService } from '../services/graph.service';
|
||||
import { AnimationService } from './../services/animation.service';
|
||||
import { spread } from 'q';
|
||||
|
||||
@Directive({
|
||||
selector: '[guilloche]'
|
||||
})
|
||||
export class GuillocheDirective implements OnChanges {
|
||||
export class GuillocheDirective implements OnChanges, OnInit {
|
||||
|
||||
private canvas: any;
|
||||
private group: any;
|
||||
private animationInterval: any;
|
||||
private x: any;
|
||||
private y: any;
|
||||
|
||||
@Input() graph: Graph;
|
||||
@Input() matrix: any;
|
||||
@Input() config: any;
|
||||
@Input() animate: boolean;
|
||||
|
||||
@Output() guillocheChange = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private canvasService: CanvasService,
|
||||
private el: ElementRef,
|
||||
private arithmetics: ArithmeticService
|
||||
private math: MathService,
|
||||
private graphService: GraphService,
|
||||
private animationService: AnimationService
|
||||
) {
|
||||
this.group = Selection.select(el.nativeElement);
|
||||
this.canvas = Selection.select(this.canvasService.get);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// console.log('guilloche:init');
|
||||
// Timer.timer(function(elapsed) {
|
||||
// let t = (elapsed % 3000) / 3000;
|
||||
// console.log(t);
|
||||
// // dot1.attr("cx", x(t)).attr("cy", y(ease(t)));
|
||||
// // dot2.attr("cy", y(ease(t)));
|
||||
// });
|
||||
|
||||
// console.log(Ease.easeLinear(0.5));
|
||||
// const t = Timer.timer(function(elapsed) {
|
||||
// if (elapsed > 200) {
|
||||
// t.stop();
|
||||
// }
|
||||
// }, 1000);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
// console.log(this.graph);
|
||||
// @todo modify graph here instead of in graphs.component.ts
|
||||
this.group.selectAll('*').remove();
|
||||
|
||||
// console.log('guilloche:changes', changes);
|
||||
|
||||
if (this.graphService.isAnimated) {
|
||||
console.log('is animated');
|
||||
// this.graphService.startAnimation();
|
||||
this.animationInterval = setInterval(() => this.animateGraph(), 60);
|
||||
} else {
|
||||
if (this.animationInterval) {
|
||||
console.log('not animated');
|
||||
// this.graphService.stopAnimation();
|
||||
clearInterval(this.animationInterval);
|
||||
}
|
||||
}
|
||||
|
||||
const points = [
|
||||
this.graph.start.point,
|
||||
@@ -63,6 +106,18 @@ export class GuillocheDirective implements OnChanges {
|
||||
this.guillocheChanged();
|
||||
}
|
||||
|
||||
private animateGraph() {
|
||||
this.group.selectAll('*').remove();
|
||||
this.graph = this.animationService.animate(this.graph);
|
||||
// this.saveGraph();
|
||||
const points = [
|
||||
this.graph.start.point,
|
||||
...this.graph.nodes,
|
||||
this.graph.end.point
|
||||
];
|
||||
this.spreadLines(points);
|
||||
}
|
||||
|
||||
public guillocheChanged() {
|
||||
this.guillocheChange.emit(this.el.nativeElement);
|
||||
}
|
||||
@@ -77,50 +132,93 @@ export class GuillocheDirective implements OnChanges {
|
||||
.attr('stroke-width', this.graph.stroke)
|
||||
.attr('fill', 'none');
|
||||
|
||||
if (!env.production) {
|
||||
if (env.grid) {
|
||||
this.showGrid();
|
||||
}
|
||||
}
|
||||
|
||||
private spreadLines(points: Point[]) {
|
||||
const indexMiddle = Math.floor(points.length * 0.5);
|
||||
const pointMiddle = points[indexMiddle];
|
||||
const closestCenter = this.arithmetics.getClosestCenter(pointMiddle, this.matrix);
|
||||
const radius = this.arithmetics.Δ(pointMiddle, closestCenter);
|
||||
const spreadPoints = [];
|
||||
const pies = 80;
|
||||
const shiftedMedians = [];
|
||||
const medianPoint = this.math.centerOfCurve(points);
|
||||
const medianIndex = this.math.medianIndex(points);
|
||||
const genshiftedMedians = this.graphService.spreadOrthogonal(medianPoint, 20);
|
||||
|
||||
for (let i = 0; i < pies; i++) {
|
||||
spreadPoints.push({
|
||||
x: radius * Math.cos(2 * i * Math.PI / pies) + closestCenter.x,
|
||||
y: radius * Math.sin(2 * i * Math.PI / pies) + closestCenter.y,
|
||||
for (let i = 0; i < this.config.spread; i++) {
|
||||
shiftedMedians.push(genshiftedMedians.next().value);
|
||||
}
|
||||
|
||||
// const indexMiddle = Math.floor(points.length * 0.5);
|
||||
// const pointMiddle = points[indexMiddle];
|
||||
// const closestCenter = this.math.getClosestCenter(pointMiddle, this.matrix);
|
||||
// const radius = this.math.Δ(pointMiddle, closestCenter);
|
||||
// const shiftedMedians = [];
|
||||
// const pies = 200;
|
||||
|
||||
// for (let i = 0; i < pies; i++) {
|
||||
// shiftedMedians.push({
|
||||
// x: radius * Math.cos(2 * i * Math.PI / pies) + closestCenter.x,
|
||||
// y: radius * Math.sin(2 * i * Math.PI / pies) + closestCenter.y,
|
||||
// });
|
||||
// }
|
||||
|
||||
// shiftedMedians.sort((a, b) => {
|
||||
// // Good possibility to align orientation points outsite
|
||||
// return this.math.Δ(b, pointMiddle) - this.math.Δ(a, pointMiddle);
|
||||
// });
|
||||
|
||||
// console.log(shiftedMedians);
|
||||
|
||||
// shiftedMedians.some((point, index) => {
|
||||
// points[indexMiddle] = point;
|
||||
|
||||
// this.drawGraph(points);
|
||||
|
||||
// return index === this.config.spread - 1;
|
||||
// });
|
||||
if (env.grid) {
|
||||
[medianPoint, ...shiftedMedians].forEach((point, index) => {
|
||||
this.group.append('circle')
|
||||
.attr('cx', point.x)
|
||||
.attr('cy', point.y)
|
||||
.attr('r', 10 / index)
|
||||
.attr('fill-opacity', 0.6)
|
||||
.attr('fill', 'darkgray');
|
||||
});
|
||||
}
|
||||
|
||||
spreadPoints.sort((a, b) => {
|
||||
// Good possibility to align orientation points outsite
|
||||
return this.arithmetics.Δ(b, pointMiddle) - this.arithmetics.Δ(a, pointMiddle);
|
||||
});
|
||||
|
||||
spreadPoints.some((point, index) => {
|
||||
points[indexMiddle] = point;
|
||||
|
||||
this.drawGraph(points);
|
||||
|
||||
return index === this.config.spread - 1;
|
||||
shiftedMedians.forEach(median => {
|
||||
const shiftedGraph = points.slice();
|
||||
shiftedGraph.splice(medianIndex, 1, median);
|
||||
this.drawGraph(shiftedGraph);
|
||||
});
|
||||
|
||||
// this.drawGraph(points);
|
||||
}
|
||||
|
||||
// private animateRange(n: number) {
|
||||
// return Ease.scaleLinear().range([n, n + 100]);
|
||||
// }
|
||||
|
||||
private showGrid() {
|
||||
this.graph.nodes.forEach(point => {
|
||||
this.group.append('circle')
|
||||
this.graph.nodes.forEach((point, index) => {
|
||||
const circle = this.group.append('g');
|
||||
// const xRange = this.animateRange(point.x);
|
||||
// const yRange = this.animateRange(point.y);
|
||||
|
||||
circle.append('circle')
|
||||
.attr('cx', point.x)
|
||||
.attr('cy', point.y)
|
||||
.attr('r', 3)
|
||||
.attr('stroke-width', 0.1)
|
||||
.attr('fill-opacity', 0)
|
||||
.attr('stroke', 'darkgray');
|
||||
.attr('fill-opacity', 0.6)
|
||||
.attr('fill', this.graph.color);
|
||||
|
||||
circle.append('text')
|
||||
.attr('x', point.x)
|
||||
.attr('y', point.y)
|
||||
.attr('dx', 8)
|
||||
.attr('dy', 15)
|
||||
.attr('fill', this.graph.color)
|
||||
.text(index);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,5 @@ export interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
color?: string;
|
||||
ascent?: number;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { interval, Observable } from 'rxjs';
|
||||
import * as Selection from 'd3-selection';
|
||||
|
||||
import { Graph } from '../models/graph.model';
|
||||
import { ArithmeticService } from './arithmetic.service';
|
||||
import { MathService } from './math.service';
|
||||
import { HistoryService } from './history.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -33,27 +33,28 @@ export class AnimationService {
|
||||
// private subscribtion: any;
|
||||
|
||||
constructor(
|
||||
private arithmetics: ArithmeticService,
|
||||
private math: MathService,
|
||||
private historyService: HistoryService,
|
||||
) {
|
||||
}
|
||||
|
||||
public animate(initialGraphs: Graph[]) {
|
||||
const newGraphs = initialGraphs.slice();
|
||||
// public animate(initialGraphs: Graph[]) {
|
||||
public animate(initialGraph: Graph) {
|
||||
// const newGraphs = initialGraphs.slice();
|
||||
|
||||
return newGraphs.map(graph => {
|
||||
// return newGraphs.map(graph => {
|
||||
|
||||
const newGraph = Object.assign({}, graph);
|
||||
const newGraph = Object.assign({}, initialGraph);
|
||||
const indexMiddle = Math.floor(newGraph.nodes.length * 0.5);
|
||||
const pointMiddle = newGraph.nodes[indexMiddle];
|
||||
|
||||
newGraph.nodes.splice(indexMiddle, 1, {
|
||||
x: pointMiddle.x - 1,
|
||||
y: pointMiddle.y + 1,
|
||||
x: pointMiddle.x - 2,
|
||||
y: pointMiddle.y + 2,
|
||||
});
|
||||
|
||||
return newGraph;
|
||||
});
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
76
src/app/services/graph.service.ts
Normal file
76
src/app/services/graph.service.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Validators } from '@angular/forms';
|
||||
/**
|
||||
* 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, Renderer2, RendererFactory2 } from '@angular/core';
|
||||
import * as Selection from 'd3-selection';
|
||||
|
||||
import { MathService } from './math.service';
|
||||
import { Graph } from './../models/graph.model';
|
||||
import { Point } from './../models/point.model';
|
||||
|
||||
@Injectable()
|
||||
export class GraphService {
|
||||
private graphs: Graph[];
|
||||
private animation: boolean | null;
|
||||
|
||||
constructor(
|
||||
private math: MathService
|
||||
) {}
|
||||
|
||||
public get() {
|
||||
return this.graphs;
|
||||
}
|
||||
|
||||
public set(newGraphs: Graph[]) {
|
||||
// console.log('GraphService:set', newGraphs);
|
||||
this.graphs = newGraphs;
|
||||
}
|
||||
|
||||
public get isAnimated() {
|
||||
return this.animation;
|
||||
}
|
||||
|
||||
public startAnimation() {
|
||||
this.animation = true;
|
||||
}
|
||||
|
||||
public stopAnimation() {
|
||||
this.animation = false;
|
||||
}
|
||||
|
||||
public *spreadOrthogonal(start: Point, spacing: number) {
|
||||
const sign = this.math.flipSign();
|
||||
let currentPoint = start;
|
||||
let i = 0;
|
||||
|
||||
while (true) {
|
||||
const currentSpacing = sign.next().value * spacing * i;
|
||||
currentPoint = this.shiftPoint(currentPoint, start.ascent, currentSpacing);
|
||||
|
||||
yield currentPoint;
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public shiftPoint(point: Point, radians: number, spacing: number) {
|
||||
return {
|
||||
x: Math.sin(radians * Math.PI) * spacing + point.x,
|
||||
y: Math.cos(radians * Math.PI) * spacing + point.y
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,10 @@ import * as Selection from 'd3-selection';
|
||||
import * as Random from 'd3-random';
|
||||
|
||||
import { Point } from './../models/point.model';
|
||||
import { Graph } from './../models/graph.model';
|
||||
|
||||
@Injectable()
|
||||
export class ArithmeticService {
|
||||
export class MathService {
|
||||
|
||||
/**
|
||||
* Calculate distance between to points with coordinates.
|
||||
@@ -64,10 +65,58 @@ export class ArithmeticService {
|
||||
};
|
||||
}
|
||||
|
||||
public centerPoint(width, height): Point {
|
||||
public centerOfArea(width, height): Point {
|
||||
return {
|
||||
x: width * 0.5,
|
||||
y: height * 0.5
|
||||
};
|
||||
}
|
||||
|
||||
public centerOfPoints(p1: Point, p2: Point) {
|
||||
return {
|
||||
x: (p1.x + p2.x) * 0.5,
|
||||
y: (p1.y + p2.y) * 0.5
|
||||
};
|
||||
}
|
||||
|
||||
public centerOfCurve(curve: Point[]) {
|
||||
const genMedian = this.medianPoint(curve);
|
||||
const p1 = genMedian.next().value;
|
||||
const p2 = genMedian.next().value;
|
||||
const radians = this.angleRadians(p1, p2);
|
||||
|
||||
return Object.assign(this.centerOfPoints(p1, p2), { ascent: radians });
|
||||
}
|
||||
|
||||
public angleRadians(p1: Point, p2: Point) {
|
||||
return Math.atan2(p2.y - p1.y, p2.x - p1.x);
|
||||
}
|
||||
|
||||
public angleDegree(p1: Point, p2: Point) {
|
||||
return this.angleRadians(p1, p2) * 180 / Math.PI;
|
||||
}
|
||||
|
||||
public medianIndex(list: any): number {
|
||||
return Math.floor(list.length * 0.5);
|
||||
}
|
||||
|
||||
public *medianPoint(points: Point[]) {
|
||||
let index: number;
|
||||
const list: Point[] = points.slice();
|
||||
|
||||
while (list) {
|
||||
index = this.medianIndex(points);
|
||||
yield list[index];
|
||||
|
||||
list.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public *flipSign() {
|
||||
let sign = 1;
|
||||
|
||||
while (true) {
|
||||
yield sign = sign * (-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
grid: false,
|
||||
guilloche: {
|
||||
colors: {
|
||||
primary: '#cb0c4d',
|
||||
@@ -30,11 +31,11 @@ export const environment = {
|
||||
vectors: {
|
||||
start: 1,
|
||||
end: 0,
|
||||
range: 0.4,
|
||||
range: 0.4
|
||||
},
|
||||
nodes: 4,
|
||||
stroke: 1,
|
||||
spread: 12,
|
||||
space: 5
|
||||
space: 10
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user