2 * Grace - GRaphing, Advanced Computation and Exploration of data
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2003 Grace Development Team
9 * Maintained by Evgeny Stambulchik
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 * utilities for graphs
40 #include "core_utils.h"
42 static int graph_count_hook(Quark
*q
, void *udata
, QTraverseClosure
*closure
)
44 int *ngraphs
= (int *) udata
;
46 if (quark_fid_get(q
) == QFlavorGraph
) {
53 int number_of_graphs(Quark
*project
)
57 quark_traverse(project
, graph_count_hook
, &ngraphs
);
62 static int frame_count_hook(Quark
*q
, void *udata
, QTraverseClosure
*closure
)
64 int *nframes
= (int *) udata
;
66 if (quark_fid_get(q
) == QFlavorFrame
) {
73 int number_of_frames(Quark
*project
)
77 quark_traverse(project
, frame_count_hook
, &nframes
);
82 Quark
*graph_get_current(const Quark
*project
)
85 Project
*pr
= project_get_data(project
);
92 Quark
*graph_next(Quark
*project
)
100 f
= frame_new(project
);
102 if (g
&& number_of_graphs(project
) == 1) {
103 Project
*pr
= project_get_data(project
);
109 int select_graph(Quark
*gr
)
111 if (quark_is_active(gr
)) {
112 Project
*pr
= project_get_data(get_parent_project(gr
));
116 return RETURN_SUCCESS
;
118 return RETURN_FAILURE
;
121 return RETURN_FAILURE
;
126 int is_log_axis(const Quark
*q
)
128 Quark
*gr
= get_parent_graph(q
);
129 if ((axisgrid_is_x(q
) && islogx(gr
)) ||
130 (axisgrid_is_y(q
) && islogy(gr
))) {
137 int is_logit_axis(const Quark
*q
)
139 Quark
*gr
= get_parent_graph(q
);
140 if ((axisgrid_is_x(q
) && islogitx(gr
)) ||
141 (axisgrid_is_y(q
) && islogity(gr
))) {
148 int islogx(Quark
*gr
)
150 if (graph_get_xscale(gr
) == SCALE_LOG
) {
157 int islogy(Quark
*gr
)
159 if (graph_get_yscale(gr
) == SCALE_LOG
) {
166 int islogitx(Quark
*gr
)
168 if (graph_get_xscale(gr
) == SCALE_LOGIT
) {
175 int islogity(Quark
*gr
)
177 if (graph_get_yscale(gr
) == SCALE_LOGIT
) {
185 /* The following routines determine default axis range and tickmarks */
187 static void autorange_bysets(Quark
**sets
, int nsets
, int autos_type
);
189 static int autotick_hook(Quark
*q
, void *udata
, QTraverseClosure
*closure
)
191 int *amask
= (int *) udata
;
193 switch (quark_fid_get(q
)) {
195 closure
->descend
= FALSE
;
196 if (((*amask
& AXIS_MASK_X
) && axisgrid_is_x(q
)) ||
197 ((*amask
& AXIS_MASK_Y
) && axisgrid_is_y(q
))) {
198 axisgrid_autotick(q
);
206 void autotick_graph_axes(Quark
*q
, int amask
)
208 quark_traverse(q
, autotick_hook
, &amask
);
211 void autoscale_bysets(Quark
**sets
, int nsets
, int autos_type
)
219 gr
= get_parent_graph(sets
[0]);
221 autorange_bysets(sets
, nsets
, autos_type
);
222 autotick_graph_axes(gr
, autos_type
);
225 int autoscale_graph(Quark
*gr
, int autos_type
)
229 nsets
= quark_get_descendant_sets(gr
, &sets
);
231 autoscale_bysets(sets
, nsets
, autos_type
);
233 return RETURN_SUCCESS
;
235 return RETURN_FAILURE
;
239 static void round_axis_limits(double *amin
, double *amax
, int scale
)
244 if (*amin
== *amax
) {
245 switch (sign(*amin
)) {
261 if (scale
== SCALE_LOG
) {
263 errmsg("Can't autoscale a log axis by non-positive data");
267 } else if (*amin
<= 0.0) {
268 errmsg("Data have non-positive values");
273 } else if (scale
== SCALE_LOGIT
) {
275 errmsg("Can't autoscale a logit axis by non-positive data");
279 } else if (*amin
<= 0.0) {
280 errmsg("Data have non-positive values");
283 smin
= log(*amin
/(1-*amin
));
284 smax
= log(*amax
/(1-*amax
));
290 if (sign(smin
) == sign(smax
)) {
291 nrange
= -rint(log10(fabs(2*(smax
- smin
)/(smax
+ smin
))));
292 nrange
= MAX2(0, nrange
);
296 smin
= nicenum(smin
, nrange
, NICE_FLOOR
);
297 smax
= nicenum(smax
, nrange
, NICE_CEIL
);
298 if (sign(smin
) == sign(smax
)) {
299 if (smax
/smin
> 5.0) {
301 } else if (smin
/smax
> 5.0) {
306 if (scale
== SCALE_LOG
) {
307 *amin
= pow(10.0, smin
);
308 *amax
= pow(10.0, smax
);
309 } else if (scale
== SCALE_LOGIT
) {
310 *amin
= exp(smin
)/(1.0 + exp(smin
));
311 *amax
= exp(smax
)/(1.0 + exp(smax
));
318 static void autorange_bysets(Quark
**sets
, int nsets
, int autos_type
)
322 double xmax
, xmin
, ymax
, ymin
;
325 if (autos_type
== AUTOSCALE_NONE
|| nsets
<= 0) {
329 gr
= get_parent_graph(sets
[0]);
331 graph_get_world(gr
, &w
);
333 if (graph_get_type(gr
) == GRAPH_SMITH
) {
334 if (autos_type
== AUTOSCALE_X
|| autos_type
== AUTOSCALE_XY
) {
338 if (autos_type
== AUTOSCALE_Y
|| autos_type
== AUTOSCALE_XY
) {
342 graph_set_world(gr
, &w
);
350 if (autos_type
== AUTOSCALE_XY
) {
351 getsetminmax(sets
, nsets
, &xmin
, &xmax
, &ymin
, &ymax
);
352 } else if (autos_type
== AUTOSCALE_X
) {
353 getsetminmax_c(sets
, nsets
, &xmin
, &xmax
, &ymin
, &ymax
, 2);
354 } else if (autos_type
== AUTOSCALE_Y
) {
355 getsetminmax_c(sets
, nsets
, &xmin
, &xmax
, &ymin
, &ymax
, 1);
358 if (autos_type
== AUTOSCALE_X
|| autos_type
== AUTOSCALE_XY
) {
359 scale
= graph_get_xscale(gr
);
360 round_axis_limits(&xmin
, &xmax
, scale
);
365 if (autos_type
== AUTOSCALE_Y
|| autos_type
== AUTOSCALE_XY
) {
366 scale
= graph_get_yscale(gr
);
367 round_axis_limits(&ymin
, &ymax
, scale
);
372 graph_set_world(gr
, &w
);
376 * pan through world coordinates
378 int graph_scroll(Quark
*gr
, int type
)
380 RunTime
*rt
= rt_from_quark(gr
);
382 double xmax
, xmin
, ymax
, ymin
;
385 if (graph_get_world(gr
, &w
) == RETURN_SUCCESS
) {
386 if (islogx(gr
) == TRUE
) {
394 if (islogy(gr
) == TRUE
) {
407 dwc
*= rt
->scrollper
* (xmax
- xmin
);
414 dwc
*= rt
->scrollper
* (ymax
- ymin
);
420 if (islogx(gr
) == TRUE
) {
421 w
.xg1
= pow(10.0, xmin
);
422 w
.xg2
= pow(10.0, xmax
);
428 if (islogy(gr
) == TRUE
) {
429 w
.yg1
= pow(10.0, ymin
);
430 w
.yg2
= pow(10.0, ymax
);
436 graph_set_world(gr
, &w
);
438 return RETURN_SUCCESS
;
440 return RETURN_FAILURE
;
444 int graph_zoom(Quark
*gr
, int type
)
446 RunTime
*rt
= rt_from_quark(gr
);
448 double xmax
, xmin
, ymax
, ymin
;
451 if (graph_get_world(gr
, &w
) == RETURN_SUCCESS
) {
453 if (islogx(gr
) == TRUE
) {
461 if (islogy(gr
) == TRUE
) {
469 dx
= rt
->shexper
* (xmax
- xmin
);
470 dy
= rt
->shexper
* (ymax
- ymin
);
471 if (type
== GZOOM_SHRINK
) {
481 if (islogx(gr
) == TRUE
) {
482 w
.xg1
= pow(10.0, xmin
);
483 w
.xg2
= pow(10.0, xmax
);
489 if (islogy(gr
) == TRUE
) {
490 w
.yg1
= pow(10.0, ymin
);
491 w
.yg2
= pow(10.0, ymax
);
497 graph_set_world(gr
, &w
);
499 return RETURN_SUCCESS
;
501 return RETURN_FAILURE
;
508 int arrange_frames(Quark
**frames
, int nframes
,
509 int nrows
, int ncols
, int order
, int snake
,
510 double loff
, double roff
, double toff
, double boff
,
511 double vgap
, double hgap
,
512 int hpack
, int vpack
)
514 int i
, imax
, j
, jmax
, iw
, ih
, nf
;
520 return RETURN_FAILURE
;
524 pr
= get_parent_project(f
);
526 return RETURN_FAILURE
;
535 if (ncols
< 1 || nrows
< 1) {
536 errmsg("# of rows and columns must be > 0");
537 return RETURN_FAILURE
;
539 if (hgap
< 0.0 || vgap
< 0.0) {
540 errmsg("hgap and vgap must be >= 0");
541 return RETURN_FAILURE
;
544 project_get_viewport(pr
, &pw
, &ph
);
545 w
= (pw
- loff
- roff
)/(ncols
+ (ncols
- 1)*hgap
);
546 h
= (ph
- toff
- boff
)/(nrows
+ (nrows
- 1)*vgap
);
547 if (h
<= 0.0 || w
<= 0.0) {
548 errmsg("Page offsets are too large");
549 return RETURN_FAILURE
;
553 if (order
& GA_ORDER_HV_INV
) {
560 for (i
= 0; i
< imax
&& nf
< nframes
; i
++) {
561 for (j
= 0; j
< jmax
&& nf
< nframes
; j
++) {
564 if (order
& GA_ORDER_HV_INV
) {
567 if (snake
&& (iw
% 2)) {
573 if (snake
&& (ih
% 2)) {
577 if (order
& GA_ORDER_H_INV
) {
580 /* viewport y coord goes bottom -> top ! */
581 if (!(order
& GA_ORDER_V_INV
)) {
585 v
.xv1
= loff
+ iw
*w
*(1.0 + hgap
);
587 v
.yv1
= boff
+ ih
*h
*(1.0 + vgap
);
589 frame_set_view(f
, &v
);
594 return RETURN_SUCCESS
;
597 void move_legend(Quark
*fr
, const VVector
*shift
)
599 legend
*l
= frame_get_legend(fr
);
601 l
->offset
.x
+= shift
->x
;
602 l
->offset
.y
+= shift
->y
;
604 quark_dirtystate_set(fr
, TRUE
);
612 static int hook(Quark
*q
, void *udata
, QTraverseClosure
*closure
)
618 ext_xy_t
*ext_xy
= (ext_xy_t
*) udata
;
620 switch (quark_fid_get(q
)) {
622 f
= frame_get_data(q
);
623 frame_get_view(q
, &v
);
628 frame_set_view(q
, &v
);
630 f
->l
.offset
.x
*= ext_xy
->x
;
631 f
->l
.offset
.y
*= ext_xy
->y
;
633 /* TODO: tickmark offsets */
634 quark_dirtystate_set(q
, TRUE
);
637 o
= object_get_data(q
);
638 if (object_get_loctype(q
) == COORD_VIEW
) {
639 o
->ap
.x
*= ext_xy
->x
;
640 o
->ap
.y
*= ext_xy
->y
;
641 o
->offset
.x
*= ext_xy
->x
;
642 o
->offset
.y
*= ext_xy
->y
;
644 quark_dirtystate_set(q
, TRUE
);
648 at
= atext_get_data(q
);
649 if (object_get_loctype(q
) == COORD_VIEW
) {
650 at
->ap
.x
*= ext_xy
->x
;
651 at
->ap
.y
*= ext_xy
->y
;
652 at
->offset
.x
*= ext_xy
->x
;
653 at
->offset
.y
*= ext_xy
->y
;
655 quark_dirtystate_set(q
, TRUE
);
663 void rescale_viewport(Quark
*project
, double ext_x
, double ext_y
)
669 quark_traverse(project
, hook
, &ext_xy
);