diff --git a/doc/dox/whatsnew.dox b/doc/dox/whatsnew.dox index b4d2b2f204..6485321c9e 100644 --- a/doc/dox/whatsnew.dox +++ b/doc/dox/whatsnew.dox @@ -38,6 +38,7 @@ Changes, compared to \ref page_whatsnew_V4_0_0 "v4.0.0" include:
  • 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 ... , underbrace/overbrace
  • IMPROVED: added x-correction for sub/superscript above/below/besides integrals
  • +
  • IMPROVED: rendering of sqrt
  • 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/doc/images/jkqtmathtext/jkqtmathtext_cbrt.png b/doc/images/jkqtmathtext/jkqtmathtext_cbrt.png new file mode 100644 index 0000000000..f1e5676816 Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_cbrt.png differ diff --git a/doc/images/jkqtmathtext/jkqtmathtext_sqrt.png b/doc/images/jkqtmathtext/jkqtmathtext_sqrt.png new file mode 100644 index 0000000000..51ac9478f8 Binary files /dev/null and b/doc/images/jkqtmathtext/jkqtmathtext_sqrt.png differ diff --git a/examples/jkqtmathtext_test/mathtest.pdf b/examples/jkqtmathtext_test/mathtest.pdf index 97fe5e51f6..d962b30eed 100644 Binary files a/examples/jkqtmathtext_test/mathtest.pdf and b/examples/jkqtmathtext_test/mathtest.pdf differ diff --git a/examples/jkqtmathtext_test/mathtest.tex b/examples/jkqtmathtext_test/mathtest.tex index 8033de02b6..f8527026c0 100644 --- a/examples/jkqtmathtext_test/mathtest.tex +++ b/examples/jkqtmathtext_test/mathtest.tex @@ -89,7 +89,12 @@ \item\textbf{math 1:} \[f(x)=\int_{-\infty}^xe^{-t^2}\;\mathrm{d}t \] \item\textbf{math 2:} \[\sum_{i=1}^\infty\frac{-e^{i\pi}}{2^n} \] \item\textbf{math 3:} \[\mbox{det} \begin{pmatrix} 1 & x_1 & \ldots & x_1^{n-1} \\ 1 & x_2 & \ldots & x_2^{n-1} \\ \vdots & \vdots & \ddots & \vdots \\ 1 & x_n & \ldots & x_n^{n-1} \end{pmatrix} = \prod_{1 \leq i < j \leq n} (x_j - x_i) \] - \item\textbf{math 4:} \[\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+x}}}}}} \] + \item\textbf{math 4:} \[M\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+x}}}}}} \] + \item\textbf{math 4:} \[M\sqrt[3]{1+\sqrt[3]{1+\sqrt[3]{1+\sqrt[3]{1+\sqrt[3]{1+\sqrt[3]{1+x}}}}}} \] + \item\textbf{math 4:} \[M\sqrt{1+X}\sqrt[3]{1+X}\sqrt[3.14156]{1+X} \] + \item\textbf{math 4:} \[M\sqrt{1+X}\sqrt[\frac{1}{2}]{1+X}\sqrt[3.14156\cdot\frac{1}{2}]{1+X} \] + \item\textbf{math 4:} \[M\sqrt{\frac{1}{2}+\sqrt{\frac{1}{2}+\sqrt{\frac{1}{2}+\sqrt{\frac{1}{2}+\sqrt{\frac{1}{2}+\sqrt{1+x}}}}}} \] + \item\textbf{math 4:} \[M\sqrt{a}\frac{\sqrt{a}}{\sqrt{a}}\sqrt{\frac{1}{a}} \] \item\textbf{math 5:} \[\left(\stackrel{p}{2}\right)=x^2y^{p-2}-\frac{1}{1-x}\frac{1}{1-x^2} \] \item\textbf{math 6:} \[a_0+\frac{1}{a_1+\frac{1}{a_2+\frac{1}{a_3+\frac{1}{a_4}}}} \] \item\textbf{math 7:} \[\left(\frac{\partial^2}{\partial x^2}+\frac{\partial^2}{\partial y^2}\right)\left|\varphi(x+\mathrm{i}y)\right|^2=0 \] diff --git a/examples/jkqtmathtext_test/testform.cpp b/examples/jkqtmathtext_test/testform.cpp index f0689d34b4..ce3ce5cf12 100644 --- a/examples/jkqtmathtext_test/testform.cpp +++ b/examples/jkqtmathtext_test/testform.cpp @@ -149,12 +149,14 @@ TestForm::TestForm(QWidget *parent) : ui->cmbTestset->addItem("math 1", "$f(x)=\\int_{-\\infty}^xe^{-t^2}\\;\\mathrm{d}t$"); ui->cmbTestset->addItem("math 2", "$\\sum_{i=1}^\\infty\\frac{-e^{i\\pi}}{2^n}$"); ui->cmbTestset->addItem("math 3", "$\\mbox{det} \\begin{pmatrix} 1 & x_1 & \\ldots & x_1^{n-1} \\\\ 1 & x_2 & \\ldots & x_2^{n-1} \\\\ \\vdots & \\vdots & \\ddots & \\vdots \\\\ 1 & x_n & \\ldots & x_n^{n-1} \\end{pmatrix} = \\prod_{1 \\leq i < j \\leq n} (x_j - x_i) $"); - ui->cmbTestset->addItem("math 4", "$\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+x}}}}}}$"); - ui->cmbTestset->addItem("math 5", "$\\left(\\stackrel{p}{2}\\right)=x^2y^{p-2}-\\frac{1}{1-x}\\frac{1}{1-x^2}$"); - ui->cmbTestset->addItem("math 6", "$a_0+\\frac{1}{a_1+\\frac{1}{a_2+\\frac{1}{a_3+\\frac{1}{a_4}}}}$"); - ui->cmbTestset->addItem("math 7", "$\\left(\\frac{\\partial^2}{\\partial x^2}+\\frac{\\partial^2}{\\partial y^2}\\right)\\left|\\varphi(x+\\mathrm{i}y)\\right|^2=0$"); - ui->cmbTestset->addItem("math 8", "$2^{2^{2^{x}}}$"); - ui->cmbTestset->addItem("math 9", "$\\iint_Df(x,y)\\;\\mathrm{d}x\\;\\mathrm{d}y$"); + ui->cmbTestset->addItem("math: nestes sqrt", "$\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+\\sqrt{1+x}}}}}}$"); + ui->cmbTestset->addItem("math: nestes cbrt", "$\\sqrt[3]{1+\\sqrt[3]{1+\\sqrt[3]{1+\\sqrt[3]{1+\\sqrt[3]{1+\\sqrt[3]{1+x}}}}}}$"); + ui->cmbTestset->addItem("math: nestes sqrt, high contents", "$\\sqrt{\\frac{1}{2}+\\sqrt{\\frac{1}{2}+\\sqrt{\\frac{1}{2}+\\sqrt{\\frac{1}{2}+\\sqrt{\\frac{1}{2}+\\sqrt{\\frac{1}{2}+x}}}}}}$"); + ui->cmbTestset->addItem("math 5 (frac+stackrel)", "$\\left(\\stackrel{p}{2}\\right)=x^2y^{p-2}-\\frac{1}{1-x}\\frac{1}{1-x^2}$"); + ui->cmbTestset->addItem("math 6 (nested frac)", "$a_0+\\frac{1}{a_1+\\frac{1}{a_2+\\frac{1}{a_3+\\frac{1}{a_4}}}}$"); + ui->cmbTestset->addItem("math 7 (partial derivatives)", "$\\left(\\frac{\\partial^2}{\\partial x^2}+\\frac{\\partial^2}{\\partial y^2}\\right)\\left|\\varphi(x+\\mathrm{i}y)\\right|^2=0$"); + ui->cmbTestset->addItem("math 8 (powers of powers)", "$2^{2^{2^{x}}}$"); + ui->cmbTestset->addItem("math 9 (integral)", "$\\iint_Df(x,y)\\;\\mathrm{d}x\\;\\mathrm{d}y$"); ui->cmbTestset->addItem("math 10 (overbrace+overbracket)", "$\\overbrace{x+x+...+x}{k\\ \\mathrm{times}}\\ \\ \\ \\overbracket{x+x+...+x}{k\\ \\mathrm{times}}$"); ui->cmbTestset->addItem("math 11 (underbrace+underbracket)", "$\\underbrace{x+x+...+x}{k\\ \\mathrm{times}}\\ \\ \\ \\underbracket{x+x+...+x}{k\\ \\mathrm{times}}$"); ui->cmbTestset->addItem("math 12 (under/overbrace+under/overbracket)", "$\\underbrace{\\overbrace{x+x+...+x}{k\\ \\mathrm{times}} \\overbrace{x+x+...+x}{k\\ \\mathrm{times}}}{2k\\ \\mathrm{times}}\\ \\ \\ \\underbracket{\\overbracket{x+x+...+x}{k\\ \\mathrm{times}} \\overbracket{x+x+...+x}{k\\ \\mathrm{times}}}{2k\\ \\mathrm{times}}$"); diff --git a/lib/jkqtmathtext/jkqtmathtext.cpp b/lib/jkqtmathtext/jkqtmathtext.cpp index e35ef4f576..e8b14ced3c 100644 --- a/lib/jkqtmathtext/jkqtmathtext.cpp +++ b/lib/jkqtmathtext/jkqtmathtext.cpp @@ -92,6 +92,10 @@ JKQTMathText::JKQTMathText(QObject* parent): mathoperator_width_factor=1.5; bigmathoperator_font_factor=1.8; + sqrt_width_Xfactor=0.8; + sqrt_height_factor=1.2; + sqrt_smallfont_factor=0.57; + blackboardSimulated=true; @@ -234,6 +238,9 @@ void JKQTMathText::loadSettings(const QSettings& settings, const QString& group) mathoperator_width_factor=settings.value(group+"mathoperator_width_factor", mathoperator_width_factor).toDouble(); intsubsuper_xcorrection_factor=settings.value(group+"intsubsuper_xcorrection_factor", intsubsuper_xcorrection_factor).toDouble(); intsubbesides_xcorrection_xfactor=settings.value(group+"intsubbesides_xcorrection_xfactor", intsubbesides_xcorrection_xfactor).toDouble(); + sqrt_width_Xfactor=settings.value(group+"sqrt_width_Xfactor", sqrt_width_Xfactor).toDouble(); + sqrt_height_factor=settings.value(group+"sqrt_height_factor", sqrt_height_factor).toDouble(); + sqrt_smallfont_factor=settings.value(group+"sqrt_smallfont_factor", sqrt_smallfont_factor).toDouble(); if (settings.value(group+"use_stix_fonts", false).toBool()) useSTIX(); @@ -269,6 +276,9 @@ void JKQTMathText::saveSettings(QSettings& settings, const QString& group) const settings.setValue(group+ "brace_y_shift_factor", brace_y_shift_factor); settings.setValue(group+ "decoration_height_factor", decoration_height_factor); settings.setValue(group+ "decoration_width_reduction_xfactor", decoration_width_reduction_Xfactor); + settings.setValue(group+ "sqrt_width_Xfactor", sqrt_width_Xfactor); + settings.setValue(group+ "sqrt_height_factor", sqrt_height_factor); + settings.setValue(group+ "sqrt_smallfont_factor", sqrt_smallfont_factor); } bool JKQTMathText::useSTIX(bool mathModeOnly) { @@ -908,6 +918,36 @@ double JKQTMathText::getDecorationWidthReductionXFactor() const return decoration_width_reduction_Xfactor; } +void JKQTMathText::setSqrtWidthXFactor(double __value) +{ + sqrt_width_Xfactor=__value; +} + +double JKQTMathText::getSqrtWidthXFactor() const +{ + return sqrt_width_Xfactor; +} + +void JKQTMathText::setSqrtHeightFactor(double __value) +{ + sqrt_height_factor=__value; +} + +double JKQTMathText::getSqrtHeightFactor() const +{ + return sqrt_height_factor; +} + +void JKQTMathText::setSqrtSmallFontFactor(double __value) +{ + sqrt_smallfont_factor=__value; +} + +double JKQTMathText::getSqrtSmallFontFactor() const +{ + return sqrt_smallfont_factor; +} + void JKQTMathText::setUseUnparsed(bool __value) { this->useUnparsed = __value; diff --git a/lib/jkqtmathtext/jkqtmathtext.h b/lib/jkqtmathtext/jkqtmathtext.h index 715d889783..793d3b9e9e 100644 --- a/lib/jkqtmathtext/jkqtmathtext.h +++ b/lib/jkqtmathtext/jkqtmathtext.h @@ -152,6 +152,12 @@ class JKQTMathTextNode; // forward - You can use \c \\left. or \c \\right. to have only right or only left brace . + \subsection JKQTMathTextSuppoertedLaTeXRoots Roots + There are also instructions that allow to write roots: + - $\\sqrt{1+\\sqrt{1+x}}$ \image html jkqtmathtext/jkqtmathtext_sqrt.png + - $\\sqrt[3]{1+\\sqrt[3]{1+x}}$ \image html jkqtmathtext/jkqtmathtext_cbrt.png + . + \subsection JKQTMathTextSuppoertedLaTeXUnderOver Undersetting, Oversetting, Underbraces, Overbraces ... There are also instructions that allow to under/overset braces, arrows, ...: - $\\underbrace{x+x+...+x}{k\\ \\mathrm{times}}$ \image html jkqtmathtext/jkqtmathtext_brace_underbrace.png @@ -535,6 +541,23 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { void setDecorationWidthReductionXFactor(double __value); /** \copydoc decoration_width_reduction_Xfactor */ double getDecorationWidthReductionXFactor() const; + + + /** \copydoc sqrt_width_Xfactor */ + void setSqrtWidthXFactor(double __value); + /** \copydoc sqrt_width_Xfactor */ + double getSqrtWidthXFactor() const; + /** \copydoc sqrt_height_factor */ + void setSqrtHeightFactor(double __value); + /** \copydoc sqrt_height_factor */ + double getSqrtHeightFactor() const; + /** \copydoc sqrt_smallfont_factor */ + void setSqrtSmallFontFactor(double __value); + /** \copydoc sqrt_smallfont_factor */ + double getSqrtSmallFontFactor() const; + + + /** \copydoc useUnparsed */ void setUseUnparsed(bool __value); /** \copydoc useUnparsed */ @@ -692,6 +715,12 @@ class JKQTMATHTEXT_LIB_EXPORT JKQTMathText : public QObject { * \image html jkqtmathtext/decoration_sizing.png */ double decoration_width_reduction_Xfactor; + /** \brief scaling factor for the small font used to indicate the degree of the sqrt */ + double sqrt_smallfont_factor; + /** \brief width of the sqrt-symbol, as factor to width("X") */ + double sqrt_width_Xfactor; + /** \brief height-increase of the sqrt-symbol, as factor of the child's height */ + double sqrt_height_factor; /** \brief a list that will be filled with error messages while parsing, if any error occur */ QStringList error_list; diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.cpp b/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.cpp index 0f00a79bfc..66d072e981 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.cpp +++ b/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.cpp @@ -45,54 +45,92 @@ JKQTMathTextSqrtNode::~JKQTMathTextSqrtNode() { } void JKQTMathTextSqrtNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) { - QFontMetricsF fm(currentEv.getFont(parentMathText), painter.device()); + const QFontMetricsF fm(currentEv.getFont(parentMathText), painter.device()); + QFont fsmall=currentEv.getFont(parentMathText); + fsmall.setPointSizeF(fsmall.pointSizeF()*parentMathText->getSqrtSmallFontFactor()); + fsmall.setItalic(false); + const QFontMetricsF fmsmall(fsmall, painter.device()); getChild()->getSize(painter, currentEv, width, baselineHeight, overallHeight, strikeoutPos); + const double descent=overallHeight-baselineHeight; + const double sqrtwidth=fm.boundingRect("X").width()*parentMathText->getSqrtWidthXFactor(); + const double newAscent=qMax(baselineHeight*parentMathText->getSqrtHeightFactor(), fm.ascent()); + const double newDescent=qMax(descent*parentMathText->getSqrtHeightFactor(), fm.descent()); - overallHeight=overallHeight*1.2;//+fm.ascent()*0.1; - baselineHeight=baselineHeight*1.2;//+fm.ascent()*0.1; - width=width+fm.boundingRect("A").width()*2; // 1.53 + overallHeight=newAscent+newDescent;; + baselineHeight=newAscent; + width=width+sqrtwidth; + if (degree!=2) { + const QString degreetext=QLocale::c().toString(degree); + const double degwidth=fmsmall.width(degreetext); + const double smalltextIndent=0.6*sqrtwidth; + if (degwidth>smalltextIndent) width=width+(degwidth-smalltextIndent); + } } double JKQTMathTextSqrtNode::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) { doDrawBoxes(painter, x, y, currentEv); - double width=0, baselineHeight=0, overallHeight=0, sp=0; - getChild()->getSize(painter, currentEv, width, baselineHeight, overallHeight, sp); - QFont f=currentEv.getFont(parentMathText); + + const QFont f=currentEv.getFont(parentMathText); QFont fsmall=f; - QFontMetricsF fm(f, painter.device()); - double w=fm.boundingRect("A").width(); - double a=baselineHeight*1.15; - double d=overallHeight-baselineHeight; + const QFontMetricsF fm(f, painter.device()); + fsmall.setPointSizeF(fsmall.pointSizeF()*parentMathText->getSqrtSmallFontFactor()); + fsmall.setItalic(false); + const QFontMetricsF fmsmall(fsmall, painter.device()); + + double width=0, baselineHeight=0, overallHeight=0, strikeoutPos=0; + getChild()->getSize(painter, currentEv, width, baselineHeight, overallHeight, strikeoutPos); + const double descent=overallHeight-baselineHeight; + const double sqrtwidth=fm.boundingRect("X").width()*parentMathText->getSqrtWidthXFactor(); + const double newAscent=qMax(baselineHeight*parentMathText->getSqrtHeightFactor(), fm.ascent()); + const double newDescent=qMax(descent*parentMathText->getSqrtHeightFactor(), fm.descent()); + const double linewidth=fm.lineWidth(); + const double tinyhookSize=sqrtwidth*0.1; + const double smalltextIndent=0.6*sqrtwidth; + const QString degreetext=(degree!=2)?QLocale::c().toString(degree):""; + const double degwidth=fmsmall.width(degreetext); + const double degheight=fmsmall.boundingRect(degreetext).height(); + const double degree_overwidth=(degwidth>smalltextIndent)?(degwidth-smalltextIndent):0.0; + //painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); QPen p=painter.pen(); p.setColor(currentEv.color); - p.setWidthF(fm.lineWidth()); + p.setWidthF(linewidth); + p.setCapStyle(Qt::RoundCap); + p.setJoinStyle(Qt::RoundJoin); //painter.setPen(p); - QPainterPath path; - if (w>0) { - path.moveTo(x+0.1*w, y-0.4*a); - path.lineTo(x+0.33*w, y-0.4*a); - path.lineTo( x+0.66*w, y+0.5*d); - path.lineTo(x+w, y-a); - } - if (degree!=2) { - fsmall.setPointSizeF(fsmall.pointSizeF()/2.0); - fsmall.setItalic(false); - painter.setFont(fsmall); - painter.drawText(QPointF(x+0.33*w, y-0.55*a), QLocale::c().toString(degree)); - } - //painter.restore(); - double xnew=getChild()->draw(painter, x+1.2*w, y, currentEv); - painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();}); - painter.setPen(p); - if (w>0) { - path.lineTo( xnew+0.2*w, y-a); - path.lineTo(xnew+0.2*w, y-0.8*a); + + double xnew=getChild()->draw(painter, x+sqrtwidth+degree_overwidth, y, currentEv); + + const bool useAltForm=overallHeight>4.0*sqrtwidth; + const double y_tinyhooktop=y-strikeoutPos; + const double y_bottom=y+newDescent-linewidth/2.0; + const double y_top=y-newAscent+linewidth/2.0; + const double x_start=x+degree_overwidth+linewidth/2.0; + const double x_tinyhooktop=x_start+tinyhookSize; + const double x_hookbottom=(!useAltForm)?(x_start+0.33*sqrtwidth):(x_start+0.66*sqrtwidth); + const double x_hooktop=(!useAltForm)?(x_start+sqrtwidth):x_hookbottom; + const double x_smalltextend=x_start+smalltextIndent; + const double y_smalltext=y_top+fmsmall.ascent()+(fabs(y_top-(y_tinyhooktop-linewidth))-degheight)/2.0; + if (sqrtwidth>0) { + QPainterPath path; + path.moveTo(x_start, y_tinyhooktop+tinyhookSize); + path.lineTo(x_tinyhooktop, y_tinyhooktop); + path.lineTo(x_hookbottom, y_bottom); + path.lineTo(x_hooktop, y_top); + path.lineTo(xnew-linewidth/2.0, y_top); + path.moveTo(x_tinyhooktop,y_tinyhooktop); + path.lineTo(x_tinyhooktop+linewidth*0.8, y_tinyhooktop-linewidth*0.8); + path.lineTo(x_hookbottom, y_bottom-2.0*linewidth); + painter.setPen(p); painter.drawPath(path); } + if (degree!=2) { + painter.setFont(fsmall); + painter.drawText(QPointF(x_smalltextend-degwidth, y_smalltext), degreetext); + } - return xnew+0.33*w; + return xnew; } bool JKQTMathTextSqrtNode::toHtml(QString &html, JKQTMathTextEnvironment currentEv, JKQTMathTextEnvironment defaultEv) { diff --git a/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.h b/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.h index c9da40e0c5..a14da4087d 100644 --- a/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.h +++ b/lib/jkqtmathtext/nodes/jkqtmathtextsqrtnode.h @@ -35,6 +35,10 @@ class JKQTMathText; // forward /** \brief subclass representing a sqrt node * \ingroup jkqtmathtext_items + * + * This node renders square roots without and with an explicitly shown degree: + * \image html jkqtmathtext/jkqtmathtext_sqrt.png + * \image html jkqtmathtext/jkqtmathtext_cbrt.png */ class JKQTMATHTEXT_LIB_EXPORT JKQTMathTextSqrtNode: public JKQTMathTextSingleChildNode { public: