blob: 8cbca5a09e13c756f86479e01c3efe2e3ab7054d [file] [log] [blame]
Sean Condonf4f54a12018-10-10 23:25:46 +01001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { Injectable } from '@angular/core';
17import * as d3 from 'd3';
18import { LogService } from '../log.service';
19
20export interface ZoomOpts {
21 svg: any; // D3 selection of <svg> element
22 zoomLayer: any; // D3 selection of <g> element
23 zoomMin: number; // Min zoom level - usually 0.25
24 zoomMax: number; // Max zoom level - usually 10
25 zoomEnabled(ev: any): boolean; // Function that takes event and returns boolean
26 zoomCallback(translate: number[], scale: number): void; // Function that is called on zoom
27}
28
29export interface Zoomer {
30 panZoom(translate: number[], scale: number, transition?: number): void;
31 reset(): void;
32 translate(): number[];
33 scale(): number;
34 scaleExtent(): number[];
35}
36
37export const CZ: string = 'ZoomService.createZoomer(): ';
38export const D3S: string = ' (D3 selection) property defined';
39
40/**
41 * ONOS GUI -- Topology Zoom Service Module.
42 */
43@Injectable({
44 providedIn: 'root',
45})
46export class ZoomService {
47 defaultSettings: ZoomOpts;
48
49 zoom: any;
50 public zoomer: Zoomer;
51 settings: ZoomOpts;
52
53 constructor(
54 protected log: LogService,
55 ) {
56 this.defaultSettings = <ZoomOpts>{
57 zoomMin: 0.05,
58 zoomMax: 50,
59 zoomEnabled: (ev) => true,
60 zoomCallback: (t, s) => { return; }
61 };
62
63 this.log.debug('ZoomService constructed');
64 }
65
66 createZoomer(opts: ZoomOpts): Zoomer {
67 this.settings = Object.assign(this.defaultSettings, opts);
68
69 if (!this.settings.svg) {
70 this.log.error(CZ + 'No "svg" (svg tag)' + D3S);
71 throw new Error(CZ + 'No "svg" (svg tag)' + D3S);
72 }
73 if (!this.settings.zoomLayer) {
74 this.log.error(CZ + 'No "zoomLayer" (g tag)' + D3S);
75 throw new Error(CZ + 'No "zoomLayer" (g tag)' + D3S);
76 }
77
78 this.zoom = d3.zoom()
79 .scaleExtent([this.settings.zoomMin, this.settings.zoomMax])
80 .extent([[0, 0], [1000, 1000]])
81 .on('zoom', () => this.zoomed);
82
83
84 this.zoomer = <Zoomer>{
85 panZoom: (translate: number[], scale: number, transition?: number) => {
86 this.settings.svg.call(this.zoom.translateBy, translate[0], translate[1]);
87 this.settings.svg.call(this.zoom.scaleTo, scale);
88 this.adjustZoomLayer(translate, scale, transition);
89 },
90
91 reset: () => {
92 this.settings.svg.call(this.zoom.translateTo, 500, 500);
93 this.settings.svg.call(this.zoom.scaleTo, 1);
94 this.adjustZoomLayer([0, 0], 1, 0);
95 },
96
97 translate: () => {
98 const trans = d3.zoomTransform(this.settings.svg.node());
99 return [trans.x, trans.y];
100 },
101
102 scale: () => {
103 const trans = d3.zoomTransform(this.settings.svg.node());
104 return trans.k;
105 },
106
107 scaleExtent: () => {
108 return this.zoom.scaleExtent();
109 },
110 };
111
112 // apply the zoom behavior to the SVG element
113/*
114 if (this.settings.svg ) {
115 this.settings.svg.call(this.zoom);
116 }
117*/
118 // Remove zoom on double click (prevents a
119 // false zoom navigating regions)
Sean Condon55c30532018-10-29 12:26:57 +0000120 // this.settings.svg.on('dblclick.zoom', null);
Sean Condonf4f54a12018-10-10 23:25:46 +0100121
122 return this.zoomer;
123 }
124
125 /**
126 * zoom events from mouse gestures...
127 */
128 zoomed() {
129 const ev = d3.event.sourceEvent;
130 if (this.settings.zoomEnabled(ev)) {
131 this.adjustZoomLayer(d3.event.translate, d3.event.scale);
132 }
133 }
134
135 /**
136 * Adjust the zoom layer
137 */
138 adjustZoomLayer(translate: number[], scale: number, transition?: any): void {
139
140 this.settings.zoomLayer.transition()
141 .duration(transition || 0)
142 .attr('transform',
143 'translate(' + translate + ') scale(' + scale + ')');
144
145 this.settings.zoomCallback(translate, scale);
146 }
147
148}