Clean up and fixes to fractions metrics and drawing

* Factor code for easier maintainance.

* Avoid computing metrics several times. This duplication explained the
  exponential blowup during the metrics phase for nested fractions (see
  b2b87330). This happened in particular when using lyxproofs which heavily uses
  nested \dfracs for on-screen drawing.

* Call MetricsBase::changeScript instead of MetricsBase::changeFrac for
  \nicefrac and \unitfrac.
This commit is contained in:
Guillaume Munch 2016-11-20 20:08:11 +01:00
parent a9eb87a89d
commit ffb865d6e8

View File

@ -152,62 +152,71 @@ void InsetMathFrac::metrics(MetricsInfo & mi, Dimension & dim) const
{ {
Dimension dim0, dim1, dim2; Dimension dim0, dim1, dim2;
// This could be simplified, including avoiding useless recalculation of switch (kind_) {
// cell metrics case UNIT: {
if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) { // \unitone, \unittwo
if (nargs() == 1) { dim.wid = 0;
Changer dummy = mi.base.font.changeShape(UP_SHAPE); int unit_cell = 0;
cell(0).metrics(mi, dim0); // is there an extra cell holding the value being given a dimension?
dim.wid = dim0.width()+ 3; // (this is \unittwo)
dim.asc = dim0.asc; if (nargs() == 2) {
dim.des = dim0.des; cell(0).metrics(mi, dim1);
} else if (nargs() == 2) { dim.wid += dim1.wid + 4;
cell(0).metrics(mi, dim0); unit_cell = 1;
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
cell(1).metrics(mi, dim1);
dim.wid = dim0.width() + dim1.wid + 5;
dim.asc = max(dim0.asc, dim1.asc);
dim.des = max(dim0.des, dim1.des);
} else {
cell(2).metrics(mi, dim2);
Changer dummy = mi.base.font.changeShape(UP_SHAPE);
Changer dummy2 = mi.base.changeFrac();
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
dim.wid = dim0.width() + dim1.wid + dim2.wid + 10;
dim.asc = max(dim2.asc, dim0.height() + 5);
dim.des = max(dim2.des, dim1.height() - 5);
} }
} else { Changer dummy = mi.base.font.changeShape(UP_SHAPE);
// general cell metrics used for \frac cell(unit_cell).metrics(mi, dim0);
Changer dummy = mi.base.changeFrac(); dim.wid += dim0.width() + 1;
// FIXME: Exponential blowup dim.asc = max(dim0.asc, dim1.asc);
dim.des = max(dim0.des, dim1.des);
}
break;
case UNITFRAC:
case NICEFRAC: {
// \unitfrac, \unitfracthree, \nicefrac
dim.wid = 0;
// is there an extra cell holding the value being given a dimension?
// (this is \unitfracthree)
if (kind_ == UNITFRAC && nargs() == 3) {
cell(2).metrics(mi, dim2);
dim.wid += dim2.wid + 4;
}
Changer dummy = mi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
Changer dummy2 = mi.base.changeScript();
cell(0).metrics(mi, dim0); cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1); cell(1).metrics(mi, dim1);
if (nargs() == 3) dim.wid += dim0.wid + dim1.wid + 5;
cell(2).metrics(mi, dim2); dim.asc = max(dim2.asc, dim0.height() + 5);
// metrics for special fraction types dim.des = max(dim2.des, dim1.height() - 5);
if (kind_ == NICEFRAC || kind_ == UNITFRAC) {
Changer dummy2 = mi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
dim.wid = dim0.width() + dim1.wid + 5;
dim.asc = dim0.height() + 5;
dim.des = dim1.height() - 5;
} else {
if (kind_ == CFRAC || kind_ == CFRACLEFT || kind_ == CFRACRIGHT
|| kind_ == DFRAC || kind_ == TFRAC) {
// \cfrac and \dfrac are always in display size
// \tfrac is in always in text size
Changer dummy2 = mi.base.font.changeStyle((kind_ == TFRAC)
? LM_ST_SCRIPT
: LM_ST_DISPLAY);
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
}
dim.wid = max(dim0.wid, dim1.wid) + 2;
dim.asc = dim0.height() + 2 + 5;
dim.des = dim1.height() + 2 - 5;
}
} }
break;
case FRAC:
case CFRAC:
case CFRACLEFT:
case CFRACRIGHT:
case DFRAC:
case TFRAC:
case OVER:
case ATOP: {
Changer dummy =
// \tfrac is always in text size
(kind_ == TFRAC) ? mi.base.font.changeStyle(LM_ST_SCRIPT) :
// \cfrac and \dfrac are always in display size
(kind_ == CFRAC
|| kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT
|| kind_ == DFRAC) ? mi.base.font.changeStyle(LM_ST_DISPLAY) :
// all others
mi.base.changeFrac();
cell(0).metrics(mi, dim0);
cell(1).metrics(mi, dim1);
dim.wid = max(dim0.wid, dim1.wid) + 2;
dim.asc = dim0.height() + 2 + 5;
dim.des = dim1.height() + 2 - 5;
}
} //switch (kind_)
metricsMarkers(mi, dim); metricsMarkers(mi, dim);
} }
@ -217,74 +226,89 @@ void InsetMathFrac::draw(PainterInfo & pi, int x, int y) const
setPosCache(pi, x, y); setPosCache(pi, x, y);
Dimension const dim = dimension(*pi.base.bv); Dimension const dim = dimension(*pi.base.bv);
Dimension const dim0 = cell(0).dimension(*pi.base.bv); Dimension const dim0 = cell(0).dimension(*pi.base.bv);
if (kind_ == UNIT || (kind_ == UNITFRAC && nargs() == 3)) { switch (kind_) {
if (nargs() == 1) { case UNIT: {
Changer dummy = pi.base.font.changeShape(UP_SHAPE); // \unitone, \unittwo
int xx = x;
int unit_cell = 0;
// is there an extra cell holding the value being given a dimension?
// (this is \unittwo)
if (nargs() == 2) {
cell(0).draw(pi, x + 1, y); cell(0).draw(pi, x + 1, y);
} else if (nargs() == 2) { xx += dim0.wid + 4;
cell(0).draw(pi, x + 1, y); unit_cell = 1;
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
cell(1).draw(pi, x + dim0.width() + 5, y);
} else {
cell(2).draw(pi, x + 1, y);
Changer dummy = pi.base.font.changeShape(UP_SHAPE);
Changer dummy2 = pi.base.changeFrac();
Dimension const dim1 = cell(1).dimension(*pi.base.bv);
Dimension const dim2 = cell(2).dimension(*pi.base.bv);
int xx = x + dim2.wid + 5;
cell(0).draw(pi, xx + 2,
y - dim0.des - 5);
cell(1).draw(pi, xx + dim0.width() + 5,
y + dim1.asc / 2);
} }
} else { Changer dummy = pi.base.font.changeShape(UP_SHAPE);
Changer dummy = pi.base.changeFrac(); cell(unit_cell).draw(pi, xx + 1, y);
}
break;
case UNITFRAC:
case NICEFRAC: {
// \unitfrac, \unitfracthree, \nicefrac
int xx = x;
// is there an extra cell holding the value being given a dimension?
// (this is \unitfracthree)
if (kind_ == UNITFRAC && nargs() == 3) {
cell(2).draw(pi, x + 1, y);
xx += cell(2).dimension(*pi.base.bv).wid + 4;
}
Changer dummy = pi.base.font.changeShape(UP_SHAPE, kind_ == UNITFRAC);
// nice fraction
// FIXME:
// * the solidus should be \kern-2mu/\kern-1mu.
// * the vertical offset of the first cell should be such that the
// top of M in the first cell matches the one of the
// surrounding text.
Changer dummy2 = pi.base.changeScript();
cell(0).draw(pi, xx + 2, y - dim0.des - 5);
Dimension const dim1 = cell(1).dimension(*pi.base.bv);
cell(1).draw(pi, xx + dim0.wid + 5, y + dim1.asc / 2);
// Diag line:
pi.pain.line(xx + dim0.wid + 1, y + dim.des - 2,
xx + dim0.wid + 6, y - dim.asc + 2,
pi.base.font.color());
}
break;
case FRAC:
case CFRAC:
case CFRACLEFT:
case CFRACRIGHT:
case DFRAC:
case TFRAC:
case OVER:
case ATOP: {
Changer dummy =
// \tfrac is always in text size
(kind_ == TFRAC) ? pi.base.font.changeStyle(LM_ST_SCRIPT) :
// \cfrac and \dfrac are always in display size
(kind_ == CFRAC
|| kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT
|| kind_ == DFRAC) ? pi.base.font.changeStyle(LM_ST_DISPLAY) :
// all others
pi.base.changeFrac();
Dimension const dim1 = cell(1).dimension(*pi.base.bv); Dimension const dim1 = cell(1).dimension(*pi.base.bv);
int m = x + dim.wid / 2; int m = x + dim.wid / 2;
if (kind_ == NICEFRAC) { int xx =
cell(0).draw(pi, x + 2, // align left
y - dim0.des - 5); (kind_ == CFRACLEFT) ? x + 2 :
cell(1).draw(pi, x + dim0.width() + 5, // align right
y + dim1.asc / 2); (kind_ == CFRACRIGHT) ? x + dim.wid - dim0.wid - 2 :
} else if (kind_ == UNITFRAC) { // center
Changer dummy2 = pi.base.font.changeShape(UP_SHAPE); m - dim0.wid / 2;
cell(0).draw(pi, x + 2, y - dim0.des - 5); // FIXME: vertical offset should be based on ex
cell(1).draw(pi, x + dim0.width() + 5, y + dim1.asc / 2); //int dy = theFontMetrics(pi.base.font).ex() / 2;
} else if (kind_ == FRAC || kind_ == ATOP || kind_ == OVER cell(0).draw(pi, xx, y - dim0.des - 2 - 5);
|| kind_ == TFRAC) { // center
// tfrac is in always in text size cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
Changer dummy2 = pi.base.font.changeStyle(LM_ST_SCRIPT, // horizontal line
kind_ == TFRAC); if (kind_ != ATOP)
cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5); pi.pain.line(x + 1, y - 5,
cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5); x + dim.wid - 2, y - 5, pi.base.font.color());
} else {
// \cfrac and \dfrac are always in display size
Changer dummy2 = pi.base.font.changeStyle(LM_ST_DISPLAY);
if (kind_ == CFRAC || kind_ == DFRAC)
cell(0).draw(pi, m - dim0.wid / 2, y - dim0.des - 2 - 5);
else if (kind_ == CFRACLEFT)
cell(0).draw(pi, x + 2, y - dim0.des - 2 - 5);
else if (kind_ == CFRACRIGHT)
cell(0).draw(pi, x + dim.wid - dim0.wid - 2,
y - dim0.des - 2 - 5);
cell(1).draw(pi, m - dim1.wid / 2, y + dim1.asc + 2 - 5);
}
} }
if (kind_ == NICEFRAC || kind_ == UNITFRAC) { } //switch (kind_)
// Diag line:
int xx = x;
if (nargs() == 3)
xx += cell(2).dimension(*pi.base.bv).wid + 5;
pi.pain.line(xx + dim0.wid,
y + dim.des - 2,
xx + dim0.wid + 5,
y - dim.asc + 2, pi.base.font.color());
}
if (kind_ == FRAC || kind_ == CFRAC || kind_ == CFRACLEFT
|| kind_ == CFRACRIGHT || kind_ == DFRAC
|| kind_ == TFRAC || kind_ == OVER)
pi.pain.line(x + 1, y - 5,
x + dim.wid - 2, y - 5, pi.base.font.color());
drawMarkers(pi, x, y); drawMarkers(pi, x, y);
} }