[TechDraw] Issue #5903 - Autofill template information
This commit is contained in:
@@ -132,10 +132,10 @@ QString DrawSVGTemplate::processTemplate()
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&substitutions, &templateDocument](QDomElement& tspan) -> bool {
|
||||
// Replace the editable text spans with new nodes holding actual values
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
std::map<std::string, std::string>::iterator item =
|
||||
substitutions.find(editableName.toStdString());
|
||||
if (item != substitutions.end()) {
|
||||
@@ -296,15 +296,28 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
|
||||
query.processItems(QString::fromUtf8(
|
||||
"declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[this, &editables](QDomElement& tspan) -> bool {
|
||||
QDomElement parent = tspan.parentNode().toElement();
|
||||
QString editableName = parent.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
|
||||
editables[std::string(editableName.toUtf8().constData())] =
|
||||
std::string(editableValue.toUtf8().constData());
|
||||
return true;
|
||||
});
|
||||
QString editableValue;
|
||||
if (parent.hasAttribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL))) {
|
||||
QString autofillValue = getAutofillValue(parent.attribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL)));
|
||||
if (!autofillValue.isNull()) {
|
||||
editableValue = autofillValue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the autofill value is not specified or unsupported, use the default text value
|
||||
if (editableValue.isNull()) {
|
||||
editableValue = tspan.firstChild().nodeValue();
|
||||
}
|
||||
|
||||
editables[std::string(editableName.toUtf8().constData())] =
|
||||
std::string(editableValue.toUtf8().constData());
|
||||
return true;
|
||||
});
|
||||
|
||||
return editables;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,19 @@
|
||||
|
||||
#ifndef _PreComp_
|
||||
# include <sstream>
|
||||
# include <QCollator>
|
||||
# include <QDateTime>
|
||||
#endif
|
||||
|
||||
#include <Base/Console.h>
|
||||
|
||||
#include <App/Application.h>
|
||||
#include <App/Document.h>
|
||||
|
||||
#include "DrawTemplate.h"
|
||||
#include "DrawTemplatePy.h"
|
||||
#include "DrawPage.h"
|
||||
#include "DrawUtil.h"
|
||||
|
||||
|
||||
using namespace TechDraw;
|
||||
@@ -94,6 +100,67 @@ DrawPage* DrawTemplate::getParentPage() const
|
||||
return page;
|
||||
}
|
||||
|
||||
QString DrawTemplate::getAutofillValue(const QString &id) const
|
||||
{
|
||||
// author
|
||||
if (id.compare(QString::fromUtf8(Autofill::Author)) == 0) {
|
||||
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
|
||||
->GetGroup("Document")->GetASCII("prefAuthor");
|
||||
if (!value.empty()) {
|
||||
return QString::fromUtf8(value.c_str());
|
||||
}
|
||||
}
|
||||
// date
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Date)) == 0) {
|
||||
QDateTime date = QDateTime::currentDateTime();
|
||||
return date.toString(QLocale().dateFormat(QLocale::ShortFormat));
|
||||
}
|
||||
// organization
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Organization)) == 0) {
|
||||
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
|
||||
->GetGroup("Document")->GetASCII("prefCompany");
|
||||
if (!value.empty()) {
|
||||
return QString::fromUtf8(value.c_str());
|
||||
}
|
||||
}
|
||||
// scale
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Scale)) == 0) {
|
||||
DrawPage *page = getParentPage();
|
||||
if (page) {
|
||||
std::pair<int, int> scale = DrawUtil::nearestFraction(page->Scale.getValue());
|
||||
return QString::asprintf("%d : %d", scale.first, scale.second);
|
||||
}
|
||||
}
|
||||
// sheet
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Sheet)) == 0) {
|
||||
std::vector<DocumentObject *> pages = getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
|
||||
std::vector<QString> pageNames;
|
||||
for (auto page : pages) {
|
||||
pageNames.push_back(QString::fromUtf8(page->Label.getValue()));
|
||||
}
|
||||
|
||||
QCollator collator;
|
||||
std::sort(pageNames.begin(), pageNames.end(), collator);
|
||||
|
||||
int pos = 0;
|
||||
DrawPage *page = getParentPage();
|
||||
if (page) {
|
||||
auto it = std::find(pageNames.begin(), pageNames.end(), QString::fromUtf8(page->Label.getValue()));
|
||||
if (it != pageNames.end()) {
|
||||
pos = it - pageNames.begin() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return QString::asprintf("%d / %d", pos, (int) pageNames.size());
|
||||
}
|
||||
// title
|
||||
else if (id.compare(QString::fromUtf8(Autofill::Title)) == 0) {
|
||||
return QString::fromUtf8(getDocument()->Label.getValue());
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Python Template feature ---------------------------------------------------------
|
||||
|
||||
namespace App {
|
||||
|
||||
@@ -57,6 +57,8 @@ public:
|
||||
|
||||
virtual DrawPage* getParentPage() const;
|
||||
|
||||
virtual QString getAutofillValue(const QString &id) const;
|
||||
|
||||
/// returns the type name of the ViewProvider
|
||||
const char* getViewProviderName(void) const override{
|
||||
return "TechDrawGui::ViewProviderTemplate";
|
||||
@@ -65,6 +67,17 @@ public:
|
||||
// from base class
|
||||
PyObject *getPyObject(void) override;
|
||||
|
||||
class Autofill
|
||||
{
|
||||
public:
|
||||
static constexpr const char *Author = "author";
|
||||
static constexpr const char *Date = "date";
|
||||
static constexpr const char *Organization = "organization";
|
||||
static constexpr const char *Scale = "scale";
|
||||
static constexpr const char *Sheet = "sheet";
|
||||
static constexpr const char *Title = "title";
|
||||
};
|
||||
|
||||
private:
|
||||
static const char* OrientationEnums[];
|
||||
|
||||
|
||||
@@ -1312,6 +1312,80 @@ double DrawUtil::angleDifference(double fi1, double fi2, bool reflex)
|
||||
return fi1;
|
||||
}
|
||||
|
||||
std::pair<int, int> DrawUtil::nearestFraction(double val, int maxDenom)
|
||||
{
|
||||
// Find rational approximation to given real number
|
||||
// David Eppstein / UC Irvine / 8 Aug 1993
|
||||
//
|
||||
// With corrections from Arno Formella, May 2008
|
||||
// and additional fiddles by WF 2017
|
||||
// usage: a.out r d
|
||||
// r is real number to approx
|
||||
// d is the maximum denominator allowed
|
||||
//
|
||||
// Based on the theory of continued fractions
|
||||
// if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
||||
// then best approximation is found by truncating this series
|
||||
// (with some adjustments in the last term).
|
||||
//
|
||||
// Note the fraction can be recovered as the first column of the matrix
|
||||
// ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
||||
// ( 1 0 ) ( 1 0 ) ( 1 0 )
|
||||
// Instead of keeping the sequence of continued fraction terms,
|
||||
// we just keep the last partial product of these matrices.
|
||||
std::pair<int, int> result;
|
||||
long m[2][2];
|
||||
long maxden = maxDenom;
|
||||
long ai;
|
||||
double x = val;
|
||||
double startx = x;
|
||||
|
||||
/* initialize matrix */
|
||||
m[0][0] = m[1][1] = 1;
|
||||
m[0][1] = m[1][0] = 0;
|
||||
|
||||
/* loop finding terms until denom gets too big */
|
||||
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
|
||||
long t;
|
||||
t = m[0][0] * ai + m[0][1];
|
||||
m[0][1] = m[0][0];
|
||||
m[0][0] = t;
|
||||
t = m[1][0] * ai + m[1][1];
|
||||
m[1][1] = m[1][0];
|
||||
m[1][0] = t;
|
||||
if(x == (double) ai)
|
||||
break; // AF: division by zero
|
||||
x = 1/(x - (double) ai);
|
||||
if(x > (double) std::numeric_limits<int>::max())
|
||||
break; // AF: representation failure
|
||||
}
|
||||
|
||||
/* now remaining x is between 0 and 1/ai */
|
||||
/* approx as either 0 or 1/m where m is max that will fit in maxden */
|
||||
/* first try zero */
|
||||
double error1 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n1 = m[0][0];
|
||||
int d1 = m[1][0];
|
||||
|
||||
/* now try other possibility */
|
||||
ai = (maxden - m[1][1]) / m[1][0];
|
||||
m[0][0] = m[0][0] * ai + m[0][1];
|
||||
m[1][0] = m[1][0] * ai + m[1][1];
|
||||
double error2 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n2 = m[0][0];
|
||||
int d2 = m[1][0];
|
||||
|
||||
if (std::fabs(error1) <= std::fabs(error2)) {
|
||||
result.first = n1;
|
||||
result.second = d1;
|
||||
} else {
|
||||
result.first = n2;
|
||||
result.second = d2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Interval marking functions
|
||||
// ==========================
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
#define SVG_NS_URI "http://www.w3.org/2000/svg"
|
||||
#define FREECAD_SVG_NS_URI "https://www.freecad.org/wiki/index.php?title=Svg_Namespace"
|
||||
|
||||
#define FREECAD_ATTR_EDITABLE "freecad:editable"
|
||||
#define FREECAD_ATTR_AUTOFILL "freecad:autofill"
|
||||
|
||||
//some shapes are being passed in where edges that should be connected are in fact
|
||||
//separated by more than 2*Precision::Confusion (expected tolerance for 2 TopoDS_Vertex)
|
||||
//this value is used in EdgeWalker, DrawProjectSplit and DrawUtil and needs to be in sync in
|
||||
@@ -217,6 +220,7 @@ public:
|
||||
static void angleNormalize(double& fi);
|
||||
static double angleComposition(double fi, double delta);
|
||||
static double angleDifference(double fi1, double fi2, bool reflex = false);
|
||||
static std::pair<int, int> nearestFraction(double val, int maxDenom = 999);
|
||||
|
||||
// Interval marking functions
|
||||
static unsigned int intervalMerge(std::vector<std::pair<double, bool>>& marking,
|
||||
|
||||
@@ -125,7 +125,7 @@ std::vector<std::string> DrawViewSymbol::getEditableFields()
|
||||
// has "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&editables](QDomElement& tspan) -> bool {
|
||||
QString editableValue = tspan.firstChild().nodeValue();
|
||||
editables.emplace_back(editableValue.toStdString());
|
||||
@@ -154,7 +154,7 @@ void DrawViewSymbol::updateFieldsInSymbol()
|
||||
// has "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]/tspan"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
|
||||
[&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool {
|
||||
|
||||
if (count >= editText.size()) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <QDomDocument>
|
||||
#endif
|
||||
|
||||
#include "DrawUtil.h"
|
||||
#include "XMLQuery.h"
|
||||
|
||||
|
||||
@@ -51,7 +52,7 @@ static bool processElements(const QDomElement& element, const QString& queryStr,
|
||||
for(int i = 0; i < editable.count(); i++) {
|
||||
QDomNode node = editable.item(i);
|
||||
QDomElement element = node.toElement();
|
||||
if (element.hasAttribute(QString(QLatin1String("freecad:editable")))) {
|
||||
if (element.hasAttribute(QString(QLatin1String(FREECAD_ATTR_EDITABLE)))) {
|
||||
if (find_tspan) {
|
||||
element = element.firstChildElement();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <Mod/TechDraw/App/DrawPage.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroup.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
#include <Mod/TechDraw/App/DrawSVGTemplate.h>
|
||||
#include <Mod/TechDraw/App/DrawViewArch.h>
|
||||
#include <Mod/TechDraw/App/DrawViewClip.h>
|
||||
#include <Mod/TechDraw/App/DrawViewDetail.h>
|
||||
@@ -105,40 +106,33 @@ void CmdTechDrawPageDefault::activated(int iMsg)
|
||||
Q_UNUSED(iMsg);
|
||||
|
||||
QString templateFileName = Preferences::defaultTemplate();
|
||||
|
||||
std::string PageName = getUniqueObjectName("Page");
|
||||
std::string TemplateName = getUniqueObjectName("Template");
|
||||
|
||||
QFileInfo tfi(templateFileName);
|
||||
if (tfi.isReadable()) {
|
||||
Gui::WaitCursor wc;
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page"));
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')",
|
||||
PageName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')",
|
||||
PageName.c_str(), PageName.c_str());
|
||||
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')",
|
||||
TemplateName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')",
|
||||
TemplateName.c_str(), TemplateName.c_str());
|
||||
auto page = dynamic_cast<TechDraw::DrawPage *>
|
||||
(getDocument()->addObject("TechDraw::DrawPage", "Page"));
|
||||
if (!page) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault - page not created");
|
||||
}
|
||||
page->translateLabel("DrawPage", "Page", page->getNameInDocument());
|
||||
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = '%s'", TemplateName.c_str(),
|
||||
templateFileName.toStdString().c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s",
|
||||
PageName.c_str(), TemplateName.c_str());
|
||||
auto svgTemplate = dynamic_cast<TechDraw::DrawSVGTemplate *>
|
||||
(getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template"));
|
||||
if (!svgTemplate) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault - template not created");
|
||||
}
|
||||
svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument());
|
||||
|
||||
page->Template.setValue(svgTemplate);
|
||||
svgTemplate->Template.setValue(templateFileName.toStdString());
|
||||
|
||||
updateActive();
|
||||
commitCommand();
|
||||
TechDraw::DrawPage* fp =
|
||||
dynamic_cast<TechDraw::DrawPage*>(getDocument()->getObject(PageName.c_str()));
|
||||
if (!fp) {
|
||||
throw Base::TypeError("CmdTechDrawPageDefault fp not found\n");
|
||||
}
|
||||
|
||||
Gui::ViewProvider* vp =
|
||||
Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp);
|
||||
TechDrawGui::ViewProviderPage* dvp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
|
||||
TechDrawGui::ViewProviderPage *dvp = dynamic_cast<TechDrawGui::ViewProviderPage *>
|
||||
(Gui::Application::Instance->getViewProvider(page));
|
||||
if (dvp) {
|
||||
dvp->show();
|
||||
}
|
||||
@@ -182,44 +176,33 @@ void CmdTechDrawPageTemplate::activated(int iMsg)
|
||||
return;
|
||||
}
|
||||
|
||||
std::string PageName = getUniqueObjectName("Page");
|
||||
std::string TemplateName = getUniqueObjectName("Template");
|
||||
|
||||
QFileInfo tfi(templateFileName);
|
||||
if (tfi.isReadable()) {
|
||||
Gui::WaitCursor wc;
|
||||
openCommand(QT_TRANSLATE_NOOP("Command", "Drawing create page"));
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawPage', '%s')",
|
||||
PageName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawPage', 'Page', '%s')",
|
||||
PageName.c_str(), PageName.c_str());
|
||||
|
||||
// Create the Template Object to attach to the page
|
||||
doCommand(Doc, "App.activeDocument().addObject('TechDraw::DrawSVGTemplate', '%s')",
|
||||
TemplateName.c_str());
|
||||
doCommand(Doc, "App.activeDocument().%s.translateLabel('DrawSVGTemplate', 'Template', '%s')",
|
||||
TemplateName.c_str(), TemplateName.c_str());
|
||||
auto page = dynamic_cast<TechDraw::DrawPage *>
|
||||
(getDocument()->addObject("TechDraw::DrawPage", "Page"));
|
||||
if (!page) {
|
||||
throw Base::TypeError("CmdTechDrawPageTemplate - page not created");
|
||||
}
|
||||
page->translateLabel("DrawPage", "Page", page->getNameInDocument());
|
||||
|
||||
//why is "Template" property set twice? -wf
|
||||
// once to set DrawSVGTemplate.Template to OS template file name
|
||||
templateFileName = Base::Tools::escapeEncodeFilename(templateFileName);
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = \"%s\"", TemplateName.c_str(),
|
||||
templateFileName.toUtf8().constData());
|
||||
// once to set Page.Template to DrawSVGTemplate.Name
|
||||
doCommand(Doc, "App.activeDocument().%s.Template = App.activeDocument().%s",
|
||||
PageName.c_str(), TemplateName.c_str());
|
||||
// consider renaming DrawSVGTemplate.Template property?
|
||||
auto svgTemplate = dynamic_cast<TechDraw::DrawSVGTemplate *>
|
||||
(getDocument()->addObject("TechDraw::DrawSVGTemplate", "Template"));
|
||||
if (!svgTemplate) {
|
||||
throw Base::TypeError("CmdTechDrawPageTemplate - template not created");
|
||||
}
|
||||
svgTemplate->translateLabel("DrawSVGTemplate", "Template", svgTemplate->getNameInDocument());
|
||||
|
||||
page->Template.setValue(svgTemplate);
|
||||
svgTemplate->Template.setValue(templateFileName.toStdString());
|
||||
|
||||
updateActive();
|
||||
commitCommand();
|
||||
TechDraw::DrawPage* fp =
|
||||
dynamic_cast<TechDraw::DrawPage*>(getDocument()->getObject(PageName.c_str()));
|
||||
if (!fp) {
|
||||
throw Base::TypeError("CmdTechDrawNewPagePick fp not found\n");
|
||||
}
|
||||
Gui::ViewProvider* vp =
|
||||
Gui::Application::Instance->getDocument(getDocument())->getViewProvider(fp);
|
||||
TechDrawGui::ViewProviderPage* dvp = dynamic_cast<TechDrawGui::ViewProviderPage*>(vp);
|
||||
|
||||
TechDrawGui::ViewProviderPage *dvp = dynamic_cast<TechDrawGui::ViewProviderPage *>
|
||||
(Gui::Application::Instance->getViewProvider(page));
|
||||
if (dvp) {
|
||||
dvp->show();
|
||||
}
|
||||
|
||||
@@ -178,9 +178,9 @@ void QGISVGTemplate::createClickHandles()
|
||||
// XPath query to select all <text> nodes with "freecad:editable" attribute
|
||||
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
|
||||
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
|
||||
"//text[@freecad:editable]"),
|
||||
"//text[@" FREECAD_ATTR_EDITABLE "]"),
|
||||
[&](QDomElement& textElement) -> bool {
|
||||
QString name = textElement.attribute(QString::fromUtf8("freecad:editable"));
|
||||
QString name = textElement.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
|
||||
double x = Rez::guiX(
|
||||
textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble());
|
||||
double y = Rez::guiX(
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <Mod/TechDraw/App/DrawPage.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroupItem.h>
|
||||
#include <Mod/TechDraw/App/DrawProjGroup.h>
|
||||
#include <Mod/TechDraw/App/DrawUtil.h>
|
||||
|
||||
#include "TaskProjGroup.h"
|
||||
#include "ui_TaskProjGroup.h"
|
||||
@@ -306,81 +307,6 @@ void TaskProjGroup::spacingChanged()
|
||||
multiView->recomputeFeature();
|
||||
}
|
||||
|
||||
std::pair<int, int> TaskProjGroup::nearestFraction(const double val, const long int maxDenom) const
|
||||
{
|
||||
/*
|
||||
** find rational approximation to given real number
|
||||
** David Eppstein / UC Irvine / 8 Aug 1993
|
||||
**
|
||||
** With corrections from Arno Formella, May 2008
|
||||
** and additional fiddles by WF 2017
|
||||
** usage: a.out r d
|
||||
** r is real number to approx
|
||||
** d is the maximum denominator allowed
|
||||
**
|
||||
** based on the theory of continued fractions
|
||||
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
|
||||
** then best approximation is found by truncating this series
|
||||
** (with some adjustments in the last term).
|
||||
**
|
||||
** Note the fraction can be recovered as the first column of the matrix
|
||||
** ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
|
||||
** ( 1 0 ) ( 1 0 ) ( 1 0 )
|
||||
** Instead of keeping the sequence of continued fraction terms,
|
||||
** we just keep the last partial product of these matrices.
|
||||
*/
|
||||
std::pair<int, int> result;
|
||||
long m[2][2];
|
||||
long maxden = maxDenom;
|
||||
long ai;
|
||||
double x = val;
|
||||
double startx = x;
|
||||
|
||||
/* initialize matrix */
|
||||
m[0][0] = m[1][1] = 1;
|
||||
m[0][1] = m[1][0] = 0;
|
||||
|
||||
/* loop finding terms until denom gets too big */
|
||||
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
|
||||
long t;
|
||||
t = m[0][0] * ai + m[0][1];
|
||||
m[0][1] = m[0][0];
|
||||
m[0][0] = t;
|
||||
t = m[1][0] * ai + m[1][1];
|
||||
m[1][1] = m[1][0];
|
||||
m[1][0] = t;
|
||||
if(x == (double) ai)
|
||||
break; // AF: division by zero
|
||||
x = 1/(x - (double) ai);
|
||||
if(x > (double) std::numeric_limits<int>::max())
|
||||
break; // AF: representation failure
|
||||
}
|
||||
|
||||
/* now remaining x is between 0 and 1/ai */
|
||||
/* approx as either 0 or 1/m where m is max that will fit in maxden */
|
||||
/* first try zero */
|
||||
double error1 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n1 = m[0][0];
|
||||
int d1 = m[1][0];
|
||||
|
||||
/* now try other possibility */
|
||||
ai = (maxden - m[1][1]) / m[1][0];
|
||||
m[0][0] = m[0][0] * ai + m[0][1];
|
||||
m[1][0] = m[1][0] * ai + m[1][1];
|
||||
double error2 = startx - ((double) m[0][0] / (double) m[1][0]);
|
||||
int n2 = m[0][0];
|
||||
int d2 = m[1][0];
|
||||
|
||||
if (std::fabs(error1) <= std::fabs(error2)) {
|
||||
result.first = n1;
|
||||
result.second = d1;
|
||||
} else {
|
||||
result.first = n2;
|
||||
result.second = d2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TaskProjGroup::updateTask()
|
||||
{
|
||||
// Update the scale type
|
||||
@@ -398,7 +324,7 @@ void TaskProjGroup::setFractionalScale(double newScale)
|
||||
{
|
||||
blockUpdate = true;
|
||||
|
||||
std::pair<int, int> fraction = nearestFraction(newScale);
|
||||
std::pair<int, int> fraction = DrawUtil::nearestFraction(newScale);
|
||||
|
||||
ui->sbScaleNum->setValue(fraction.first);
|
||||
ui->sbScaleDen->setValue(fraction.second);
|
||||
|
||||
@@ -60,7 +60,6 @@ public:
|
||||
QPushButton* btnApply);
|
||||
|
||||
void updateTask();
|
||||
std::pair<int, int> nearestFraction(double val, long int maxDenom = 999) const;
|
||||
// Sets the numerator and denominator widgets to match newScale
|
||||
void setFractionalScale(double newScale);
|
||||
void setCreateMode(bool mode) { m_createMode = mode;}
|
||||
|
||||
@@ -192,13 +192,13 @@
|
||||
</g>
|
||||
</g>
|
||||
<g id="g3298" fill="#000000" font-family="Arial" letter-spacing="0px" word-spacing="0px">
|
||||
<text id="text3266" x="147.9312" y="160.67236" style="line-height:0%" freecad:editable="Designed_by_Name"><tspan id="tspan3268" x="147.9312" y="160.67236" font-size="3.95px" style="line-height:1.25">Designed by Name</tspan></text>
|
||||
<text id="text3270" x="147.93056" y="168.59135" style="line-height:0%" freecad:editable="FC-Date"><tspan id="tspan3272" x="147.93056" y="168.59135" font-size="3.95px" style="line-height:1.25">Date</tspan></text>
|
||||
<text id="text3274" x="154.46243" y="191.45177" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="FC-SC"><tspan id="tspan3276" x="154.46243" y="191.45177" font-size="3.95px" style="line-height:1.25">Scale</tspan></text>
|
||||
<text id="text3266" x="147.9312" y="160.67236" style="line-height:0%" freecad:editable="Designed_by_Name" freecad:autofill="author"><tspan id="tspan3268" x="147.9312" y="160.67236" font-size="3.95px" style="line-height:1.25">Designed by Name</tspan></text>
|
||||
<text id="text3270" x="147.93056" y="168.59135" style="line-height:0%" freecad:editable="FC-Date" freecad:autofill="date"><tspan id="tspan3272" x="147.93056" y="168.59135" font-size="3.95px" style="line-height:1.25">Date</tspan></text>
|
||||
<text id="text3274" x="154.46243" y="191.45177" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="FC-SC" freecad:autofill="scale"><tspan id="tspan3276" x="154.46243" y="191.45177" font-size="3.95px" style="line-height:1.25">Scale</tspan></text>
|
||||
<text id="text3278" x="173.94231" y="191.44733" text-align="center" text-anchor="middle" style="line-height:0%" freecad:editable="Weight"><tspan id="tspan3280" x="173.94231" y="191.44733" font-size="3.95px" style="line-height:1.25">Weight</tspan></text>
|
||||
<text id="text3282" x="186.05237" y="158.72597" style="line-height:0%" freecad:editable="FC-Title"><tspan id="tspan3284" x="186.05237" y="158.72597" font-size="5.6444px" style="line-height:1.25">Title</tspan></text>
|
||||
<text id="text3282" x="186.05237" y="158.72597" style="line-height:0%" freecad:editable="FC-Title" freecad:autofill="title"><tspan id="tspan3284" x="186.05237" y="158.72597" font-size="5.6444px" style="line-height:1.25">Title</tspan></text>
|
||||
<text id="text3286" x="185.99422" y="165.73558" style="line-height:0%" freecad:editable="Subtitle"><tspan id="tspan3288" x="185.99422" y="165.73558" font-size="3.9511px" style="line-height:1.25">Subtitle</tspan></text>
|
||||
<text id="text3290" x="185.6927" y="191.31752" style="line-height:0%" freecad:editable="Drawing_number"><tspan id="tspan3292" x="185.6927" y="191.31752" font-size="3.9511px" style="line-height:1.25">Drawing number</tspan></text>
|
||||
<text id="text3294" x="248.32477" y="191.45177" style="line-height:0%" freecad:editable="FC-SH"><tspan id="tspan3296" x="248.32477" y="191.45177" font-size="3.9511px" style="line-height:1.25">Sheet</tspan></text>
|
||||
<text id="text3294" x="248.32477" y="191.45177" style="line-height:0%" freecad:editable="FC-SH" freecad:autofill="sheet"><tspan id="tspan3296" x="248.32477" y="191.45177" font-size="3.9511px" style="line-height:1.25">Sheet</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Reference in New Issue
Block a user