├── .gitignore
├── LICENSE
├── README.md
├── main.js
├── package.json
├── panel
├── preview-webview.html
├── preview.html
└── webview-index.html
└── widget
├── linear-ticks.js
├── pixi-grid.html
└── pixi-grid.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Fireball Game Engine
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pixi grid
2 |
3 | A grid scale widget renderred by pixi graphics.
4 |
5 | 
6 |
7 | ## API
8 |
9 | ### Method: setAnchor(x,y)
10 |
11 | - `x` number - Range of [0.0,1.0]
12 | - `y` number - Range of [0.0,1.0]
13 |
14 | Sets a scale anchor.
15 |
16 | ### Method: setScaleH( lods, minScale, maxScale[, type] )
17 |
18 | - `lods` array
19 | - `minScale` number
20 | - `maxScale` number
21 | - `type` string - can be `frame`
22 |
23 | Sets the scale method for horizontal scalar. Example:
24 |
25 | ```
26 | this.setScaleH( [5,2], 0.001, 1000 );
27 | ```
28 |
29 | ### Method: setMappingH( minValue, maxValue, pixelRange )
30 |
31 | ### Method: setRangeH( minValue, maxValue )
32 |
33 | ### Method: setScaleV( lods, minScale, maxScale, type )
34 |
35 | ### Method: setMappingV( minValue, maxValue, pixelRange )
36 |
37 | ### Method: setRangeV( minValue, maxValue )
38 |
39 | ### Method: pan( deltaPixelX, deltaPixelY )
40 |
41 | ### Method: panX( deltaPixelX )
42 |
43 | ### Method: panY( deltaPixelX )
44 |
45 | ### Method: xAxisScaleAt( pixelX, scale )
46 |
47 | ### Method: yAxisScaleAt( pixelY, scale )
48 |
49 | ### Method: xAxisSync( x, scaleX )
50 |
51 | ### Method: yAxisSync( y, scaleY )
52 |
53 | ### Method: resize( w, h )
54 |
55 | ### Method: repaint()
56 |
57 | ### Method: scaleAction(event)
58 |
59 | ### Method: panAction(event)
60 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | load () {
5 | },
6 |
7 | unload () {
8 | },
9 |
10 | messages: {
11 | open () {
12 | Editor.Panel.open( 'ui-grid.preview' );
13 | },
14 |
15 | 'open-webview' () {
16 | Editor.Panel.open( 'ui-grid.preview-webview' );
17 | },
18 | }
19 | };
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui-grid",
3 | "version": "0.1.4",
4 | "description": "A Grid Widget",
5 | "author": "Firebox Technology",
6 | "main": "main.js",
7 | "widgets": {
8 | "pixi-grid": "widget/pixi-grid.html"
9 | },
10 | "main-menu": {
11 | "i18n:MAIN_MENU.developer.title/UI Preview/ui-grid": {
12 | "message": "ui-grid:open",
13 | "dev": true
14 | },
15 | "i18n:MAIN_MENU.developer.title/UI Preview/ui-grid-webview": {
16 | "message": "ui-grid:open-webview",
17 | "dev": true
18 | }
19 | },
20 | "panel.preview": {
21 | "main": "panel/preview.html",
22 | "ui": "polymer",
23 | "type": "dockable",
24 | "title": "Grid",
25 | "width": 800,
26 | "height": 600,
27 | "min-width": 400,
28 | "min-height": 300,
29 | "messages": []
30 | },
31 | "panel.preview-webview": {
32 | "main": "panel/preview-webview.html",
33 | "ui": "polymer",
34 | "type": "dockable",
35 | "title": "Grid WebView",
36 | "width": 800,
37 | "height": 600,
38 | "min-width": 400,
39 | "min-height": 300,
40 | "messages": []
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/panel/preview-webview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
29 |
30 |
31 |
35 |
36 |
--------------------------------------------------------------------------------
/panel/preview.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
167 |
168 |
--------------------------------------------------------------------------------
/panel/webview-index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/widget/linear-ticks.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | class LinearTicks {
4 | constructor () {
5 | this.ticks = [];
6 | this.tickLods = [];
7 | this.tickRatios = [];
8 |
9 | this.minScale = 0.1;
10 | this.maxScale = 1000.0;
11 |
12 | this.minValueScale = 1.0;
13 | this.maxValueScale = 1.0;
14 |
15 | this.minValue = -500;
16 | this.maxValue = 500;
17 |
18 | this.pixelRange = 500;
19 |
20 | this.minSpacing = 10;
21 | this.maxSpacing = 80;
22 | }
23 |
24 | initTicks ( lods, min, max ) {
25 | if ( min <= 0 ) {
26 | min = 1;
27 | }
28 | if ( max <= 0 ) {
29 | max = 1;
30 | }
31 | if ( max < min ) {
32 | max = min;
33 | }
34 |
35 | this.tickLods = lods;
36 | this.minScale = min;
37 | this.maxScale = max;
38 |
39 | // generate ticks
40 | this.ticks = [];
41 |
42 | let curTick = 1.0;
43 | let curIdx = 0;
44 |
45 | this.ticks.push(curTick);
46 |
47 | let minScale = min;
48 | let maxScale = max;
49 | let maxTickValue = 1;
50 | let minTickValue = 1;
51 |
52 | while ( curTick * this.tickLods[curIdx] <= maxScale ) {
53 | curTick = curTick * this.tickLods[curIdx];
54 | curIdx = curIdx + 1 > this.tickLods.length-1 ? 0 : curIdx + 1;
55 | this.ticks.push(curTick);
56 |
57 | maxTickValue = curTick;
58 | }
59 |
60 | // NOTE: we need to leave two more level for both zoom-in, so enlarge 100 times here.
61 | this.minValueScale = 1.0/maxTickValue * 100;
62 |
63 | curIdx = this.tickLods.length-1;
64 | curTick = 1.0;
65 | while ( curTick / this.tickLods[curIdx] >= minScale ) {
66 | curTick = curTick / this.tickLods[curIdx];
67 | curIdx = curIdx - 1 < 0 ? this.tickLods.length-1 : curIdx - 1;
68 | this.ticks.unshift(curTick);
69 |
70 | minTickValue = curTick;
71 | }
72 |
73 | // NOTE: we need to leave two more level for both zoom-out, so enlarge 100 times here.
74 | this.maxValueScale = 1.0/minTickValue * 100;
75 |
76 | return this;
77 | }
78 |
79 | spacing ( min, max ) {
80 | this.minSpacing = min;
81 | this.maxSpacing = max;
82 |
83 | return this;
84 | }
85 |
86 | range ( minValue, maxValue, pixelRange ) {
87 | // NOTE: Math.fround here to prevent label blinking
88 | this.minValue = Math.fround(Math.min(minValue,maxValue));
89 | this.maxValue = Math.fround(Math.max(minValue,maxValue));
90 |
91 | this.pixelRange = pixelRange;
92 |
93 | this.minTickLevel = 0;
94 | this.maxTickLevel = this.ticks.length-1;
95 |
96 | for ( let i = this.ticks.length-1; i >= 0; --i ) {
97 | let ratio = this.ticks[i] * this.pixelRange / (this.maxValue - this.minValue);
98 | this.tickRatios[i] = (ratio - this.minSpacing) / (this.maxSpacing - this.minSpacing);
99 | if ( this.tickRatios[i] >= 1.0 ) {
100 | this.maxTickLevel = i;
101 | }
102 | if ( ratio <= this.minSpacing ) {
103 | this.minTickLevel = i;
104 | break;
105 | }
106 | }
107 |
108 | for ( let j = this.minTickLevel; j <= this.maxTickLevel; ++j ) {
109 | this.tickRatios[j] = Editor.Math.clamp01(this.tickRatios[j]);
110 | }
111 |
112 | return this;
113 | }
114 |
115 | ticksAtLevel ( level, excludeHigherLevel ) {
116 | let results = [];
117 | let tick = this.ticks[level];
118 | // NOTE: we use `Math.floor` and `<= end` for one more line
119 | // so that label draw will not cut off when at the edge of the viewport
120 | let start = Math.floor( this.minValue / tick );
121 | let end = Math.ceil( this.maxValue / tick );
122 | for ( let i = start; i <= end; ++i ) {
123 | if ( !excludeHigherLevel ||
124 | level >= this.maxTickLevel ||
125 | i % Math.round(this.ticks[level+1] / tick) !== 0 )
126 | {
127 | results.push( i * tick );
128 | }
129 | }
130 |
131 | return results;
132 | }
133 |
134 | levelForStep ( step ) {
135 | for ( let i = 0; i < this.ticks.length; ++i ) {
136 | let ratio = this.ticks[i] * this.pixelRange / (this.maxValue - this.minValue);
137 | if ( ratio >= step ) {
138 | return i;
139 | }
140 | }
141 | return -1;
142 | }
143 | }
144 |
145 | module.exports = LinearTicks;
146 |
--------------------------------------------------------------------------------
/widget/pixi-grid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
X Scale: {{debugInfo.xAxisScale}}
66 |
X Offset: {{debugInfo.xAxisOffset}}
67 |
X Min Level: {{debugInfo.xMinLevel}}
68 |
X Max Level: {{debugInfo.xMaxLevel}}
69 |
70 |
Y Scale: {{debugInfo.yAxisScale}}
71 |
Y Offset: {{debugInfo.yAxisOffset}}
72 |
Y Min Level: {{debugInfo.yMinLevel}}
73 |
Y Max Level: {{debugInfo.yMaxLevel}}
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/widget/pixi-grid.js:
--------------------------------------------------------------------------------
1 | (() => {
2 | 'use strict';
3 |
4 | const Numeral = require('numeral');
5 | const PIXI = require('pixi.js');
6 | const LinearTicks = Editor.require('packages://ui-grid/widget/linear-ticks');
7 |
8 | function _snapPixel (p) {
9 | return Math.floor(p);
10 | }
11 |
12 | function _uninterpolate(a, b) {
13 | b = (b -= a) || 1 / b;
14 | return function(x) { return (x - a) / b; };
15 | }
16 |
17 | function _interpolate(a, b) {
18 | return function(t) { return a * (1 - t) + b * t; };
19 | }
20 |
21 | // pixi config
22 | PIXI.utils._saidHello = true;
23 |
24 | Editor.polymerElement({
25 | properties: {
26 | debugInfo: {
27 | type: Object,
28 | value: () => { return {
29 | xAxisScale: 0,
30 | xMinLevel: 0,
31 | xMaxLevel: 0,
32 | yAxisScale: 0,
33 | yMinLevel: 0,
34 | yMaxLevel: 0,
35 | }; },
36 | },
37 |
38 | showDebugInfo: {
39 | type: Boolean,
40 | value: false,
41 | reflectToAttribute: true
42 | },
43 |
44 | showLabelH: {
45 | type: Boolean,
46 | value: false,
47 | reflectToAttribute: true
48 | },
49 |
50 | showLabelV: {
51 | type: Boolean,
52 | value: false,
53 | reflectToAttribute: true
54 | },
55 | },
56 |
57 | hostAttributes: {
58 | tabindex: -1
59 | },
60 |
61 | created () {
62 | this.canvasWidth = 0;
63 | this.canvasHeight = 0;
64 | this.worldPosition = [0, 0];
65 |
66 | this.hticks = null;
67 | this.xAxisScale = 1.0;
68 | this.xAxisOffset = 0.0;
69 | this.xAnchor = 0.5;
70 |
71 | this.vticks = null;
72 | this.yAxisScale = 1.0;
73 | this.yAxisOffset = 0.0;
74 | this.yAnchor = 0.5;
75 |
76 | // this is generated in setMapping
77 | this._xAnchorOffset = 0.0;
78 | this._yAnchorOffset = 0.0;
79 |
80 | // init direction
81 | this.xDirection = 1.0;
82 | this.yDirection = 1.0;
83 | },
84 |
85 | ready () {
86 | let rect = this.$.view.getBoundingClientRect();
87 | this.renderer = new PIXI.WebGLRenderer( rect.width, rect.height, {
88 | view: this.$['pixi-grid-canvas'],
89 | transparent: true,
90 | antialias: false,
91 | forceFXAA: false,
92 | });
93 |
94 | this.stage = new PIXI.Container();
95 |
96 | // background
97 | let background = new PIXI.Container();
98 | this.stage.addChild(background);
99 |
100 | this.bgGraphics = new PIXI.Graphics();
101 | background.addChild(this.bgGraphics);
102 |
103 | // DISABLE
104 | // // scene
105 | // this.scene = new PIXI.Container();
106 | // this.stage.addChild(this.scene);
107 |
108 | // // foreground
109 | // let foreground = new PIXI.Container();
110 | // this.stage.addChild(foreground);
111 |
112 | // this.fgGraphics = new PIXI.Graphics();
113 | // foreground.addChild(this.fgGraphics);
114 | },
115 |
116 | attached () {
117 | this.async(() => {
118 | this.lightDomReady();
119 | });
120 | },
121 |
122 | lightDomReady () {
123 | this.resize();
124 | this.repaint();
125 | },
126 |
127 | // default 0.5, 0.5
128 | setAnchor ( x, y ) {
129 | this.xAnchor = Editor.Math.clamp( x, -1, 1 );
130 | this.yAnchor = Editor.Math.clamp( y, -1, 1 );
131 | },
132 |
133 | // recommended: [5,2], 0.001, 1000
134 | setScaleH ( lods, minScale, maxScale, type ) {
135 | this.hticks = new LinearTicks()
136 | .initTicks( lods, minScale, maxScale )
137 | .spacing ( 10, 80 )
138 | ;
139 | this.xAxisScale = Editor.Math.clamp(
140 | this.xAxisScale,
141 | this.hticks.minValueScale,
142 | this.hticks.maxValueScale
143 | );
144 |
145 | if ( type === 'frame' ) {
146 | this.hformat = frame => {
147 | return Editor.Utils.formatFrame( frame, 60.0 );
148 | };
149 | }
150 |
151 | this.pixelToValueH = x => {
152 | // return (x - this.canvasWidth * 0.5) / this.xAxisScale;
153 | return (x - this.xAxisOffset) / this.xAxisScale;
154 | };
155 |
156 | this.valueToPixelH = x => {
157 | // return x * this.xAxisScale + this.canvasWidth * 0.5;
158 | return x * this.xAxisScale + this.xAxisOffset;
159 | };
160 | },
161 |
162 | setMappingH ( minValue, maxValue, pixelRange ) {
163 | this._xAnchorOffset = minValue / (maxValue - minValue);
164 | this.xDirection = (maxValue - minValue) > 0 ? 1 : -1;
165 |
166 | this.pixelToValueH = x => {
167 | let pixelOffset = this.xAxisOffset;
168 |
169 | let ratio = this.canvasWidth / pixelRange;
170 | let u = _uninterpolate( 0.0, this.canvasWidth );
171 | let i = _interpolate( minValue * ratio, maxValue * ratio );
172 | return i(u(x - pixelOffset)) / this.xAxisScale;
173 | };
174 |
175 | this.valueToPixelH = x => {
176 | let pixelOffset = this.xAxisOffset;
177 |
178 | let ratio = this.canvasWidth / pixelRange;
179 | let u = _uninterpolate( minValue * ratio, maxValue * ratio );
180 | let i = _interpolate( 0.0, this.canvasWidth );
181 | return i(u(x * this.xAxisScale)) + pixelOffset;
182 | };
183 | },
184 |
185 | setRangeH ( minValue, maxValue ) {
186 | this.xMinRange = minValue;
187 | this.xMaxRange = maxValue;
188 | },
189 |
190 | setScaleV ( lods, minScale, maxScale, type ) {
191 | this.vticks = new LinearTicks()
192 | .initTicks( lods, minScale, maxScale )
193 | .spacing ( 10, 80 )
194 | ;
195 | this.yAxisScale = Editor.Math.clamp(
196 | this.yAxisScale,
197 | this.vticks.minValueScale,
198 | this.vticks.maxValueScale
199 | );
200 |
201 | if ( type === 'frame' ) {
202 | this.vformat = frame => {
203 | return Editor.Utils.formatFrame( frame, 60.0 );
204 | };
205 | }
206 |
207 | this.pixelToValueV = y => {
208 | // return (this.canvasHeight*0.5 - y) / this.yAxisScale;
209 | return (this.canvasHeight - y + this.yAxisOffset) / this.yAxisScale;
210 | };
211 |
212 | this.valueToPixelV = y => {
213 | // return -y * this.yAxisScale + this.canvasHeight*0.5;
214 | return -y * this.yAxisScale + this.canvasHeight + this.yAxisOffset;
215 | };
216 | },
217 |
218 | setMappingV ( minValue, maxValue, pixelRange ) {
219 | this._yAnchorOffset = minValue / (maxValue - minValue);
220 | this.yDirection = (maxValue - minValue) > 0 ? 1 : -1;
221 |
222 | this.pixelToValueV = y => {
223 | let pixelOffset = this.yAxisOffset;
224 |
225 | let ratio = this.canvasHeight / pixelRange;
226 | let u = _uninterpolate( 0.0, this.canvasHeight );
227 | let i = _interpolate( minValue * ratio, maxValue * ratio );
228 | return i(u(y - pixelOffset)) / this.yAxisScale;
229 | };
230 |
231 | this.valueToPixelV = y => {
232 | let pixelOffset = this.yAxisOffset;
233 |
234 | let ratio = this.canvasHeight / pixelRange;
235 | let u = _uninterpolate( minValue * ratio, maxValue * ratio );
236 | let i = _interpolate( 0.0, this.canvasHeight );
237 | return i(u(y * this.yAxisScale)) + pixelOffset;
238 | };
239 | },
240 |
241 | setRangeV ( minValue, maxValue ) {
242 | this.yMinRange = minValue;
243 | this.yMaxRange = maxValue;
244 | },
245 |
246 | pan ( deltaPixelX, deltaPixelY ) {
247 | this.panX(deltaPixelX);
248 | this.panY(deltaPixelY);
249 | },
250 |
251 | panX ( deltaPixelX ) {
252 | if ( !this.valueToPixelH ) {
253 | return;
254 | }
255 |
256 | let newOffset = this.xAxisOffset + deltaPixelX;
257 | this.xAxisOffset = 0.0; // calc range without offset
258 |
259 | let min, max;
260 | if ( this.xMinRange !== undefined && this.xMinRange !== null ) {
261 | min = this.valueToPixelH(this.xMinRange);
262 | }
263 | if ( this.xMaxRange !== undefined && this.xMaxRange !== null ) {
264 | max = this.valueToPixelH(this.xMaxRange);
265 | max = Math.max(0, max - this.canvasWidth);
266 | }
267 |
268 | this.xAxisOffset = newOffset;
269 |
270 | if ( min !== undefined && max !== undefined ) {
271 | this.xAxisOffset = Editor.Math.clamp( this.xAxisOffset, -max, -min );
272 | return;
273 | }
274 |
275 | if ( min !== undefined ) {
276 | this.xAxisOffset = Math.min( this.xAxisOffset, -min );
277 | return;
278 | }
279 |
280 | if ( max !== undefined ) {
281 | this.xAxisOffset = Math.max( this.xAxisOffset, -max );
282 | return;
283 | }
284 | },
285 |
286 | panY ( deltaPixelY ) {
287 | if ( !this.valueToPixelV ) {
288 | return;
289 | }
290 |
291 | let newOffset = this.yAxisOffset + deltaPixelY;
292 | this.yAxisOffset = 0.0; // calc range without offset
293 |
294 | let min, max;
295 | if ( this.yMinRange !== undefined && this.yMinRange !== null ) {
296 | min = this.valueToPixelV(this.yMinRange);
297 | }
298 | if ( this.yMaxRange !== undefined && this.yMaxRange !== null ) {
299 | max = this.valueToPixelV(this.yMaxRange);
300 | max = Math.max(0, max - this.canvasHeight);
301 | }
302 |
303 | this.yAxisOffset = newOffset;
304 |
305 | if ( min !== undefined && max !== undefined ) {
306 | this.yAxisOffset = Editor.Math.clamp( this.yAxisOffset, -max, -min );
307 | return;
308 | }
309 |
310 | if ( min !== undefined ) {
311 | this.yAxisOffset = Math.min( this.yAxisOffset, -min );
312 | return;
313 | }
314 |
315 | if ( max !== undefined ) {
316 | this.yAxisOffset = Math.max( this.yAxisOffset, -max );
317 | return;
318 | }
319 | },
320 |
321 | xAxisScaleAt ( pixelX, scale ) {
322 | let oldValueX = this.pixelToValueH(pixelX);
323 | this.xAxisScale = Editor.Math.clamp( scale, this.hticks.minValueScale, this.hticks.maxValueScale );
324 | let newScreenX = this.valueToPixelH(oldValueX);
325 | this.pan( pixelX - newScreenX, 0 );
326 | },
327 |
328 | yAxisScaleAt ( pixelY, scale ) {
329 | let oldValueY = this.pixelToValueV(pixelY);
330 | this.yAxisScale = Editor.Math.clamp( scale, this.vticks.minValueScale, this.vticks.maxValueScale );
331 | let newScreenY = this.valueToPixelV(oldValueY);
332 | this.pan( 0, pixelY - newScreenY );
333 | },
334 |
335 | xAxisSync ( x, scaleX ) {
336 | this.xAxisOffset = x;
337 | this.xAxisScale = scaleX;
338 | },
339 |
340 | yAxisSync ( y, scaleY ) {
341 | this.yAxisOffset = y;
342 | this.yAxisScale = scaleY;
343 | },
344 |
345 | resize ( w, h ) {
346 | if ( !w || !h ) {
347 | let rect = this.$.view.getBoundingClientRect();
348 | w = w || rect.width;
349 | h = h || rect.height;
350 |
351 | w = Math.round(w);
352 | h = Math.round(h);
353 | }
354 |
355 | // adjust xAxisOffset by anchor x
356 | if ( this.canvasWidth !== 0 ) {
357 | this.panX((w - this.canvasWidth) * (this.xAnchor + this._xAnchorOffset));
358 | }
359 |
360 | // adjust yAxisOffset by anchor y
361 | if ( this.canvasHeight !== 0 ) {
362 | this.panY((h - this.canvasHeight) * (this.yAnchor + this._yAnchorOffset));
363 | }
364 |
365 | this.canvasWidth = w;
366 | this.canvasHeight = h;
367 |
368 | if ( this.renderer ) {
369 | this.renderer.resize( this.canvasWidth, this.canvasHeight );
370 | }
371 | },
372 |
373 | repaint () {
374 | if ( !this.renderer ) {
375 | return;
376 | }
377 |
378 | this._updateGrids();
379 | if (!this._requestID) {
380 | this._requestID = window.requestAnimationFrame(() => {
381 | this._requestID = null;
382 | this.renderer.render(this.stage);
383 | });
384 | }
385 | },
386 |
387 | scaleAction ( event ) {
388 | event.stopPropagation();
389 |
390 | let changeX = true;
391 | let changeY = true;
392 |
393 | if ( event.metaKey ) {
394 | changeX = true;
395 | changeY = false;
396 | } else if ( event.shiftKey ) {
397 | changeX = false;
398 | changeY = true;
399 | }
400 |
401 | let newScale;
402 |
403 | if ( changeX && this.hticks ) {
404 | newScale = Editor.Utils.smoothScale(this.xAxisScale, event.wheelDelta);
405 | this.xAxisScaleAt ( event.offsetX, newScale );
406 | }
407 |
408 | if ( changeY && this.vticks ) {
409 | newScale = Editor.Utils.smoothScale(this.yAxisScale, event.wheelDelta);
410 | this.yAxisScaleAt ( event.offsetY, newScale );
411 | }
412 |
413 | // TODO: smooth animate
414 | // var curScale = this.xAxisScale;
415 | // var nextScale = scale;
416 | // var start = window.performance.now();
417 | // var duration = 300;
418 | // function animateScale ( time ) {
419 | // var requestId = window.requestAnimationFrame ( animateScale.bind(this) );
420 | // var cur = time - start;
421 | // var ratio = cur/duration;
422 | // if ( ratio >= 1.0 ) {
423 | // this.xAxisScale = nextScale;
424 | // cancelAnimationFrame(requestId);
425 | // }
426 | // else {
427 | // this.xAxisScale = Editor.Math.lerp( curScale, nextScale, ratio );
428 | // }
429 | // this.repaint();
430 | // };
431 | // animateScale.call(this,start);
432 |
433 | this.repaint();
434 | },
435 |
436 | panAction ( event ) {
437 | if ( event.which === 1 ) {
438 | this.style.cursor = '-webkit-grabbing';
439 | Editor.UI.startDrag (
440 | '-webkit-grabbing',
441 | event,
442 |
443 | // move
444 | ( event, dx, dy ) => {
445 | this.pan( dx, dy );
446 | this.repaint();
447 | },
448 |
449 | // end
450 | () => {
451 | this.style.cursor = '';
452 | }
453 | );
454 |
455 | return;
456 | }
457 | },
458 |
459 | // DISABLE
460 | // updateSelectRect ( x, y, w, h ) {
461 | // let lineColor = 0x09fff;
462 |
463 | // this.fgGraphics.clear();
464 | // this.fgGraphics.beginFill(lineColor, 0.2);
465 | // this.fgGraphics.lineStyle(1, lineColor, 1.0);
466 | // this.fgGraphics.drawRect(x,y,w,h);
467 | // this.fgGraphics.endFill();
468 | // },
469 |
470 | // clearSelectRect: function () {
471 | // this.fgGraphics.clear();
472 | // this.fgGraphics.endFill();
473 | // },
474 |
475 | _updateGrids () {
476 | let lineColor = 0x555555;
477 | let ticks, ratio;
478 | let screen_x, screen_y;
479 |
480 | this.bgGraphics.clear();
481 | this.bgGraphics.beginFill(lineColor);
482 |
483 | // draw h ticks
484 | if ( this.hticks ) {
485 | let left = this.pixelToValueH(0);
486 | let right = this.pixelToValueH(this.canvasWidth);
487 | this.hticks.range( left, right, this.canvasWidth );
488 |
489 | for ( let i = this.hticks.minTickLevel; i <= this.hticks.maxTickLevel; ++i ) {
490 | ratio = this.hticks.tickRatios[i];
491 | if ( ratio > 0 ) {
492 | this.bgGraphics.lineStyle(1, lineColor, ratio * 0.5);
493 | ticks = this.hticks.ticksAtLevel(i,true);
494 | for ( let j = 0; j < ticks.length; ++j ) {
495 | screen_x = this.valueToPixelH(ticks[j]);
496 | this.bgGraphics.moveTo( _snapPixel(screen_x), -1.0 );
497 | this.bgGraphics.lineTo( _snapPixel(screen_x), this.canvasHeight );
498 | }
499 | }
500 | }
501 | }
502 |
503 | // draw v ticks
504 | if ( this.vticks ) {
505 | let top = this.pixelToValueV(0);
506 | let bottom = this.pixelToValueV(this.canvasHeight);
507 | this.vticks.range( top, bottom, this.canvasHeight );
508 |
509 | for ( let i = this.vticks.minTickLevel; i <= this.vticks.maxTickLevel; ++i ) {
510 | ratio = this.vticks.tickRatios[i];
511 | if ( ratio > 0 ) {
512 | this.bgGraphics.lineStyle(1, lineColor, ratio * 0.5);
513 | ticks = this.vticks.ticksAtLevel(i,true);
514 | for ( let j = 0; j < ticks.length; ++j ) {
515 | screen_y = this.valueToPixelV( ticks[j] );
516 | this.bgGraphics.moveTo( 0.0, _snapPixel(screen_y) );
517 | this.bgGraphics.lineTo( this.canvasWidth, _snapPixel(screen_y) );
518 | }
519 | }
520 | }
521 | }
522 |
523 | this.bgGraphics.endFill();
524 |
525 | // draw label
526 | if ( this.showLabelH || this.showLabelV ) {
527 | var minStep = 50, labelLevel, labelEL, tickValue;
528 | var decimals, fmt;
529 |
530 | this._resetLabels();
531 |
532 | // draw hlabel
533 | if ( this.showLabelH && this.hticks ) {
534 | labelLevel = this.hticks.levelForStep(minStep);
535 | ticks = this.hticks.ticksAtLevel(labelLevel,false);
536 |
537 | tickValue = this.hticks.ticks[labelLevel];
538 | decimals = Math.max( 0, -Math.floor(Math.log10(tickValue)) );
539 | fmt = '0,' + Number(0).toFixed(decimals);
540 |
541 | var hlabelsDOM = Polymer.dom(this.$.hlabels);
542 |
543 | let j;
544 | for ( j = 0; j < ticks.length; ++j ) {
545 | screen_x = _snapPixel(this.valueToPixelH(ticks[j])) + 5;
546 |
547 | if ( j < hlabelsDOM.children.length ) {
548 | labelEL = hlabelsDOM.children[j];
549 | } else {
550 | labelEL = this._createLabel();
551 | hlabelsDOM.appendChild(labelEL);
552 | }
553 |
554 | if ( this.hformat ) {
555 | labelEL.innerText = this.hformat(ticks[j]);
556 | } else {
557 | labelEL.innerText = Numeral(ticks[j]).format(fmt);
558 | }
559 |
560 | labelEL.style.display = 'block';
561 | labelEL.style.left = _snapPixel(screen_x) + 'px';
562 | labelEL.style.bottom = '0px';
563 | labelEL.style.right = '';
564 | labelEL.style.top = '';
565 | // labelEL.style.transform = 'translate3d(' + screen_x + 'px,' + '-15px,' + '0px)';
566 | }
567 | this._hlabelIdx = j;
568 | }
569 |
570 | // draw vlabel
571 | if ( this.showLabelV && this.vticks ) {
572 | labelLevel = this.vticks.levelForStep(minStep);
573 | ticks = this.vticks.ticksAtLevel(labelLevel,false);
574 |
575 | tickValue = this.vticks.ticks[labelLevel];
576 | decimals = Math.max( 0, -Math.floor(Math.log10(tickValue)) );
577 | fmt = '0,' + Number(0).toFixed(decimals);
578 |
579 | var vlabelsDOM = Polymer.dom(this.$.vlabels);
580 |
581 | let j;
582 | for ( j = 0; j < ticks.length; ++j ) {
583 | screen_y = _snapPixel(this.valueToPixelV(ticks[j])) - 15;
584 |
585 | if ( j < vlabelsDOM.children.length ) {
586 | labelEL = vlabelsDOM.children[j];
587 | } else {
588 | labelEL = this._createLabel();
589 | vlabelsDOM.appendChild(labelEL);
590 | }
591 |
592 | if ( this.vformat ) {
593 | labelEL.innerText = this.vformat(ticks[j]);
594 | } else {
595 | labelEL.innerText = Numeral(ticks[j]).format(fmt);
596 | }
597 |
598 | labelEL.style.display = 'block';
599 | labelEL.style.left = '0px';
600 | labelEL.style.top = _snapPixel(screen_y) + 'px';
601 | labelEL.style.bottom = '';
602 | labelEL.style.right = '';
603 | // labelEL.style.transform = 'translate3d(0px,' + screen_y + 'px,' + '0px)';
604 | }
605 | this._vlabelIdx = j;
606 | }
607 |
608 | //
609 | this._hideUnusedLabels();
610 | }
611 |
612 | // DEBUG
613 | if ( this.showDebugInfo ) {
614 | this.set('debugInfo.xAxisScale', this.xAxisScale.toFixed(3));
615 | this.set('debugInfo.xAxisOffset', this.xAxisOffset.toFixed(3));
616 |
617 | if ( this.hticks ) {
618 | this.set('debugInfo.xMinLevel', this.hticks.minTickLevel);
619 | this.set('debugInfo.xMaxLevel', this.hticks.maxTickLevel);
620 | }
621 |
622 | this.set('debugInfo.yAxisScale', this.yAxisScale.toFixed(3));
623 | this.set('debugInfo.yAxisOffset', this.yAxisOffset.toFixed(3));
624 |
625 | if ( this.vticks ) {
626 | this.set('debugInfo.yMinLevel', this.vticks.minTickLevel);
627 | this.set('debugInfo.yMaxLevel', this.vticks.maxTickLevel);
628 | }
629 | }
630 | },
631 |
632 | _resetLabels () {
633 | this._hlabelIdx = 0;
634 | this._vlabelIdx = 0;
635 | },
636 |
637 | _createLabel () {
638 | let el = document.createElement('div');
639 | el.classList.add('label');
640 | return el;
641 | },
642 |
643 | _hideUnusedLabels () {
644 | let hlabelsDOM = Polymer.dom(this.$.hlabels);
645 | let vlabelsDOM = Polymer.dom(this.$.vlabels);
646 |
647 | for ( let i = this._hlabelIdx; i < hlabelsDOM.children.length; ++i ) {
648 | let el = hlabelsDOM.children[i];
649 | el.style.display = 'none';
650 | }
651 |
652 | for ( let i = this._vlabelIdx; i < vlabelsDOM.children.length; ++i ) {
653 | let el = vlabelsDOM.children[i];
654 | el.style.display = 'none';
655 | }
656 | },
657 | });
658 |
659 | })();
660 |
--------------------------------------------------------------------------------