From 56418f950421d83be6ceaabb18a50b8a4562f22d Mon Sep 17 00:00:00 2001 From: jkriege2 Date: Mon, 27 Jun 2022 21:09:22 +0200 Subject: [PATCH] IMPROVEMENTS: JKQTMathText: underbrace/overbrace and angle-braket improvements --- doc/dox/whatsnew.dox | 2 +- lib/jkqtmathtext/jkqtmathtext.cpp | 26 ++++++++ lib/jkqtmathtext/jkqtmathtext.h | 14 ++++- .../nodes/jkqtmathtextbracenode.cpp | 63 ++++++++++++------- .../nodes/jkqtmathtextfracnode.cpp | 38 ++++++----- 5 files changed, 104 insertions(+), 39 deletions(-) diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index d5a667bfb7..22a2446428 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -36,7 +36,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • IMPROVED: high-dpr-support in JKQTMathText
  • IMPROVED: typesetting of sub-/supercripts, especially for large math operators and braces
  • MODIFIED: brace node now calculates the extension of the child height above or below the strikeoutPos, in order to center braces around the strikeoutPos
  • -
  • IMPROVED: improved drawing of parantheses, square brackets ...
  • +
  • IMPROVED: improved drawing of parantheses, square brackets ... , underbrace/overbrace
  • remove/breaking: \v[a-zA-Z] and shorthand for \vec{a-zA-Z} was removed, implementation of \bbR,\bbC,... changed
  • NEW: now supports new decoration instructions: \cancel, \xcancel, \bcancel, \sout, \ocirc, \widetilde, \widehat, \breve
  • NEW: reworked drawing of decorations: improved appearance and positioning!
  • diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index 622c5db4d4..9f44662b3f 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -78,6 +78,8 @@ JKQTMathText::JKQTMathText(QObject* parent): frac_shift_factor=0.4; underbrace_factor=0.75; + underbrace_separation_xfactor=0.25; + underbrace_bracesize_xfactor=0.5; underset_factor=0.7; decoration_height_factor=0.2; decoration_width_reduction_Xfactor=0.2; @@ -218,6 +220,8 @@ void JKQTMathText::loadSettings(const QSettings& settings, const QString& group) frac_factor=settings.value(group+"frac_factor", frac_factor).toDouble(); frac_shift_factor=settings.value(group+"frac_shift_factor", frac_shift_factor).toDouble(); underbrace_factor=settings.value(group+"underbrace_factor", underbrace_factor).toDouble(); + underbrace_bracesize_xfactor=settings.value(group+"underbrace_bracesize_xfactor", underbrace_bracesize_xfactor).toDouble(); + underbrace_separation_xfactor=settings.value(group+"underbrace_separation_xfactor", underbrace_separation_xfactor).toDouble(); underset_factor=settings.value(group+"undersetFactor", underset_factor).toDouble(); brace_y_shift_factor=settings.value(group+"brace_y_shift_factor", brace_y_shift_factor).toDouble(); decoration_height_factor=settings.value(group+"decoration_height_factor", decoration_height_factor).toDouble(); @@ -249,6 +253,8 @@ void JKQTMathText::saveSettings(QSettings& settings, const QString& group) const settings.setValue(group+ "frac_factor", frac_factor); settings.setValue(group+ "frac_shift_factor", frac_shift_factor); settings.setValue(group+ "underbrace_factor", underbrace_factor); + settings.setValue(group+ "underbrace_bracesize_xfactor", underbrace_bracesize_xfactor); + settings.setValue(group+ "underbrace_separation_xfactor", underbrace_separation_xfactor); settings.setValue(group+ "undersetFactor", underset_factor); settings.setValue(group+ "operatorsubsuper_size_factor", operatorsubsuper_size_factor); settings.setValue(group+ "operatorsubsuper_distance_factor", operatorsubsuper_distance_factor); @@ -786,6 +792,26 @@ double JKQTMathText::getUnderbraceFactor() const return this->underbrace_factor; } +void JKQTMathText::setUnderbraceSeparationXFactor(double __value) +{ + underbrace_separation_xfactor=__value; +} + +double JKQTMathText::getUnderbraceSeparationXFactor() const +{ + return underbrace_separation_xfactor; +} + +void JKQTMathText::setUnderbraceBraceSizeXFactor(double __value) +{ + underbrace_bracesize_xfactor=__value; +} + +double JKQTMathText::getUnderbraceBraceSizeXFactor() const +{ + return underbrace_bracesize_xfactor; +} + void JKQTMathText::setUndersetFactor(double __value) { this->underset_factor = __value; diff --git a/lib/jkqtmathtext/jkqtmathtext.h b/lib/jkqtmathtext/jkqtmathtext.h index 90f7be78b2..c5a878b9d6 100644 --- a/lib/jkqtmathtext/jkqtmathtext.h +++ b/lib/jkqtmathtext/jkqtmathtext.h @@ -486,7 +486,15 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { void setUnderbraceFactor(double __value); /** \copydoc underbrace_factor */ double getUnderbraceFactor() const; - /** \copydoc undersetFactor */ + /** \copydoc underbrace_separation_xfactor */ + void setUnderbraceSeparationXFactor(double __value); + /** \copydoc underbrace_separation_xfactor */ + double getUnderbraceSeparationXFactor() const; + /** \copydoc underbrace_bracesize_xfactor */ + void setUnderbraceBraceSizeXFactor(double __value); + /** \copydoc underbrace_bracesize_xfactor */ + double getUnderbraceBraceSizeXFactor() const; + /** \copydoc undersetFactor */ void setUndersetFactor(double __value); /** \copydoc undersetFactor */ double getUndersetFactor() const; @@ -641,6 +649,10 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { double underbrace_factor; /** \brief scaling factor for font of underset/overset text */ double underset_factor; + /** \brief additional space between the main text to the curly brace and the brace to underbrace/overbrace, multiplied with height("x") */ + double underbrace_separation_xfactor; + /** \brief height of the brace in underbrace/overbrace, multiplied with ascent */ + double underbrace_bracesize_xfactor; /** \brief fraction of the brace ascent that the brace is shifted downwards, when scaled */ double brace_y_shift_factor; /** \brief size of the decorations (dot, tilde, ...), as fraction of the baselineheight diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextbracenode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextbracenode.cpp index e33a589583..d535e87d48 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextbracenode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextbracenode.cpp @@ -134,11 +134,30 @@ double JKQTMathTextBraceNode::draw(QPainter& painter, double x, double y, JKQTMa path.cubicTo(ptc2, pbc2, pb2); path.closeSubpath(); painter.fillPath(path, QBrush(ev.color, Qt::SolidPattern)); - painter.setPen("blue"); + /*painter.setPen("blue"); painter.drawLine(pb1,pbc1); painter.drawLine(pt1,ptc1); painter.drawLine(pb2,pbc2); - painter.drawLine(pt2,ptc2); + painter.drawLine(pt2,ptc2);*/ + } else if (openbrace==MTBTAngleBracket) { + QPainterPath path; + const double y1=y+(nodeOverallHeight-nodeBaselineHeight); + const double y2=y-nodeBaselineHeight; + const double yc=(y1+y2)/2.0; + const QPointF pb1(xbrace2-paren_topwidth/2.0, y1); + const QPointF pc1(xbrace1-paren_centerwidth/2.0, yc); + const QPointF pt1(xbrace2-paren_topwidth/2.0, y2); + const QPointF pt2(xbrace2+paren_topwidth/2.0, y2); + const QPointF pc2(xbrace1+paren_centerwidth/2.0, yc); + const QPointF pb2(xbrace2+paren_topwidth/2.0, y1); + path.moveTo(pb1); + path.lineTo(pc1); + path.lineTo(pt1); + path.lineTo(pt2); + path.lineTo(pc2); + path.lineTo(pb2); + path.closeSubpath(); + painter.fillPath(path, QBrush(ev.color, Qt::SolidPattern)); } else if (openbrace==MTBTSquareBracket) { QPainterPath path; const double y1=y+(nodeOverallHeight-nodeBaselineHeight)-lw/2.0; @@ -205,17 +224,9 @@ double JKQTMathTextBraceNode::draw(QPainter& painter, double x, double y, JKQTMa painter.setPen(plocal); const QLineF l(xbrace1, y1, xbrace1, y2); if (l.length()>0) painter.drawLine(l); - const QLineF l2(xbrace1+1.5*lw, y1, xbrace1+1.5*lw, y2); + const QLineF l2(xbrace1+2.5*lw, y1, xbrace1+2.5*lw, y2); if (l2.length()>0) painter.drawLine(l2); painter.setPen(p); - } else if (openbrace==MTBTAngleBracket) { - QPainterPath path; - const double y1=y+(nodeOverallHeight-nodeBaselineHeight); - const double y2=y-nodeBaselineHeight; - path.moveTo(xbrace2, y1); - path.lineTo(xbrace1, (y2+y1)/2.0); - path.lineTo(xbrace2, y2); - painter.drawPath(path); } else { showOpeningBrace=false; } @@ -257,7 +268,25 @@ double JKQTMathTextBraceNode::draw(QPainter& painter, double x, double y, JKQTMa painter.drawLine(pt1,ptc1); painter.drawLine(pb2,pbc2); painter.drawLine(pt2,ptc2);*/ - + } else if (closebrace==MTBTAngleBracket) { + QPainterPath path; + const double y1=y+(nodeOverallHeight-nodeBaselineHeight); + const double y2=y-nodeBaselineHeight; + const double yc=(y1+y2)/2.0; + const QPointF pb1(xbrace1-paren_topwidth/2.0, y1); + const QPointF pc1(xbrace2-paren_centerwidth/2.0, yc); + const QPointF pt1(xbrace1-paren_topwidth/2.0, y2); + const QPointF pt2(xbrace1+paren_topwidth/2.0, y2); + const QPointF pc2(xbrace2+paren_centerwidth/2.0, yc); + const QPointF pb2(xbrace1+paren_topwidth/2.0, y1); + path.moveTo(pb1); + path.lineTo(pc1); + path.lineTo(pt1); + path.lineTo(pt2); + path.lineTo(pc2); + path.lineTo(pb2); + path.closeSubpath(); + painter.fillPath(path, QBrush(ev.color, Qt::SolidPattern)); } else if (closebrace==MTBTSquareBracket) { QPainterPath path; const double y1=y+(nodeOverallHeight-nodeBaselineHeight)-lw/2.0; @@ -331,17 +360,9 @@ double JKQTMathTextBraceNode::draw(QPainter& painter, double x, double y, JKQTMa plocal.setWidthF(plocal.widthF()*absnorm_linewidth_factor); painter.setPen(plocal); if (l.length()>0) painter.drawLine(l); - const QLineF l2(xbrace2-1.5*lw, y1, xbrace2-1.5*lw, y2); + const QLineF l2(xbrace2-2.5*lw, y1, xbrace2-2.5*lw, y2); if (l2.length()>0) painter.drawLine(l2); painter.setPen(p); - } else if (closebrace==MTBTAngleBracket) { - QPainterPath path; - const double y1=y+(nodeOverallHeight-nodeBaselineHeight); - const double y2=y-nodeBaselineHeight; - path.moveTo(xbrace1, y1); - path.lineTo(xbrace2, (y2+y1)/2.0); - path.lineTo(xbrace1, y2); - painter.drawPath(path); } else { showClosingBrace=false; } diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextfracnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextfracnode.cpp index 12cb4c0552..f360976785 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextfracnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextfracnode.cpp @@ -90,6 +90,8 @@ void JKQTMathTextFracNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro const double Mheight=JKQTMathTextGetTightBoundingRect(f, "M", painter.device()).height();//fm.ascent(); const double xwidth=JKQTMathTextGetTightBoundingRect(f, "x", painter.device()).width(); const double qheight=JKQTMathTextGetTightBoundingRect(f, "q", painter.device()).height();//fm.ascent(); + const double braceheight=fm.xHeight()*parentMathText->getUnderbraceBraceSizeXFactor(); + const double braceseparation=fm.xHeight()*parentMathText->getUnderbraceSeparationXFactor(); if (mode==JKQTMathTextFracNode::MTFMunderbrace || mode==JKQTMathTextFracNode::MTFMoverbrace) { ev2.fontSize=ev2.fontSize*parentMathText->getUnderbraceFactor(); @@ -108,6 +110,7 @@ void JKQTMathTextFracNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro double width2=0, baselineHeight2=0, overallHeight2=0, strikeoutPos2=0; child1->getSize(painter, ev1, width1, baselineHeight1, overallHeight1, strikeoutPos1); child2->getSize(painter, ev2, width2, baselineHeight2, overallHeight2, strikeoutPos2); + const double descent1=overallHeight1-baselineHeight1; overallHeight=0; @@ -135,13 +138,14 @@ void JKQTMathTextFracNode::getSizeInternal(QPainter& painter, JKQTMathTextEnviro overallHeight=newascent+newdescent; baselineHeight=newascent; } else if (mode==JKQTMathTextFracNode::MTFMunderbrace) { - overallHeight=overallHeight1+overallHeight2+Mheight/2.0; + const double newdescent=descent1+overallHeight2+braceheight+2.0*braceseparation; + overallHeight=newdescent+baselineHeight1; baselineHeight=baselineHeight1; width=qMax(width1, width2)+xwidth; strikeoutPos=line_ascent; } else if (mode==JKQTMathTextFracNode::MTFMoverbrace) { - overallHeight=overallHeight1+overallHeight2+Mheight/2.0; - baselineHeight=baselineHeight1+overallHeight2+Mheight/2.0; + overallHeight=overallHeight1+overallHeight2+braceheight+2.0*braceseparation; + baselineHeight=baselineHeight1+overallHeight2+braceheight+2.0*braceseparation; width=qMax(width1, width2)+xwidth; strikeoutPos=line_ascent; } else if (mode==JKQTMathTextFracNode::MTFMunderset) { @@ -189,7 +193,8 @@ double JKQTMathTextFracNode::draw(QPainter& painter, double x, double y, JKQTMat const double linewideth=qMax(0.0,ceil(currentEv.fontSize/16.0));//fm.lineWidth(); const double Mheight=JKQTMathTextGetTightBoundingRect(f, "M", painter.device()).height();//fm.ascent(); const double qheight=JKQTMathTextGetTightBoundingRect(f, "q", painter.device()).height();//fm.ascent(); - const double bw=Mheight/2.0; + const double braceheight=fm.xHeight()*parentMathText->getUnderbraceBraceSizeXFactor(); + const double braceseparation=fm.xHeight()*parentMathText->getUnderbraceSeparationXFactor(); if (mode==JKQTMathTextFracNode::MTFMunderbrace || mode==JKQTMathTextFracNode::MTFMoverbrace) { ev2.fontSize=ev2.fontSize*parentMathText->getUnderbraceFactor(); @@ -208,12 +213,12 @@ double JKQTMathTextFracNode::draw(QPainter& painter, double x, double y, JKQTMat double width2=0, baselineHeight2=0, overallHeight2=0, strikeoutPos=0; child1->getSize(painter, ev1, width1, baselineHeight1, overallHeight1, strikeoutPos); child2->getSize(painter, ev2, width2, baselineHeight2, overallHeight2, strikeoutPos); - double ascent1=baselineHeight1; - double descent1=overallHeight1-baselineHeight1; - double ascent2=baselineHeight2; - double descent2=overallHeight2-baselineHeight2; + const double ascent1=baselineHeight1; + const double descent1=overallHeight1-baselineHeight1; + const double ascent2=baselineHeight2; + const double descent2=overallHeight2-baselineHeight2; - double yline=y-xheight*0.5; + const double yline=y-xheight*0.5; //double overallHeight=overallHeight1+overallHeight2+xh; @@ -247,35 +252,36 @@ double JKQTMathTextFracNode::draw(QPainter& painter, double x, double y, JKQTMat child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, y+descent1+xheight/6.0+ascent2, ev2); deltaWidth=xwidth; } else if (mode==JKQTMathTextFracNode::MTFMunderbrace) { - double ybrace=y+descent1+bw/2.0; + const double ybrace=y+descent1+braceseparation+braceheight/2.0; + const double ybot=y+descent1+2.0*braceseparation+braceheight+ascent2; { - const QPainterPath path=JKQTMathTextMakeHBracePath(x+xwidth/2.0+(width1)/2.0, ybrace, maxWidth, bw, p.width()); + const QPainterPath path=JKQTMathTextMakeHBracePath(x+xwidth/2.0+(width1)/2.0, ybrace, maxWidth, braceheight, p.width()); QPen plocal=p; plocal.setWidthF(0.0001); painter.fillPath(path, QBrush(ev1.color)); } child1->draw(painter, x+xwidth/2.0+(maxWidth-width1)/2.0, y, ev1); - child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, y+descent1+bw+ascent2, ev2); + child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, ybot, ev2); deltaWidth=xwidth; } else if (mode==JKQTMathTextFracNode::MTFMoverset) { child1->draw(painter, x+xwidth/2.0+(maxWidth-width1)/2.0, y, ev1); child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, y-ascent1-xheight/6.0-descent2, ev2); deltaWidth=xwidth; } else if (mode==JKQTMathTextFracNode::MTFMoverbrace) { - const double ybrace=y-ascent1-bw/2.0; - + const double ybrace=y-ascent1-braceheight/2.0-braceseparation; + const double ytop=y-ascent1-2.0*braceseparation-braceheight-descent2; { painter.save(); auto __finalpaintinner=JKQTPFinally([&painter]() {painter.restore();}); painter.translate(x+xwidth/2.0+(width1)/2.0, ybrace); painter.rotate(180); - const QPainterPath path=JKQTMathTextMakeHBracePath(0,0, maxWidth, bw, p.widthF()); + const QPainterPath path=JKQTMathTextMakeHBracePath(0,0, maxWidth, braceheight, p.widthF()); QPen plocal=p; plocal.setWidthF(0.0001); painter.fillPath(path, QBrush(ev1.color)); } child1->draw(painter, x+xwidth/2.0+(maxWidth-width1)/2.0, y, ev1); - child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, y-ascent1-bw-descent2, ev2); + child2->draw(painter, x+xwidth/2.0+(maxWidth-width2)/2.0, ytop, ev2); deltaWidth=xwidth; }