6d86f6dcb1a3c21f66381d996d746307e013dc53
[blender.git] / source / blender / editors / interface / view2d.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2008 Blender Foundation.
21  * All rights reserved.
22  * 
23  * Contributor(s): Blender Foundation, Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 #include <math.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_scene_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_space_types.h"
35 #include "DNA_view2d_types.h"
36
37 #include "BKE_global.h"
38
39 #include "WM_api.h"
40
41 #include "BIF_gl.h"
42
43 #include "UI_resources.h"
44 #include "UI_view2d.h"
45
46 /* *********************************************************************** */
47 /* Setup and Refresh Code */
48
49 /* Set view matrices to ortho for View2D drawing */
50 void UI_view2d_ortho(const bContext *C, View2D *v2d)
51 {
52         wmOrtho2(C->window, v2d->cur.xmin, v2d->cur.xmax, v2d->cur.ymin, v2d->cur.ymax);
53 }
54
55 /* Adjust mask size in response to view size changes */
56 // XXX pre2.5 -> this used to be called  calc_scrollrcts()
57 void UI_view2d_update_size(View2D *v2d, int winx, int winy)
58 {
59         /* mask - view frame */
60         v2d->mask.xmin= v2d->mask.ymin= 0;
61         v2d->mask.xmax= winx;
62         v2d->mask.ymax= winy;
63         
64         /* scrollbars shrink mask area, but should be based off regionsize */
65         // XXX scrollbars should become limited to one bottom lower edges of region like everyone else does!
66         if(v2d->scroll) {
67                 /* vertical scrollbar */
68                 if (v2d->scroll & L_SCROLL) {
69                         /* on left-hand edge of region */
70                         v2d->vert= v2d->mask;
71                         v2d->vert.xmax= SCROLLB;
72                         v2d->mask.xmin= SCROLLB;
73                 }
74                 else if(v2d->scroll & R_SCROLL) {
75                         /* on right-hand edge of region */
76                         v2d->vert= v2d->mask;
77                         v2d->vert.xmin= v2d->vert.xmax-SCROLLB;
78                         v2d->mask.xmax= v2d->vert.xmin;
79                 }
80                 
81                 /* horizontal scrollbar */
82                 if ((v2d->scroll & B_SCROLL) || (v2d->scroll & B_SCROLLO)) {
83                         /* on bottom edge of region (B_SCROLLO is outliner, the ohter is for standard) */
84                         v2d->hor= v2d->mask;
85                         v2d->hor.ymax= SCROLLH;
86                         v2d->mask.ymin= SCROLLH;
87                 }
88                 else if(v2d->scroll & T_SCROLL) {
89                         /* on upper edge of region */
90                         v2d->hor= v2d->mask;
91                         v2d->hor.ymin= v2d->hor.ymax-SCROLLH;
92                         v2d->mask.ymax= v2d->hor.ymin;
93                 }
94         }
95 }
96
97 /* *********************************************************************** */
98 /* Gridlines */
99
100 /* minimum pixels per gridstep */
101 #define MINGRIDSTEP     35
102
103 /* View2DGrid is typedef'd in UI_view2d.h */
104 struct View2DGrid {
105         float dx, dy;                   /* stepsize (in pixels) between gridlines */
106         float startx, starty;   /* */
107         int powerx, powery;             /* step as power of 10 */
108 };
109
110 /* --------------- */
111
112 /* try to write step as a power of 10 */
113 static void step_to_grid(float *step, int *power, int unit)
114 {
115         const float loga= log10(*step);
116         float rem;
117         
118         *power= (int)(loga);
119         
120         rem= loga - (*power);
121         rem= pow(10.0, rem);
122         
123         if (loga < 0.0f) {
124                 if (rem < 0.2f) rem= 0.2f;
125                 else if(rem < 0.5f) rem= 0.5f;
126                 else rem= 1.0f;
127                 
128                 *step= rem * pow(10.0, (double)(*power));
129                 
130                 /* for frames, we want 1.0 frame intervals only */
131                 if (unit == V2D_UNIT_FRAMES) {
132                         rem = 1.0f;
133                         *step = 1.0f;
134                 }
135                 
136                 /* prevents printing 1.0 2.0 3.0 etc */
137                 if (rem == 1.0f) (*power)++;    
138         }
139         else {
140                 if (rem < 2.0f) rem= 2.0f;
141                 else if(rem < 5.0f) rem= 5.0f;
142                 else rem= 10.0f;
143                 
144                 *step= rem * pow(10.0, (double)(*power));
145                 
146                 (*power)++;
147                 /* prevents printing 1.0, 2.0, 3.0, etc. */
148                 if (rem == 10.0f) (*power)++;   
149         }
150 }
151
152 /* Intialise settings necessary for drawing gridlines in a 2d-view 
153  *      - Currently, will return pointer to View2DGrid struct that needs to 
154  *        be freed with UI_view2d_free_grid()
155  *      - Is used for scrollbar drawing too (for units drawing)  --> (XXX needs review)
156  *      
157  *      - unit  = V2D_UNIT_*  grid steps in seconds or frames 
158  *      - clamp = V2D_CLAMP_* only show whole-number intervals
159  *      - winx  = width of region we're drawing to
160  *      - winy  = height of region we're drawing into
161  */
162 View2DGrid *UI_view2d_calc_grid(const bContext *C, View2D *v2d, short unit, short clamp, int winx, int winy)
163 {
164         View2DGrid *grid;
165         float space, pixels, seconddiv;
166         int secondgrid;
167         
168         /* grid here is allocated... */
169         grid= MEM_callocN(sizeof(View2DGrid), "View2DGrid");
170         
171         /* rule: gridstep is minimal GRIDSTEP pixels */
172         if (unit == V2D_UNIT_FRAMES) {
173                 secondgrid= 0;
174                 seconddiv= 0.01f * FPS;
175         }
176         else {
177                 secondgrid= 1;
178                 seconddiv= 1.0f;
179         }
180         
181         space= v2d->cur.xmax - v2d->cur.xmin;
182         pixels= v2d->mask.xmax - v2d->mask.xmin;
183         
184         grid->dx= (MINGRIDSTEP * space) / (seconddiv * pixels);
185         step_to_grid(&grid->dx, &grid->powerx, unit);
186         grid->dx *= seconddiv;
187         
188         if (clamp == V2D_GRID_CLAMP) {
189                 if (grid->dx < 0.1f) grid->dx= 0.1f;
190                 grid->powerx-= 2;
191                 if (grid->powerx < -2) grid->powerx= -2;
192         }
193         
194         space= (v2d->cur.ymax - v2d->cur.ymin);
195         pixels= winy;
196         grid->dy= MINGRIDSTEP*space/pixels;
197         step_to_grid(&grid->dy, &grid->powery, unit);
198         
199         if (clamp == V2D_GRID_CLAMP) {
200                 if (grid->dy < 1.0f) grid->dy= 1.0f;
201                 if (grid->powery < 1) grid->powery= 1;
202         }
203         
204         grid->startx= seconddiv*(v2d->cur.xmin/seconddiv - fmod(v2d->cur.xmin/seconddiv, grid->dx/seconddiv));
205         if (v2d->cur.xmin < 0.0f) grid->startx-= grid->dx;
206         
207         grid->starty= (v2d->cur.ymin-fmod(v2d->cur.ymin, grid->dy));
208         if (v2d->cur.ymin < 0.0f) grid->starty-= grid->dy;
209
210         return grid;
211 }
212
213 /* Draw gridlines in the given 2d-region */
214 void UI_view2d_draw_grid(const bContext *C, View2D *v2d, View2DGrid *grid, int flag)
215 {
216         float vec1[2], vec2[2];
217         int a, step;
218         
219         /* vertical lines */
220         if (flag & V2D_VERTICAL_LINES) {
221                 /* initialise initial settings */
222                 vec1[0]= vec2[0]= grid->startx;
223                 vec1[1]= grid->starty;
224                 vec2[1]= v2d->cur.ymax;
225                 
226                 /* minor gridlines */
227                 step= (v2d->mask.xmax - v2d->mask.xmin + 1) / MINGRIDSTEP;
228                 
229                 UI_ThemeColor(TH_GRID);
230                 
231                 for (a=0; a<step; a++) {
232                         glBegin(GL_LINE_STRIP);
233                                 glVertex2fv(vec1); 
234                                 glVertex2fv(vec2);
235                         glEnd();
236                         
237                         vec2[0]= vec1[0]+= grid->dx;
238                 }
239                 
240                 /* major gridlines */
241                 vec2[0]= vec1[0]-= 0.5f*grid->dx;
242                 
243                 UI_ThemeColorShade(TH_GRID, 16);
244                 
245                 step++;
246                 for (a=0; a<=step; a++) {
247                         glBegin(GL_LINE_STRIP);
248                                 glVertex2fv(vec1); 
249                                 glVertex2fv(vec2);
250                         glEnd();
251                         
252                         vec2[0]= vec1[0]-= grid->dx;
253                 }
254         }
255         
256         /* horizontal lines */
257         if (flag & V2D_HORIZONTAL_LINES) {
258                 /* only major gridlines */
259                 vec1[0]= grid->startx;
260                 vec1[1]= vec2[1]= grid->starty;
261                 vec2[0]= v2d->cur.xmax;
262                 
263                 step= (v2d->mask.ymax - v2d->mask.ymax + 1) / MINGRIDSTEP;
264                 
265                 UI_ThemeColor(TH_GRID);
266                 for (a=0; a<=step; a++) {
267                         glBegin(GL_LINE_STRIP);
268                                 glVertex2fv(vec1); 
269                                 glVertex2fv(vec2);
270                         glEnd();
271                         
272                         vec2[1]= vec1[1]+= grid->dy;
273                 }
274                 
275                 vec2[1]= vec1[1]-= 0.5f*grid->dy;
276                 step++;
277         }
278         
279         /* Axes are drawn as darker lines */
280         UI_ThemeColorShade(TH_GRID, -50);
281         
282         /* horizontal axis */
283         if (flag & V2D_HORIZONTAL_AXIS) {
284                 vec1[0]= v2d->cur.xmin;
285                 vec2[0]= v2d->cur.xmax;
286                 vec1[1]= vec2[1]= 0.0f;
287                 
288                 glBegin(GL_LINE_STRIP);
289                         glVertex2fv(vec1);
290                         glVertex2fv(vec2);
291                 glEnd();
292         }
293         
294         /* vertical axis */
295         if (flag & V2D_VERTICAL_AXIS) {
296                 vec1[1]= v2d->cur.ymin;
297                 vec2[1]= v2d->cur.ymax;
298                 vec1[0]= vec2[0]= 0.0f;
299                 
300                 glBegin(GL_LINE_STRIP);
301                         glVertex2fv(vec1); 
302                         glVertex2fv(vec2);
303                 glEnd();
304         }
305 }
306
307 /* free temporary memory used for drawing grid */
308 void UI_view2d_free_grid(View2DGrid *grid)
309 {
310         MEM_freeN(grid);
311 }
312
313 /* *********************************************************************** */
314 /* Coordinate Conversions */
315
316 /* Convert from screen/region space to 2d-View space 
317  *      
318  *      - x,y                   = coordinates to convert
319  *      - viewx,viewy           = resultant coordinates
320  */
321 void UI_view2d_region_to_view(View2D *v2d, short x, short y, float *viewx, float *viewy)
322 {
323         float div, ofs;
324
325         if (viewx) {
326                 div= v2d->mask.xmax - v2d->mask.xmin;
327                 ofs= v2d->mask.xmin;
328                 
329                 *viewx= v2d->cur.xmin + (v2d->cur.xmax-v2d->cur.xmin) * (x - ofs) / div;
330         }
331
332         if (viewy) {
333                 div= v2d->mask.ymax - v2d->mask.ymin;
334                 ofs= v2d->mask.ymin;
335                 
336                 *viewy= v2d->cur.ymin + (v2d->cur.ymax - v2d->cur.ymin) * (y - ofs) / div;
337         }
338 }
339
340 /* Convert from 2d-View space to screen/region space
341  *      - Coordinates are clamped to lie within bounds of region
342  *
343  *      - x,y                           = coordinates to convert
344  *      - regionx,regiony       = resultant coordinates 
345  */
346 void UI_view2d_view_to_region(View2D *v2d, float x, float y, short *regionx, short *regiony)
347 {
348         /* set initial value in case coordinate lies outside of bounds */
349         if (regionx)
350                 *regionx= V2D_IS_CLIPPED;
351         if (regiony)
352                 *regiony= V2D_IS_CLIPPED;
353         
354         /* express given coordinates as proportional values */
355         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
356         y= (x - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
357         
358         /* check if values are within bounds */
359         if ((x>=0.0f) && (x<=1.0f) && (y>=0.0f) && (y<=1.0f)) {
360                 if (regionx)
361                         *regionx= v2d->mask.xmin + x*(v2d->mask.xmax-v2d->mask.xmin);
362                 if (regiony)
363                         *regiony= v2d->mask.ymin + y*(v2d->mask.ymax-v2d->mask.ymin);
364         }
365 }
366
367 /* Convert from 2d-view space to screen/region space
368  *      - Coordinates are NOT clamped to lie within bounds of region
369  *
370  *      - x,y                           = coordinates to convert
371  *      - regionx,regiony       = resultant coordinates 
372  */
373 void UI_view2d_to_region_no_clip(View2D *v2d, float x, float y, short *regionx, short *regiony)
374 {
375         /* step 1: express given coordinates as proportional values */
376         x= (x - v2d->cur.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
377         y= (x - v2d->cur.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
378         
379         /* step 2: convert proportional distances to screen coordinates  */
380         x= v2d->mask.xmin + x*(v2d->mask.xmax - v2d->mask.xmin);
381         y= v2d->mask.ymin + y*(v2d->mask.ymax - v2d->mask.ymin);
382         
383         /* although we don't clamp to lie within region bounds, we must avoid exceeding size of shorts */
384         if (regionx) {
385                 if (x < -32760) *regionx= -32760;
386                 else if(x > 32760) *regionx= 32760;
387                 else *regionx= x;
388         }
389         if (regiony) {
390                 if (y < -32760) *regiony= -32760;
391                 else if(y > 32760) *regiony= 32760;
392                 else *regiony= y;
393         }
394 }
395
396 /* *********************************************************************** */
397 /* Utilities */
398
399 /* Calculate the scale per-axis of the drawing-area
400  *      - Is used to inverse correct drawing of icons, etc. that need to follow view 
401  *        but not be affected by scale
402  *
403  *      - x,y   = scale on each axis
404  */
405 void UI_view2d_getscale(View2D *v2d, float *x, float *y) 
406 {
407         if (x) *x = (v2d->mask.xmax - v2d->mask.xmin) / (v2d->cur.xmax - v2d->cur.xmin);
408         if (y) *y = (v2d->mask.ymax - v2d->mask.ymin) / (v2d->cur.ymax - v2d->cur.ymin);
409 }