#include "GeoFun.h" using namespace Upp; #define IMAGECLASS GFImg #define IMAGEFILE #include // When idraw = 1 it will create image at actual size based on radius values // Else image created to screen window diaplay size template void DrawCanvas::DoPaint0(T& sw, int idraw) { double x1, y1; int cnt1, cnt2; int RunTo; Sizef sz; if(idraw == 1) { sz = Sizef(2 * Radius1 + 20, 2 * Radius1 + 20); } else { sz = GetSize(); } sw.Clear(White()); x1 = sz.cx / 2; y1 = sz.cy / 2; // Show color fill. Draw bacground circle with color if(ColorFill) { if(ShowGradient) { sw.Ellipse(x1, y1, Radius1, Radius1).Fill(x1, y1, fcolorB1, Radius1, fcolorB2); } else { sw.Ellipse(x1, y1, Radius1, Radius1).Fill(fcolorB1); } } cnt1 = data.GetCount(); for(int i = 0; i < data.GetCount(); i++) { Vector& p = data[i].point; sw.Move(x1 + p[0].x, y1 + p[0].y); cnt2 = p.GetCount(); // If animation flag set do incremental display of curve // elase show all curve points. RunTo = ShowAnimation ? ShowTo : cnt2; if(RunTo > cnt2) RunTo = cnt2; for(int j = 1; j < RunTo; j++) sw.Line(x1 + p[j].x, y1 + p[j].y); sw.Stroke(data[i].penwidth, data[i].lcolor); sw.EvenOdd(true); if(ColorFill) { if(ShowGradient) { sw.Fill(x1, y1, data[i].fcolorM1, Radius1, data[i].fcolorM2, GRADIENT_REFLECT); } else { sw.Fill(data[i].fcolorM1); } } // Show if option animation set if(ShowAnimation) { sw.BeginOnPath(1.0); sw.DrawImage(0, -GFImg::Bee().GetHeight() / 2, GFImg::Bee()); sw.End(); sw.Ellipse(x1, y1, BaseR, BaseR).Stroke(3, Blue()); sw.Ellipse(x1 + data[i].CenR[RunTo - 2].x, y1 + data[i].CenR[RunTo - 2].y, RollR, RollR) .Stroke(3, LtBlue()); sw.Move(x1, y1); sw.Line(x1 + data[i].CenR[RunTo - 2].x, y1 + data[i].CenR[RunTo - 2].y); sw.Line(x1 + data[i].point[RunTo - 2].x, y1 + data[i].point[RunTo - 2].y); sw.Stroke(3, Red()); ColorFill = false; // this is required to stop colorfill if other options are // changed during animation } } } DrawCanvas::DrawCanvas() {} // at present this function is redundant but kept here to improve printing in future void DrawCanvas::DoPaint(Painter& sw) { bool painting = false; if(painting) { PaintingPainter h(4000, 4000); DoPaint0(h, 0); sw.Paint(h); } else DoPaint0(sw, 0); } void DrawCanvas::Paint(Draw& drw) { Size sz = GetSize(); ImageBuffer ib(sz); BufferPainter sw(ib); bool transparent = false; DoPaint(sw); drw.DrawImage(0, 0, ib); } Image GeoFun::GetImage() { Size sz = Size(int(dc1.Radius1 * 2 + 20), int(dc1.Radius1 * 2 + 20)); ImageBuffer ib(sz); BufferPainter bp(ib, MODE_ANTIALIASED); dc1.DoPaint0(bp, 1); return ib; } void GeoFun::SaveToImageFile(String fileName) { GuiLock __; if(IsNull(fileName)) { FileSel fs; fs.Type("PNG file", "*.png"); fs.Type("JPEG file", "*.jpg"); if(!fs.ExecuteSaveAs(t_("Saving plot to PNG or JPEG file"))) { Exclamation(t_("Plot has not been saved")); return; } fileName = fs; } if(GetFileExt(fileName) == ".png") { PNGEncoder encoder; encoder.SaveFile(fileName, GetImage()); } else if(GetFileExt(fileName) == ".jpg") { JPGEncoder encoder(90); encoder.SaveFile(fileName, GetImage()); } else Exclamation(Format(t_("File format \"%s\" not found"), GetFileExt(fileName))); } void GeoFun::SaveToAutoCadScript(String fileName) { if(IsNull(fileName)) { FileSel fs; fs.Type("SCR file", "*.scr"); if(!fs.ExecuteSaveAs(t_("Saving plot to Polyline -- AutoCAD Script "))) { Exclamation(t_("Plot has not been saved")); return; } fileName = fs; } if(GetFileExt(fileName) == ".scr") { String sScr; for(int i = 0; i < dc1.data.GetCount(); i++) { sScr << "PLINE\n"; Vector& p = dc1.data[i].point; for(int j = 0; j < p.GetCount(); j++) sScr << p[j].x << "," << p[j].y << "\n"; sScr << "\nZ\nE\n"; } FileOut fo; fo.Open(fileName); fo.Put(sScr); fo.Close(); } } void GeoFun::SaveToSVG(String fileName) { if(IsNull(fileName)) { FileSel fs; fs.Type("SVG file", "*.svg"); if(!fs.ExecuteSaveAs(t_("Saving as SVG file "))) { Exclamation(t_("SVG has not been saved")); return; } fileName = fs; } if(GetFileExt(fileName) == ".svg") { String sXML; sXML.Clear(); sXML << ""; sXML << ""; sXML << " \n \n"; for(int i = 0; i < dc1.data.GetCount(); i++) { sXML << "\n& p = dc1.data[i].point; for(int j = 0; j < p.GetCount(); j++) sXML << Format("%0.4f", p[j].x) << "," << Format("%0.4f", p[j].y) << " "; sXML << "\" style=\"fill:none;stroke:#00C000;stroke-width:0.4\" />\n"; } sXML << "\n"; FileOut fo; fo.Open(fileName); fo.Put(sXML); fo.Close(); } } void GeoFun::Print() { Report r; String sQtf; r.SetPageSize(4400, 4400); Size sz = Size(4200, 4200); Image im = GetImage(); qtfim = CreatePNGObject(im, Size(600, 600), sz); sQtf << "[s0;= "; sQtf << qtfim << " ]"; r << sQtf; Perform(r); } // This function calculates curve data points for display void GeoFun::PrepareData() { int p, q, pw; double a, b, h; double x, y; double Period, step, t; step = 1.0; // value in deg int q1 = InputPane.edValP; if(q1 > 0) { p = InputPane.edValP; } else { p = 1; InputPane.edValP.SetData(p); } q1 = InputPane.edValQ; if(q1 > 0) { q = InputPane.edValQ; } else { q = 1; InputPane.edValQ.SetData(q); } q1 = InputPane.edPenWidth; if(q1 > 0) { pw = InputPane.edPenWidth; } else { pw = 1; InputPane.edPenWidth.SetData(pw); } Period = 2 * M_PI * p; int iCrv = ~InputPane.CurveType; if((iCrv == 1) || (iCrv == 3)) { p = -p; } double tmp = InputPane.edBaseCircleRad; if(tmp < 10) { a = 10; InputPane.edBaseCircleRad.SetData(a); } else { a = InputPane.edBaseCircleRad; } b = (double)p * a / (double)q; dc1.BaseR = a; dc1.RollR = fabs(b); if(iCrv < 2) { h = fabs(b); } else { tmp = InputPane.edArmLength; if(tmp < 0) { h = 0; InputPane.edArmLength.SetData(h); } else { h = InputPane.edArmLength; } } dc1.Radius1 = fabs(a + b) + h; InputPane.RollingRad = "Rolling Radius : " + Format("%0.2f", b); dc1.Zoomed = InputPane.optZoom.GetData(); if(dc1.Zoomed) { Size sz = dc1.GetSize(); double dmax = sz.cx; if(sz.cy < dmax) dmax = sz.cy; dmax -= 10; double zScale = fabs(dmax / (2 * dc1.Radius1)); a = a * zScale; h = h * zScale; b = b * zScale; dc1.BaseR = a; dc1.RollR = fabs(b); dc1.Radius1 = fabs(a + b) + h; } dc1.data.Clear(); dc1.data.Add(); dc1.data[0].point.Clear(); dc1.data[0].CenR.Clear(); dc1.data[0].lcolor = ~InputPane.LineColor; dc1.data[0].fcolorM1 = ~InputPane.FillColorM1; dc1.data[0].fcolorM2 = ~InputPane.FillColorM2; dc1.data[0].penwidth = pw; dc1.fcolorB1 = ~InputPane.FillColorB1; dc1.fcolorB2 = ~InputPane.FillColorB2; dc1.ShowGradient = ~InputPane.optGradient; dc1.ColorFill = ~InputPane.optColorFill; int NumSteps = abs((int)(2.0 * (double)p * 180.0 / step)) + 1; for(int i = 0; i < NumSteps; i++) { t = (double)i / (double)NumSteps * Period; x = (a + b) * cos(t) + h * cos(((a + b) / b) * t); y = (a + b) * sin(t) + h * sin(((a + b) / b) * t); Pointf p1; p1.Clear(); p1.x = x; p1.y = y; dc1.data[0].point.Add(p1); p1.Clear(); x = (a + b) * cos(t); y = (a + b) * sin(t); p1.x = x; p1.y = y; dc1.data[0].CenR.Add(p1); } Pointf p1; p1.x = dc1.data[0].point[0].x; p1.y = dc1.data[0].point[0].y; dc1.data[0].point.Add(p1); dc1.Refresh(); } void GeoFun::ShowAnimated() { int MaxCnt = dc1.data[0].point.GetCount(); if(dc1.ShowAnimation) { if(dc1.ShowTo < MaxCnt) { dc1.Refresh(); dc1.ShowTo += dc1.AnimSpeed; } else { dc1.ShowAnimation = false; } } else { Animate.Kill(); PrepareData(); } } void GeoFun::SetAnimation() { dc1.AnimSpeed = InputPane.edAnimSpeed.GetData(); dc1.ShowTo = 5; dc1.ShowAnimation = dc1.ShowAnimation ? false : true; dc1.ColorFill = false; if(dc1.ShowAnimation) { InputPane.btnShowAnimation.SetLabel("Stop Animation"); } else { InputPane.btnShowAnimation.SetLabel("Start Animation"); } Animate.KillSet(-50, [=] { ShowAnimated(); }); } void GeoFun::FirstDraw() { First = false; PrepareData(); DUMP("First Draw"); } void GeoFun::Serialize(Stream& s) { s % InputPane.edArmLength % InputPane.edBaseCircleRad % InputPane.edPenWidth % InputPane.edValP % InputPane.edValQ % InputPane.LineColor % InputPane.FillColorM1 % InputPane.FillColorM2 % InputPane.FillColorB1 % InputPane.FillColorB2 % InputPane.optGradient % InputPane.optColorFill % InputPane.CurveType % InputPane.optZoom % InputPane.edAnimSpeed; SerializePlacement(s); } // Using layout to redraw on windows resize void GeoFun::Layout() { if(!First) { PrepareData(); } TopWindow::Layout(); } void GeoFun::About() { WithAboutLayout dlg; dlg.AboutText = GetTopic("topic://GeoFun/app/About$en-us").text; CtrlLayoutOK(dlg, "About"); dlg.CenterScreen(); dlg.Run(); } void GeoFun::Help() { HelpWindow helpme; helpme.GoTo("topic://GeoFun/app/Help$en-us"); helpme.Execute(); } void GeoFun::Exit() { Close(); } void GeoFun::SetAnimationSpeed() { dc1.AnimSpeed = InputPane.edAnimSpeed.GetData(); if(dc1.AnimSpeed < 1) dc1.AnimSpeed = 1; if(dc1.AnimSpeed > 20) dc1.AnimSpeed = 20; InputPane.edAnimSpeed.SetData(dc1.AnimSpeed); } void GeoFun::OptionColorFill() { int i = InputPane.optColorFill.Get(); i = i ? 0 : 1; InputPane.optColorFill.Set(i); PrepareData(); } void GeoFun::OptionGradient() { int i = InputPane.optGradient.Get(); i = i ? 0 : 1; InputPane.optGradient.Set(i); PrepareData(); } void GeoFun::OptionZoom() { int i = InputPane.optZoom.Get(); i = i ? 0 : 1; InputPane.optZoom.Set(i); PrepareData(); } void GeoFun::SetCurveType(int CrvType) { InputPane.CurveType.SetData(CrvType); SetCurveTypeMenu(CrvType); } void GeoFun::SetCurveTypeMenu(int CrvType) { EpiCyclo = HypoCyclo = EpiTroch = HypoTroch = false; switch(CrvType) { case 1: HypoCyclo = true; break; case 2: EpiTroch = true; break; case 3: HypoTroch = true; break; default: EpiCyclo = true; } PrepareData(); } // Menu Functions void GeoFun::SettingsMenu(Bar& bar) { bar.Add(t_("Color Fill"), [=] { OptionColorFill(); }) .Check(dc1.ColorFill) .Help(t_("Change settings")); bar.Add(t_("Gradient Fill"), [=] { OptionGradient(); }) .Check(dc1.ShowGradient) .Help(t_("Change settings")); bar.Add(t_("Zoom Fit"), [=] { OptionZoom(); }) .Check(dc1.Zoomed) .Help(t_("Zoom fit to window area")); } void GeoFun::FileMenu(Bar& bar) { bar.Add(t_("Save Image"), CtrlImg::save_as(), [=] { SaveToImageFile(""); }) .Help(t_("Save Image as PNG or JPG")); bar.Add(t_("Save AutoCAD Script"), CtrlImg::save(), [=] { SaveToAutoCadScript(""); }) .Help(t_("Save as AutoCAD Script (.scr) file")); bar.Add(t_("Save as SVG"), CtrlImg::save(), [=] { SaveToSVG(""); }) .Help(t_("Save as Scalable vector graphics (.svg) file")); bar.Separator(); bar.Add(t_("Print"), CtrlImg::print(), [=] { Print(); }) .Help(t_("Print the image , will fit to A4 page")); bar.Separator(); bar.Add(t_("Exit"), [=] { Exit(); }).Help(t_("Exit the application")); } void GeoFun::CurvesMenu(Bar& bar) { bar.Add(t_("Epicycloid"), [=] { SetCurveType(0); }) .Help(t_("Plot Epicycloid curve")) .Check(EpiCyclo); bar.Add(t_("Hypocycloid"), [=] { SetCurveType(1); }) .Help(t_("Plot Hypocycloid curve")) .Check(HypoCyclo); bar.Add(t_("Epitrochoid"), [=] { SetCurveType(2); }) .Help(t_("Plot Epitrochoid curve")) .Check(EpiTroch); bar.Add(t_("Hypotrochoid"), [=] { SetCurveType(3); }) .Help(t_("Plot Hypotrochoid curve")) .Check(HypoTroch); bar.Separator(); bar.Add(t_("Animated Plot"), [=] { SetAnimation(); }) .Help(t_("Show animation of the curve drawing")) .Check(AnimMn); } void GeoFun::MainMenu(Bar& bar) { bar.Sub(t_("File"), [=](Bar& bar) { FileMenu(bar); }); bar.Sub(t_("Curves"), [=](Bar& bar) { CurvesMenu(bar); }); bar.Sub(t_("Options"), [=](Bar& bar) { SettingsMenu(bar); }); bar.Sub(t_("Assist"), [=](Bar& bar) { HelpMenu(bar); }); } void GeoFun::HelpMenu(Bar& bar) { bar.Add(t_("About"), [=] { About(); }).Help(t_("About GeoFun")); bar.Separator(); bar.Add(t_("Help"), CtrlImg::help(), [=] { Help(); }).Help(t_("Help GeoFun")); } GeoFun::GeoFun() { CtrlLayout(*this, "GeoFun : Enjoy Geometric Curves"); Icon(GFImg::gficon()); CtrlLayout(InputPane); Sizeable().Zoomable(); InputPane.btnImg << [=] { SaveToImageFile(""); }; InputPane.btnPrint << [=] { Print(); }; InputPane.btnShowAnimation << [=] { SetAnimation(); }; Add(dc1.HSizePosZ(5, 160).VSizePosZ(25, 5)); Add(InputPane.RightPosZ(5, 150).VSizePosZ(2, 5)); BackPaint(); dc1.data.Clear(); // Check config file available else set default data if(!cfgAvailable) { InputPane.edArmLength.SetData(112); InputPane.edBaseCircleRad.SetData(145); InputPane.edPenWidth.SetData(3); InputPane.edValP.SetData(5); InputPane.edValQ.SetData(12); InputPane.edAnimSpeed.SetData(5); InputPane.LineColor.SetData(Blue()); InputPane.FillColorM1.SetData(LtGreen()); InputPane.FillColorM2.SetData(Yellow()); InputPane.FillColorB1.SetData(Blue()); InputPane.FillColorB2.SetData(LtCyan()); InputPane.optGradient.Set(1); InputPane.optColorFill.Set(1); InputPane.CurveType.SetData(2); } InputPane.CurveType.WhenAction << [=] { SetCurveTypeMenu((int)InputPane.CurveType.GetData()); }; InputPane.optGradient.WhenAction << [=] { PrepareData(); }; InputPane.optColorFill.WhenAction << [=] { PrepareData(); }; InputPane.FillColorB1.WhenAction << [=] { PrepareData(); }; InputPane.FillColorB2.WhenAction << [=] { PrepareData(); }; InputPane.FillColorM1.WhenAction << [=] { PrepareData(); }; InputPane.FillColorM2.WhenAction << [=] { PrepareData(); }; InputPane.LineColor.WhenAction << [=] { PrepareData(); }; InputPane.edPenWidth.WhenAction << [=] { PrepareData(); }; InputPane.edValP.WhenAction << [=] { PrepareData(); }; InputPane.edValQ.WhenAction << [=] { PrepareData(); }; InputPane.edArmLength.WhenAction << [=] { PrepareData(); }; InputPane.edBaseCircleRad.WhenAction << [=] { PrepareData(); }; InputPane.edAnimSpeed.WhenAction << [=] { SetAnimationSpeed(); }; InputPane.optZoom.WhenAction << [=] { PrepareData(); }; // Set timercallback to do first painting with calculations // You can not call this directly from constructor SetTimeCallback(200, [=] { FirstDraw(); }); dc1.ShowTo = 5; // start point of animation. menu.Set([=](Bar& bar) { MainMenu(bar); }); dc1.ShowAnimation = false; } GUI_APP_MAIN { GeoFun gf; gf.First = true; String cfgfile = ConfigFile(); if(FileExists(cfgfile)) { cfgAvailable = true; if(!LoadFromFile(gf, cfgfile)) { cfgAvailable = false; Exclamation("Error loading configuration file!"); } } else { cfgAvailable = false; } gf.Run(); StoreToFile(gf); }