Merge pull request #530 from GNS3/Fit-in-view-option

Option to fit in view
This commit is contained in:
piotrpekala7 2019-10-25 14:29:56 +02:00 committed by GitHub
commit 2d588c411d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 163 additions and 3 deletions

View File

@ -33,6 +33,7 @@ import { ToolsService } from '../../../services/tools.service';
import { TextEditorComponent } from '../text-editor/text-editor.component';
import { MapScaleService } from '../../../services/mapScale.service';
import { Project } from '../../../models/project';
import { MapSettingsService } from '../../../services/mapsettings.service';
@Component({
selector: 'app-d3-map',
@ -75,7 +76,8 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
protected movingToolWidget: MovingTool,
public graphLayout: GraphLayout,
private toolsService: ToolsService,
private mapScaleService: MapScaleService
private mapScaleService: MapScaleService,
private mapSettingsService: MapSettingsService
) {
this.parentNativeElement = element.nativeElement;
}
@ -193,6 +195,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
this.graphLayout.draw(this.svg, this.context);
this.textEditor.activateTextEditingForDrawings();
this.textEditor.activateTextEditingForNodeLabels();
this.mapSettingsService.mapRenderedEmitter.emit(true);
}
@HostListener('window:resize', ['$event'])

View File

@ -47,6 +47,10 @@
<mat-icon>developer_board</mat-icon>
<span>Go to servers</span>
</button>
<button mat-menu-item (click)="fitInView()">
<mat-icon>fullscreen</mat-icon>
<span>Fit in view</span>
</button>
<button mat-menu-item (click)="addNewProject()">
<mat-icon>add</mat-icon>
<span>Add new blank project</span>

View File

