diff --git a/compile.cmd b/compile.cmd index 03049dd..939e407 100644 --- a/compile.cmd +++ b/compile.cmd @@ -1,7 +1,15 @@ +@echo off + rem create sub-directories: md obj\inc\pugixml > nul 2> nul +rem delete old files: +del obj\inc\pugixml\*.o > nul 2> nul +del obj\*.o > nul 2> nul +del QET_ElementScaler.exe > nul 2> nul + rem compile with GCC in "path" +@echo on g++.exe -Wall -O2 -std=c++17 -c inc\pugixml\pugixml.cpp -o obj\inc\pugixml\pugixml.o g++.exe -Wall -O2 -std=c++17 -c main.cpp -o obj\main.o g++.exe -o QET_ElementScaler.exe obj\inc\pugixml\pugixml.o obj\main.o -s diff --git a/compile.sh b/compile.sh index 94f4f4e..7ba674d 100755 --- a/compile.sh +++ b/compile.sh @@ -3,6 +3,10 @@ # create sub-directories mkdir -p obj/inc/pugixml +# delete old files: +rm obj/inc/pugixml/*.o &> /dev/null +rm obj/*.o &> /dev/null +rm QET_ElementScaler # compile QET_ElementScaler g++ -Wall -O2 -std=c++17 -c inc/pugixml/pugixml.cpp -o obj/inc/pugixml/pugixml.o diff --git a/main.cpp b/main.cpp index 746248b..9a02df8 100644 --- a/main.cpp +++ b/main.cpp @@ -12,6 +12,11 @@ // g++ -Wall -O2 -std=c++17 -c main.cpp -o obj/main.o // g++ -o QET_ElementScaler obj/inc/pugixml/pugixml.o obj/main.o -s // +// clang works as well: +// clang++ -Weverything -O2 -std=c++17 -c inc/pugixml/pugixml.cpp -o obj/inc/pugixml/pugixml.o +// clang++ -Weverything -O2 -std=c++17 -c main.cpp -o obj/main.o +// clang++ -o QET_ElementScaler obj/inc/pugixml/pugixml.o obj/main.o -s +// // these commands work (TDM-GCC 10.3.0) on ReactOS to compile: // g++.exe -Wall -O2 -std=c++17 -c inc\pugixml\pugixml.cpp -o obj\inc\pugixml\pugixml.o // g++.exe -Wall -O2 -std=c++17 -c main.cpp -o obj\main.o @@ -33,6 +38,11 @@ // Result is a new file "FILENAME.SCALED.elmt" or output on stdout // in both cases without the XML declaration-line // +// Change(s) for 0.4beta8 +// - graphical element "arc": normalize "start" and "angle" to positive values +// - internal: replaced struct for element's min-max-values by own class +// - internal: renamed variables, translated comments, sorted headers +// // Change(s) for 0.4beta7 // - recalculate "hotspot_x", "hotspot_y", "width" and "height" of definition-line // @@ -61,7 +71,7 @@ // - added possibility to flip all Lines and Polygons // - added possibility to replace file with scaled one // -// Created from September to December 2022 +// Created from September 2022 to January 2023 // Author: plc-user // /~https://github.com/plc-user // @@ -70,7 +80,7 @@ // /* - * Copyright (c) 2022 plc-user + * Copyright (c) 2022-2023 plc-user * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -98,7 +108,7 @@ #include "inc/pugixml/pugixml.hpp" #include "main.h" -const string sVersion = "0.4beta7"; +const string sVersion = "0.4beta8"; const int _debug_ = 0; const int _debug_points_ = 0; @@ -143,7 +153,7 @@ string CheckForDoubleString(string &sArg){ /****************************************************************************/ string FormatValue(double &val, const size_t dec){ - size_t i = 0; // Laufindex + size_t i = 0; // index for loops double epsilon = 0.1; // too small values will be set to "0" for (i=0; i1) { - int Teiler = 1; - for (i=0; i 0) && (Rest < 5)) || ((Rest < 0) && (Rest > -5))) { - if (_debug_) cerr << "Teiler: " << Teiler << " - Rest: " << Rest - << " - subtrahiere " << Rest << endl; + if (_debug_) cerr << "Divider: " << Divider << " - Rest: " << Rest + << " - subtract " << Rest << endl; iVal -= Rest; } - if ((Rest > 0) && (Rest > (Teiler - 5))) { - if (_debug_) cerr << "Teiler: " << Teiler << " - Rest: " << Rest - << " - addiere " << (Teiler - Rest) << endl; - iVal += (Teiler - Rest); + if ((Rest > 0) && (Rest > (Divider - 5))) { + if (_debug_) cerr << "Divider: " << Divider << " - Rest: " << Rest + << " - add " << (Divider - Rest) << endl; + iVal += (Divider - Rest); } - if ((Rest < 0) && (Rest < (-1)*(Teiler - 5))) { - if (_debug_) cerr << "Teiler: " << Teiler << " - Rest: " << Rest - << " - subtrahiere " << (Teiler + Rest) << endl; - iVal -= (Teiler + Rest); + if ((Rest < 0) && (Rest < (-1)*(Divider - 5))) { + if (_debug_) cerr << "Divider: " << Divider << " - Rest: " << Rest + << " - subtract " << (Divider + Rest) << endl; + iVal -= (Divider + Rest); } } @@ -326,9 +336,9 @@ int parseCommandline(int argc, char *argv[]) { /*****************************************************************************/ int ScaleFontSize(string &sFont, double dFactor){ - // sFont an "," aufteilen in Teil-Strings - // das zweite Datum ist die Schriftgröße -> mit scaleX multiplizieren, - // dann zuerst runden und größer "0" lassen, danach zurückschreiben + // split sFont at "," + // the second date is the font-size -> multiply with scaleX, + // then first round and keep greater than "0", after that write back stringstream ss (sFont); int i = 0; int iSize = 0; @@ -355,15 +365,15 @@ int ScaleFontSize(string &sFont, double dFactor){ /****************************************************************************/ -/*** nun die XML-spezifischen Funktionen ***/ +/*** now the XML-specific functions ***/ /****************************************************************************/ /*****************************************************************************/ -int ReCalcDefinition(pugi::xml_node &node, XYMinMaxVals MiMaVals){ +int ReCalcDefinition(pugi::xml_node &node){ - int width = round(MiMaVals.xmax - MiMaVals.xmin); - int height = round(MiMaVals.ymax - MiMaVals.ymin); + int width = round(XYMinMax.width()); + int height = round(XYMinMax.height()); // calculation taken from QET-sources: int upwidth = ((width/10)*10)+10; @@ -375,8 +385,8 @@ int ReCalcDefinition(pugi::xml_node &node, XYMinMaxVals MiMaVals){ int xmargin = upwidth - width; int ymargin = upheight - height; - int hotspot_x = -(round(MiMaVals.xmin - (xmargin/2))); - int hotspot_y = -(round(MiMaVals.ymin - (ymargin/2))); + int hotspot_x = -(round(XYMinMax.xmin() - (xmargin/2))); + int hotspot_y = -(round(XYMinMax.ymin() - (ymargin/2))); // write new values to Definition-Line: for (pugi::xml_attribute attr: node.attributes()) @@ -457,18 +467,6 @@ int ScaleElement(pugi::xml_node &node){ node.attribute("height") = FormatValue(height, decimals).c_str(); } - if (sizeof(node.attribute("angle").name()) != 0) { - double angle = node.attribute("angle").as_double(); - // no scaling of angle-value, just rounding - node.attribute("angle") = FormatValue(angle, decimals).c_str(); - } - - if (sizeof(node.attribute("start").name()) != 0) { - double start = node.attribute("start").as_double(); - // no scaling of start-value, just rounding - node.attribute("start") = FormatValue(start, decimals).c_str(); - } - double posX = node.attribute("x").as_double(); posX *= scaleX; if (static_cast(node.name())=="terminal") { @@ -527,7 +525,7 @@ int PolyLineFlipHor(pugi::xml_node &node){ // only for elements "line" and "polygon" if (_debug_) cerr << "Flip horizontal: " << node.name() << endl; double posX; - // bei Punkten von "polygon" und "line" das Vorzeichen umkehren: + // for points of "polygon" and "line" invert the sign: for (pugi::xml_attribute attr: node.attributes()) { if (_debug_points_) cerr << " " << attr.as_double() << "=" << attr.value(); @@ -546,7 +544,7 @@ int PolyLineFlipVert(pugi::xml_node &node){ // only for elements "line" and "polygon" if (_debug_) cerr << "Flip vertical: " << node.name() << endl; double posY; - // bei Punkten von "polygon" und "line" das Vorzeichen umkehren: + // for points of "polygon" and "line" invert the sign: for (pugi::xml_attribute attr: node.attributes()) { if (_debug_points_) cerr << " " << attr.as_double() << "=" << attr.value(); @@ -589,8 +587,8 @@ int ArcFlipVert(pugi::xml_node &node){ double posY = (-1) * node.attribute("y").as_double() - node.attribute("height").as_double(); node.attribute("y") = FormatValue(posY, decimals).c_str(); double Start = (-1) * node.attribute("start").as_double(); - node.attribute("start") = FormatValue(Start, decimals).c_str(); double Angle = (-1) * node.attribute("angle").as_double(); + node.attribute("start") = FormatValue(Start, decimals).c_str(); node.attribute("angle") = FormatValue(Angle, decimals).c_str(); return 0; } @@ -601,8 +599,18 @@ int ArcFlipHor(pugi::xml_node &node){ double posX = (-1) * node.attribute("x").as_double() - node.attribute("width").as_double(); node.attribute("x") = FormatValue(posX, decimals).c_str(); double Start = 180 - node.attribute("start").as_double(); - node.attribute("start") = FormatValue(Start, decimals).c_str(); double Angle = (-1) * node.attribute("angle").as_double(); + node.attribute("start") = FormatValue(Start, decimals).c_str(); + node.attribute("angle") = FormatValue(Angle, decimals).c_str(); + return 0; +} +/*****************************************************************************/ +int NormalizeArc(pugi::xml_node &node){ + if (_debug_) cerr << "normalize angles of: " << node.name() << endl; + double Start = node.attribute("start").as_double(); + double Angle = node.attribute("angle").as_double(); + NormalizeArcVals(Start, Angle); + node.attribute("start") = FormatValue(Start, decimals).c_str(); node.attribute("angle") = FormatValue(Angle, decimals).c_str(); return 0; } @@ -702,34 +710,28 @@ int InputFlipHor(pugi::xml_node &node){ /*****************************************************************************/ -XYMinMaxVals DetermineMinMax(pugi::xml_node &node, XYMinMaxVals MiMaIn){ - XYMinMaxVals ValMinMax = MiMaIn; +void DetermineMinMax(pugi::xml_node &node){ // no need to make a difference, what node it is: non-existing attributes return "0.0" - ValMinMax.xmin = min( node.attribute("x").as_double(), ValMinMax.xmin); - ValMinMax.xmax = max( node.attribute("x").as_double(), ValMinMax.xmax); - ValMinMax.xmax = max((node.attribute("x").as_double() + node.attribute("width").as_double()), ValMinMax.xmax); - ValMinMax.xmax = max((node.attribute("x").as_double() + node.attribute("diameter").as_double()), ValMinMax.xmax); - ValMinMax.ymin = min( node.attribute("y").as_double(), ValMinMax.ymin); - ValMinMax.ymax = max( node.attribute("y").as_double(), ValMinMax.ymax); - ValMinMax.ymax = max((node.attribute("y").as_double() + node.attribute("height").as_double()), ValMinMax.ymax); - ValMinMax.ymax = max((node.attribute("y").as_double() + node.attribute("diameter").as_double()), ValMinMax.ymax); - + XYMinMax.addx(node.attribute("x").as_double()); + XYMinMax.addx(node.attribute("x").as_double() + node.attribute("width").as_double()); + XYMinMax.addx(node.attribute("x").as_double() + node.attribute("diameter").as_double()); + XYMinMax.addy(node.attribute("y").as_double()); + XYMinMax.addy(node.attribute("y").as_double() + node.attribute("height").as_double()); + XYMinMax.addy(node.attribute("y").as_double() + node.attribute("diameter").as_double()); // separate handling for lines and polygons: - if (((string(node.name())) == "line") || - ((string(node.name())) == "polygon")) { + if (((string(node.name())) == "line") || + ((string(node.name())) == "polygon")) { for (pugi::xml_attribute attr: node.attributes()) { if (attr.name()[0]=='x') { - ValMinMax.xmin = min(attr.as_double(), ValMinMax.xmin); - ValMinMax.xmax = max(attr.as_double(), ValMinMax.xmax); + XYMinMax.addx(attr.as_double()); } if (attr.name()[0]=='y') { - ValMinMax.ymin = min(attr.as_double(), ValMinMax.ymin); - ValMinMax.ymax = max(attr.as_double(), ValMinMax.ymax); + XYMinMax.addy(attr.as_double()); } } } - return ValMinMax; + return; } /*****************************************************************************/ @@ -800,7 +802,7 @@ int main(int argc, char *argv[]) { /*************************************************************************/ - /*** nun werden endlich die XML-Elemente bearbeitet ***/ + /*** now finally the XML elements are edited ***/ /*************************************************************************/ @@ -842,7 +844,6 @@ int main(int argc, char *argv[]) { // edit the graphical elements of the QET-Element: node = doc.child("definition").child("description").first_child(); - XYMinMaxVals ElmtMinMax; // to determine element's min-max-Values (X and Y) for (; node; node = node.next_sibling()) { if (((string(node.name())) == "line") || @@ -850,32 +851,33 @@ int main(int argc, char *argv[]) { ScalePoints(node); if (xFlipHor==true) { PolyLineFlipHor(node); } if (xFlipVert==true) { PolyLineFlipVert(node); } - ElmtMinMax = DetermineMinMax(node, ElmtMinMax); + DetermineMinMax(node); } if (((string(node.name())) == "ellipse") || ((string(node.name())) == "rect")) { ScaleElement(node); if (xFlipHor==true) { RectEllipseFlipHor(node); } if (xFlipVert==true) { RectEllipseFlipVert(node); } - ElmtMinMax = DetermineMinMax(node, ElmtMinMax); + DetermineMinMax(node); } if ((string(node.name())) == "circle") { ScaleElement(node); if (xFlipHor==true) { CircleFlipHor(node); } if (xFlipVert==true) { CircleFlipVert(node); } - ElmtMinMax = DetermineMinMax(node, ElmtMinMax); + DetermineMinMax(node); } if ((string(node.name())) == "arc") { ScaleElement(node); if (xFlipHor==true) { ArcFlipHor(node); } if (xFlipVert==true) { ArcFlipVert(node); } - ElmtMinMax = DetermineMinMax(node, ElmtMinMax); + NormalizeArc(node); + DetermineMinMax(node); } if ((string(node.name())) == "terminal") { ScaleElement(node); if (xFlipHor==true) { TerminalFlipHor(node); } if (xFlipVert==true) { TerminalFlipVert(node); } - ElmtMinMax = DetermineMinMax(node, ElmtMinMax); + DetermineMinMax(node); } if (((string(node.name())) == "dynamic_text") || ((string(node.name())) == "text")) { @@ -892,9 +894,14 @@ int main(int argc, char *argv[]) { } } + if (_debug_ ){ + cerr << "XYMinMax: " << XYMinMax << endl + << "Diagonal: " << XYMinMax.diagonal() << " Angle: " << XYMinMax.angle() << "°" << endl; + } + // calculate new values for Definition-Line: node = doc.child("definition"); - ReCalcDefinition(node, ElmtMinMax); + ReCalcDefinition(node); if (xPrintToStdOut==true){ diff --git a/main.h b/main.h index 8ace27d..4216c50 100644 --- a/main.h +++ b/main.h @@ -6,13 +6,13 @@ // // more information: see "main.cpp" // -// Created from September to December 2022 +// Created from September 2022 to January 2023 // Author: plc-user // /~https://github.com/plc-user // /* - * Copyright (c) 2022 plc-user + * Copyright (c) 2022-2023 plc-user * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -40,15 +40,202 @@ #define _QETscaler_MAIN_H #include // I/O -#include // write stringstream to file -#include // for "round" #include // for Commandline-Parameters -#include #include // for "double"-Check #include // for exe-filename -#include // für Typ-Konvertierung -#include // für das Ergebnis als std::string -#include // für Zufallszahlen der uuid +#include // for std::string - handling +#include // for Type-Convertion +#include // for random values of uuid + + +// +//--- definition of class "RectMinMax" ----------------------------------------- +// +class RectMinMax { + private: + double xMin = 0.0; + double xMax = 0.0; + double yMin = 0.0; + double yMax = 0.0; + public: + RectMinMax(); // default-constructor + RectMinMax(double, double); // constructor with value + RectMinMax(const RectMinMax&); // copy-constructor + void addx(double); // add a new x-value + void addy(double); // add a new y-value + void add(double, double); // add new values + void clear(void); // clear min/max and set to 0 + void clear(double, double); // clear min/max and set new values + void clear(const RectMinMax&); // clear with value + double xmin(void); // returns minimum x-value + double xmax(void); // returns maximum x-value + double ymin(void); // returns minimum y-value + double ymax(void); // returns maximum y-value + double width(void); // returns (xmax - xmin) + double height(void); // returns (ymax - ymin) + double diagonal(void); // returns length of diagonal + double angle(void); // returns angle of diagonal + friend std::ostream& operator << (std::ostream&, const RectMinMax&); // Standardoutput-operator +}; +// +//--- implementation of class "RectMinMax" ------------------------------------- +// +RectMinMax::RectMinMax(){ + xMin = 0.0; + xMax = 0.0; + yMin = 0.0; + yMax = 0.0; +} +RectMinMax::RectMinMax(double x, double y){ + xMin = x; + xMax = x; + yMin = y; + yMax = y; +} +RectMinMax::RectMinMax(const RectMinMax& r){ + xMin = r.xMin; + xMax = r.xMax; + yMin = r.yMin; + yMax = r.yMax; +} +void RectMinMax::addx(double x){ + if (x < xMin) { xMin = x; } + if (x > xMax) { xMax = x; } +} +void RectMinMax::addy(double y){ + if (y < yMin) { yMin = y; } + if (y > yMax) { yMax = y; } +} +void RectMinMax::add(double x, double y){ + if (x < xMin) { xMin = x; } + if (x > xMax) { xMax = x; } + if (y < yMin) { yMin = y; } + if (y > yMax) { yMax = y; } +} +// reset all values to ... +void RectMinMax::clear(void){ + xMin = 0.0; + xMax = 0.0; + yMin = 0.0; + yMax = 0.0; +} +void RectMinMax::clear(double x, double y){ + xMin = x; + xMax = x; + yMin = y; + yMax = y; +} +void RectMinMax::clear(const RectMinMax& r){ + xMin = r.xMin; + xMax = r.xMax; + yMin = r.yMin; + yMax = r.yMax; +} +// return values seperately +double RectMinMax::xmin(void){ + return xMin; +} +double RectMinMax::xmax(void){ + return xMax; +} +double RectMinMax::ymin(void){ + return yMin; +} +double RectMinMax::ymax(void){ + return yMax; +} +double RectMinMax::width(void){ + return (xMax - xMin); +} +double RectMinMax::height(void){ + return (yMax - yMin); +} +double RectMinMax::diagonal(void){ + double w = (xMax - xMin); + double h = (yMax - yMin); + return std::sqrt((w * w)+(h * h)); +} +double RectMinMax::angle(void){ + double dx = (xMax - xMin); + double dy = (yMax - yMin); + const double pi = 3.14159265359; + return (atan2(dy, dx) * 180 / pi); +} +// for outputting the whole bunch: +inline std::ostream& operator << (std::ostream& strm, const RectMinMax& r) +{ + strm << "x:(" << r.xMin << " ... " << r.xMax << ")" + << " | " + << "y:(" << r.yMin << " ... " << r.yMax << ")"; + return strm; +} +// +//--- END - implementation of class "RectMinMax" ------------------------------- +// + + +// +// --- function-prototypes for UUID-calculation -------------------------------- +// +unsigned int random_char(void); +std::string generate_hex(const unsigned int); +std::string CreateUUID(void); +std::string CreateUUID(bool); +// +// ############################################################ +// ### we build a (random?) UUID ### +// ############################################################ +// +unsigned int random_char(void) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 255); + return dis(gen); +} +// +std::string generate_hex(const unsigned int len) { + std::stringstream ss; + for (unsigned int i = 0; i < len; i++) { + const auto rc = random_char(); + std::stringstream hexstream; + hexstream << std::hex << rc; + auto hexa = hexstream.str(); + ss << (hexa.length() < 2 ? '0' + hexa : hexa); + } + return ss.str(); +} +// +std::string CreateUUID(void) { + std::string uuid = ""; + uuid = generate_hex(4); + uuid += '-' + generate_hex(2); + uuid += '-' + generate_hex(2); + uuid += '-' + generate_hex(2); + uuid += '-' + generate_hex(6); + return uuid; +} +// +std::string CreateUUID(bool UpCase){ + std::string uuid = CreateUUID(); + for (size_t i=0; i { - char do_decimal_point() const { return cDecSep; } // Dezimal-Trenner + char do_decimal_point() const { return cDecSep; } // Decimal-Separator }; @@ -106,8 +288,9 @@ string CheckForDoubleString(string &sArg); string FormatValue(double &val, const size_t decimals); int parseCommandline(int argc, char *argv[]); void PrintHelp(const string &s, const string &v); +void NormalizeArcVals(double &start, double &angle); int ScaleFontSize(string &sFont, double dFactor); -int ReCalcDefinition(pugi::xml_node &node, XYMinMaxVals MiMaVals); +int ReCalcDefinition(pugi::xml_node &node); int ScaleElement(pugi::xml_node &node); int ScalePoints(pugi::xml_node &node); // Mirror basic shapes on the coordinate axes @@ -125,19 +308,14 @@ int ArcFlipVert(pugi::xml_node &node); int ArcFlipHor(pugi::xml_node &node); int InputFlipVert(pugi::xml_node &node); int InputFlipHor(pugi::xml_node &node); - - -unsigned int random_char(); -string generate_hex(const unsigned int len); -string CreateUUID(void); -string CreateUUID(bool UpCase); +void DetermineMinMax(pugi::xml_node &node); //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -/*****************************************************************************/ +/******************************************************************************/ void PrintHelp(const string &s, const string &v){ stringstream ss; ss << filesystem::path(s).filename(); @@ -188,55 +366,33 @@ void PrintHelp(const string &s, const string &v){ << "As always with free software: Use it at your own risk! " << smilie << endl << endl; } -/*****************************************************************************/ - +/******************************************************************************/ -// ############################################################ -// ### wir bauen eine (zufällige?) UUID ### -// ############################################################ -// -unsigned int random_char() { - random_device rd; - mt19937 gen(rd()); - uniform_int_distribution<> dis(0, 255); - return dis(gen); -} -// -string generate_hex(const unsigned int len) { - stringstream ss; - for (unsigned int i = 0; i < len; i++) { - const auto rc = random_char(); - stringstream hexstream; - hexstream << hex << rc; - auto hex = hexstream.str(); - ss << (hex.length() < 2 ? '0' + hex : hex); +/******************************************************************************/ +void NormalizeArcVals(double &start, double &angle){ + int istart = round(std::fmod(start, 360.0)); + int iangle = round(std::fmod(angle, 360.0)); + if (istart < 0) { + // for example: + // old values: start = -20; angle = 330; + // new values: start = 340; angle = 330; + istart = (360 + istart) % 360; + iangle = iangle; } - return ss.str(); -} -// -string CreateUUID(void) { - string sRet = ""; - sRet = generate_hex(4); - sRet += '-' + generate_hex(2); - sRet += '-' + generate_hex(2); - sRet += '-' + generate_hex(2); - sRet += '-' + generate_hex(6); - return sRet; -} -// -string CreateUUID(bool UpCase){ - string uuid = CreateUUID(); - for (size_t i=0; i