# HG changeset patch
# User Konstantinos Poulios
# Date 1295644823 -3600
# Branch minorticks-restructure-draw_axes-bug-31800
# Node ID 2b1a942b84d387a962cc14caa4427d0b075e05eb
# Parent 62b7ea59a6ff354d2841a83ba170773f1b70357a
Restructuring draw_axes, enabling minorticks/grid in fltk, closing bug 31800
diff -r 62b7ea59a6ff -r 2b1a942b84d3 src/ChangeLog
--- a/src/ChangeLog Fri Jan 21 20:22:42 2011 +0100
+++ b/src/ChangeLog Fri Jan 21 22:20:23 2011 +0100
@@ -1,3 +1,19 @@
+2011-01-22 Konstantinos Poulios
+
+ * graphics.h.in, graphics.cc (xmtick, ymtick, zmtick):
+ New properties holding minor ticks positions.
+ (axes::properties::calc_ticks_and_lims): Calculation of minor ticks
+ positions.
+ * gl-render.h, gl-render.cc
+ (opengl_renderer::render_grid, opengl_renderer::render_tickmarks,
+ opengl_renderer::render_ticktexts): New functions.
+ (opengl_renderer::draw_axes): Make use of new rendering functions
+ and minor ticks positions.
+ Correct axis label positioning for x axis at top and y axis at right
+ position. Bug #31800.
+ Change axis positioning policy for 3D plots, keeping x and y axis
+ always at bottom.
+
2011-01-21 Konstantinos Poulios
* src/graphics.cc (axes::properties::set_xlabel,
diff -r 62b7ea59a6ff -r 2b1a942b84d3 src/gl-render.cc
--- a/src/gl-render.cc Fri Jan 21 20:22:42 2011 +0100
+++ b/src/gl-render.cc Fri Jan 21 22:20:23 2011 +0100
@@ -606,6 +606,137 @@
}
void
+opengl_renderer::render_grid (std::string& gridstyle, Matrix& ticks,
+ double lim1, double lim2,
+ double p1, double p1N, double p2, double p2N,
+ int xyz, bool is_3D)
+{
+ set_linestyle (gridstyle, true);
+ glBegin (GL_LINES);
+ for (int i = 0; i < ticks.numel (); i++)
+ {
+ double val = ticks(i);
+ if (lim1 <= val && val <= lim2)
+ {
+ if (xyz == 0) // X
+ {
+ glVertex3d (val, p1N, p2);
+ glVertex3d (val, p1, p2);
+ if (is_3D)
+ {
+ glVertex3d (val, p1, p2N);
+ glVertex3d (val, p1, p2);
+ }
+ }
+ else if (xyz == 1) // Y
+ {
+ glVertex3d (p1N, val, p2);
+ glVertex3d (p1, val, p2);
+ if (is_3D)
+ {
+ glVertex3d (p1, val, p2N);
+ glVertex3d (p1, val, p2);
+ }
+ }
+ else if (xyz == 2) // Z
+ {
+ glVertex3d (p1N, p2, val);
+ glVertex3d (p1, p2, val);
+ glVertex3d (p1, p2N, val);
+ glVertex3d (p1, p2, val);
+ }
+ }
+ }
+ glEnd ();
+ set_linestyle ("-", true);
+}
+
+void
+opengl_renderer::render_tickmarks(Matrix& ticks, double lim1, double lim2,
+ double p1, double p1N, double p2, double p2N,
+ double dx, double dy, double dz,
+ int xyz, bool doubleside)
+{
+ glBegin (GL_LINES);
+ for (int i = 0; i < ticks.numel (); i++)
+ {
+ double val = ticks(i);
+
+ if (lim1 <= val && val <= lim2)
+ {
+ if (xyz == 0) // X
+ {
+ glVertex3d (val, p1, p2);
+ glVertex3d (val, p1+dy, p2+dz);
+ if (doubleside)
+ {
+ glVertex3d (val, p1N, p2N);
+ glVertex3d (val, p1N-dy, p2N-dz);
+ }
+ }
+ else if (xyz == 1) // Y
+ {
+ glVertex3d (p1, val, p2);
+ glVertex3d (p1+dx, val, p2+dz);
+ if (doubleside)
+ {
+ glVertex3d (p1N, val, p2N);
+ glVertex3d (p1N-dx, val, p2N-dz);
+ }
+ }
+ else if (xyz == 2) // Z
+ {
+ glVertex3d (p1, p2, val);
+ glVertex3d (p1+dx, p2+dy, val);
+ if (doubleside)
+ {
+ glVertex3d (p1N, p2N, val);
+ glVertex3d (p1N-dx, p2N-dy, val);
+ }
+ }
+ }
+ }
+ glEnd ();
+}
+
+void
+opengl_renderer::render_ticktexts(Matrix& ticks, string_vector& ticklabels,
+ double lim1, double lim2,
+ double p1, double p2,
+ int xyz, int ha, int va,
+ int& wmax, int& hmax)
+{
+ int n = std::min (ticklabels.numel (), ticks.numel ());
+
+ for (int i = 0; i < n; i++)
+ {
+ double val = ticks(i);
+
+ if (lim1 <= val && val <= lim2)
+ {
+ Matrix b;
+ // FIXME: as tick text is transparent, shouldn't be
+ // drawn after axes object, for correct rendering?
+ if (xyz == 0) // X
+ {
+ b = render_text (ticklabels(i), val, p1, p2, ha, va);
+ }
+ else if (xyz == 1) // Y
+ {
+ b = render_text (ticklabels(i), p1, val, p2, ha, va);
+ }
+ else if (xyz == 2) // Z
+ {
+ b = render_text (ticklabels(i), p1, p2, val, ha, va);
+ }
+
+ wmax = std::max (wmax, static_cast (b(2)));
+ hmax = std::max (hmax, static_cast (b(3)));
+ }
+ }
+}
+
+void
opengl_renderer::draw_axes (const axes::properties& props)
{
// setup OpenGL transformation
@@ -783,7 +914,15 @@
bool xySym = (xd*yd*(xPlane-xPlaneN)*(yPlane-yPlaneN) > 0);
bool x2Dtop = false;
bool y2Dright = false;
- double zpTick = zPlane;
+ bool layer2Dtop = false;
+ bool zSign = (zd*(zPlane-zPlaneN) <= 0);
+ bool xyzSym = zSign ? xySym : !xySym;
+ double xpTick = (zSign ? xPlaneN : xPlane);
+ double ypTick = (zSign ? yPlaneN : yPlane);
+ double zpTick = (zSign ? zPlane : zPlaneN);
+ double xpTickN = (zSign ? xPlane : xPlaneN);
+ double ypTickN = (zSign ? yPlane : yPlaneN);
+ double zpTickN = (zSign ? zPlaneN : zPlane);
/* 2D mode */
if (xstate == AXE_HORZ_DIR && ystate == AXE_VERT_DIR)
@@ -795,6 +934,8 @@
yPlaneN = tmp;
x2Dtop = true;
}
+ ypTick = yPlaneN;
+ ypTickN = yPlane;
if (props.yaxislocation_is ("right"))
{
double tmp = xPlane;
@@ -802,10 +943,20 @@
xPlaneN = tmp;
y2Dright = true;
}
+ xpTick = xPlaneN;
+ xpTickN = xPlane;
if (props.layer_is ("top"))
- zpTick = zPlaneN;
+ {
+ zpTick = zPlaneN;
+ layer2Dtop = true;
+ }
+ else
+ zpTick = zPlane;
}
+ Matrix view = props.get_view ().matrix_value ();
+ bool nearhoriz = std::abs(view(1)) <= 5;
+
Matrix axe_color = props.get_color_rgb ();
bool visible = props.is_visible ();
bool box = props.is_box ();
@@ -853,30 +1004,30 @@
// X box
set_color (props.get_xcolor_rgb ());
- glVertex3d (xPlaneN, yPlaneN, zPlane);
- glVertex3d (xPlane, yPlaneN, zPlane);
+ glVertex3d (xPlaneN, ypTick, zpTick);
+ glVertex3d (xPlane, ypTick, zpTick);
if (box)
{
- glVertex3d (xPlaneN, yPlane, zPlane);
- glVertex3d (xPlane, yPlane, zPlane);
- glVertex3d (xPlaneN, yPlane, zPlaneN);
- glVertex3d (xPlane, yPlane, zPlaneN);
- glVertex3d (xPlaneN, yPlaneN, zPlaneN);
- glVertex3d (xPlane, yPlaneN, zPlaneN);
+ glVertex3d (xPlaneN, ypTickN, zpTick);
+ glVertex3d (xPlane, ypTickN, zpTick);
+ glVertex3d (xPlaneN, ypTickN, zpTickN);
+ glVertex3d (xPlane, ypTickN, zpTickN);
+ glVertex3d (xPlaneN, ypTick, zpTickN);
+ glVertex3d (xPlane, ypTick, zpTickN);
}
// Y box
set_color (props.get_ycolor_rgb ());
- glVertex3d (xPlaneN, yPlaneN, zPlane);
- glVertex3d (xPlaneN, yPlane, zPlane);
+ glVertex3d (xpTick, yPlaneN, zpTick);
+ glVertex3d (xpTick, yPlane, zpTick);
if (box)
{
- glVertex3d (xPlane, yPlaneN, zPlane);
- glVertex3d (xPlane, yPlane, zPlane);
- glVertex3d (xPlane, yPlaneN, zPlaneN);
- glVertex3d (xPlane, yPlane, zPlaneN);
- glVertex3d (xPlaneN, yPlaneN, zPlaneN);
- glVertex3d (xPlaneN, yPlane, zPlaneN);
+ glVertex3d (xpTickN, yPlaneN, zpTick);
+ glVertex3d (xpTickN, yPlane, zpTick);
+ glVertex3d (xpTickN, yPlaneN, zpTickN);
+ glVertex3d (xpTickN, yPlane, zpTickN);
+ glVertex3d (xpTick, yPlaneN, zpTickN);
+ glVertex3d (xpTick, yPlane, zpTickN);
}
// Z box
@@ -925,186 +1076,73 @@
bool do_xminorgrid = (props.is_xminorgrid () && (minorgridstyle != "none"));
bool do_xminortick = props.is_xminortick ();
Matrix xticks = xform.xscale (props.get_xtick ().matrix_value ());
- // FIXME: use pre-computed minor ticks
- Matrix xmticks;
+ Matrix xmticks = xform.xscale (props.get_xmtick ().matrix_value ());
string_vector xticklabels = props.get_xticklabel ().all_strings ();
int wmax = 0, hmax = 0;
- bool tick_along_z = xisinf (fy);
- Matrix tickpos (xticks.numel (), 3);
+ bool tick_along_z = nearhoriz || xisinf (fy);
set_color (props.get_xcolor_rgb ());
// grid lines
if (do_xgrid)
- {
- set_linestyle (gridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < xticks.numel (); i++)
- {
- double xval = xticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zpTick);
- glVertex3d (xval, yPlane, zpTick);
- if (zstate != AXE_DEPTH_DIR)
- {
- glVertex3d (xval, yPlane, zPlaneN);
- glVertex3d (xval, yPlane, zPlane);
- }
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
+ render_grid (gridstyle, xticks, x_min, x_max,
+ yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+ 0, (zstate != AXE_DEPTH_DIR));
// tick marks
if (tick_along_z)
{
- glBegin (GL_LINES);
- for (int i = 0; i < xticks.numel (); i++)
- {
- double xval = xticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zPlane);
- glVertex3d (xval, yPlaneN, zPlane+signum(zPlane-zPlaneN)*fz*xticklen*tickdir);
- if (box && xstate != AXE_ANY_DIR)
- {
- glVertex3d (xval, yPlaneN, zPlaneN);
- glVertex3d (xval, yPlaneN,
- zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen*tickdir);
- }
- tickpos(i,0) = xval;
- tickpos(i,1) = yPlaneN;
- tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*xtickoffset;
- }
- }
- glEnd ();
+ render_tickmarks (xticks, x_min, x_max, ypTick, ypTick, zpTick, zpTickN,
+ 0., 0., signum(zpTick-zpTickN)*fz*xticklen*tickdir,
+ 0, (box && xstate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < xticks.numel (); i++)
- {
- double xval = xticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zpTick);
- glVertex3d (xval, yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen*tickdir, zpTick);
- if (box && xstate != AXE_ANY_DIR)
- {
- glVertex3d (xval, yPlane, zpTick);
- glVertex3d (xval,
- yPlane+signum(yPlane-yPlaneN)*fy*xticklen*tickdir, zpTick);
- }
- tickpos(i,0) = xval;
- tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*xtickoffset;
- tickpos(i,2) = zPlane;
- }
- }
- glEnd ();
+ render_tickmarks (xticks, x_min, x_max, ypTick, ypTickN, zpTick, zpTick,
+ 0., signum(ypTick-ypTickN)*fy*xticklen*tickdir, 0.,
+ 0, (box && xstate != AXE_ANY_DIR));
}
// tick texts
if (xticklabels.numel () > 0)
{
- int n = std::min (xticklabels.numel (), xticks.numel ());
- int halign = (xstate == AXE_HORZ_DIR ? 1 : (xySym ? 0 : 2));
- int valign = (xstate == AXE_VERT_DIR
- ? 1
- : (zd*zv(2) <= 0 && !x2Dtop ? 2 : 0));
-
- for (int i = 0; i < n; i++)
+ int halign = (xstate == AXE_HORZ_DIR ? 1 : (xyzSym ? 0 : 2));
+ int valign = (xstate == AXE_VERT_DIR ? 1 : (x2Dtop ? 0 : 2));
+
+ if (tick_along_z)
{
- double xval = xticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- // FIXME: as tick text is transparent, shouldn't be
- // drawn after axes object, for correct rendering?
- Matrix b = render_text (xticklabels(i),
- tickpos(i,0), tickpos(i,1), tickpos(i,2),
- halign, valign);
-
- wmax = std::max (wmax, static_cast (b(2)));
- hmax = std::max (hmax, static_cast (b(3)));
- }
+ render_ticktexts (xticks, xticklabels, x_min, x_max,
+ ypTick, zpTick+signum(zpTick-zpTickN)*fz*xtickoffset,
+ 0, halign, valign, wmax, hmax);
+ }
+ else
+ {
+ render_ticktexts (xticks, xticklabels, x_min, x_max,
+ ypTick+signum(ypTick-ypTickN)*fy*xtickoffset, zpTick,
+ 0, halign, valign, wmax, hmax);
}
}
// minor grid lines
if (do_xminorgrid)
- {
- set_linestyle (minorgridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < xmticks.numel (); i++)
- {
- double xval = xmticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zpTick);
- glVertex3d (xval, yPlane, zpTick);
- if (zstate != AXE_DEPTH_DIR)
- {
- glVertex3d (xval, yPlane, zPlaneN);
- glVertex3d (xval, yPlane, zPlane);
- }
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
-
+ render_grid (minorgridstyle, xmticks, x_min, x_max,
+ yPlane, yPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+ 0, (zstate != AXE_DEPTH_DIR));
+
// minor tick marks
if (do_xminortick)
{
if (tick_along_z)
{
- glBegin (GL_LINES);
- for (int i = 0; i < xmticks.numel (); i++)
- {
- double xval = xmticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zPlane);
- glVertex3d (xval, yPlaneN,
- zPlane+signum(zPlane-zPlaneN)*fz*xticklen/2*tickdir);
- if (box && xstate != AXE_ANY_DIR)
- {
- glVertex3d (xval, yPlaneN, zPlaneN);
- glVertex3d (xval, yPlaneN,
- zPlaneN+signum(zPlaneN-zPlane)*fz*xticklen/2*tickdir);
- }
- }
- }
- glEnd ();
+ render_tickmarks (xmticks, x_min, x_max, ypTick, ypTick, zpTick, zpTickN,
+ 0., 0., signum(zpTick-zpTickN)*fz*xticklen/2*tickdir,
+ 0, (box && xstate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < xmticks.numel (); i++)
- {
- double xval = xmticks(i);
-
- if (xlim(0) <= xval && xlim(1) >= xval)
- {
- glVertex3d (xval, yPlaneN, zpTick);
- glVertex3d (xval,
- yPlaneN+signum(yPlaneN-yPlane)*fy*xticklen/2*tickdir, zpTick);
- if (box && xstate != AXE_ANY_DIR)
- {
- glVertex3d (xval, yPlane, zpTick);
- glVertex3d (xval,
- yPlane+signum(yPlane-yPlaneN)*fy*xticklen/2*tickdir, zpTick);
- }
- }
- }
- glEnd ();
+ render_tickmarks (xmticks, x_min, x_max, ypTick, ypTickN, zpTick, zpTick,
+ 0., signum(ypTick-ypTickN)*fy*xticklen/2*tickdir, 0.,
+ 0, (box && xstate != AXE_ANY_DIR));
}
}
@@ -1117,37 +1155,37 @@
{
if (xlabel_props.horizontalalignmentmode_is("auto"))
{
- xlabel_props.set_horizontalalignment (xstate > AXE_DEPTH_DIR ? "center" : (xySym ? "left" : "right"));
+ xlabel_props.set_horizontalalignment (xstate > AXE_DEPTH_DIR ? "center" : (xyzSym ? "left" : "right"));
xlabel_props.set_horizontalalignmentmode("auto");
}
if (xlabel_props.verticalalignmentmode_is("auto"))
{
- xlabel_props.set_verticalalignment (xstate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
+ xlabel_props.set_verticalalignment (xstate == AXE_VERT_DIR || x2Dtop ? "bottom" : "top");
xlabel_props.set_verticalalignmentmode("auto");
}
if (xlabel_props.positionmode_is("auto") || xlabel_props.rotationmode_is("auto"))
{
double angle = 0;
- ColumnVector p = graphics_xform::xform_vector ((x_min+x_max)/2, yPlaneN, zPlane);
+ ColumnVector p = graphics_xform::xform_vector ((x_min+x_max)/2, ypTick, zpTick);
if (tick_along_z)
- p(2) += (signum(zPlane-zPlaneN)*fz*xtickoffset);
+ p(2) += (signum(zpTick-zpTickN)*fz*xtickoffset);
else
- p(1) += (signum(yPlaneN-yPlane)*fy*xtickoffset);
+ p(1) += (signum(ypTick-ypTickN)*fy*xtickoffset);
p = xform.transform (p(0), p(1), p(2), false);
switch (xstate)
{
case AXE_ANY_DIR:
- p(0) += (xySym ? wmax : -wmax);
- p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
+ p(0) += (xyzSym ? wmax : -wmax);
+ p(1) += hmax;
break;
case AXE_VERT_DIR:
p(0) -= wmax;
angle = 90;
break;
case AXE_HORZ_DIR:
- p(1) += hmax;
+ p(1) += (x2Dtop ? -hmax : hmax);
break;
}
if (xlabel_props.positionmode_is("auto"))
@@ -1177,184 +1215,73 @@
bool do_yminorgrid = (props.is_yminorgrid () && (minorgridstyle != "none"));
bool do_yminortick = props.is_yminortick ();
Matrix yticks = xform.yscale (props.get_ytick ().matrix_value ());
- // FIXME: use pre-computed minor ticks
- Matrix ymticks;
+ Matrix ymticks = xform.yscale (props.get_ymtick ().matrix_value ());
string_vector yticklabels = props.get_yticklabel ().all_strings ();
int wmax = 0, hmax = 0;
- bool tick_along_z = xisinf (fx);
- Matrix tickpos (yticks.numel (), 3);
+ bool tick_along_z = nearhoriz || xisinf (fx);
set_color (props.get_ycolor_rgb ());
// grid lines
if (do_ygrid)
- {
- set_linestyle (gridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < yticks.numel (); i++)
- {
- double yval = yticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zpTick);
- glVertex3d (xPlane, yval, zpTick);
- if (zstate != AXE_DEPTH_DIR)
- {
- glVertex3d (xPlane, yval, zPlaneN);
- glVertex3d (xPlane, yval, zPlane);
- }
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
+ render_grid (gridstyle, yticks, y_min, y_max,
+ xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+ 1, (zstate != AXE_DEPTH_DIR));
// tick marks
if (tick_along_z)
{
- glBegin (GL_LINES);
- for (int i = 0; i < yticks.numel (); i++)
- {
- double yval = yticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zPlane);
- glVertex3d (xPlaneN, yval, zPlane+signum(zPlane-zPlaneN)*fz*yticklen*tickdir);
- if (box && ystate != AXE_ANY_DIR)
- {
- glVertex3d (xPlaneN, yval, zPlaneN);
- glVertex3d (xPlaneN, yval,
- zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen*tickdir);
- }
- tickpos(i,0) = xPlaneN;
- tickpos(i,1) = yval;
- tickpos(i,2) = zPlane+signum(zPlane-zPlaneN)*fz*ytickoffset;
- }
- }
- glEnd ();
+ render_tickmarks (yticks, y_min, y_max, xpTick, xpTick, zpTick, zpTickN,
+ 0., 0., signum(zpTick-zpTickN)*fz*yticklen*tickdir,
+ 1, (box && ystate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < yticks.numel (); i++)
- {
- double yval = yticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zpTick);
- glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen*tickdir, yval, zpTick);
- if (box && ystate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yval, zpTick);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen*tickdir,
- yval, zpTick);
- }
- tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ytickoffset;
- tickpos(i,1) = yval;
- tickpos(i,2) = zPlane;
- }
- }
- glEnd ();
+ render_tickmarks (yticks, y_min, y_max, xpTick, xpTickN, zpTick, zpTick,
+ signum(xPlaneN-xPlane)*fx*yticklen*tickdir, 0., 0.,
+ 1, (box && ystate != AXE_ANY_DIR));
}
// tick texts
if (yticklabels.numel () > 0)
{
- int n = std::min (yticklabels.numel (), yticks.numel ());
- int halign = (ystate == AXE_HORZ_DIR ? 1 : (!xySym || y2Dright ? 0 : 2));
- int valign = (ystate == AXE_VERT_DIR ? 1 : (zd*zv(2) <= 0 ? 2 : 0));
-
- for (int i = 0; i < n; i++)
+ int halign = (ystate == AXE_HORZ_DIR ? 1 : (!xyzSym || y2Dright ? 0 : 2));
+ int valign = (ystate == AXE_VERT_DIR ? 1 : 2);
+
+ if (tick_along_z)
{
- double yval = yticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- // FIXME: as tick text is transparent, shouldn't be
- // drawn after axes object, for correct rendering?
- Matrix b = render_text (yticklabels(i),
- tickpos(i,0), tickpos(i,1), tickpos(i,2),
- halign, valign);
-
- wmax = std::max (wmax, static_cast (b(2)));
- hmax = std::max (hmax, static_cast (b(3)));
- }
+ render_ticktexts (yticks, yticklabels, y_min, y_max,
+ xpTick, zpTick+signum(zpTick-zpTickN)*fz*ytickoffset,
+ 1, halign, valign, wmax, hmax);
+ }
+ else
+ {
+ render_ticktexts (yticks, yticklabels, y_min, y_max,
+ xpTick+signum(xpTick-xpTickN)*fx*ytickoffset, zpTick,
+ 1, halign, valign, wmax, hmax);
}
}
// minor grid lines
if (do_yminorgrid)
- {
- set_linestyle (minorgridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < ymticks.numel (); i++)
- {
- double yval = ymticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zpTick);
- glVertex3d (xPlane, yval, zpTick);
- if (zstate != AXE_DEPTH_DIR)
- {
- glVertex3d (xPlane, yval, zPlaneN);
- glVertex3d (xPlane, yval, zPlane);
- }
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
+ render_grid (minorgridstyle, ymticks, y_min, y_max,
+ xPlane, xPlaneN, layer2Dtop ? zPlaneN : zPlane, zPlaneN,
+ 1, (zstate != AXE_DEPTH_DIR));
// minor tick marks
if (do_yminortick)
{
if (tick_along_z)
{
- glBegin (GL_LINES);
- for (int i = 0; i < ymticks.numel (); i++)
- {
- double yval = ymticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zPlane);
- glVertex3d (xPlaneN, yval,
- zPlane+signum(zPlane-zPlaneN)*fz*yticklen/2*tickdir);
- if (box && ystate != AXE_ANY_DIR)
- {
- glVertex3d (xPlaneN, yval, zPlaneN);
- glVertex3d (xPlaneN, yval,
- zPlaneN+signum(zPlaneN-zPlane)*fz*yticklen/2*tickdir);
- }
- }
- }
- glEnd ();
+ render_tickmarks (ymticks, y_min, y_max, xpTick, xpTick, zpTick, zpTickN,
+ 0., 0., signum(zpTick-zpTickN)*fz*yticklen/2*tickdir,
+ 1, (box && ystate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < ymticks.numel (); i++)
- {
- double yval = ymticks(i);
-
- if (ylim(0) <= yval && ylim(1) >= yval)
- {
- glVertex3d (xPlaneN, yval, zpTick);
- glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*yticklen/2*tickdir,
- yval, zpTick);
- if (box && ystate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yval, zpTick);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*yticklen/2*tickdir,
- yval, zpTick);
- }
- }
- }
- glEnd ();
+ render_tickmarks (ymticks, y_min, y_max, xpTick, xpTickN, zpTick, zpTick,
+ signum(xpTick-xpTickN)*fx*yticklen/2*tickdir, 0., 0.,
+ 1, (box && ystate != AXE_ANY_DIR));
}
}
@@ -1367,33 +1294,33 @@
{
if (ylabel_props.horizontalalignmentmode_is("auto"))
{
- ylabel_props.set_horizontalalignment (ystate > AXE_DEPTH_DIR ? "center" : (!xySym ? "left" : "right"));
+ ylabel_props.set_horizontalalignment (ystate > AXE_DEPTH_DIR ? "center" : (!xyzSym ? "left" : "right"));
ylabel_props.set_horizontalalignmentmode("auto");
}
if (ylabel_props.verticalalignmentmode_is("auto"))
{
- ylabel_props.set_verticalalignment (ystate == AXE_VERT_DIR ? "bottom" : (zd*zv(2) <= 0 ? "top" : "bottom"));
+ ylabel_props.set_verticalalignment (ystate == AXE_VERT_DIR && !y2Dright ? "bottom" : "top");
ylabel_props.set_verticalalignmentmode("auto");
}
if (ylabel_props.positionmode_is("auto") || ylabel_props.rotationmode_is("auto"))
{
double angle = 0;
- ColumnVector p = graphics_xform::xform_vector (xPlaneN, (y_min+y_max)/2, zPlane);
+ ColumnVector p = graphics_xform::xform_vector (xpTick, (y_min+y_max)/2, zpTick);
if (tick_along_z)
- p(2) += (signum(zPlane-zPlaneN)*fz*ytickoffset);
+ p(2) += (signum(zpTick-zpTickN)*fz*ytickoffset);
else
- p(0) += (signum(xPlaneN-xPlane)*fx*ytickoffset);
+ p(0) += (signum(xpTick-xpTickN)*fx*ytickoffset);
p = xform.transform (p(0), p(1), p(2), false);
switch (ystate)
{
case AXE_ANY_DIR:
- p(0) += (!xySym ? wmax : -wmax);
- p(1) += (zd*zv(2) <= 0 ? hmax : -hmax);
+ p(0) += (!xyzSym ? wmax : -wmax);
+ p(1) += hmax;
break;
case AXE_VERT_DIR:
- p(0) -= wmax;
+ p(0) += (y2Dright ? wmax : -wmax);
angle = 90;
break;
case AXE_HORZ_DIR:
@@ -1427,172 +1354,91 @@
bool do_zminorgrid = (props.is_zminorgrid () && (minorgridstyle != "none"));
bool do_zminortick = props.is_zminortick ();
Matrix zticks = xform.zscale (props.get_ztick ().matrix_value ());
- // FIXME: use pre-computed minor ticks
- Matrix zmticks;
+ Matrix zmticks = xform.zscale (props.get_zmtick ().matrix_value ());
string_vector zticklabels = props.get_zticklabel ().all_strings ();
int wmax = 0, hmax = 0;
- Matrix tickpos (zticks.numel (), 3);
set_color (props.get_zcolor_rgb ());
// grid lines
if (do_zgrid)
- {
- set_linestyle (gridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < zticks.numel (); i++)
- {
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane, yPlane, zval);
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
+ render_grid (gridstyle, zticks, z_min, z_max,
+ xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
// tick marks
if (xySym)
{
if (xisinf (fy))
{
- glBegin (GL_LINES);
- for (int i = 0; i < zticks.numel (); i++)
- {
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen*tickdir,
- yPlane, zval);
- if (box && zstate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir,
- yPlane, zval);
- }
- tickpos(i,0) = xPlaneN+signum(xPlaneN-xPlane)*fx*ztickoffset;
- tickpos(i,1) = yPlane;
- tickpos(i,2) = zval;
- }
- }
- glEnd ();
+ render_tickmarks( zticks, z_min, z_max, xPlaneN, xPlane, yPlane, yPlane,
+ signum(xPlaneN-xPlane)*fx*zticklen*tickdir, 0., 0.,
+ 2, (box && zstate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < zticks.numel (); i++)
- {
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
- tickpos(i,0) = xPlaneN;
- tickpos(i,1) = yPlane+signum(yPlane-yPlaneN)*fy*ztickoffset;
- tickpos(i,2) = zval;
- }
- }
- glEnd ();
+ render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlaneN, yPlane, yPlane,
+ 0., signum(yPlane-yPlaneN)*fy*zticklen*tickdir, 0.,
+ 2, false);
}
}
else
{
if (xisinf (fx))
{
- glBegin (GL_LINES);
- for (int i = 0; i < zticks.numel (); i++)
- {
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen*tickdir, zval);
- if (box && zstate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen*tickdir, zval);
- }
- tickpos(i,0) = xPlane;
- tickpos(i,1) = yPlaneN+signum(yPlaneN-yPlane)*fy*ztickoffset;
- tickpos(i,2) = zval;
- }
- }
- glEnd ();
+ render_tickmarks (zticks, z_min, z_max, xPlaneN, xPlane, yPlaneN, yPlane,
+ 0., signum(yPlaneN-yPlane)*fy*zticklen*tickdir, 0.,
+ 2, (box && zstate != AXE_ANY_DIR));
}
else
- {
- glBegin (GL_LINES);
- for (int i = 0; i < zticks.numel (); i++)
- {
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen*tickdir, yPlaneN, zval);
- tickpos(i,0) = xPlane+signum(xPlane-xPlaneN)*fx*ztickoffset;
- tickpos(i,1) = yPlaneN;
- tickpos(i,2) = zval;
- }
- }
- glEnd ();
- }
+ {
+ render_tickmarks (zticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlane,
+ signum(xPlane-xPlaneN)*fx*zticklen*tickdir, 0., 0.,
+ 2, false);
+ }
}
// FIXME: tick texts
if (zticklabels.numel () > 0)
{
- int n = std::min (zticklabels.numel (), zticks.numel ());
int halign = 2;
- int valign = (zstate == AXE_VERT_DIR ? 1 : (zd*zv(2) < 0 ? 3 : 2));
-
- for (int i = 0; i < n; i++)
+ int valign = (zstate == AXE_VERT_DIR ? 1 : (zSign ? 3 : 2));
+
+ if (xySym)
{
- double zval = zticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
+ if (xisinf (fy))
{
- // FIXME: as tick text is transparent, shouldn't be
- // drawn after axes object, for correct rendering?
- Matrix b = render_text (zticklabels(i),
- tickpos(i,0), tickpos(i,1), tickpos(i,2),
- halign, valign);
-
- wmax = std::max (wmax, static_cast (b(2)));
- hmax = std::max (hmax, static_cast (b(3)));
+ render_ticktexts (zticks, zticklabels, z_min, z_max,
+ xPlaneN+signum(xPlaneN-xPlane)*fx*ztickoffset, yPlane,
+ 2, halign, valign, wmax, hmax);
+ }
+ else
+ {
+ render_ticktexts (zticks, zticklabels, z_min, z_max,
+ xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*ztickoffset,
+ 2, halign, valign, wmax, hmax);
+ }
+ }
+ else
+ {
+ if (xisinf (fx))
+ {
+ render_ticktexts (zticks, zticklabels, z_min, z_max,
+ xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*ztickoffset,
+ 2, halign, valign, wmax, hmax);
+ }
+ else
+ {
+ render_ticktexts (zticks, zticklabels, z_min, z_max,
+ xPlane+signum(xPlane-xPlaneN)*fx*ztickoffset, yPlaneN,
+ 2, halign, valign, wmax, hmax);
}
}
}
// minor grid lines
if (do_zminorgrid)
- {
- set_linestyle (minorgridstyle, true);
- glBegin (GL_LINES);
- for (int i = 0; i < zmticks.numel (); i++)
- {
- double zval = zmticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane, yPlane, zval);
- }
- }
- glEnd ();
- set_linestyle ("-", true);
- }
+ render_grid (minorgridstyle, zmticks, z_min, z_max,
+ xPlane, xPlaneN, yPlane, yPlaneN, 2, true);
// minor tick marks
if (do_zminortick)
@@ -1601,78 +1447,30 @@
{
if (xisinf (fy))
{
- glBegin (GL_LINES);
- for (int i = 0; i < zmticks.numel (); i++)
- {
- double zval = zmticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlaneN+signum(xPlaneN-xPlane)*fx*zticklen/2*tickdir,
- yPlane, zval);
- if (box && zstate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir,
- yPlane, zval);
- }
- }
- }
- glEnd ();
+ render_tickmarks( zmticks, z_min, z_max, xPlaneN, xPlane, yPlane, yPlane,
+ signum(xPlaneN-xPlane)*fx*zticklen/2*tickdir, 0., 0.,
+ 2, (box && zstate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < zmticks.numel (); i++)
- {
- double zval = zmticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlaneN, yPlane, zval);
- glVertex3d (xPlaneN, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
- }
- }
- glEnd ();
+ render_tickmarks (zmticks, z_min, z_max, xPlaneN, xPlaneN, yPlane, yPlane,
+ 0., signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, 0.,
+ 2, false);
}
}
else
{
if (xisinf (fx))
{
- glBegin (GL_LINES);
- for (int i = 0; i < zmticks.numel (); i++)
- {
- double zval = zmticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane, yPlaneN+signum(yPlaneN-yPlane)*fy*zticklen/2*tickdir, zval);
- if (box && zstate != AXE_ANY_DIR)
- {
- glVertex3d (xPlane, yPlane, zval);
- glVertex3d (xPlane, yPlane+signum(yPlane-yPlaneN)*fy*zticklen/2*tickdir, zval);
- }
- }
- }
- glEnd ();
+ render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlane,
+ 0., signum(yPlaneN-yPlane)*fy*zticklen/2*tickdir, 0.,
+ 2, (box && zstate != AXE_ANY_DIR));
}
else
{
- glBegin (GL_LINES);
- for (int i = 0; i < zmticks.numel (); i++)
- {
- double zval = zmticks(i);
-
- if (zlim(0) <= zval && zlim(1) >= zval)
- {
- glVertex3d (xPlane, yPlaneN, zval);
- glVertex3d (xPlane+signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir, yPlaneN, zval);
- }
- }
- glEnd ();
+ render_tickmarks (zmticks, z_min, z_max, xPlane, xPlane, yPlaneN, yPlaneN,
+ signum(xPlane-xPlaneN)*fx*zticklen/2*tickdir, 0., 0.,
+ 2, false);
}
}
}
@@ -1693,7 +1491,7 @@
}
if (zlabel_props.verticalalignmentmode_is("auto"))
{
- zlabel_props.set_verticalalignment(zstate == AXE_VERT_DIR ? "bottom" : ((zd*zv(2) < 0 || camAuto) ? "bottom" : "top"));
+ zlabel_props.set_verticalalignment(zstate == AXE_VERT_DIR ? "bottom" : ((zSign || camAuto) ? "bottom" : "top"));
zlabel_props.set_verticalalignmentmode("auto");
}
@@ -1729,7 +1527,7 @@
}
/* FIXME: what's the correct offset?
p[0] += (!xySym ? wmax : -wmax);
- p[1] += (zd*zv[2] <= 0 ? hmax : -hmax);
+ p[1] += (zSign ? hmax : -hmax);
*/
break;
case AXE_VERT_DIR:
diff -r 62b7ea59a6ff -r 2b1a942b84d3 src/gl-render.h
--- a/src/gl-render.h Fri Jan 21 20:22:42 2011 +0100
+++ b/src/gl-render.h Fri Jan 21 22:20:23 2011 +0100
@@ -117,6 +117,22 @@
virtual void draw_pixels (GLsizei w, GLsizei h, GLenum format,
GLenum type, const GLvoid *data);
+ virtual void render_grid (std::string& gridstyle, Matrix& ticks,
+ double lim1, double lim2,
+ double p1, double p1N, double p2, double p2N,
+ int xyz, bool is_3D);
+
+ virtual void render_tickmarks(Matrix& ticks, double lim1, double lim2,
+ double p1, double p1N, double p2, double p2N,
+ double dx, double dy, double dz,
+ int xyz, bool doubleside);
+
+ virtual void render_ticktexts(Matrix& ticks, string_vector& ticklabels,
+ double lim1, double lim2,
+ double p1, double p2,
+ int xyz, int ha, int va,
+ int& wmax, int& hmax);
+
private:
opengl_renderer (const opengl_renderer&)
: toolkit (), xform (), xmin (), xmax (), ymin (), ymax (),
diff -r 62b7ea59a6ff -r 2b1a942b84d3 src/graphics.cc
--- a/src/graphics.cc Fri Jan 21 20:22:42 2011 +0100
+++ b/src/graphics.cc Fri Jan 21 22:20:23 2011 +0100
@@ -4371,6 +4371,7 @@
void
axes::properties::calc_ticks_and_lims (array_property& lims,
array_property& ticks,
+ array_property& mticks,
bool limmode_is_auto, bool is_logscale)
{
// FIXME -- add log ticks and lims
@@ -4432,6 +4433,19 @@
}
ticks = tmp_ticks;
+
+ int n = is_logscale ? 9 : 4;
+ Matrix tmp_mticks (1, n * tmp_ticks.numel ());
+
+ for (int i = 0; i < tmp_ticks.numel ()-1; i++)
+ {
+ double d = (tmp_ticks (i+1) - tmp_ticks (i)) / (n+1);
+ for (int j = 0; j < n; j++)
+ {
+ tmp_mticks (n*i+j) = tmp_ticks (i) + d * (j+1);
+ }
+ }
+ mticks = tmp_mticks;
}
void
diff -r 62b7ea59a6ff -r 2b1a942b84d3 src/graphics.h.in
--- a/src/graphics.h.in Fri Jan 21 20:22:42 2011 +0100
+++ b/src/graphics.h.in Fri Jan 21 22:20:23 2011 +0100
@@ -3207,6 +3207,10 @@
array_property x_viewporttransform h , Matrix (4, 4, 0.0)
array_property x_normrendertransform h , Matrix (4, 4, 0.0)
array_property x_rendertransform h , Matrix (4, 4, 0.0)
+ // hidden properties for minor ticks
+ row_vector_property xmtick h , Matrix ()
+ row_vector_property ymtick h , Matrix ()
+ row_vector_property zmtick h , Matrix ()
END_PROPERTIES
protected:
@@ -3247,7 +3251,7 @@
{
if (xtickmode.is ("auto"))
{
- calc_ticks_and_lims (xlim, xtick, xlimmode.is ("auto"), xscale.is ("log"));
+ calc_ticks_and_lims (xlim, xtick, xmtick, xlimmode.is ("auto"), xscale.is ("log"));
update_xtick ();
}
}
@@ -3255,7 +3259,7 @@
{
if (ytickmode.is ("auto"))
{
- calc_ticks_and_lims (ylim, ytick, ylimmode.is ("auto"), yscale.is ("log"));
+ calc_ticks_and_lims (ylim, ytick, ymtick, ylimmode.is ("auto"), yscale.is ("log"));
update_ytick ();
}
}
@@ -3263,7 +3267,7 @@
{
if (ztickmode.is ("auto"))
{
- calc_ticks_and_lims (zlim, ztick, zlimmode.is ("auto"), zscale.is ("log"));
+ calc_ticks_and_lims (zlim, ztick, zmtick, zlimmode.is ("auto"), zscale.is ("log"));
update_ztick ();
}
}
@@ -3289,7 +3293,8 @@
void update_position (void) { sync_positions (); }
double calc_tick_sep (double minval, double maxval);
- void calc_ticks_and_lims (array_property& lims, array_property& ticks, bool limmode_is_auto, bool is_logscale);
+ void calc_ticks_and_lims (array_property& lims, array_property& ticks, array_property& mticks,
+ bool limmode_is_auto, bool is_logscale);
void calc_ticklabels (const array_property& ticks, any_property& labels, bool is_logscale);
void fix_limits (array_property& lims)
{
@@ -3317,7 +3322,7 @@
void update_xlim (bool do_clr_zoom = true)
{
if (xtickmode.is ("auto"))
- calc_ticks_and_lims (xlim, xtick, xlimmode.is ("auto"), xscale.is ("log"));
+ calc_ticks_and_lims (xlim, xtick, xmtick, xlimmode.is ("auto"), xscale.is ("log"));
if (xticklabelmode.is ("auto"))
calc_ticklabels (xtick, xticklabel, xscale.is ("log"));
@@ -3330,7 +3335,7 @@
void update_ylim (bool do_clr_zoom = true)
{
if (ytickmode.is ("auto"))
- calc_ticks_and_lims (ylim, ytick, ylimmode.is ("auto"), yscale.is ("log"));
+ calc_ticks_and_lims (ylim, ytick, ymtick, ylimmode.is ("auto"), yscale.is ("log"));
if (yticklabelmode.is ("auto"))
calc_ticklabels (ytick, yticklabel, yscale.is ("log"));
@@ -3343,7 +3348,7 @@
void update_zlim (void)
{
if (ztickmode.is ("auto"))
- calc_ticks_and_lims (zlim, ztick, zlimmode.is ("auto"), zscale.is ("log"));
+ calc_ticks_and_lims (zlim, ztick, zmtick, zlimmode.is ("auto"), zscale.is ("log"));
if (zticklabelmode.is ("auto"))
calc_ticklabels (ztick, zticklabel, zscale.is ("log"));