@ -53,7 +53,7 @@ import { MapLinkNodeToLinkNodeConverter } from '../../cartography/converters/map
import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component';
import { ToasterService } from '../../services/toaster.service';
import { ImportProjectDialogComponent } from '../projects/import-project-dialog/import-project-dialog.component';
import { MatDialog, MatBottomSheet } from '@angular/material';
import { MatDialog, MatBottomSheet, mixinColor } from '@angular/material';
import { AddBlankProjectDialogComponent } from '../projects/add-blank-project-dialog/add-blank-project-dialog.component';
import { SaveProjectDialogComponent } from '../projects/save-project-dialog/save-project-dialog.component';
import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource, Indexed } from '../../cartography/datasources/map-datasource';
@ -95,6 +95,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
protected settings: Settings;
private inReadOnlyMode = false;
private scrollX: number = 0;
private scrollY: number = 0;
private scrollEnabled: boolean = false;
@ViewChild(ContextMenuComponent, {static: false}) contextMenu: ContextMenuComponent;
@ViewChild(D3MapComponent, {static: false}) mapChild: D3MapComponent;
@ -200,6 +203,12 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.subscriptions.push(routeSub);
this.subscriptions.push(
this.mapSettingsService.mapRenderedEmitter.subscribe((value: boolean) => {
if (this.scrollEnabled) this.centerCanvas();
})
);
this.subscriptions.push(
this.drawingsDataSource.changes.subscribe((drawings: Drawing[]) => {
this.drawings = drawings;
@ -414,6 +423,133 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
});
}
public fitInView() {
this.drawings.forEach(drawing => {
let splittedSvg = drawing.svg.split("\"");
let height: number = parseInt(splittedSvg[1], 10);
let width: number = parseInt(splittedSvg[3], 10);
drawing.element = {
width: width,
height: height
};
});
if ((this.nodes.length === 0) && (this.drawings.length === 0)) { return };
let minX : number, maxX : number, minY: number, maxY : number;
let borderedNodes: BorderedNode[] = [];
this.nodes.forEach(n => {
let borderedNode: BorderedNode = new BorderedNode();
borderedNode.node = n;
borderedNode.top = n.y;
borderedNode.left = n.x;
borderedNode.bottom = n.y + n.height;
borderedNode.right = n.x + n.width;
if ((n.y + n.label.y) < borderedNode.top) {
borderedNode.top = n.y + n.label.y;
};
if ((n.x + n.label.x) < borderedNode.left) {
borderedNode.left = n.x + n.label.x;
};
if ((n.y + n.label.y) > borderedNode.bottom) {
borderedNode.bottom = n.y + n.label.y;
};
if ((n.x + n.label.x) > borderedNode.right) {
borderedNode.right = n.x + n.label.x;
};
borderedNodes.push(borderedNode);
});
let nodeMinX = borderedNodes.sort((n,m) => n.left - m.left)[0];
let nodeMaxX = borderedNodes.sort((n,m) => n.right - m.right)[borderedNodes.length - 1];
let nodeMinY = borderedNodes.sort((n,m) => n.top - m.top)[0];
let nodeMaxY = borderedNodes.sort((n,m) => n.bottom - m.bottom)[borderedNodes.length - 1];
let borderedDrawings: BorderedDrawing[] = [];
this.drawings.forEach(n => {
let borderedDrawing: BorderedDrawing = new BorderedDrawing();
borderedDrawing.drawing = n;
borderedDrawing.top = n.y;
borderedDrawing.left = n.x;
borderedDrawing.bottom = n.y + n.element.height;
borderedDrawing.right = n.x + n.element.width;
borderedDrawings.push(borderedDrawing);
});
let drawingMinX = borderedDrawings.sort((n,m) => n.left - m.left)[0];
let drawingMaxX = borderedDrawings.sort((n,m) => n.right - m.right)[borderedDrawings.length - 1];
let drawingMinY = borderedDrawings.sort((n,m) => n.top - m.top)[0];
let drawingMaxY = borderedDrawings.sort((n,m) => n.bottom - m.bottom)[borderedDrawings.length - 1];
if (nodeMinX.left < drawingMinX.left) {
minX = nodeMinX.left;
} else {
minX = drawingMinX.left;
}
if (nodeMaxX.right > drawingMaxX.right) {
maxX = nodeMaxX.right;
} else {
maxX = drawingMaxX.right;
}
if (nodeMinY.top < drawingMinY.top) {
minY = nodeMinY.top;
} else {
minY = drawingMinY.top;
}
if (nodeMaxY.bottom > drawingMaxY.bottom) {
maxY = nodeMaxY.bottom;
} else {
maxY = drawingMaxY.bottom;
}
let margin: number = 20;
minX = minX - margin;
maxX = maxX + margin;
minY = minY - margin;
maxY = maxY + margin;
let windowWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
let windowHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
let widthOfAreaToShow = maxX - minX;
let heightOfAreaToShow = maxY - minY;
let widthToSceneWidthRatio = widthOfAreaToShow / windowWidth;
let heightToSceneHeightRatio = heightOfAreaToShow / windowHeight;
let scale = 1 / Math.max(widthToSceneWidthRatio, heightToSceneHeightRatio);
if (scale !== this.mapScaleService.currentScale) {
this.mapScaleService.setScale(scale);
this.project.scene_width = this.project.scene_width * scale;
this.project.scene_height = this.project.scene_height * scale;
if (heightToSceneHeightRatio < widthOfAreaToShow) {
this.scrollX = (minX * scale) - ((windowWidth - widthOfAreaToShow*scale)/2) + this.project.scene_width/2;
this.scrollY = (minY * scale) + this.project.scene_height/2;
} else {
this.scrollX = (minX * scale) + this.project.scene_width/2;
this.scrollY = (minY * scale) - ((windowHeight - heightOfAreaToShow*scale)/2) + this.project.scene_height/2;
}
} else {
this.scrollX = (minX * scale) + this.project.scene_width/2;
this.scrollY = (minY * scale) + this.project.scene_height/2;
}
this.scrollEnabled = true;
}
public centerCanvas() {
window.scrollTo(this.scrollX, this.scrollY);
this.scrollEnabled = false;
}
public centerView() {
if (this.project) {
let scrollX: number = (this.project.scene_width - document.documentElement.clientWidth) > 0 ? (this.project.scene_width - document.documentElement.clientWidth)/2 : 0;
@ -650,3 +786,19 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe());
}
}
export class BorderedNode {
top: number;
left: number;
bottom: number;
right: number;
node: Node;
}
export class BorderedDrawing {
top: number;
left: number;
bottom: number;
right: number;
drawing: Drawing;
}

View File

@ -1,4 +1,4 @@
import { Injectable } from "@angular/core";
import { Injectable, EventEmitter } from "@angular/core";
import { Subject } from 'rxjs';
@Injectable()
@ -8,6 +8,7 @@ export class MapSettingsService {
public isLogConsoleVisible: boolean = false;
public isLayerNumberVisible: boolean = false;
public interfaceLabels: Map<string, boolean> = new Map<string, boolean>();
public mapRenderedEmitter = new EventEmitter<boolean>();
constructor() {
this.isLayerNumberVisible = localStorage.getItem('layersVisibility') === 'true' ? true : false;