2022-06-08 21:38:26 +08:00
|
|
|
/*
|
|
|
|
Copyright (c) 2008-2022 Jan W. Krieger (<jan@jkrieger.de>)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
This software is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Lesser General Public License (LGPL) as published by
|
|
|
|
the Free Software Foundation, either version 2.1 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Lesser General Public License (LGPL) for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License (LGPL)
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "jkqtmathtext/nodes/jkqtmathtextfracnode.h"
|
2022-06-09 05:52:22 +08:00
|
|
|
#include "jkqtmathtext/nodes/jkqtmathtextbracenode.h"
|
2022-06-08 21:38:26 +08:00
|
|
|
#include "jkqtmathtext/jkqtmathtexttools.h"
|
|
|
|
#include "jkqtmathtext/jkqtmathtext.h"
|
|
|
|
#include "jkqtcommon/jkqtpcodestructuring.h"
|
|
|
|
#include "jkqtcommon/jkqtpstringtools.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include <QFontMetricsF>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QFontDatabase>
|
|
|
|
#include <QFontInfo>
|
|
|
|
#include <QApplication>
|
|
|
|
#include <QFont>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JKQTMathTextFracNode::JKQTMathTextFracNode(JKQTMathText* _parent, JKQTMathTextNode* child_top, JKQTMathTextNode* child_bottom, JKQTMathTextFracMode mode):
|
2022-06-09 05:52:22 +08:00
|
|
|
JKQTMathTextDualChildNode(child_top, child_bottom, _parent)
|
2022-06-08 21:38:26 +08:00
|
|
|
{
|
|
|
|
this->mode=mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
JKQTMathTextFracNode::~JKQTMathTextFracNode() {
|
|
|
|
}
|
|
|
|
|
|
|
|
QString JKQTMathTextFracNode::getTypeName() const
|
|
|
|
{
|
|
|
|
return "MTfracNode";
|
|
|
|
}
|
|
|
|
|
|
|
|
void JKQTMathTextFracNode::getSizeInternal(QPainter& painter, JKQTMathTextEnvironment currentEv, double& width, double& baselineHeight, double& overallHeight, double& strikeoutPos, const JKQTMathTextNodeSize* /*prevNodeSize*/) {
|
2022-06-09 17:32:57 +08:00
|
|
|
const QFont f=currentEv.getFont(parentMathText);
|
|
|
|
const QFontMetricsF fm(f, painter.device());
|
2022-06-08 21:38:26 +08:00
|
|
|
JKQTMathTextEnvironment ev1=currentEv;
|
|
|
|
JKQTMathTextEnvironment ev2=currentEv;
|
|
|
|
|
2022-06-09 05:52:22 +08:00
|
|
|
const double xheight=fm.xHeight(); //tightBoundingRect("x").height();
|
|
|
|
const double line_ascent=xheight/2.0;
|
2022-06-09 17:32:57 +08:00
|
|
|
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();
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
if (mode==MTFMunderbrace || mode==MTFMoverbrace) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev2.fontSize=ev2.fontSize*parentMathText->getUnderbraceFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMunderset || mode==MTFMoverset) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev2.fontSize=ev2.fontSize*parentMathText->getUndersetFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMfrac || mode==MTFMsfrac) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev1.fontSize=ev1.fontSize*getFracScalingFactor();
|
|
|
|
ev2.fontSize=ev2.fontSize*getFracScalingFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMtfrac || mode==MTFMstfrac) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev1.fontSize=ev1.fontSize*getFracScalingFactor()*0.7;
|
|
|
|
ev2.fontSize=ev2.fontSize*getFracScalingFactor()*0.7;
|
2022-06-08 21:38:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double width1=0, baselineHeight1=0, overallHeight1=0, strikeoutPos1=0;
|
|
|
|
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);
|
|
|
|
|
2022-06-09 05:52:22 +08:00
|
|
|
|
|
|
|
const double maxHeight=qMax(overallHeight1,overallHeight2);
|
|
|
|
const bool _isBraceParentNearerThanFrac=isBraceParentNearerThanFrac();
|
|
|
|
const double height1OrMaxHeight=(_isBraceParentNearerThanFrac)?maxHeight:overallHeight1;
|
|
|
|
const double height2OrMaxHeight=(_isBraceParentNearerThanFrac)?maxHeight:overallHeight2;
|
2022-06-08 21:38:26 +08:00
|
|
|
overallHeight=0;
|
|
|
|
baselineHeight=0;
|
|
|
|
width=0;
|
2022-06-09 05:52:22 +08:00
|
|
|
if (mode==MTFMfrac || mode==MTFMdfrac || mode==MTFMtfrac || mode==MTFMstackrel) {
|
|
|
|
const double top_ascent=line_ascent+xheight*parentMathText->getFracShiftFactor();
|
|
|
|
const double bot_ascent=line_ascent-xheight*parentMathText->getFracShiftFactor();
|
|
|
|
// here we use maxHeight (as LaTeX does) so braces are centered around the xHieght!!!
|
|
|
|
// if there are no braces, we can use the actual height
|
|
|
|
const double newascent=height1OrMaxHeight+top_ascent;
|
|
|
|
const double newdescent=height2OrMaxHeight-bot_ascent;
|
|
|
|
width=qMax(width1, width2);
|
|
|
|
if (mode!=MTFMstackrel) width+=xwidth/2.0;
|
|
|
|
strikeoutPos=line_ascent;
|
|
|
|
|
|
|
|
overallHeight=newascent+newdescent;
|
|
|
|
baselineHeight=newascent;
|
|
|
|
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMstfrac || mode==MTFMsfrac) {
|
2022-06-09 05:52:22 +08:00
|
|
|
const double top_ascent=line_ascent;
|
|
|
|
// here we use maxHeight (as LaTeX does) so braces are centered around the xHieght!!!
|
|
|
|
// if there are no braces, we can use the actual height
|
|
|
|
const double newascent=height1OrMaxHeight+top_ascent;
|
2022-06-09 17:32:57 +08:00
|
|
|
const double newdescent=qMax(height2OrMaxHeight-baselineHeight2, qheight-xheight);
|
|
|
|
width=width1+width2+xwidth/2.0;
|
2022-06-09 05:52:22 +08:00
|
|
|
strikeoutPos=line_ascent;
|
|
|
|
|
|
|
|
overallHeight=newascent+newdescent;
|
|
|
|
baselineHeight=newascent;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMunderbrace) {
|
2022-06-09 05:52:22 +08:00
|
|
|
overallHeight=overallHeight1+overallHeight2+Mheight/2.0;
|
2022-06-08 21:38:26 +08:00
|
|
|
baselineHeight=baselineHeight1;
|
2022-06-09 05:52:22 +08:00
|
|
|
width=qMax(width1, width2)+xwidth;
|
|
|
|
strikeoutPos=line_ascent;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMoverbrace) {
|
2022-06-09 05:52:22 +08:00
|
|
|
overallHeight=overallHeight1+overallHeight2+Mheight/2.0;
|
|
|
|
baselineHeight=baselineHeight1+overallHeight2+Mheight/2.0;
|
|
|
|
width=qMax(width1, width2)+xwidth;
|
|
|
|
strikeoutPos=line_ascent;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMunderset) {
|
2022-06-09 05:52:22 +08:00
|
|
|
overallHeight=overallHeight1+overallHeight2+xheight/6.0;
|
2022-06-08 21:38:26 +08:00
|
|
|
baselineHeight=baselineHeight1;
|
2022-06-09 05:52:22 +08:00
|
|
|
width=qMax(width1, width2)+xwidth;
|
|
|
|
strikeoutPos=line_ascent;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMoverset) {
|
2022-06-09 05:52:22 +08:00
|
|
|
overallHeight=overallHeight1+overallHeight2+xheight/6.0;
|
|
|
|
baselineHeight=baselineHeight1+overallHeight2+xheight/6.0;
|
|
|
|
width=qMax(width1, width2)+xwidth;
|
|
|
|
strikeoutPos=line_ascent;
|
2022-06-08 21:38:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-09 05:52:22 +08:00
|
|
|
int JKQTMathTextFracNode::getNestingLevel(bool sameType) const
|
|
|
|
{
|
|
|
|
QList<const JKQTMathTextFracNode*> parents=getParents<JKQTMathTextFracNode>();
|
|
|
|
int cnt=0;
|
|
|
|
for (auto& p: parents) {
|
|
|
|
if (p && p->getMode()==getMode()) cnt++;
|
|
|
|
}
|
|
|
|
return cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
double JKQTMathTextFracNode::getFracScalingFactor() const
|
|
|
|
{
|
|
|
|
if (mode!=MTFMdfrac) {
|
|
|
|
const int level=getNestingLevel(true);
|
|
|
|
if (level>=1) return parentMathText->getFracNestedFactor();
|
|
|
|
}
|
|
|
|
return parentMathText->getFracFactor();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JKQTMathTextFracNode::isBraceParentNearerThanFrac() const
|
|
|
|
{
|
|
|
|
auto parents=getParents<JKQTMathTextNode>();
|
|
|
|
for (const auto& p: parents) {
|
|
|
|
if (p!=nullptr) {
|
|
|
|
if (dynamic_cast<const JKQTMathTextBraceNode*>(p)!=nullptr) return true;
|
|
|
|
if (dynamic_cast<const JKQTMathTextFracNode*>(p)!=nullptr) return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-06-08 21:38:26 +08:00
|
|
|
double JKQTMathTextFracNode::draw(QPainter& painter, double x, double y, JKQTMathTextEnvironment currentEv, const JKQTMathTextNodeSize* /*prevNodeSize*/) {
|
|
|
|
doDrawBoxes(painter, x, y, currentEv);
|
2022-06-09 17:32:57 +08:00
|
|
|
const QFont f=currentEv.getFont(parentMathText);
|
2022-06-09 05:52:22 +08:00
|
|
|
const QFontMetricsF fm(f, painter.device());
|
2022-06-08 21:38:26 +08:00
|
|
|
JKQTMathTextEnvironment ev1=currentEv;
|
|
|
|
JKQTMathTextEnvironment ev2=currentEv;
|
|
|
|
|
|
|
|
|
2022-06-09 17:32:57 +08:00
|
|
|
const double xheight=fm.xHeight();
|
|
|
|
const double xwidth=JKQTMathTextGetTightBoundingRect(f, "x", painter.device()).width();
|
|
|
|
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;
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
if (mode==MTFMunderbrace || mode==MTFMoverbrace) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev2.fontSize=ev2.fontSize*parentMathText->getUnderbraceFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMunderset || mode==MTFMoverset) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev2.fontSize=ev2.fontSize*parentMathText->getUndersetFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMfrac || mode==MTFMsfrac) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev1.fontSize=ev1.fontSize*getFracScalingFactor();
|
|
|
|
ev2.fontSize=ev2.fontSize*getFracScalingFactor();
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMtfrac || mode==MTFMstfrac) {
|
2022-06-09 05:52:22 +08:00
|
|
|
ev1.fontSize=ev1.fontSize*getFracScalingFactor()*0.7;
|
|
|
|
ev2.fontSize=ev2.fontSize*getFracScalingFactor()*0.7;
|
2022-06-08 21:38:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double width1=0, baselineHeight1=0, overallHeight1=0;//, strikeoutPos1=0;
|
|
|
|
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;
|
|
|
|
|
2022-06-09 17:32:57 +08:00
|
|
|
double yline=y-xheight*0.5;
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
|
|
|
|
//double overallHeight=overallHeight1+overallHeight2+xh;
|
|
|
|
//double baselineHeight=3.0*xh/2.0+overallHeight1;
|
2022-06-09 17:32:57 +08:00
|
|
|
const double maxWidth=qMax(width1, width2);
|
2022-06-09 05:52:22 +08:00
|
|
|
double deltaWidth=0;
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
QPen p=painter.pen();
|
|
|
|
p.setColor(ev1.color);
|
|
|
|
p.setStyle(Qt::SolidLine);
|
2022-06-09 17:32:57 +08:00
|
|
|
p.setWidthF(qMax(parentMathText->ABS_MIN_LINEWIDTH, linewideth));
|
2022-06-08 21:38:26 +08:00
|
|
|
painter.save(); auto __finalpaint=JKQTPFinally([&painter]() {painter.restore();});
|
|
|
|
painter.setPen(p);
|
|
|
|
if (mode==MTFMfrac || mode==MTFMdfrac || mode==MTFMtfrac) {
|
2022-06-09 17:32:57 +08:00
|
|
|
deltaWidth=xwidth/2.0;
|
2022-06-09 05:52:22 +08:00
|
|
|
const QLineF l(x+p.widthF(), yline, x+maxWidth+deltaWidth-p.widthF(), yline);
|
2022-06-08 21:38:26 +08:00
|
|
|
if (l.length()>0) painter.drawLine(l);
|
2022-06-09 17:32:57 +08:00
|
|
|
child1->draw(painter, x+deltaWidth/2.0+(maxWidth-width1)/2.0, yline-xheight*(parentMathText->getFracShiftFactor())-descent1, ev1);
|
|
|
|
child2->draw(painter, x+deltaWidth/2.0+(maxWidth-width2)/2.0, yline+xheight*(parentMathText->getFracShiftFactor())+ascent2, ev2);
|
2022-06-09 05:52:22 +08:00
|
|
|
} else if (mode==MTFMstackrel) {
|
2022-06-09 17:32:57 +08:00
|
|
|
child1->draw(painter, x+(maxWidth-width1)/2.0, yline-xheight*(parentMathText->getFracShiftFactor())-descent1, ev1);
|
|
|
|
child2->draw(painter, x+(maxWidth-width2)/2.0, yline+xheight*(parentMathText->getFracShiftFactor())+ascent2, ev2);
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMstfrac || mode==MTFMsfrac) {
|
2022-06-09 17:32:57 +08:00
|
|
|
deltaWidth=xwidth/2.0;
|
|
|
|
child1->draw(painter, x, yline, ev1);
|
|
|
|
child2->draw(painter, x+width1+deltaWidth, y, ev2);
|
|
|
|
const QLineF l(x+width1+deltaWidth, y-Mheight, x+width1, y+(qheight-xheight));
|
2022-06-08 21:38:26 +08:00
|
|
|
if (l.length()>0) painter.drawLine(l);
|
|
|
|
} else if (mode==MTFMunderset) {
|
2022-06-09 17:32:57 +08:00
|
|
|
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+xheight/6.0+ascent2, ev2);
|
|
|
|
deltaWidth=xwidth;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMunderbrace) {
|
|
|
|
double ybrace=y+descent1+bw/2.0;
|
2022-06-09 17:32:57 +08:00
|
|
|
const QPainterPath path=JKQTMathTextMakeHBracePath(x+xwidth/2.0+(width1)/2.0, ybrace, maxWidth, bw);
|
2022-06-08 21:38:26 +08:00
|
|
|
painter.drawPath(path);
|
|
|
|
|
2022-06-09 17:32:57 +08:00
|
|
|
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);
|
|
|
|
deltaWidth=xwidth;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMoverset) {
|
2022-06-09 17:32:57 +08:00
|
|
|
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;
|
2022-06-08 21:38:26 +08:00
|
|
|
} else if (mode==MTFMoverbrace) {
|
2022-06-09 05:52:22 +08:00
|
|
|
const double ybrace=y-ascent1-bw/2.0;
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
{
|
|
|
|
painter.save(); auto __finalpaintinner=JKQTPFinally([&painter]() {painter.restore();});
|
2022-06-09 17:32:57 +08:00
|
|
|
painter.translate(x+xwidth/2.0+(width1)/2.0, ybrace);
|
2022-06-08 21:38:26 +08:00
|
|
|
painter.rotate(180);
|
2022-06-09 05:52:22 +08:00
|
|
|
const QPainterPath path=JKQTMathTextMakeHBracePath(0,0, maxWidth, bw);
|
2022-06-08 21:38:26 +08:00
|
|
|
painter.drawPath(path);
|
|
|
|
}
|
|
|
|
|
2022-06-09 17:32:57 +08:00
|
|
|
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);
|
|
|
|
deltaWidth=xwidth;
|
2022-06-08 21:38:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-06-09 05:52:22 +08:00
|
|
|
if (mode==MTFMstfrac || mode==MTFMsfrac) return x+width1+width2+deltaWidth;
|
|
|
|
else return x+maxWidth+deltaWidth;
|
2022-06-08 21:38:26 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool JKQTMathTextFracNode::toHtml(QString &/*html*/, JKQTMathTextEnvironment /*currentEv*/, JKQTMathTextEnvironment /*defaultEv*/) {
|
|
|
|
bool ok=false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
JKQTMathTextFracMode JKQTMathTextFracNode::getMode() const {
|
|
|
|
return this->mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|