From ff40ef3586bfa041e01d0d9e3ad176382f4ccfc5 Mon Sep 17 00:00:00 2001 From: Shai Seger Date: Wed, 22 May 2024 18:16:34 +0300 Subject: [PATCH] New Cam simulator based on low level OpenGL functions (faster and more precise) (#13884) * Initial opengl test window * added core files * some fixes for code comparability with other platforms * more compatibility cleanup * add missing opengl libraries * Basic simulation window works! * try using different define * fix wrapper for better compatibility * Gui is now operational * Finally SIM works on actual freecad models * support drill commands * cleanup python script and add tool profile generation * Now using actual tools specified in the operation. * support mouse wheel and freecad-style 3d navigation * Support accuracy gauge * remove endsimulation reference * show simulation speed indicator * apply clang-format * apply changes suggested by code review * gui items are loaded via QT resource system instead of hard coded * use vector instead of pointer to pass tool profile points * Fix some more formatting errors. --- src/Mod/CAM/CMakeLists.txt | 1 + src/Mod/CAM/Gui/Resources/Path.qrc | 13 + src/Mod/CAM/Gui/Resources/gl_simulator/0.png | Bin 0 -> 952 bytes src/Mod/CAM/Gui/Resources/gl_simulator/1.png | Bin 0 -> 456 bytes src/Mod/CAM/Gui/Resources/gl_simulator/4.png | Bin 0 -> 826 bytes .../CAM/Gui/Resources/gl_simulator/Faster.png | Bin 0 -> 980 bytes .../CAM/Gui/Resources/gl_simulator/Pause.png | Bin 0 -> 359 bytes .../CAM/Gui/Resources/gl_simulator/Play.png | Bin 0 -> 697 bytes .../CAM/Gui/Resources/gl_simulator/Rotate.png | Bin 0 -> 1281 bytes .../Gui/Resources/gl_simulator/SingleStep.png | Bin 0 -> 796 bytes .../CAM/Gui/Resources/gl_simulator/Slider.png | Bin 0 -> 263 bytes .../CAM/Gui/Resources/gl_simulator/Thumb.png | Bin 0 -> 573 bytes src/Mod/CAM/Gui/Resources/gl_simulator/X.png | Bin 0 -> 874 bytes .../Gui/Resources/icons/CAM_SimulatorGL.svg | 928 ++++++++++++++++++ .../Gui/Resources/panels/TaskCAMSimulator.ui | 154 +++ src/Mod/CAM/InitGui.py | 1 + src/Mod/CAM/Path/GuiInit.py | 1 + src/Mod/CAM/Path/Main/Gui/SimulatorGL.py | 309 ++++++ src/Mod/CAM/PathGlobal.h | 13 +- .../PathSimulator/AppGL/AppCAMSimulator.cpp | 85 ++ src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp | 72 ++ src/Mod/CAM/PathSimulator/AppGL/CAMSim.h | 77 ++ src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml | 61 ++ .../CAM/PathSimulator/AppGL/CAMSimPyImp.cpp | 128 +++ .../CAM/PathSimulator/AppGL/CMakeLists.txt | 103 ++ .../PathSimulator/AppGL/DlgCAMSimulator.cpp | 240 +++++ .../CAM/PathSimulator/AppGL/DlgCAMSimulator.h | 110 +++ src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp | 124 +++ src/Mod/CAM/PathSimulator/AppGL/EndMill.h | 62 ++ .../CAM/PathSimulator/AppGL/GCodeParser.cpp | 234 +++++ src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h | 59 ++ src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp | 56 ++ src/Mod/CAM/PathSimulator/AppGL/GlUtils.h | 53 + .../CAM/PathSimulator/AppGL/GuiDisplay.cpp | 243 +++++ src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h | 103 ++ src/Mod/CAM/PathSimulator/AppGL/MillMotion.h | 65 ++ .../PathSimulator/AppGL/MillPathSegment.cpp | 242 +++++ .../CAM/PathSimulator/AppGL/MillPathSegment.h | 98 ++ .../PathSimulator/AppGL/MillSimulation.cpp | 601 ++++++++++++ .../CAM/PathSimulator/AppGL/MillSimulation.h | 149 +++ .../CAM/PathSimulator/AppGL/OpenGlWrapper.h | 71 ++ .../CAM/PathSimulator/AppGL/PreCompiled.cpp | 23 + src/Mod/CAM/PathSimulator/AppGL/PreCompiled.h | 56 ++ src/Mod/CAM/PathSimulator/AppGL/Shader.cpp | 272 +++++ src/Mod/CAM/PathSimulator/AppGL/Shader.h | 77 ++ src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp | 403 ++++++++ src/Mod/CAM/PathSimulator/AppGL/SimShapes.h | 114 +++ .../CAM/PathSimulator/AppGL/StockObject.cpp | 64 ++ src/Mod/CAM/PathSimulator/AppGL/StockObject.h | 52 + src/Mod/CAM/PathSimulator/AppGL/Texture.cpp | 61 ++ src/Mod/CAM/PathSimulator/AppGL/Texture.h | 60 ++ .../CAM/PathSimulator/AppGL/TextureLoader.cpp | 98 ++ .../CAM/PathSimulator/AppGL/TextureLoader.h | 55 ++ src/Mod/CAM/PathSimulator/AppGL/linmath.h | 634 ++++++++++++ src/Mod/CAM/PathSimulator/CMakeLists.txt | 7 +- 55 files changed, 6427 insertions(+), 5 deletions(-) create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/0.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/1.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/4.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Faster.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Pause.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Play.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Rotate.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/SingleStep.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Slider.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/Thumb.png create mode 100644 src/Mod/CAM/Gui/Resources/gl_simulator/X.png create mode 100644 src/Mod/CAM/Gui/Resources/icons/CAM_SimulatorGL.svg create mode 100644 src/Mod/CAM/Gui/Resources/panels/TaskCAMSimulator.ui create mode 100644 src/Mod/CAM/Path/Main/Gui/SimulatorGL.py create mode 100644 src/Mod/CAM/PathSimulator/AppGL/AppCAMSimulator.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/CAMSim.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml create mode 100644 src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt create mode 100644 src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/EndMill.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GlUtils.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillMotion.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/PreCompiled.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/PreCompiled.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/Shader.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/Shader.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/SimShapes.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/StockObject.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/Texture.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/Texture.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp create mode 100644 src/Mod/CAM/PathSimulator/AppGL/TextureLoader.h create mode 100644 src/Mod/CAM/PathSimulator/AppGL/linmath.h diff --git a/src/Mod/CAM/CMakeLists.txt b/src/Mod/CAM/CMakeLists.txt index 1dacd878c8..1fe39d9e2e 100644 --- a/src/Mod/CAM/CMakeLists.txt +++ b/src/Mod/CAM/CMakeLists.txt @@ -96,6 +96,7 @@ SET(PathPythonMainGui_SRCS Path/Main/Gui/PreferencesJob.py Path/Main/Gui/SanityCmd.py Path/Main/Gui/Simulator.py + Path/Main/Gui/SimulatorGL.py ) SET(PathPythonMainSanity_SRCS diff --git a/src/Mod/CAM/Gui/Resources/Path.qrc b/src/Mod/CAM/Gui/Resources/Path.qrc index 1f57b53a6e..18c1ed1234 100644 --- a/src/Mod/CAM/Gui/Resources/Path.qrc +++ b/src/Mod/CAM/Gui/Resources/Path.qrc @@ -49,6 +49,7 @@ icons/CAM_Shape.svg icons/CAM_SimpleCopy.svg icons/CAM_Simulator.svg + icons/CAM_SimulatorGL.svg icons/CAM_Slot.svg icons/CAM_Stop.svg icons/CAM_ThreadMilling.svg @@ -121,7 +122,19 @@ panels/ToolBitSelector.ui panels/TaskPathCamoticsSim.ui panels/TaskPathSimulator.ui + panels/TaskCAMSimulator.ui panels/ZCorrectEdit.ui + gl_simulator/0.png + gl_simulator/1.png + gl_simulator/4.png + gl_simulator/X.png + gl_simulator/Faster.png + gl_simulator/Pause.png + gl_simulator/Play.png + gl_simulator/Rotate.png + gl_simulator/SingleStep.png + gl_simulator/Slider.png + gl_simulator/Thumb.png preferences/Advanced.ui preferences/PathDressupHoldingTags.ui preferences/PathJob.ui diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/0.png b/src/Mod/CAM/Gui/Resources/gl_simulator/0.png new file mode 100644 index 0000000000000000000000000000000000000000..4ce56804690e4e0218eced68499181555c09ab66 GIT binary patch literal 952 zcmV;p14sOcP)ecBAMY z;g4GpjDZbA-6T^dyAs9GjWy|piP2UxU-;f$BuCEqOpNTnfj94Y-Uq&O!g&uOLb(at z1V({v;1lp4@G~$B)K{#lAd3MhKq}1lz*pB20?z;&kj~CdSzcb2v$HeFWHPe2xG2$R zR7vx|(<_C*W1s-Y*w~mjjw9~jIF5{uk1J^b=q@V+8i2n5866$HByx}8;bA3x2l&eg ze*~nhtxfXzd{yE3`MHEbAtill2!T7mM?h9rSB=7aJ}>cjT;lP#*tTt~t*opl;W2Pa z7rq9hx3||Qux(rV`uZ+TcVJ-PlC-*jx;wnEBI^RTR;MVfFV3SK9;(=IsqKuKEQK8VzHP3?(Xg? zSr(`%%`6~$dwT|}ZObzYcnHwi+KN8b*49+GE+PdHDF7P)>+9R%hYPAn6;0u73mKFm_r_(BYr=|Y{NTpH+8wdncc-I1a0DiyUfHIkk z3J>)(KsK8-Sf9_QLazmQ0lZ$X0ck~gb+(GoXQ@d2ht%+RJQty!cCEQ<9Sb-D$mMbd zq-}w|7k<|ouG*XhkpvJ~V6@ICun*o}dnhbV-e_w@v zTfh#$&d!bjg~MSLc4+AcKzn<;!D_X?*C^rU=B5EfA`uln@_0No6b~c}(9zLhuv=SO zD*QuzMzzb~8{j)CTn@Fhwd!)Xr;dCJkh!@z1D>3m)UU4(fJ{$M8_dkijAD}defSEH zo}L~9wryMb`};30*jOwkg+jq#y1Kg5`U_on8#tj%3$Sfl5{ZN)5{YUppg~+s>IXnV zq0s+nQg5V8*Yi_qqa!0DRfR)CLsz#^aW(qE!NKb^dK-uu4*f?-`jp}`L!!~BEG;d` z>FKE)9v;fV!h%F15hXhZzQ0uOPL!kr6dy!aJ(ix7tu2=Hwjpz(CAQKqXsnM+ef*XNEd?>2 zK~zYI#g;!v8*voJKQGakWM~M4ATGtFK@h=R3N{2X*hQ#IC#4EnORZ!z)?g@e6IV<$R*9HUk0Cl_Fnog(n@bFMmsg$~0E(QJot(L)n9ry}p zJRaB6*5BV>?)B5NbvCZH0idn9xtY1DKkcqQ1VqlH1(eWM*dO z+C7c~p02Jg!r`zf5s5@NIy$lr7Lj(K*Wqwr7=}r-va&)dm8uz90ZVZTB>?;I@GyZu zpdvUvK4xTO#1a&su5^6>-1YnYM59rYAel_EwzhT^CnO>{;Df5J0@R6!3wQ}IIXQ{P z5hKSh>$+GAu7I1I7O_KR-{QPyl!c#ns;c3RK^P zD?TwXVS4pwGQq&Mh=?SCAXWA+AXLg-WEtQc@JUsF!pQ_dRW~ReM$Vat zxUT?zRMq#V8*165uVw!o{0m@bXUEjIy}exyv*#+bwj)+6aO`wCb#87>bGe+(&d#dC z;ZTaBDR8c)7H|n}P<XVhD~-e-wb>(m*2xPGw;0L z_aX`43*ZTG2|NVelZ49xJ_r5;o&rAuA2?*W1E^@70Pi|LJ_Yhcj5P3mP>IKY=H}*< zPN&u1-*1p7z#9P}Q9v^@Gdevz)!^WuG5!J?f=GCPJkOIPot>SjqoczhKLV8jASpnp zR7#SR$z;^k)n$wgpz2i;0_4Bq@wl3rnhY`*Kq3n$o6SCFBoYa=wzeAMp0k7`MWa#G z)YKSc#8pC)wzjrZTU%?4FP$YMX>oDU02!dmRYH=MmX?fh0o-(!kfhPkQG+CaR#yp0 z8XFrk#y;?tv&6;4g?f8?4H5%tT_r9rFBOSIjPWb*nv;Yi9UdO4t*y-XQE2ZGR|!d4Sy?eg z4*1wvLXsvYC+$i8w)$|9kff=pDP#N#Tz8hp<#PUx`gItITrQ{X?rvjz>ntG^Ua7U@ zFcOlqwzg)BN1-GR4h{_PUs#Fb<6~o_t`Xu&CX>%^!{mz@p(IvUSMBzhs;sQEPIp@&+V;lAhN`NnMBzMoM=ou9cXw9}4Gs1L-zmff7LUi( z*w|={yK>pUJkRrY$nSw`t`eC{MxC9V##pE5-1M|9x9W6Q6iB@ zsHLUF!ZYOrN=fYP?fDy0*|CAE|5ZmvN0OxT^K<{;@T9{`PykIVBse+p00009gP2NC6dn@pN$v@#uUzZDa0X0|D3h zF&)8`EY`nM7@Tzv2F?A}vDv|h%hS;>`;UoALV%D9i>Bv;hURzs-94`=a(Xi4A5i}= z`4wNx=bF`hGi0O~-d@RduQ~DLc52VWVn0K-qY|lWry8oY>r3a?oS0zeea>>~@u=C& z-<{%)GT1O2l?ZoyU(2W0x~J=9(By?zU+(HNc^@|~)NuLPm-chj@;^b*|J{EW4bFeg zl5jflHmGOS4B-#F2ELcIEc%l3_ICxE7OSpJyB5_d@MQ0G1D;pE;|>U0hbP0l+XkK_XL$L literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/Play.png b/src/Mod/CAM/Gui/Resources/gl_simulator/Play.png new file mode 100644 index 0000000000000000000000000000000000000000..ce1c9c1560c7b556cbbdfff2b54dadc0db03d4b5 GIT binary patch literal 697 zcmV;q0!ICbP)1H*B7!=DwQ-jIcY!-cnw?* z1(T$9yRDU#6-6QugZ=>Shk}{x?(S-Se%?OXcR)N8Od1queSKY{qoW3F0Z)NQ;Fw9h zUf0snlD*4Mz|BxFlhYI^Fii3-P#t&>3TCpiv!l7WIs0f|goa6j0%fyVMWayxN5I|C zFq2#^XLeV1{$TX`eZt7W!2#pr;{d z)55}nJuN-}Hx%R`?)7>~r_+kXVg_jd%Yfqt4UKJYZ)1SFY*_&)Q1e7)=dWQ f|L12dz)9;jXZ^g#wtgp=00000NkvXXu0mjfL9sP} literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/Rotate.png b/src/Mod/CAM/Gui/Resources/gl_simulator/Rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..69aaa951306b4fc62cc9b515fd76e73534ac4eb8 GIT binary patch literal 1281 zcmV+c1^)VpP)uaC_4i{JunH#aU4Y=5#7CeSA`-Li|O>~({dcgWMKukSTw^f@F4I6P(wq5 z#>U19f+cBia8R~w8}L)$G7u;R-(&!&V{Jky91d$?VWB8ok|L3afqxJ9N&%Z@=hVCs z_!fBi`0?Xhy?T}H+qV}ipNYoCM$?^=^m|EzHqHTH0MMR2do(*cThf=C7#SHc8JH;N z>(@(=P5qK>+nSh|C={s?%z96%OQ1AGU30eChVxCCfmV89hVK0a>Jo-Q}NS->vfmz)ya1YXNE z@Drfn;bB(;y}i9=;J_B(wZN}{nwpyA^ZAU{6Ch*^BmhlKO}QG_yLYb{)LbUq5Bvt` zz<~owCX-4e5^8U6&s{O^1IzCW4%%~0;&zwfHl(a~YL@0DSo3m_a0yFQG^!Po}M0T+Xm>d zEGt-yY+2TRpxbdAdV70a-x(PhG2Kys6M$M;T3qLVB%M2V&eZHr;LQTH-w#Xy>hJH* ztJmDzZ1he5R00c@WodG9(se*4lgV8^OTZw|3cLus1iZ!ChPDjo=+UEEUS4+9x^?T8 z(MbSa#dW~nfZE#HTz9c@ zV|scz7ybx%|x?Kj?SDpqdRx* zlnlOo`?gM;2qX} r-)e!KKq_nQaW>UI14ELQO40ovT}du(^6)Ak00000NkvXXu0mjfe#cXu literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/SingleStep.png b/src/Mod/CAM/Gui/Resources/gl_simulator/SingleStep.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5e48f91317841c6d83128d3e846085205ec013 GIT binary patch literal 796 zcmV+%1LOROP)`Q(+W_pKH<|Qc4#WGZd^_z^S0p5;dbz5q~IjbZCckC`gq~9U3x8p@TH5 zG~i@Abc!gbg{p(aK%tYAG$>helQyv^iOK8Gd^Oy}rbF*N@WFTa&ig)``^Py*LK47z z-~;dzC`}>-loHXF?z=hL5-vde{5^_d79yexd z4$(HCot+&?QngxDBoZ;XAAz1u&@!M>sni19+uJinS%>HwpiCxnoNL>*hK7br{)mj^C-0Vfz0DDe#f;AnAm9nKUaxCpWW;zqb?uophhd4N$R$xpRQ30<&$N$)!fM&AAmI+zEILZ5Ty>cN~q12f%Ain8G-k+%|wB@aeQb aN&E$c!)y?4#zJ8L0000xAkupA zbFyZLq(bU71G!ey1>G7NlXMO+^)%hrO5+Jus9GbkU+He}-cQ==;|?p|``FXmu|xd+ zk0Y4}B=v8U`*wA#{Z?^kUE;xot9l;aFxOW4w|14!Wft$lT(5VP^>%f3Z}G&AF+-6(70VbXoj@@lR_a8p!*m+UHx3v IIVCg!04Zl_1poj5 literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/Thumb.png b/src/Mod/CAM/Gui/Resources/gl_simulator/Thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..84fca01b5b3b3806116230da37567933338e721b GIT binary patch literal 573 zcmV-D0>b@?P)DW0mbEFGX6 z*@dg7!896;fXAQfpXvg*04{+41z-S-F@u?wW%;!9fFA(6-EJ^I%j>5Hd;!R^Y%qY< z+ATkWZJA*h4$?B~x|Z>H?E1lLcx=oMfTU@9HXx2;XMPVT*J!SRy;4fF)+d{1S=Js~ zUPx!~ngODek|arvN>^Ri6320S@Omo;M#q_@OBjZ-SS%#ZbE&H8nQ2v3vfXZFKA*R9 zz6ajXH#)XJSME%XjC+?B=^K7K5Cz--x4>7R>M^YB-mR^c-tqkxYupyxyC7Jv00000 LNkvXXu0mjf7nA$L literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/gl_simulator/X.png b/src/Mod/CAM/Gui/Resources/gl_simulator/X.png new file mode 100644 index 0000000000000000000000000000000000000000..c983441030552bb2ca26916bc3dbce71018f26c0 GIT binary patch literal 874 zcmV-w1C{)VP)FnDWKDIWlk6<#3{#V^HRBC8G9`giD^W&3QCD&?eG?*9LuU*A3VJP(op62M!a z0ek{p15YF|Y66ddUjg5E-vSA<$3x%}P~ZFu@J2vqfG-1l7r+C6mw;wwW)uX0PESv@ zyu55hO<-aG^l>N@`V44kX-Ow1CptSjQ#PBmz9QzY>*(l6-D7@!-ij&%z!gAqb8|hO zgM$O>`3UeQpv%k49#OqsS2P;6Laz(z0g6VWs@Lm1p7Zl_s}BI)0ovKwQU53wi&p3Z zVKaR|#bVLXZ*Ol~{T~270!pXTYPZ`x!d9!LR4Qe~c{7ztrPOM*dbI6!TT@e0R{H|r zKJWohxmqEe}JUt|+drBZQp<#O5TJ^=UaxjhFIkH>X&b>*0Hxm+ila=Dzd-)J-x zi^Z(wxg;z&1pE$Yb#>J-`o6DlIIM6uEZ_H?{gss!3;hiUb-)Jx6ljl)jp^|4&@t!p zd9AOnJL}bIRpaC10@^^j!&7g3mH|yqPdk8Sv#DmY>8xim8T+;69#$XtJNvwMcXu5~ z|JdK(x6msfJ``-=tAG-Tgs!izheB^|ZZt75VZkd7X8?E%I0RHE6o!H~Ha0A@3XIkhZQy}hAOJH9&H__hbKH4w}#PkL=_P5FG@Ss(eNxvM0ENt>})OpQjvF?ycYNt>SM zIs1KS^O<4(KZ$;8Ys>0R4f9S)(=v=GrlnG8XfZ97N{+t2nBH0WAK0+W&dz>w!%_m2 z&1O}r)pUG(tc8UI0Wxh=asar~Vl6H%%J+TMYPIgi@-zI~z+KpI>Y9HuBqbVn)@b^D zb>Mp|N&s)n54;Cn0S|^jyWkIjUySF%Fn*B4Kf+gJ3x+iV_y7O^07*qoM6N<$f<;t` A`Tzg` literal 0 HcmV?d00001 diff --git a/src/Mod/CAM/Gui/Resources/icons/CAM_SimulatorGL.svg b/src/Mod/CAM/Gui/Resources/icons/CAM_SimulatorGL.svg new file mode 100644 index 0000000000..fb0669f91d --- /dev/null +++ b/src/Mod/CAM/Gui/Resources/icons/CAM_SimulatorGL.svg @@ -0,0 +1,928 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + CAM_Machine + 2015-07-04 + https://www.freecad.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/CAM/Gui/Resources/icons/CAM_Machine.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Mod/CAM/Gui/Resources/panels/TaskCAMSimulator.ui b/src/Mod/CAM/Gui/Resources/panels/TaskCAMSimulator.ui new file mode 100644 index 0000000000..ab70e91cd7 --- /dev/null +++ b/src/Mod/CAM/Gui/Resources/panels/TaskCAMSimulator.ui @@ -0,0 +1,154 @@ + + + TaskPathSimulator + + + + 0 + 0 + 288 + 335 + + + + Path Simulator + + + + + + + 0 + 0 + + + + + 16777215 + 5 + + + + false + + + + + + + 0 + + + + + + 50 + 0 + + + + Accuracy: + + + + + + + 1 + + + 10 + + + 2 + + + 10 + + + Qt::Horizontal + + + + + + + + 50 + 0 + + + + + 40 + 16777215 + + + + % + + + + + + + + + + + + 24 + 16777215 + + + + Job: + + + + + + + + + + Activate / resume simulation + + + Play + + + + :/icons/CAM_BPlay.svg:/icons/CAM_BPlay.svg + + + + 32 + 32 + + + + + + + + + + QAbstractItemView::NoSelection + + + + + + + + + + + SimStop() + SimPlay() + SimPause() + SimStep() + SimFF() + + diff --git a/src/Mod/CAM/InitGui.py b/src/Mod/CAM/InitGui.py index 515ac7adec..7c6b36a123 100644 --- a/src/Mod/CAM/InitGui.py +++ b/src/Mod/CAM/InitGui.py @@ -97,6 +97,7 @@ class CAMWorkbench(Workbench): toolcmdlist = [ "CAM_Inspect", "CAM_Simulator", + "CAM_SimulatorGL", "CAM_SelectLoop", "CAM_OpActiveToggle", ] diff --git a/src/Mod/CAM/Path/GuiInit.py b/src/Mod/CAM/Path/GuiInit.py index 2655ef5818..533a0da7cd 100644 --- a/src/Mod/CAM/Path/GuiInit.py +++ b/src/Mod/CAM/Path/GuiInit.py @@ -53,6 +53,7 @@ def Startup(): from Path.Main.Gui import Fixture from Path.Main.Gui import Inspect from Path.Main.Gui import Simulator + from Path.Main.Gui import SimulatorGL from Path.Main.Sanity import Sanity diff --git a/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py b/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py new file mode 100644 index 0000000000..f3867148e3 --- /dev/null +++ b/src/Mod/CAM/Path/Main/Gui/SimulatorGL.py @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- +# *************************************************************************** +# * Copyright (c) 2017 Shai Seger * +# * * +# * This program 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 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * 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 Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import Path +import Path.Base.Util as PathUtil +import Path.Dressup.Utils as PathDressup +import PathScripts.PathUtils as PathUtils +import Path.Main.Job as PathJob +import PathGui +import CAMSimulator +import math +import os + +from FreeCAD import Vector, Base +from PySide2.QtWidgets import QDialogButtonBox + +# lazily loaded modules +from lazy_loader.lazy_loader import LazyLoader + +Mesh = LazyLoader("Mesh", globals(), "Mesh") +Part = LazyLoader("Part", globals(), "Part") + +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtGui, QtCore + +_filePath = os.path.dirname(os.path.abspath(__file__)) + +def IsSame(x, y): + return abs(x - y) < 0.0001 + +def RadiusAt(edge, p): + x = edge.valueAt(p).x + y = edge.valueAt(p).y + return math.sqrt(x * x + y * y) + + +class CAMSimTaskUi: + def __init__(self, parent): + # this will create a Qt widget from our ui file + self.form = FreeCADGui.PySideUic.loadUi(":/panels/TaskCAMSimulator.ui") + self.parent = parent + + def getStandardButtons(self, *args): + return QDialogButtonBox.Close + + def reject(self): + self.parent.cancel() + FreeCADGui.Control.closeDialog() + + +def TSError(msg): + QtGui.QMessageBox.information(None, "Path Simulation", msg) + + +class CAMSimulation: + def __init__(self): + self.debug = False + self.stdrot = FreeCAD.Rotation(Vector(0, 0, 1), 0) + self.iprogress = 0 + self.numCommands = 0 + self.simperiod = 20 + self.quality = 10 + self.resetSimulation = False + self.jobs = [] + + def Connect(self, but, sig): + QtCore.QObject.connect(but, QtCore.SIGNAL("clicked()"), sig) + + ## Convert tool shape to tool profile needed by GL simulator + def FindClosestEdge(self, edges, px, pz): + for edge in edges: + p1 = edge.FirstParameter + p2 = edge.LastParameter + rad = RadiusAt(edge, p1) + z = edge.valueAt(p1).z + if IsSame(px, rad) and IsSame(pz, z): + return edge, p1, p2 + rad = RadiusAt(edge, p2) + z = edge.valueAt(p2).z + if IsSame(px, rad) and IsSame(pz, z): + return edge, p2, p1 + return None, 0.0, 0.0 + + def FindTopMostEdge(self, edges): + maxz = 0.0 + topedge = None + top_p1 = 0.0 + top_p2 = 0.0 + for edge in edges: + p1 = edge.FirstParameter + p2 = edge.LastParameter + z = edge.valueAt(p1).z + if z > maxz: + topedge = edge + top_p1 = p1 + top_p2 = p2 + maxz = z + z = edge.valueAt(p2).z + if z > maxz: + topedge = edge + top_p1 = p2 + top_p2 = p1 + maxz = z + return topedge, top_p1, top_p2 + + #the algo is based on locating the side edge that OCC creates on any revolved object + def GetToolProfile(self, tool, resolution): + shape = tool.Shape + sideEdgeList = [] + for i in range(len(shape.Edges)): + edge = shape.Edges[i] + if not edge.isClosed(): + v1 = edge.firstVertex() + v2 = edge.lastVertex() + tp = "arc" if type(edge.Curve) is Part.Circle else "line" + sideEdgeList.append(edge) + + # sort edges as a single 3d line on the x-z plane + profile = [0.0, 0.0] + + # first find the topmost edge + edge, p1, p2 = self.FindTopMostEdge(sideEdgeList) + profile = [RadiusAt(edge, p1), edge.valueAt(p1).z] + endrad = 0.0 + # one by one find all connecting edges + while edge is not None: + sideEdgeList.remove(edge) + if type(edge.Curve) is Part.Circle: + # if edge is curved, aproximate it with lines based on resolution + nsegments = int(edge.Length / resolution) + 1 + step = (p2 - p1) / nsegments + location = p1 + step + print (edge.Length, nsegments, step) + while nsegments > 0: + endrad = RadiusAt(edge, location) + endz = edge.valueAt(location).z + profile.append(endrad) + profile.append(endz) + location += step + nsegments -= 1 + else: + endrad = RadiusAt(edge, p2) + endz = edge.valueAt(p2).z + profile.append(endrad) + profile.append(endz) + edge, p1, p2 = self.FindClosestEdge(sideEdgeList, endrad, endz) + if edge is None: + break + return profile + + def Activate(self): + self.initdone = False + self.taskForm = CAMSimTaskUi(self) + form = self.taskForm.form + self.Connect(form.toolButtonPlay, self.SimPlay) + form.sliderAccuracy.valueChanged.connect(self.onAccuracyBarChange) + self.onAccuracyBarChange() + self._populateJobSelection(form) + form.comboJobs.currentIndexChanged.connect(self.onJobChange) + self.onJobChange() + FreeCADGui.Control.showDialog(self.taskForm) + self.disableAnim = False + self.firstDrill = True + self.millSim = CAMSimulator.PathSim() + self.initdone = True + self.job = self.jobs[self.taskForm.form.comboJobs.currentIndex()] + self.SetupSimulation() + + def _populateJobSelection(self, form): + # Make Job selection combobox + setJobIdx = 0 + jobName = "" + jIdx = 0 + # Get list of Job objects in active document + jobList = FreeCAD.ActiveDocument.findObjects("Path::FeaturePython", "Job.*") + jCnt = len(jobList) + + # Check if user has selected a specific job for simulation + guiSelection = FreeCADGui.Selection.getSelectionEx() + if guiSelection: # Identify job selected by user + sel = guiSelection[0] + if hasattr(sel.Object, "Proxy") and isinstance( + sel.Object.Proxy, PathJob.ObjectJob + ): + jobName = sel.Object.Name + FreeCADGui.Selection.clearSelection() + + # populate the job selection combobox + form.comboJobs.blockSignals(True) + form.comboJobs.clear() + form.comboJobs.blockSignals(False) + for j in jobList: + form.comboJobs.addItem(j.ViewObject.Icon, j.Label) + self.jobs.append(j) + if j.Name == jobName or jCnt == 1: + setJobIdx = jIdx + jIdx += 1 + + # Pre-select GUI-selected job in the combobox + if jobName or jCnt == 1: + form.comboJobs.setCurrentIndex(setJobIdx) + else: + form.comboJobs.setCurrentIndex(0) + + def SetupSimulation(self): + form = self.taskForm.form + self.activeOps = [] + self.numCommands = 0 + self.ioperation = 0 + for i in range(form.listOperations.count()): + if form.listOperations.item(i).checkState() == QtCore.Qt.CheckState.Checked: + self.firstDrill = True + self.activeOps.append(self.operations[i]) + self.numCommands += len(self.operations[i].Path.Commands) + + self.stock = self.job.Stock.Shape + self.busy = False + + def onJobChange(self): + form = self.taskForm.form + j = self.jobs[form.comboJobs.currentIndex()] + self.job = j + form.listOperations.clear() + self.operations = [] + for op in j.Operations.OutList: + if PathUtil.opProperty(op, "Active"): + listItem = QtGui.QListWidgetItem(op.ViewObject.Icon, op.Label) + listItem.setFlags(listItem.flags() | QtCore.Qt.ItemIsUserCheckable) + listItem.setCheckState(QtCore.Qt.CheckState.Checked) + self.operations.append(op) + form.listOperations.addItem(listItem) + if self.initdone: + self.SetupSimulation() + + def onAccuracyBarChange(self): + form = self.taskForm.form + self.quality = form.sliderAccuracy.value() + qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "High") + if (self.quality < 4): + qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Low") + elif (self.quality < 9): + qualText = QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "Medium") + form.labelAccuracy.setText(qualText) + + def SimPlay(self): + self.millSim.ResetSimulation() + for op in self.activeOps: + tool = PathDressup.toolController(op).Tool + toolNumber = PathDressup.toolController(op).ToolNumber + toolProfile = self.GetToolProfile(tool, 0.5) + self.millSim.AddTool(toolProfile, toolNumber, tool.Diameter, 1) + opCommands = PathUtils.getPathWithPlacement(op).Commands + for cmd in opCommands: + self.millSim.AddCommand(cmd) + self.millSim.BeginSimulation(self.stock, self.quality) + + def cancel(self): + #self.EndSimulation() + pass + + +class CommandCAMSimulate: + def GetResources(self): + return { + "Pixmap": "CAM_SimulatorGL", + "MenuText": QtCore.QT_TRANSLATE_NOOP("CAM_Simulator", "New CAM Simulator"), + "Accel": "P, N", + "ToolTip": QtCore.QT_TRANSLATE_NOOP( + "CAM_Simulator", "Simulate G-code on stock" + ), + } + + def IsActive(self): + if FreeCAD.ActiveDocument is not None: + for o in FreeCAD.ActiveDocument.Objects: + if o.Name[:3] == "Job": + return True + return False + + def Activated(self): + CamSimulation = CAMSimulation() + CamSimulation.Activate() + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand("CAM_SimulatorGL", CommandCAMSimulate()) + FreeCAD.Console.PrintLog("Loading PathSimulator Gui... done\n") diff --git a/src/Mod/CAM/PathGlobal.h b/src/Mod/CAM/PathGlobal.h index 04e0505d95..22bd897467 100644 --- a/src/Mod/CAM/PathGlobal.h +++ b/src/Mod/CAM/PathGlobal.h @@ -47,9 +47,18 @@ // PathSimulator #ifndef PathSimulatorExport #ifdef PathSimulator_EXPORTS -# define PathSimulatorExport FREECAD_DECL_EXPORT +#define PathSimulatorExport FREECAD_DECL_EXPORT #else -# define PathSimulatorExport FREECAD_DECL_IMPORT +#define PathSimulatorExport FREECAD_DECL_IMPORT +#endif +#endif + +// CAMSimulator (new GL simulator) +#ifndef CAMSimulatorExport +#ifdef CAMSimulator_EXPORTS +#define CAMSimulatorExport FREECAD_DECL_EXPORT +#else +#define CAMSimulatorExport FREECAD_DECL_IMPORT #endif #endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/AppCAMSimulator.cpp b/src/Mod/CAM/PathSimulator/AppGL/AppCAMSimulator.cpp new file mode 100644 index 0000000000..8f9ce1d39e --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/AppCAMSimulator.cpp @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include +#include + +#include "CAMSim.h" +#include "CAMSimPy.h" + + +namespace CAMSimulator +{ +class Module: public Py::ExtensionModule +{ +public: + Module() + : Py::ExtensionModule("CAMSimulator") + { + initialize("This module is the CAMSimulator module."); // register with Python + } + + ~Module() override + {} + +private: +}; + +PyObject* initModule() +{ + return Base::Interpreter().addModule(new Module); +} + + +} // namespace CAMSimulator + + +/* Python entry */ +PyMOD_INIT_FUNC(CAMSimulator) +{ + // load dependent module + try { + Base::Interpreter().runString("import Part"); + Base::Interpreter().runString("import Path"); + Base::Interpreter().runString("import Mesh"); + } + catch (const Base::Exception& e) { + PyErr_SetString(PyExc_ImportError, e.what()); + PyMOD_Return(nullptr); + } + + // + PyObject* mod = CAMSimulator::initModule(); + Base::Console().Log("Loading CAMSimulator module.... done\n"); + + // Add Types to module + Base::Interpreter().addType(&CAMSimulator::CAMSimPy::Type, mod, "PathSim"); + + // NOTE: To finish the initialization of our own type objects we must + // call PyType_Ready, otherwise we run into a segmentation fault, later on. + // This function is responsible for adding inherited slots from a type's base class. + CAMSimulator::CAMSim::init(); + + PyMOD_Return(mod); +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp new file mode 100644 index 0000000000..24f1ff2f83 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" +#include "CAMSim.h" +#include "DlgCAMSimulator.h" +#include + + +using namespace Base; +using namespace CAMSimulator; + +TYPESYSTEM_SOURCE(CAMSimulator::CAMSim, Base::BaseClass); + +#define MAX_GCODE_LINE_LEN 120 + +CAMSim::CAMSim() +{} + +CAMSim::~CAMSim() +{} + +void CAMSim::BeginSimulation(Part::TopoShape* stock, float quality) +{ + Base::BoundBox3d bbox = stock->getBoundBox(); + SimStock stk = {(float)bbox.MinX, + (float)bbox.MinY, + (float)bbox.MinZ, + (float)bbox.LengthX(), + (float)bbox.LengthY(), + (float)bbox.LengthZ(), + quality}; + DlgCAMSimulator::GetInstance()->startSimulation(&stk, quality); +} + +void CAMSimulator::CAMSim::resetSimulation() +{ + DlgCAMSimulator::GetInstance()->resetSimulation(); +} + +void CAMSim::addTool(const std::vector toolProfilePoints, + int toolNumber, + float diameter, + float resolution) +{ + DlgCAMSimulator::GetInstance()->addTool(toolProfilePoints, toolNumber, diameter, resolution); +} + +void CAMSim::AddCommand(Command* cmd) +{ + std::string gline = cmd->toGCode(); + DlgCAMSimulator::GetInstance()->addGcodeCommand(gline.c_str()); +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h new file mode 100644 index 0000000000..9fd1d67ab3 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSim.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef CAMSimulator_CAMSim_H +#define CAMSimulator_CAMSim_H + +#include +#include + +#include +#include +#include +#include +#include + +#include "DlgCAMSimulator.h" + +using namespace Path; + +namespace CAMSimulator +{ + +/** The representation of a CNC Toolpath Simulator */ + +class CAMSimulatorExport CAMSim: public Base::BaseClass +{ + // TYPESYSTEM_HEADER(); + +public: + static Base::Type getClassTypeId(void); + virtual Base::Type getTypeId(void) const; + static void init(void); + static void* create(void); + +private: + static Base::Type classTypeId; + + +public: + CAMSim(); + ~CAMSim(); + + void BeginSimulation(Part::TopoShape* stock, float resolution); + void resetSimulation(); + void addTool(const std::vector toolProfilePoints, + int toolNumber, + float diameter, + float resolution); + void AddCommand(Command* cmd); + +public: + std::unique_ptr m_stock; +}; + +} // namespace CAMSimulator + + +#endif // CAMSimulator_CAMSim_H diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml new file mode 100644 index 0000000000..5010204ffe --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPy.xml @@ -0,0 +1,61 @@ + + + + + + + FreeCAD python wrapper of CAMSimulator + + CAMSimulator.CAMSim(): + + Create a path simulator object + + + + + + BeginSimulation(stock, resolution): + + Start a simulation process on a box shape stock with given resolution + + + + + + + ResetSimulation(): + + Clear the simulation and all gcode commands + + + + + + + SetToolShape(shape, toolnumber, diameter, resolution): + + Set the shape of the tool to be used for simulation + + + + + + + AddCommand(command): + + Add a path command to the simulation. + + + + + diff --git a/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp new file mode 100644 index 0000000000..7618fb13b1 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/CAMSimPyImp.cpp @@ -0,0 +1,128 @@ +/************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include +#include + +#include +#include +#include + +// inclusion of the generated files (generated out of CAMSimPy.xml) +#include "CAMSimPy.h" +#include "CAMSimPy.cpp" + + +using namespace CAMSimulator; + +// returns a string which represents the object e.g. when printed in python +std::string CAMSimPy::representation() const +{ + return std::string(""); +} + +PyObject* CAMSimPy::PyMake(struct _typeobject*, PyObject*, PyObject*) // Python wrapper +{ + // create a new instance of CAMSimPy and the Twin object + return new CAMSimPy(new CAMSim); +} + +// constructor method +int CAMSimPy::PyInit(PyObject* /*args*/, PyObject* /*kwd*/) +{ + return 0; +} + + +PyObject* CAMSimPy::ResetSimulation(PyObject* args) +{ + CAMSim* sim = getCAMSimPtr(); + sim->resetSimulation(); + Py_IncRef(Py_None); + return Py_None; +} + +PyObject* CAMSimPy::BeginSimulation(PyObject* args, PyObject* kwds) +{ + static const std::array kwlist {"stock", "resolution", nullptr}; + PyObject* pObjStock; + float resolution; + if (!Base::Wrapped_ParseTupleAndKeywords(args, kwds,"O!f", + kwlist, &(Part::TopoShapePy::Type), &pObjStock, &resolution)) { + return nullptr; + } + CAMSim* sim = getCAMSimPtr(); + Part::TopoShape* stock = static_cast(pObjStock)->getTopoShapePtr(); + sim->BeginSimulation(stock, resolution); + Py_IncRef(Py_None); + return Py_None; +} + +PyObject* CAMSimPy::AddTool(PyObject* args, PyObject* kwds) +{ + static const std::array kwlist { + "shape", "toolnumber", "diameter", "resolution", nullptr}; + PyObject* pObjToolShape; + int toolNumber; + float resolution; + float diameter; + if (!Base::Wrapped_ParseTupleAndKeywords(args, kwds, "Oiff", kwlist, &pObjToolShape, + &toolNumber, &diameter, &resolution)) { + return nullptr; + } + // The tool shape is defined by a list of 2d points that represents the tool revolving profile + Py_ssize_t num_floats = PyList_Size(pObjToolShape); + std::vector toolProfile; + for (Py_ssize_t i = 0; i < num_floats; ++i) { + PyObject* item = PyList_GetItem(pObjToolShape, i); + toolProfile.push_back(static_cast(PyFloat_AsDouble(item))); + } + + CAMSim* sim = getCAMSimPtr(); + sim->addTool(toolProfile, toolNumber, diameter, resolution); + + return Py_None; +} + +PyObject* CAMSimPy::AddCommand(PyObject* args) +{ + PyObject* pObjCmd; + if (!PyArg_ParseTuple(args, "O!", &(Path::CommandPy::Type), &pObjCmd)) { + return nullptr; + } + CAMSim* sim = getCAMSimPtr(); + Path::Command* cmd = static_cast(pObjCmd)->getCommandPtr(); + sim->AddCommand(cmd); + return Py_None; +} + +PyObject* CAMSimPy::getCustomAttributes(const char* /*attr*/) const +{ + return nullptr; +} + +int CAMSimPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) +{ + return 0; +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt b/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt new file mode 100644 index 0000000000..832db7ad15 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/CMakeLists.txt @@ -0,0 +1,103 @@ +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/src + ${CMAKE_BINARY_DIR}/src + ${CMAKE_CURRENT_BINARY_DIR} + ${Boost_INCLUDE_DIRS} + ${COIN3D_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR} + ${OCC_INCLUDE_DIR} + ${EIGEN3_INCLUDE_DIR} + ${PYTHON_INCLUDE_DIRS} + ${XercesC_INCLUDE_DIRS} + +) + +link_directories(${OCC_LIBRARY_DIR}) + +set(CAMSimulator_LIBS + Path + Part + Mesh + FreeCADApp + FreeCADGui + ${QtOpenGL_LIBRARIES} + ${OPENGL_gl_LIBRARY} +) + +SET(CAMSimulator_SRCS_Python + CAMSimPy.xml + CAMSimPyImp.cpp +) + + +SET(CAMSimulator_SRCS_Module + AppCAMSimulator.cpp + CAMSim.cpp + CAMSim.h + CAMSimPyImp.cpp + DlgCAMSimulator.cpp + DlgCAMSimulator.h + PreCompiled.cpp + PreCompiled.h +) + +SET(CAMSimulator_SRCS_Core + EndMill.cpp + EndMill.h + GCodeParser.cpp + GCodeParser.h + GlUtils.cpp + GlUtils.h + GuiDisplay.cpp + GuiDisplay.h + linmath.h + MillMotion.h + MillPathSegment.cpp + MillPathSegment.h + MillSimulation.cpp + MillSimulation.h + OpenGlWrapper.h + Shader.cpp + Shader.h + SimShapes.cpp + SimShapes.h + StockObject.cpp + StockObject.h + Texture.cpp + Texture.h + TextureLoader.cpp + TextureLoader.h +) + + +generate_from_xml(CAMSimPy) + + +SOURCE_GROUP("Module" FILES ${CAMSimulator_SRCS_Module}) +SOURCE_GROUP("Python" FILES ${CAMSimulator_SRCS_Python}) +SOURCE_GROUP("Core" FILES ${CAMSimulator_SRCS_Core}) + +SET(CAMSimulator_SRCS_precomp + ${CAMSimulator_SRCS_Module} + ${CAMSimulator_SRCS_Python} +) + +SET(CAMSimulator_SRCS + ${CAMSimulator_SRCS_precomp} + ${CAMSimulator_SRCS_Core} +) + +if(FREECAD_USE_PCH) + add_definitions(-D_PreComp_) + GET_MSVC_PRECOMPILED_SOURCE("PreCompiled.cpp" PCH_SRCS ${CAMSimulator_SRCS_precomp}) + ADD_MSVC_PRECOMPILED_HEADER(CAMSimulator PreCompiled.h PreCompiled.cpp PCH_SRCS) +endif(FREECAD_USE_PCH) + +add_library(CAMSimulator SHARED ${CAMSimulator_SRCS}) +target_link_libraries(CAMSimulator ${CAMSimulator_LIBS}) + +SET_BIN_DIR(CAMSimulator CAMSimulator /Mod/CAM) +SET_PYTHON_PREFIX_SUFFIX(CAMSimulator) + +INSTALL(TARGETS CAMSimulator DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp new file mode 100644 index 0000000000..4f3f0e2d66 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.cpp @@ -0,0 +1,240 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" + +#include "DlgCAMSimulator.h" +#include "MillSimulation.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace CAMSimulator; +using namespace MillSim; + +QOpenGLContext* gOpenGlContext; + +using namespace MillSim; + +namespace CAMSimulator +{ + +DlgCAMSimulator::DlgCAMSimulator(QWindow* parent) + : QWindow(parent) +{ + setSurfaceType(QWindow::OpenGLSurface); + mMillSimulator = new MillSimulation(); +} + +void DlgCAMSimulator::render(QPainter* painter) +{ + Q_UNUSED(painter); +} + +void DlgCAMSimulator::render() +{ + mMillSimulator->ProcessSim((unsigned int)(QDateTime::currentMSecsSinceEpoch())); +} + +void DlgCAMSimulator::renderLater() +{ + requestUpdate(); +} + +bool DlgCAMSimulator::event(QEvent* event) +{ + switch (event->type()) { + case QEvent::UpdateRequest: + renderNow(); + return true; + default: + return QWindow::event(event); + } +} + +void DlgCAMSimulator::exposeEvent(QExposeEvent* event) +{ + Q_UNUSED(event); + + if (isExposed()) { + renderNow(); + } +} + +void DlgCAMSimulator::mouseMoveEvent(QMouseEvent* ev) +{ + mMillSimulator->MouseMove(ev->x(), ev->y()); +} + +void DlgCAMSimulator::mousePressEvent(QMouseEvent* ev) +{ + mMillSimulator->MousePress(ev->button(), true, ev->x(), ev->y()); +} + +void DlgCAMSimulator::mouseReleaseEvent(QMouseEvent* ev) +{ + mMillSimulator->MousePress(ev->button(), false, ev->x(), ev->y()); +} + +void DlgCAMSimulator::wheelEvent(QWheelEvent* ev) +{ + mMillSimulator->MouseScroll((float)ev->angleDelta().y() / 120.0f); +} + +void DlgCAMSimulator::resetSimulation() +{ + mMillSimulator->Clear(); +} + +void DlgCAMSimulator::addGcodeCommand(const char* cmd) +{ + mMillSimulator->AddGcodeLine(cmd); +} + +void DlgCAMSimulator::addTool(const std::vector toolProfilePoints, + int toolNumber, + float diameter, + float resolution) +{ + std::string toolCmd = "T" + std::to_string(toolNumber); + mMillSimulator->AddGcodeLine(toolCmd.c_str()); + if (!mMillSimulator->ToolExists(toolNumber)) { + mMillSimulator->AddTool(toolProfilePoints, toolNumber, diameter); + } +} + +void DlgCAMSimulator::hideEvent(QHideEvent* ev) +{ + mAnimating = false; +} + +void DlgCAMSimulator::startSimulation(const SimStock* stock, float quality) +{ + mStock = *stock; + mQuality = quality; + mNeedsInitialize = true; + show(); + setAnimating(true); +} + +void DlgCAMSimulator::initialize() +{ + mMillSimulator + ->SetBoxStock(mStock.mPx, mStock.mPy, mStock.mPz, mStock.mLx, mStock.mLy, mStock.mLz); + mMillSimulator->InitSimulation(mQuality); + + const qreal retinaScale = devicePixelRatio(); + glViewport(0, 0, width() * retinaScale, height() * retinaScale); +} + +void DlgCAMSimulator::checkInitialization() +{ + if (!mContext) { + mContext = new QOpenGLContext(this); + mContext->setFormat(requestedFormat()); + mContext->create(); + gOpenGlContext = mContext; + mNeedsInitialize = true; + } + + mContext->makeCurrent(this); + + if (mNeedsInitialize) { + initializeOpenGLFunctions(); + initialize(); + mNeedsInitialize = false; + } +} + +void DlgCAMSimulator::renderNow() +{ + static unsigned int lastTime = 0; + static int frameCount = 0; + static int fps = 0; + if (!isExposed()) { + return; + } + + checkInitialization(); + + frameCount++; + unsigned int curtime = QDateTime::currentMSecsSinceEpoch(); + unsigned int timediff = curtime - lastTime; + if (timediff > 10000) { + fps = frameCount * 1000 / timediff; // for debug only. not used otherwise. + lastTime = curtime; + frameCount = 0; + } + render(); + mContext->swapBuffers(this); + + if (mAnimating) { + renderLater(); + } +} + +void DlgCAMSimulator::setAnimating(bool animating) +{ + mAnimating = animating; + + if (animating) { + renderLater(); + } +} + +DlgCAMSimulator* DlgCAMSimulator::GetInstance() +{ + if (mInstance == nullptr) { + QSurfaceFormat format; + format.setSamples(16); + format.setSwapInterval(2); + mInstance = new DlgCAMSimulator(); + mInstance->setFormat(format); + mInstance->resize(800, 600); + mInstance->setModality(Qt::ApplicationModal); + mInstance->show(); + } + return mInstance; +} + +DlgCAMSimulator* DlgCAMSimulator::mInstance = nullptr; + +//************************************************************************************************************ +// stock +//************************************************************************************************************ +SimStock::SimStock(float px, float py, float pz, float lx, float ly, float lz, float res) + : mPx(px) + , mPy(py) + , mPz(pz + 0.005 * lz) + , mLx(lx) + , mLy(ly) + , mLz(1.01 * lz) +{} + +SimStock::~SimStock() +{} + +} // namespace CAMSimulator \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h new file mode 100644 index 0000000000..d5c5bc5a65 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/DlgCAMSimulator.h @@ -0,0 +1,110 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef PATHSIMULATOR_CAMSimulatorGui_H +#define PATHSIMULATOR_CAMSimulatorGui_H + +// #include +// #include + +// #include +// #include +// #include +#include +#include +#include +#include + + +namespace MillSim +{ +class MillSimulation; // use short declaration as using 'include' causes a header loop +} + +namespace CAMSimulator +{ + +struct SimStock +{ +public: + SimStock(float px, float py, float pz, float lx, float ly, float lz, float res); + ~SimStock(); + +public: + float mPx, mPy, mPz; // stock zero position + float mLx, mLy, mLz; // stock dimensions +}; + +class DlgCAMSimulator: public QWindow, public QOpenGLExtraFunctions +{ + Q_OBJECT +public: + explicit DlgCAMSimulator(QWindow* parent = nullptr); + ~DlgCAMSimulator() + {} + + virtual void render(QPainter* painter); + virtual void render(); + virtual void initialize(); + + void setAnimating(bool animating); + static DlgCAMSimulator* GetInstance(); + +public: // slots: + void renderLater(); + void renderNow(); + void startSimulation(const SimStock* stock, float quality); + void resetSimulation(); + void addGcodeCommand(const char* cmd); + void addTool(const std::vector toolProfilePoints, + int toolNumber, + float diameter, + float resolution); + +protected: + bool event(QEvent* event) override; + + void checkInitialization(); + void exposeEvent(QExposeEvent* event) override; + void mouseMoveEvent(QMouseEvent* ev) override; + void mousePressEvent(QMouseEvent* ev) override; + void mouseReleaseEvent(QMouseEvent* ev) override; + void wheelEvent(QWheelEvent* ev) override; + void hideEvent(QHideEvent* ev) override; + +private: + bool mAnimating = false; + bool mNeedsInitialize = false; + + QOpenGLContext* mContext = nullptr; + QOpenGLPaintDevice* mDevice = nullptr; + MillSim::MillSimulation* mMillSimulator = nullptr; + static DlgCAMSimulator* mInstance; + SimStock mStock = {0, 0, 0, 1, 1, 1, 1}; + float mQuality = 10; +}; + + +} // namespace CAMSimulator + + +#endif // PATHSIMULATOR_PathSim_H diff --git a/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp b/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp new file mode 100644 index 0000000000..68f8f5a73b --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/EndMill.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "EndMill.h" +#include "OpenGlWrapper.h" +#include "SimShapes.h" + +using namespace MillSim; + +EndMill::EndMill(int toolid, float diameter) +{ + radius = diameter / 2; + toolId = toolid; +} + +EndMill::EndMill(const std::vector& toolProfile, int toolid, float diameter) + : EndMill(toolid, diameter) +{ + profilePoints = nullptr; + mHandleAllocation = false; + + int srcBuffSize = toolProfile.size(); + nPoints = srcBuffSize / 2; + if (nPoints < 2) { + return; + } + + // make sure last point is at 0,0 else, add it + bool missingCenterPoint = fabs(toolProfile[nPoints * 2 - 2]) > 0.0001f; + if (missingCenterPoint) { + nPoints++; + } + + int buffSize = PROFILE_BUFFER_SIZE(nPoints); + profilePoints = new float[buffSize]; + if (profilePoints == nullptr) { + return; + } + + // copy profile points + mHandleAllocation = true; + for (int i = 0; i < srcBuffSize; i++) { + profilePoints[i] = toolProfile[i] + 0.01f; // add some width to reduce simulation artifacts + } + if (missingCenterPoint) { + profilePoints[srcBuffSize] = profilePoints[srcBuffSize + 1] = 0.0f; + } + + MirrorPointBuffer(); +} + +EndMill::~EndMill() +{ + toolShape.FreeResources(); + halfToolShape.FreeResources(); + pathShape.FreeResources(); + if (mHandleAllocation) { + delete[] profilePoints; + } +} + +void EndMill::GenerateDisplayLists(float quality) +{ + // calculate number of slices based on quality. + int nslices = 16; + if (quality < 3) { + nslices = 4; + } + else if (quality < 7) { + nslices = 8; + } + + // full tool + toolShape.RotateProfile(profilePoints, nPoints, 0, 0, nslices, false); + + // half tool + halfToolShape.RotateProfile(profilePoints, nPoints, 0, 0, nslices / 2, true); + + // unit path + int nFullPoints = PROFILE_BUFFER_POINTS(nPoints); + pathShape.ExtrudeProfileLinear(profilePoints, nFullPoints, 0, 1, 0, 0, true, false); +} + +unsigned int +EndMill::GenerateArcSegmentDL(float radius, float angleRad, float zShift, Shape* retShape) +{ + int nFullPoints = PROFILE_BUFFER_POINTS(nPoints); + retShape->ExtrudeProfileRadial(profilePoints, + PROFILE_BUFFER_POINTS(nPoints), + radius, + angleRad, + zShift, + true, + true); + return 0; +} + +void EndMill::MirrorPointBuffer() +{ + int endpoint = PROFILE_BUFFER_POINTS(nPoints) - 1; + for (int i = 0, j = endpoint * 2; i < (nPoints - 1) * 2; i += 2, j -= 2) { + profilePoints[j] = -profilePoints[i]; + profilePoints[j + 1] = profilePoints[i + 1]; + } +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/EndMill.h b/src/Mod/CAM/PathSimulator/AppGL/EndMill.h new file mode 100644 index 0000000000..f60a2d81e4 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/EndMill.h @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __end_mill_h__ +#define __end_mill_h__ + +#include "SimShapes.h" +#include + +#define PROFILE_BUFFER_POINTS(npoints) ((npoints) * 2 - 1) +#define PROFILE_BUFFER_SIZE(npoints) (PROFILE_BUFFER_POINTS(npoints) * 2) +#define MILL_HEIGHT 10 + +namespace MillSim +{ +class EndMill +{ +public: + float* profilePoints = nullptr; + float radius; + int nPoints = 0; + int toolId = -1; + + Shape pathShape; + Shape halfToolShape; + Shape toolShape; + +public: + EndMill(int toolid, float diameter); + EndMill(const std::vector& toolProfile, int toolid, float diameter); + virtual ~EndMill(); + void GenerateDisplayLists(float quality); + unsigned int GenerateArcSegmentDL(float radius, float angleRad, float zShift, Shape* retShape); + +protected: + void MirrorPointBuffer(); + +private: + bool mHandleAllocation = false; +}; +} // namespace MillSim + +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp new file mode 100644 index 0000000000..3868147cce --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.cpp @@ -0,0 +1,234 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifdef _MSC_VER +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#endif + +#include "GCodeParser.h" +#include +#include + +using namespace MillSim; + +static char TokTypes[] = "GTXYZIJKR"; + +GCodeParser::~GCodeParser() +{ + // Clear the vector + Operations.clear(); +} + +bool GCodeParser::Parse(const char* filename) +{ + Operations.clear(); + lastState = {eNop, -1, 0, 0, 0, 0, 0, 0}; + lastTool = -1; + + FILE* fl; + if ((fl = fopen(filename, "rt")) == nullptr) { + return false; + } + + char line[120]; + + while (!feof(fl)) { + if (fgets(line, 120, fl) != NULL) { + AddLine(line); + } + } + fclose(fl); + return false; +} + +const char* GCodeParser::GetNextToken(const char* ptr, GCToken* token) +{ + float tokval; + token->letter = '*'; + while (*ptr != 0) { + char letter = toupper(*ptr); + ptr++; + + if (letter == ' ') { + continue; + } + if (letter == '(') { + break; + } + + if (IsValidToken(letter)) { + ptr = ParseFloat(ptr, &tokval); + token->letter = letter; + token->fval = tokval; + token->ival = (int)(tokval + 0.5); + break; + } + } + return ptr; +} + +bool GCodeParser::IsValidToken(char tok) +{ + int len = (int)strlen(TokTypes); + for (int i = 0; i < len; i++) { + if (tok == TokTypes[i]) { + return true; + } + } + return false; +} + +const char* GCodeParser::ParseFloat(const char* ptr, float* retFloat) +{ + float decPos = 10; + float sign = 1; + bool decimalPointFound = false; + float res = 0; + while (*ptr != 0) { + char letter = toupper(*ptr); + ptr++; + + if (letter == ' ') { + continue; + } + + if (letter == '-') { + sign = -1; + } + else if (letter == '.') { + decimalPointFound = true; + } + else if (letter >= '0' && letter <= '9') { + float digitVal = (float)(letter - '0'); + if (decimalPointFound) { + res = res + digitVal / decPos; + decPos *= 10; + } + else { + res = res * 10 + digitVal; + } + } + else { + ptr--; + break; + } + } + *retFloat = res * sign; + return ptr; +} + +bool GCodeParser::ParseLine(const char* ptr) +{ + GCToken token; + bool validMotion = false; + bool exitLoop = false; + int cmd = 0; + while (*ptr != 0 && !exitLoop) { + ptr = GetNextToken(ptr, &token); + lastLastState = lastState; + switch (token.letter) { + case '*': + exitLoop = true; + break; + + case 'G': + cmd = token.ival; + if (cmd == 0 || cmd == 1) { + lastState.cmd = eMoveLiner; + } + else if (cmd == 2) { + lastState.cmd = eRotateCW; + } + else if (cmd == 3) { + lastState.cmd = eRotateCCW; + } + else if (cmd == 73 || cmd == 81 || cmd == 82 || cmd == 83) { + lastState.cmd = eDril; + } + break; + + case 'T': + lastState.tool = token.ival; + break; + + case 'X': + lastState.x = token.fval; + validMotion = true; + break; + + case 'Y': + lastState.y = token.fval; + validMotion = true; + break; + + case 'Z': + lastState.z = token.fval; + validMotion = true; + break; + + case 'I': + lastState.i = token.fval; + break; + + case 'J': + lastState.j = token.fval; + break; + + case 'K': + lastState.k = token.fval; + break; + + case 'R': + lastState.r = token.fval; + break; + } + } + return validMotion; +} + +bool GCodeParser::AddLine(const char* ptr) +{ + bool res = ParseLine(ptr); + if (res) { + if (lastState.cmd == eDril) { + // split to several motions + lastState.cmd = eMoveLiner; + float rPlane = lastState.r; + float finalDepth = lastState.z; + lastState.z = rPlane; + Operations.push_back(lastState); + lastState.z = finalDepth; + Operations.push_back(lastState); + lastState.z = rPlane; + Operations.push_back(lastState); + // restore original state + lastState.z = finalDepth; + lastState.cmd = eDril; + } + else { + Operations.push_back(lastState); + } + } + return res; +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h new file mode 100644 index 0000000000..db494fb131 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GCodeParser.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __csgcodeparser_h__ +#define __csgcodeparser_h__ +#include "MillMotion.h" +#include + +namespace MillSim +{ +struct GCToken +{ + char letter; + float fval; + int ival; +}; + +class GCodeParser +{ +public: + GCodeParser() + {} + virtual ~GCodeParser(); + bool Parse(const char* filename); + bool AddLine(const char* ptr); + +public: + std::vector Operations; + MillMotion lastState = {eNop}; + MillMotion lastLastState = {eNop}; + +protected: + const char* GetNextToken(const char* ptr, GCToken* token); + bool IsValidToken(char tok); + const char* ParseFloat(const char* ptr, float* retFloat); + bool ParseLine(const char* ptr); + int lastTool = -1; +}; +} // namespace MillSim +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp new file mode 100644 index 0000000000..50f91983f4 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "GlUtils.h" +#include + +namespace MillSim +{ + +int gDebug = -1; + +mat4x4 identityMat = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}; + +void GLClearError() +{ + while (glGetError() != GL_NO_ERROR) + ; +} + +bool GLLogError() +{ + bool isError = false; + while (GLenum err = glGetError()) { + std::cout << "[Opengl Error] (" << err << ")" << std::endl; + isError = true; + } + return isError; +} + + +typedef struct Vertex +{ + vec3 pos; + vec3 col; +} Vertex; + +} // namespace MillSim diff --git a/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h new file mode 100644 index 0000000000..255664b760 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GlUtils.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __glutils_h__ +#define __glutils_h__ +#include "OpenGlWrapper.h" +#include "linmath.h" + +#define PI 3.14159265f +#define PI2 (PI * 2) + +constexpr auto EPSILON = 0.00001f; +#define EQ_FLOAT(x, y) (fabs((x) - (y)) < EPSILON) + +#define MS_MOUSE_LEFT 1 +#define MS_MOUSE_RIGHT 2 +#define MS_MOUSE_MID 4 +#define GL(x) \ + { \ + GLClearError(); \ + x; \ + if (GLLogError()) \ + __debugbreak(); \ + } +#define RadToDeg(x) (x * 180.0f / PI) + +namespace MillSim +{ +void GLClearError(); +bool GLLogError(); +extern mat4x4 identityMat; +extern int gDebug; +} // namespace MillSim +#endif // !__glutils_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp new file mode 100644 index 0000000000..aa730df6bd --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "GuiDisplay.h" +#include "OpenGlWrapper.h" +#include "MillSimulation.h" +#include +#include "GlUtils.h" +#include + +using namespace MillSim; + +GuiItem guiItems[] = { + {0, 0, 360, 554, 0}, + {0, 0, 448, 540, 1}, + {0, 0, 170, 540, 'P', true}, + {0, 0, 170, 540, 'S', false}, + {0, 0, 210, 540, 'T'}, + {0, 0, 250, 540, 'F'}, + {0, 0, 290, 540, ' '}, + {0, 0, 620, 540, 0, false, 0}, + {0, 0, 660, 540, 0, false, 0}, + {0, 0, 645, 540, 0, false, 0}, + {0, 0, 640, 540, 0, true, 0}, +}; + +#define NUM_GUI_ITEMS (sizeof(guiItems) / sizeof(GuiItem)) +#define TEX_SIZE 256 + +std::vector guiFileNames = {"Slider.png", + "Thumb.png", + "Pause.png", + "Play.png", + "SingleStep.png", + "Faster.png", + "Rotate.png", + "X.png", + "0.png", + "1.png", + "4.png"}; + +bool GuiDisplay::GenerateGlItem(GuiItem* guiItem) +{ + Vertex2D verts[4]; + int x = guiItem->texItem.tx; + int y = guiItem->texItem.ty; + int w = guiItem->texItem.w; + int h = guiItem->texItem.h; + + verts[0] = {0, (float)h, mTexture.getTexX(x), mTexture.getTexY(y + h)}; + verts[1] = {(float)w, (float)h, mTexture.getTexX(x + w), mTexture.getTexY(y + h)}; + verts[2] = {0, 0, mTexture.getTexX(x), mTexture.getTexY(y)}; + verts[3] = {(float)w, 0, mTexture.getTexX(x + w), mTexture.getTexY(y)}; + + // vertex buffer + glGenBuffers(1, &(guiItem->vbo)); + glBindBuffer(GL_ARRAY_BUFFER, guiItem->vbo); + glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(Vertex2D), verts, GL_STATIC_DRAW); + + // glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr); + // vertex array + glGenVertexArrays(1, &(guiItem->vao)); + glBindVertexArray(guiItem->vao); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*)offsetof(Vertex2D, x)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(Vertex2D), + (void*)offsetof(Vertex2D, tx)); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo); + glBindVertexArray(0); + + return true; +} + +bool GuiDisplay::InutGui() +{ + // index buffer + glGenBuffers(1, &mIbo); + GLshort indices[6] = {0, 2, 3, 0, 3, 1}; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), indices, GL_STATIC_DRAW); + TextureLoader tLoader(":/gl_simulator/", guiFileNames, TEX_SIZE); + unsigned int* buffer = tLoader.GetRawData(); + if (buffer == nullptr) { + return false; + } + mTexture.LoadImage(buffer, TEX_SIZE, TEX_SIZE); + for (int i = 0; i < NUM_GUI_ITEMS; i++) { + guiItems[i].texItem = *tLoader.GetTextureItem(i); + GenerateGlItem(&(guiItems[i])); + } + + mThumbStartX = guiItems[eGuiItemSlider].sx - guiItems[eGuiItemThumb].texItem.w / 2; + mThumbMaxMotion = (float)guiItems[eGuiItemSlider].texItem.w; + + UpdateSimSpeed(1); + + // shader + mat4x4 projmat; + // mat4x4 viewmat; + mat4x4_ortho(projmat, 0, 800, 600, 0, -1, 1); + mShader.CompileShader((char*)VertShader2DTex, (char*)FragShader2dTex); + mShader.UpdateTextureSlot(0); + mShader.UpdateProjectionMat(projmat); + return true; +} + +void GuiDisplay::RenderItem(int itemId) +{ + GuiItem* item = &(guiItems[itemId]); + if (item->hidden) { + return; + } + mat4x4 model; + mat4x4_translate(model, (float)item->sx, (float)item->sy, 0); + mShader.UpdateModelMat(model, nullptr); + if (itemId == mPressedItem) { + mShader.UpdateObjColor(mPressedColor); + } + else if (item->mouseOver) { + mShader.UpdateObjColor(mHighlightColor); + } + else if (itemId > 1 && item->actionKey == 0) { + mShader.UpdateObjColor(mTextColor); + } + else { + mShader.UpdateObjColor(mStdColor); + } + + glBindVertexArray(item->vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr); +} + +void GuiDisplay::MouseCursorPos(int x, int y) +{ + for (int i = 0; i < NUM_GUI_ITEMS; i++) { + GuiItem* g = &(guiItems[i]); + if (g->actionKey == 0) { + continue; + } + g->mouseOver = + (x > g->sx && y > g->sy && x < (g->sx + g->texItem.w) && y < (g->sy + g->texItem.h)); + } +} + +void GuiDisplay::MousePressed(int button, bool isPressed, bool isSimRunning) +{ + if (button == MS_MOUSE_LEFT) { + if (isPressed) { + mPressedItem = eGuiItemMax; + for (int i = 1; i < NUM_GUI_ITEMS; i++) { + GuiItem* g = &(guiItems[i]); + if (g->mouseOver && !g->hidden) { + mPressedItem = (eGuiItems)i; + break; + } + } + if (mPressedItem != eGuiItemMax) { + GuiItem* g = &(guiItems[mPressedItem]); + if (g->actionKey >= 32) { + mMillSim->HandleKeyPress(g->actionKey); + } + } + } + else // button released + { + UpdatePlayState(isSimRunning); + mPressedItem = eGuiItemMax; + } + } +} + +void GuiDisplay::MouseDrag(int buttons, int dx, int dy) +{ + if (mPressedItem == eGuiItemThumb) { + GuiItem* g = &(guiItems[eGuiItemThumb]); + int newx = g->sx + dx; + if (newx < mThumbStartX) { + newx = mThumbStartX; + } + if (newx > ((int)mThumbMaxMotion + mThumbStartX)) { + newx = (int)mThumbMaxMotion + mThumbStartX; + } + if (newx != g->sx) { + mMillSim->SetSimulationStage((float)(newx - mThumbStartX) / mThumbMaxMotion); + g->sx = newx; + } + } +} + +void GuiDisplay::UpdatePlayState(bool isRunning) +{ + guiItems[eGuiItemPause].hidden = !isRunning; + guiItems[eGuiItemPlay].hidden = isRunning; +} + +void MillSim::GuiDisplay::UpdateSimSpeed(int speed) +{ + guiItems[eGuiItemChar0Img].hidden = speed == 1; + guiItems[eGuiItemChar1Img].hidden = speed == 40; + guiItems[eGuiItemChar4Img].hidden = speed != 40; +} + +void GuiDisplay::Render(float progress) +{ + if (mPressedItem != eGuiItemThumb) { + guiItems[eGuiItemThumb].sx = (int)(mThumbMaxMotion * progress) + mThumbStartX; + } + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + mTexture.Activate(); + mShader.Activate(); + mShader.UpdateTextureSlot(0); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for (int i = 0; i < NUM_GUI_ITEMS; i++) { + RenderItem(i); + } +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h new file mode 100644 index 0000000000..0d9574e4d2 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/GuiDisplay.h @@ -0,0 +1,103 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __guidisplay_t__ +#define __guidisplay_t__ +#include "OpenGlWrapper.h" +#include "Texture.h" +#include "Shader.h" +#include "TextureLoader.h" + +namespace MillSim +{ +class MillSimulation; + +struct GuiItem +{ + unsigned int vbo, vao; + int sx, sy; // screen location + int actionKey; // action key when item pressed + bool hidden; // is item hidden + bool mouseOver; + TextureItem texItem; +}; + +struct Vertex2D +{ + float x, y; + float tx, ty; +}; + +enum eGuiItems +{ + eGuiItemSlider, + eGuiItemThumb, + eGuiItemPause, + eGuiItemPlay, + eGuiItemSingleStep, + eGuiItemFaster, + eGuiItemRotate, + eGuiItemCharXImg, + eGuiItemChar0Img, + eGuiItemChar1Img, + eGuiItemChar4Img, + eGuiItemMax +}; + + +class GuiDisplay +{ +public: + // GuiDisplay() {}; + bool InutGui(); + void Render(float progress); + void MouseCursorPos(int x, int y); + void MousePressed(int button, bool isPressed, bool isRunning); + void MouseDrag(int buttons, int dx, int dy); + void SetMillSimulator(MillSimulation* millSim) + { + mMillSim = millSim; + } + void UpdatePlayState(bool isRunning); + void UpdateSimSpeed(int speed); + +private: + bool GenerateGlItem(GuiItem* guiItem); + void RenderItem(int itemId); + + vec3 mStdColor = {0.8f, 0.8f, 0.4f}; + vec3 mHighlightColor = {1.0f, 1.0f, 0.9f}; + vec3 mPressedColor = {1.0f, 0.5f, 0.0f}; + vec3 mTextColor = {1.0f, 0.5f, 0.0f}; + + Shader mShader; + Texture mTexture; + eGuiItems mPressedItem = eGuiItemMax; + MillSimulation* mMillSim = nullptr; + unsigned int mIbo = 0; + int mThumbStartX = 0; + float mThumbMaxMotion = 0; +}; + +} // namespace MillSim + +#endif // __guidisplay_t__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillMotion.h b/src/Mod/CAM/PathSimulator/AppGL/MillMotion.h new file mode 100644 index 0000000000..6e0bc427a1 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillMotion.h @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __mill_operation_h__ +#define __mill_operation_h__ +// #include +#include "EndMill.h" +#include "linmath.h" +namespace MillSim +{ + +enum eEndMillType +{ + eEndmillFlat, + eEndmillV, + eEndmillBall, + eEndmillFillet +}; + +enum eCmdType +{ + eNop, + eMoveLiner, + eRotateCW, + eRotateCCW, + eDril, + eChangeTool +}; + +struct MillMotion +{ + eCmdType cmd; + int tool; + float x, y, z; + float i, j, k; + float r; +}; + +static inline void MotionPosToVec(vec3 vec, const MillMotion* motion) +{ + vec[0] = motion->x; + vec[1] = motion->y; + vec[2] = motion->z; +} +} // namespace MillSim +#endif \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp new file mode 100644 index 0000000000..b1ea18ba4d --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.cpp @@ -0,0 +1,242 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "OpenGlWrapper.h" +#include "MillPathSegment.h" +#include "SimShapes.h" +#include "linmath.h" +#include "GlUtils.h" +#include + +#define N_MILL_SLICES 8 +#define MAX_SEG_DEG (PI / 2.0f) // 90 deg +#define NIN_SEG_DEG (PI / 90.0f) // 2 deg +#define SWEEP_ARC_PAD 1.05f +#define PX 0 +#define PY 1 +#define PZ 2 + + +namespace MillSim +{ + +bool IsVerticalMotion(MillMotion* m1, MillMotion* m2) +{ + return (m1->z != m2->z && EQ_FLOAT(m1->x, m2->x) && EQ_FLOAT(m1->y, m2->y)); +} + +bool IsArcMotion(MillMotion* m) +{ + if (m->cmd != eRotateCCW && m->cmd != eRotateCW) { + return false; + } + return fabs(m->i > EPSILON) || fabs(m->j) > EPSILON; +} + +float MillPathSegment::mResolution = 1; +float MillPathSegment::mSmallRadStep = (PI / 8); + +MillPathSegment::MillPathSegment(EndMill* _endmill, MillMotion* from, MillMotion* to) + : mShearMat {1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 1.0f} +{ + + MotionPosToVec(mStartPos, from); + MotionPosToVec(mDiff, to); + vec3_sub(mDiff, mDiff, mStartPos); + mXYDistance = sqrtf(mDiff[PX] * mDiff[PX] + mDiff[PY] * mDiff[PY]); + mZDistance = fabsf(mDiff[PY]); + mXYZDistance = sqrtf(mXYDistance * mXYDistance + mDiff[PZ] * mDiff[PZ]); + mXYAngle = atan2f(mDiff[PY], mDiff[PX]); + endmill = _endmill; + mStartAngRad = mStepAngRad = 0; + if (IsArcMotion(to)) { + mMotionType = MTCurved; + mRadius = sqrtf(to->j * to->j + to->i * to->i); + mSmallRad = mRadius <= endmill->radius; + + if (mSmallRad) { + mStepAngRad = mSmallRadStep; + } + else { + mStepAngRad = asinf(mResolution / mRadius); + if (mStepAngRad > MAX_SEG_DEG) { + mStepAngRad = MAX_SEG_DEG; + } + else if (mStepAngRad < NIN_SEG_DEG) { + mStepAngRad = NIN_SEG_DEG; + } + } + + MotionPosToVec(mCenter, from); + mCenter[PX] += to->i; + mCenter[PY] += to->j; + mArcDir = to->cmd == eRotateCCW ? -1.f : 1.f; + mStartAngRad = atan2f(mCenter[PX] - from->x, from->y - mCenter[PY]); + float endAng = atan2f(mCenter[PX] - to->x, to->y - mCenter[PY]); + mSweepAng = (mStartAngRad - endAng) * mArcDir; + if (mSweepAng < EPSILON) { + mSweepAng += PI * 2; + } + numSimSteps = (int)(mSweepAng / mStepAngRad) + 1; + mStepAngRad = mArcDir * mSweepAng / numSimSteps; + if (mSmallRad) { + // when the radius is too small, we just use the tool itself to carve the stock + mShape = endmill->toolShape; + } + else { + endmill->GenerateArcSegmentDL(mRadius, + mStepAngRad * SWEEP_ARC_PAD, + mDiff[PZ] / numSimSteps, + &mShape); + numSimSteps++; + } + + isMultyPart = true; + } + else { + numSimSteps = (int)(mXYZDistance / mResolution); + if (numSimSteps == 0) { + numSimSteps = 1; + } + isMultyPart = false; + mStepDistance = mXYDistance / numSimSteps; + mStepLength[PX] = mDiff[PX]; + mStepLength[PY] = mDiff[PY]; + mStepLength[PZ] = mDiff[PZ]; + vec3_scale(mStepLength, mStepLength, 1.f / (float)numSimSteps); + + if (IsVerticalMotion(from, to)) { + mMotionType = MTVertical; + } + else { + mMotionType = MTHorizontal; + mShearMat[0][2] = mDiff[PZ] / mXYDistance; + } + } +} + +MillPathSegment::~MillPathSegment() +{ + mShape.FreeResources(); +} + + +void MillPathSegment::render(int step) +{ + mStepNumber = step; + mat4x4 mat, mat2, rmat; + mat4x4_identity(mat); + mat4x4_identity(rmat); + if (mMotionType == MTCurved) { + mat4x4_translate_in_place(mat, + mCenter[PX], + mCenter[PY], + mCenter[PZ] + mDiff[PZ] * (step - 1) / numSimSteps); + mat4x4_rotate_Z(mat, mat, mStartAngRad - (step - 1) * mStepAngRad); + mat4x4_rotate_Z(rmat, rmat, mStartAngRad - (step - 1) * mStepAngRad); + + if (mSmallRad || step == numSimSteps) { + mat4x4_translate_in_place(mat, 0, mRadius, 0); + endmill->toolShape.Render(mat, rmat); + } + else { + mShape.Render(mat, rmat); + } + } + else { + if (mMotionType == MTVertical) { + if (mStepLength[PZ] > 0) { + mat4x4_translate_in_place_v(mat, mStartPos); + } + else { + mat4x4_translate_in_place(mat, + mStartPos[PX], + mStartPos[PY], + mStartPos[PZ] + mStepNumber * mStepLength[PZ]); + } + endmill->toolShape.Render(mat, rmat); + } + else { + float renderDist = step * mStepDistance; + mat4x4_translate_in_place_v(mat, mStartPos); + mat4x4_rotate_Z(mat, mat, mXYAngle); + mat4x4_rotate_Z(rmat, rmat, mXYAngle); + mat4x4_dup(mat2, mat); + if (mDiff[PZ] != 0.0) { + mat4x4_mul(mat2, mat2, mShearMat); + } + mat4x4_scale_aniso(mat2, mat2, renderDist, 1, 1); + endmill->pathShape.Render(mat2, rmat); + mat4x4_translate_in_place(mat, renderDist, 0, mDiff[PZ]); + endmill->halfToolShape.Render(mat, rmat); + } + } +} + +void MillPathSegment::GetHeadPosition(vec3 headPos) +{ + if (mMotionType == MTCurved) { + float angRad = mStartAngRad - mStepNumber * mStepAngRad; + vec3_set(mHeadPos, -mRadius * sinf(angRad), mRadius * cosf(angRad), 0); + vec3_add(mHeadPos, mHeadPos, mCenter); + } + else { + vec3_dup(mHeadPos, mStepLength); + vec3_scale(mHeadPos, mHeadPos, (float)mStepNumber); + vec3_add(mHeadPos, mHeadPos, mStartPos); + } + vec3_dup(headPos, mHeadPos); +} +float MillPathSegment::SetQuality(float quality, float maxStockDimension) +{ + mResolution = maxStockDimension * 0.05 / quality; + if (mResolution > 4) { + mResolution = 4; + } + if (mResolution < 0.5) { + mResolution = 0.5; + } + mSmallRadStep = PI / 8; + if (quality < 4) { + mSmallRadStep = PI / 2; + } + else if (quality < 8) { + mSmallRadStep = PI / 4; + } + return mResolution; +} +} // namespace MillSim \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h new file mode 100644 index 0000000000..43369cb5f7 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillPathSegment.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __mill_path_segment_h__ +#define __mill_path_segment_h__ + + +#include "MillMotion.h" +#include "EndMill.h" +#include "linmath.h" + +namespace MillSim +{ + +enum MotionType +{ + MTVertical = 0, + MTHorizontal, + MTCurved +}; + + +bool IsVerticalMotion(MillMotion* m1, MillMotion* m2); + + +class MillPathSegment +{ +public: + /// + /// Create a mill path segment primitive + /// + /// Mill object + /// Start point + /// End point + MillPathSegment(EndMill* endmill, MillMotion* from, MillMotion* to); + virtual ~MillPathSegment(); + + + /// Calls the display list. + virtual void render(int substep); + virtual void GetHeadPosition(vec3 headPos); + static float SetQuality(float quality, float maxStockDimension); // 1 minimum, 10 maximum + +public: + EndMill* endmill = nullptr; + bool isMultyPart; + int numSimSteps; + int indexInArray; + + +protected: + mat4x4 mShearMat; + Shape mShape; + float mXYDistance; + float mXYZDistance; + float mZDistance; + float mXYAngle; + float mStartAngRad; + float mStepAngRad; + float mStepDistance = 0; + float mSweepAng; + float mRadius = 0; + float mArcDir = 0; + bool mSmallRad = false; + int mStepNumber = 0; + + static float mSmallRadStep; + static float mResolution; + + vec3 mDiff; + vec3 mStepLength = {0}; + vec3 mCenter = {0}; + vec3 mStartPos; + vec3 mHeadPos = {0}; + MotionType mMotionType; +}; +} // namespace MillSim + +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp new file mode 100644 index 0000000000..be822cc28b --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.cpp @@ -0,0 +1,601 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "MillSimulation.h" +#include "OpenGlWrapper.h" +#include +#include + +namespace MillSim { + + MillSimulation::MillSimulation() + { + mCurMotion = { eNop, -1, 0, 0, 0, 0, 0, 0 }; + guiDisplay.SetMillSimulator(this); + } + + void MillSimulation::ClearMillPathSegments() { + //for (std::vector::const_iterator i = MillPathSegments.begin(); i != MillPathSegments.end(); ++i) { + // MillSim::MillPathSegment* p = *i; + // delete p; + //} + for (int i = 0; i < MillPathSegments.size(); i++) + delete MillPathSegments[i]; + MillPathSegments.clear(); + } + + void MillSimulation::Clear() + { + mCodeParser.Operations.clear(); + for (int i = 0; i < mToolTable.size(); i++) + delete mToolTable[i]; + mToolTable.clear(); + mCurStep = 0; + mPathStep = -1; + mNTotalSteps = 0; + } + + + + void MillSimulation::SimNext() + { + static int simDecim = 0; + + simDecim++; + if (simDecim < 1) + return; + + simDecim = 0; + + if (mCurStep < mNTotalSteps) + { + mCurStep += mSimSpeed; + CalcSegmentPositions(); + } + } + + void MillSimulation::InitSimulation(float quality) + { + ClearMillPathSegments(); + + mDestMotion = mZeroPos; + //gDestPos = curMillOperation->startPos; + mCurStep = 0; + mPathStep = -1; + mNTotalSteps = 0; + MillPathSegment::SetQuality(quality, mMaxFar); + int nOperations = (int)mCodeParser.Operations.size();; + for (int i = 0; i < nOperations; i++) + { + mCurMotion = mDestMotion; + mDestMotion = mCodeParser.Operations[i]; + EndMill* tool = GetTool(mDestMotion.tool); + if (tool != nullptr) + { + MillSim::MillPathSegment* segment = new MillSim::MillPathSegment(tool, &mCurMotion, &mDestMotion); + segment->indexInArray = i; + mNTotalSteps += segment->numSimSteps; + MillPathSegments.push_back(segment); + } + } + mNPathSteps = (int)MillPathSegments.size(); + InitDisplay(quality); + } + + EndMill* MillSimulation::GetTool(int toolId) + { + for (int i = 0; i < mToolTable.size(); i++) + { + if (mToolTable[i]->toolId == toolId) + { + return mToolTable[i]; + } + } + return nullptr; + } + + void MillSimulation::RemoveTool(int toolId) + { + EndMill* tool; + if ((tool = GetTool(toolId)) != nullptr) { + auto it = std::find(mToolTable.begin(), mToolTable.end(), tool); + if (it != mToolTable.end()) { + mToolTable.erase(it); + } + delete tool; + } + } + + void MillSimulation::AddTool(EndMill* tool) + { + // if we have another tool with same id, remove it + RemoveTool(tool->toolId); + mToolTable.push_back(tool); + } + + void + MillSimulation::AddTool(const std::vector& toolProfile, int toolid, float diameter) + { + // if we have another tool with same id, remove it + RemoveTool(toolid); + EndMill* tool = new EndMill(toolProfile, toolid, diameter); + mToolTable.push_back(tool); + } + + void MillSimulation::GlsimStart() + { + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + } + + void MillSimulation::GlsimToolStep1(void) + { + glCullFace(GL_BACK); + glDepthFunc(GL_LESS); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE); + glDepthMask(GL_FALSE); + } + + + void MillSimulation::GlsimToolStep2(void) + { + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthFunc(GL_GREATER); + glCullFace(GL_FRONT); + glDepthMask(GL_TRUE); + } + + void MillSimulation::GlsimClipBack(void) + { + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_ZERO); + glDepthFunc(GL_LESS); + glCullFace(GL_FRONT); + glDepthMask(GL_FALSE); + } + + + void MillSimulation::GlsimRenderStock(void) + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDepthFunc(GL_EQUAL); + glCullFace(GL_BACK); + } + + void MillSimulation::GlsimRenderTools(void) + { + glCullFace(GL_FRONT); + } + + void MillSimulation::GlsimEnd(void) + { + glCullFace(GL_BACK); + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glDisable(GL_STENCIL_TEST); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthMask(GL_TRUE); + glDepthFunc(GL_LESS); + } + + void MillSimulation::renderSegmentForward(int iSeg) + { + MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); + int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; + int start = p->isMultyPart ? 1 : step; + for (int i = start; i <= step; i++) + { + GlsimToolStep1(); + p->render(i); + GlsimToolStep2(); + p->render(i); + } + } + + void MillSimulation::renderSegmentReversed(int iSeg) + { + MillSim::MillPathSegment* p = MillPathSegments.at(iSeg); + int step = iSeg == mPathStep ? mSubStep : p->numSimSteps; + int end = p->isMultyPart ? 1 : step; + for (int i = step; i >= end; i--) + { + GlsimToolStep1(); + p->render(i); + GlsimToolStep2(); + p->render(i); + } + } + + void MillSimulation::CalcSegmentPositions() + { + mSubStep = mCurStep; + for (mPathStep = 0; mPathStep < mNPathSteps; mPathStep++) + { + MillSim::MillPathSegment* p = MillPathSegments[mPathStep]; + if (mSubStep < p->numSimSteps) + break; + mSubStep -= p->numSimSteps; + } + if (mPathStep >= mNPathSteps) + { + mPathStep = mNPathSteps - 1; + mSubStep = MillPathSegments[mPathStep]->numSimSteps; + } + else + mSubStep++; + } + + void MillSimulation::Render() + { + mat4x4 matLookAt, model; + mat4x4_identity(model); + mat4x4_look_at(matLookAt, eye, target, upvec); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + mat4x4_translate_in_place(matLookAt, mEyeX * mEyeXZFactor, 0, mEyeZ * mEyeXZFactor); + mat4x4_rotate_X(matLookAt, matLookAt, mEyeInclination); + mat4x4_rotate_Z(matLookAt, matLookAt, mEyeRoration); + mat4x4_translate_in_place(matLookAt, -mStockObject.center[0], -mStockObject.center[1], -mStockObject.center[2]); + + shaderFlat.Activate(); + shaderFlat.UpdateViewMat(matLookAt); + + GlsimStart(); + mStockObject.render(); + + GlsimToolStep2(); + + for (int i = 0; i <= mPathStep; i++) + renderSegmentForward(i); + + for (int i = mPathStep; i >= 0; i--) + renderSegmentForward(i); + + for (int i = 0; i < mPathStep; i++) + renderSegmentReversed(i); + + for (int i = mPathStep; i >= 0; i--) + renderSegmentReversed(i); + + GlsimClipBack(); + mStockObject.render(); + + // start coloring + shader3D.Activate(); + shader3D.UpdateViewMat(matLookAt); + shader3D.UpdateObjColor(stockColor); + GlsimRenderStock(); + mStockObject.render(); + GlsimRenderTools(); + + // render cuts (back faces of tools) + + shaderInv3D.Activate(); + shaderInv3D.UpdateViewMat(matLookAt); + shaderInv3D.UpdateObjColor(cutColor); + for (int i = 0; i <= mPathStep; i++) + { + MillSim::MillPathSegment* p = MillPathSegments.at(i); + int step = (i == mPathStep) ? mSubStep : p->numSimSteps; + int start = p->isMultyPart ? 1 : step; + for (int j = start; j <= step; j++) + MillPathSegments.at(i)->render(j); + } + + GlsimEnd(); + + glEnable(GL_CULL_FACE); + + if (mPathStep >= 0) + { + vec3 toolPos; + MotionPosToVec(toolPos, &mDestMotion); + MillSim::MillPathSegment* p = MillPathSegments.at(mPathStep); + p->GetHeadPosition(toolPos); + mat4x4 tmat; + mat4x4_translate(tmat, toolPos[0], toolPos[1], toolPos[2]); + //mat4x4_translate(tmat, toolPos.x, toolPos.y, toolPos.z); + shader3D.Activate(); + shader3D.UpdateObjColor(toolColor); + p->endmill->toolShape.Render(tmat, identityMat); + } + + shaderFlat.Activate(); + shaderFlat.UpdateObjColor(lightColor); + mlightObject.render(); + + if (mDebug > 0) + { + mat4x4 test; + mat4x4_dup(test, model); + mat4x4_translate_in_place(test, 20, 20, 3); + mat4x4_rotate_Z(test, test, 30.f * 3.14f / 180.f); + int dpos = mNPathSteps - mDebug2; + MillSim::MillPathSegment* p = MillPathSegments.at(dpos); + if (mDebug > p->numSimSteps) + mDebug = 1; + p->render(mDebug); + } + float progress = (float)mCurStep / mNTotalSteps; + guiDisplay.Render(progress); + } + + void MillSimulation::ProcessSim(unsigned int time_ms) { + + static int ancient = 0; + static int last = 0; + static int msec = 0; + static int fps = 0; + static int renderTime = 0; + + last = msec; + msec = time_ms; + if (mIsRotate) { + mEyeRoration += (msec - last) / 4600.0f; + while (mEyeRoration >= PI2) + mEyeRoration -= PI2; + } + + if (last / 1000 != msec / 1000) { + float calcFps = 1000.0f * fps / (msec - ancient); + mFpsStream.str(""); + mFpsStream << "fps: " << calcFps << " rendertime:" << renderTime << " zpos:" << mDestMotion.z << std::ends; + ancient = msec; + fps = 0; + } + + if (mSimPlaying || mSingleStep) + { + SimNext(); + mSingleStep = false; + } + + Render(); + + ++fps; + } + + void MillSimulation::HandleKeyPress(int key) + { + switch (key) { + case ' ': + mIsRotate = !mIsRotate; + break; + + case 'S': + mSimPlaying = true; + break; + + case 'P': + mSimPlaying = false; + break; + + case 'T': + mSimPlaying = false; + mSingleStep = true; + break; + + case'D': + mDebug++; + break; + + case'K': + mDebug2++; + gDebug = mNPathSteps - mDebug2; + break; + + case 'F': + if (mSimSpeed == 1) mSimSpeed = 10; + else if (mSimSpeed == 10) mSimSpeed = 40; + else mSimSpeed = 1; + guiDisplay.UpdateSimSpeed(mSimSpeed); + break; + + default: + if (key >= '1' && key <= '9') + mSimSpeed = key - '0'; + break; + } + guiDisplay.UpdatePlayState(mSimPlaying); + } + + void MillSimulation::UpdateEyeFactor(float factor) + { + mEyeDistFactor = factor; + mEyeXZFactor = factor * mMaxFar * 0.005f; + eye[1] = -factor * mMaxFar; + } + + void MillSimulation::TiltEye(float tiltStep) + { + mEyeInclination += tiltStep; + if (mEyeInclination > PI / 2) + mEyeInclination = PI / 2; + else if (mEyeInclination < -PI / 2) + mEyeInclination = -PI / 2; + } + + void MillSimulation::RotateEye(float rotStep) + { + mEyeRoration += rotStep; + if (mEyeRoration > PI2) + mEyeRoration = PI2; + else if (mEyeRoration < 0) + mEyeRoration = 0; + } + + void MillSimulation::MoveEye(float x, float z) + { + mEyeX += x; + if (mEyeX > 100) mEyeX = 100; + else if (mEyeX < -100) mEyeX = -100; + mEyeZ += z; + if (mEyeZ > 100) mEyeZ = 100; + else if (mEyeZ < -100) mEyeZ = -100; + } + + void MillSimulation::UpdateProjection() + { + // Setup projection + mat4x4 projmat; + mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1.0f, mMaxFar); + //mat4x4_perspective(projmat, 0.7f, 4.0f / 3.0f, 1, 100); + shader3D.Activate(); + shader3D.UpdateProjectionMat(projmat); + shaderInv3D.Activate(); + shaderInv3D.UpdateProjectionMat(projmat); + shaderFlat.Activate(); + shaderFlat.UpdateProjectionMat(projmat); + } + + void MillSimulation::InitDisplay(float quality) + { + // gray background + glClearColor(0.6f, 0.8f, 1.0f, 1.0f); + + + // use shaders + // standard diffuse shader + shader3D.CompileShader((char*)VertShader3DNorm, (char*)FragShaderNorm); + shader3D.UpdateEnvColor(lightPos, lightColor, ambientCol); + + // invarted normal diffuse shader for inner mesh + shaderInv3D.CompileShader((char*)VertShader3DInvNorm, (char*)FragShaderNorm); + shaderInv3D.UpdateEnvColor(lightPos, lightColor, ambientCol); + + // null shader to calculate meshes only (simulation stage) + shaderFlat.CompileShader((char*)VertShader3DNorm, (char*)FragShaderFlat); + UpdateProjection(); + + // setup light object and generate tools + mlightObject.GenerateBoxStock(-0.5f, -0.5f, -0.5f, 1, 1, 1); + for (int i = 0; i < mToolTable.size(); i++) + mToolTable[i]->GenerateDisplayLists(quality); + + // init gui elements + guiDisplay.InutGui(); + + } + + void MillSimulation::SetBoxStock(float x, float y, float z, float l, float w, float h) + { + mStockObject.GenerateBoxStock(x, y, z, l, w, h); + mMaxStockDim = fmaxf(w, l); + mMaxFar = mMaxStockDim * 4; + UpdateProjection(); + vec3_set(eye, 0, 0, 0); + UpdateEyeFactor(0.4f); + vec3_set(lightPos, x, y, h + mMaxStockDim / 3); + mlightObject.SetPosition(lightPos); + } + + void MillSimulation::MouseDrag(int buttons, int dx, int dy) + { + if (buttons == (MS_MOUSE_MID | MS_MOUSE_LEFT)) + { + TiltEye((float)dy / 100.0f); + RotateEye((float)dx / 100.0f); + } + else if (buttons == MS_MOUSE_MID) + { + MoveEye(dx, -dy); + } + guiDisplay.MouseDrag(buttons, dx, dy); + } + + void MillSimulation::MouseMove(int px, int py) + { + if (mMouseButtonState > 0) + { + int dx = px - mLastMouseX; + int dy = py - mLastMouseY; + if (dx != 0 || dy != 0) + { + MouseDrag(mMouseButtonState, dx, dy); + mLastMouseX = px; + mLastMouseY = py; + } + } + else + MouseHover(px, py); + } + + void MillSimulation::MouseScroll(float dy) + { + float f = mEyeDistFactor; + f += 0.05f * dy; + if (f > 0.6f) f = 0.6f; + else if (f < 0.05f) f = 0.05f; + UpdateEyeFactor(f); + } + + + void MillSimulation::MouseHover(int px, int py) + { + guiDisplay.MouseCursorPos(px, py); + } + + void MillSimulation::MousePress(int button, bool isPressed, int px, int py) + { + if (isPressed) + mMouseButtonState |= button; + else + mMouseButtonState &= ~button; + + if (mMouseButtonState > 0) + { + mLastMouseX = px; + mLastMouseY = py; + } + guiDisplay.MousePressed(button, isPressed, mSimPlaying); + } + + + bool MillSimulation::LoadGCodeFile(const char* fileName) + { + if (mCodeParser.Parse(fileName)) + { + std::cout << "GCode file loaded successfuly" << std::endl; + return true; + } + return false; + } + + bool MillSimulation::AddGcodeLine(const char* line) + { + return mCodeParser.AddLine(line); + } + + void MillSimulation::SetSimulationStage(float stage) + { + mCurStep = (int)((float)mNTotalSteps * stage); + CalcSegmentPositions(); + } + +} \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h new file mode 100644 index 0000000000..2744f97b7f --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/MillSimulation.h @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __millsimulation__h__ +#define __millsimulation__h__ + +#include "MillMotion.h" +#include "GCodeParser.h" +#include "Shader.h" +#include "linmath.h" +#include "GlUtils.h" +#include "StockObject.h" +#include "MillPathSegment.h" +#include "GuiDisplay.h" +#include +#include + +namespace MillSim +{ + +class MillSimulation +{ +public: + MillSimulation(); + void ClearMillPathSegments(); + void Clear(); + void SimNext(); + void InitSimulation(float quality); + void AddTool(EndMill* tool); + void AddTool(const std::vector& toolProfile, int toolid, float diameter); + bool ToolExists(int toolid) + { + return GetTool(toolid) != nullptr; + } + void Render(); + void ProcessSim(unsigned int time_ms); + void HandleKeyPress(int key); + void UpdateEyeFactor(float factor); + void TiltEye(float tiltStep); + void RotateEye(float rotStep); + void MoveEye(float x, float y); + void UpdateProjection(); + bool LoadGCodeFile(const char* fileName); + bool AddGcodeLine(const char* line); + void SetSimulationStage(float stage); + void SetBoxStock(float x, float y, float z, float l, float w, float h); + void MouseDrag(int buttons, int dx, int dy); + void MouseMove(int px, int py); + void MouseScroll(float dy); + void MouseHover(int px, int py); + void MousePress(int button, bool isPressed, int px, int py); + + +protected: + void InitDisplay(float quality); + void GlsimStart(); + void GlsimToolStep1(void); + void GlsimToolStep2(void); + void GlsimClipBack(void); + void GlsimRenderStock(void); + void GlsimRenderTools(void); + void GlsimEnd(void); + void renderSegmentForward(int iSeg); + void renderSegmentReversed(int iSeg); + void CalcSegmentPositions(); + EndMill* GetTool(int tool); + void RemoveTool(int toolId); + + +protected: + std::vector mToolTable; + Shader shader3D, shaderInv3D, shaderFlat; + GCodeParser mCodeParser; + GuiDisplay guiDisplay; + std::vector MillPathSegments; + std::ostringstream mFpsStream; + + MillMotion mZeroPos = {eNop, -1, 0, 0, 100, 0, 0, 0}; + MillMotion mCurMotion = {eNop, -1, 0, 0, 0, 0, 0, 0}; + MillMotion mDestMotion = {eNop, -1, 0, 0, 0, 0, 0, 0}; + + StockObject mStockObject; + StockObject mlightObject; + + vec3 lightColor = {0.8f, 0.9f, 1.0f}; + vec3 lightPos = {20.0f, 20.0f, 10.0f}; + vec3 ambientCol = {0.3f, 0.3f, 0.5f}; + + vec3 eye = {0, 100, 40}; + vec3 target = {0, 0, -10}; + vec3 upvec = {0, 0, 1}; + + vec3 stockColor = {0.7f, 0.7f, 0.7f}; + vec3 cutColor = {0.4f, 0.7f, 0.4f}; + vec3 toolColor = {0.4f, 0.4f, 0.7f}; + + float mEyeDistance = 30; + float mEyeRoration = 0; + float mEyeInclination = PI / 6; // 30 degree + float mEyeStep = PI / 36; // 5 degree + + float mMaxStockDim = 100; + float mMaxFar = 100; + float mEyeDistFactor = 0.4f; + float mEyeXZFactor = 0.01f; + float mEyeXZScale = 0; + float mEyeX = 0.0f; + float mEyeZ = 0.0f; + + + int mCurStep = 0; + int mNTotalSteps = 0; + int mPathStep = 0; + int mSubStep = 0; + int mNPathSteps = 0; + int mDebug = 0; + int mDebug1 = 0; + int mDebug2 = 12; + int mSimSpeed = 1; + + int mLastMouseX = 0, mLastMouseY = 0; + int mMouseButtonState = 0; + + bool mIsInStock = false; + bool mIsRotate = true; + bool mSimPlaying = false; + bool mSingleStep = false; +}; +} // namespace MillSim +#endif \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h b/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h new file mode 100644 index 0000000000..11c144fb6b --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/OpenGlWrapper.h @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __openglwrapper_h__ +#define __openglwrapper_h__ +#ifdef CAM_SIM_USE_GLEW +#include "GL/glew.h" +#else +#include "DlgCAMSimulator.h" +extern QOpenGLContext* gOpenGlContext; +#define gSimWindow CAMSimulator::DlgCAMSimulator::GetInstance() +#define glGenBuffers gSimWindow->glGenBuffers +#define glBindBuffer gSimWindow->glBindBuffer +#define glBufferData gSimWindow->glBufferData +#define glGenVertexArrays gSimWindow->glGenVertexArrays +#define glBindVertexArray gSimWindow->glBindVertexArray +#define glEnableVertexAttribArray gSimWindow->glEnableVertexAttribArray +#define glVertexAttribPointer gSimWindow->glVertexAttribPointer +#define glShaderSource gSimWindow->glShaderSource +#define glCompileShader gSimWindow->glCompileShader +#define glAttachShader gSimWindow->glAttachShader +#define glLinkProgram gSimWindow->glLinkProgram +#define glGetProgramiv gSimWindow->glGetProgramiv +#define glGetUniformLocation gSimWindow->glGetUniformLocation +#define glGetError gSimWindow->glGetError +#define glEnable gSimWindow->glEnable +#define glColorMask gSimWindow->glColorMask +#define glCullFace gSimWindow->glCullFace +#define glDepthFunc gSimWindow->glDepthFunc +#define glStencilFunc gSimWindow->glStencilFunc +#define glStencilOp gSimWindow->glStencilOp +#define glDepthMask gSimWindow->glDepthMask +#define glDisable gSimWindow->glDisable +#define glMatrixMode gSimWindow->glMatrixMode +#define glUseProgram gSimWindow->glUseProgram +#define glDrawElements gSimWindow->glDrawElements +#define glDeleteVertexArrays gSimWindow->glDeleteVertexArrays +#define glUniformMatrix4fv gSimWindow->glUniformMatrix4fv +#define glUniform3fv gSimWindow->glUniform3fv +#define glUniform1i gSimWindow->glUniform1i +#define glCreateShader gSimWindow->glCreateShader +#define glCreateProgram gSimWindow->glCreateProgram +#define glDeleteBuffers gSimWindow->glDeleteBuffers +#define glActiveTexture gSimWindow->glActiveTexture +#define glBindTexture gSimWindow->glBindTexture +#define glGenTextures gSimWindow->glGenTextures +#define glTexParameteri gSimWindow->glTexParameteri +#define glTexImage2D gSimWindow->glTexImage2D +#define glDeleteTextures gSimWindow->glDeleteTextures +#endif // HAVE_OPENGL_EXT + +#endif // !__openglwrapper_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.cpp b/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.cpp new file mode 100644 index 0000000000..9dc04433fe --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.cpp @@ -0,0 +1,23 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "PreCompiled.h" diff --git a/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.h b/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.h new file mode 100644 index 0000000000..14ea3ae0a3 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/PreCompiled.h @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (c) 2017 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef APP_PRECOMPILED_H +#define APP_PRECOMPILED_H + +#include + +#ifdef _PreComp_ + +// standard +#include +#include +#include + +// STL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Boost +#include + +// Xerces +#include + +#endif //_PreComp_ + +#endif + diff --git a/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp b/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp new file mode 100644 index 0000000000..8831bd9fe8 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/Shader.cpp @@ -0,0 +1,272 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "GlUtils.h" +#include "Shader.h" +#include + +namespace MillSim +{ + +Shader* CurrentShader = nullptr; + +void Shader::UpdateModelMat(mat4x4 tmat, mat4x4 nmat) +{ + if (mModelPos >= 0) { + glUniformMatrix4fv(mModelPos, 1, GL_FALSE, (GLfloat*)tmat); + } + if (mNormalRotPos >= 0) { + glUniformMatrix4fv(mNormalRotPos, 1, GL_FALSE, (GLfloat*)nmat); + } +} + +void Shader::UpdateProjectionMat(mat4x4 mat) +{ + if (mProjectionPos >= 0) { + glUniformMatrix4fv(mProjectionPos, 1, GL_FALSE, (GLfloat*)mat); + } +} + +void Shader::UpdateViewMat(mat4x4 mat) +{ + if (mViewPos >= 0) { + if (mViewPos >= 0) { + glUniformMatrix4fv(mViewPos, 1, GL_FALSE, (GLfloat*)mat); + } + } +} + +void Shader::UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient) +{ + if (mLightPosPos >= 0) { + glUniform3fv(mLightPosPos, 1, lightPos); + } + if (mLightColorPos >= 0) { + glUniform3fv(mLightColorPos, 1, lightColor); + } + if (mAmbientPos >= 0) { + glUniform3fv(mAmbientPos, 1, ambient); + } +} + +void Shader::UpdateObjColor(vec3 objColor) +{ + if (mObjectColorPos >= 0) { + glUniform3fv(mObjectColorPos, 1, objColor); + } +} + +void Shader::UpdateTextureSlot(int slot) +{ + if (mTexSlotPos >= 0) { + glUniform1i(mTexSlotPos, slot); + } +} + +bool CheckCompileResult(int shader) +{ +#ifdef QT_OPENGL_LIB + return false; +#else + char log[1024]; + int res = 0; + GLsizei len; + glGetShaderiv(shader, GL_COMPILE_STATUS, &res); + if (res != 0) { + return false; + } + glGetShaderInfoLog(shader, 1020, &len, log); + if (len > 1020) { + len = 1020; + } + log[len] = 0; + std::cout << log << std::endl; + return true; +#endif +} + +unsigned int Shader::CompileShader(char* _vertShader, char* _fragShader) +{ + vertShader = _vertShader; + fragShader = _fragShader; + const GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + GLint res = 0; + glShaderSource(vertex_shader, 1, &vertShader, NULL); + glCompileShader(vertex_shader); + if (CheckCompileResult(vertex_shader)) { + return 0xdeadbeef; + } + + const GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragShader, NULL); + glCompileShader(fragment_shader); + if (CheckCompileResult(fragment_shader)) { + return 0xdeadbeef; + } + + shaderId = glCreateProgram(); + glAttachShader(shaderId, vertex_shader); + glAttachShader(shaderId, fragment_shader); + glLinkProgram(shaderId); + + glGetProgramiv(shaderId, GL_LINK_STATUS, &res); + if (res == 0) { + return 0xdeadbeef; + } + + // get all uniform parameters positions + mModelPos = glGetUniformLocation(shaderId, "model"); + mNormalRotPos = glGetUniformLocation(shaderId, "normalRot"); + mProjectionPos = glGetUniformLocation(shaderId, "projection"); + mViewPos = glGetUniformLocation(shaderId, "view"); + mLightPosPos = glGetUniformLocation(shaderId, "lightPos"); + mLightColorPos = glGetUniformLocation(shaderId, "lightColor"); + mAmbientPos = glGetUniformLocation(shaderId, "ambient"); + mObjectColorPos = glGetUniformLocation(shaderId, "objectColor"); + mTexSlotPos = glGetUniformLocation(shaderId, "texSlot"); + Activate(); + return shaderId; +} + +void Shader::Activate() +{ + if (shaderId > 0) { + glUseProgram(shaderId); + } + CurrentShader = this; +} + + +const char* VertShader3DNorm = + "#version 330 core \n" + + "layout(location = 0) in vec3 aPosition; \n" + "layout(location = 1) in vec3 aNormal; \n" + + "out vec3 Normal; \n" + "out vec3 FragPos; \n" + + "uniform mat4 model; \n" + "uniform mat4 view; \n" + "uniform mat4 projection; \n" + "uniform mat4 normalRot; \n" + + "void main(void) \n" + "{ \n" + " gl_Position = projection * view * model * vec4(aPosition, 1.0); \n" + " FragPos = vec3(model * vec4(aPosition, 1.0)); \n" + " Normal = vec3(normalRot * vec4(aNormal, 1.0)); \n" + "} \n"; + +const char* VertShader3DInvNorm = + "#version 330 core \n" + + "layout(location = 0) in vec3 aPosition; \n" + "layout(location = 1) in vec3 aNormal; \n" + + "out vec3 Normal; \n" + "out vec3 FragPos; \n" + + "uniform mat4 model; \n" + "uniform mat4 view; \n" + "uniform mat4 projection; \n" + "uniform mat4 normalRot; \n" + + "void main(void) \n" + "{ \n" + " gl_Position = projection * view * model * vec4(aPosition, 1.0); \n" + " FragPos = vec3(model * vec4(aPosition, 1.0)); \n" + " Normal = -vec3(normalRot * vec4(aNormal, 1.0)); \n" + "} \n"; + + +const char* VertShader2DTex = + "#version 330 core \n" // -----> add long remark for a uniform auto formatting + + "layout(location = 0) in vec2 aPosition; \n" + "layout(location = 1) in vec2 aTexCoord; \n" + + "out vec2 texCoord; \n" + + "uniform mat4 projection; \n" + "uniform mat4 model; \n" + + "void main(void) \n" + "{ \n" + " gl_Position = projection * model * vec4(aPosition, 0.0, 1.0); \n" + " texCoord = aTexCoord; \n" + "} \n"; + +const char* FragShader2dTex = + "#version 330\n" // -----> add long remark for a uniform auto formatting + + "out vec4 FragColor; \n" + "in vec2 texCoord; \n" + + "uniform vec3 objectColor; \n" + "uniform sampler2D texSlot; \n" + + "void main() \n" + "{ \n" + " vec4 texColor = texture(texSlot, texCoord); \n" + " FragColor = vec4(objectColor, 1.0) * texColor; \n" + "} \n"; + + +const char* FragShaderNorm = + "#version 330\n" // -----> add long remark for a uniform auto formatting + + "out vec4 FragColor; \n" + + "in vec3 Normal; \n" + "in vec3 FragPos; \n" + + "uniform vec3 lightPos; \n" + "uniform vec3 lightColor; \n" + "uniform vec3 objectColor; \n" + "uniform vec3 ambient; \n" + + "void main() \n" + "{ \n" + " vec3 norm = normalize(Normal); \n" + " vec3 lightDir = normalize(lightPos - FragPos); \n" + " float diff = max(dot(norm, lightDir), 0.0); \n" + " vec3 diffuse = diff * lightColor; \n" + " vec3 result = (ambient + diffuse) * objectColor; \n" + " FragColor = vec4(result, 1.0); \n" + "} \n"; + +const char* FragShaderFlat = + "#version 330\n" // -----> add long remark for a uniform auto formatting + + "out vec4 FragColor; \n" + + "in vec3 Normal; \n" + "in vec3 FragPos; \n" + "uniform vec3 objectColor; \n" + + "void main() \n" + "{ \n" + " FragColor = vec4(objectColor, 1.0); \n" + "} \n"; + +} // namespace MillSim \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/Shader.h b/src/Mod/CAM/PathSimulator/AppGL/Shader.h new file mode 100644 index 0000000000..3b11ec3a5c --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/Shader.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __shader_h__ +#define __shader_h__ + +#include "OpenGlWrapper.h" +#include "linmath.h" + +namespace MillSim +{ +class Shader +{ +public: + Shader() + {} + +public: + unsigned int shaderId = 0; + void UpdateModelMat(mat4x4 transformMat, mat4x4 normalMat); + void UpdateProjectionMat(mat4x4 mat); + void UpdateViewMat(mat4x4 mat); + void UpdateEnvColor(vec3 lightPos, vec3 lightColor, vec3 ambient); + void UpdateObjColor(vec3 objColor); + void UpdateTextureSlot(int slot); + unsigned int CompileShader(char* vertShader, char* fragShader); + void Activate(); + bool IsValid() + { + return shaderId > 0; + } + + +protected: + int mModelPos = -1; + int mNormalRotPos = -1; + int mProjectionPos = -1; + int mViewPos = -1; + int mLightPosPos = -1; + int mLightColorPos = -1; + int mAmbientPos = -1; + int mObjectColorPos = -1; + int mTexSlotPos = -1; + + const char* vertShader = nullptr; + const char* fragShader = nullptr; +}; + +extern Shader* CurrentShader; + +extern const char* FragShaderNorm; +extern const char* FragShaderFlat; +extern const char* VertShader3DNorm; +extern const char* VertShader3DInvNorm; +extern const char* VertShader2DTex; +extern const char* FragShader2dTex; +} // namespace MillSim +#endif // !__shader_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp new file mode 100644 index 0000000000..d21eb6df20 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.cpp @@ -0,0 +1,403 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "SimShapes.h" +#include "Shader.h" +#include "GlUtils.h" +#include +#include +#include +#include + +using namespace MillSim; + +static float* sinTable = nullptr; +static float* cosTable = nullptr; +static int lastNumSlices = 0; +static int lastNumSectionIndices = 0; +static GLshort quadIndices[] = { 0, 2, 3, 0, 3, 1 }; +static GLshort quadIndicesReversed[] = { 0, 3, 2, 0, 1, 3 }; +static GLshort* sectionIndicesQuad = nullptr; +static GLshort* sectionIndicesTri = nullptr; + +static bool GenerateSinTable(int nSlices) +{ + if (nSlices == lastNumSlices) + return true; + if (sinTable != nullptr) + free(sinTable); + if (cosTable != nullptr) + free(cosTable); + sinTable = cosTable = nullptr; + + float slice = (float)(2 * PI / nSlices); + int nvals = nSlices + 1; + sinTable = (float*)malloc(nvals * sizeof(float)); + if (sinTable == nullptr) + return false; + cosTable = (float*)malloc(nvals * sizeof(float)); + if (cosTable == nullptr) + { + free(sinTable); + sinTable = nullptr; + return false; + } + for (int i = 0; i < nvals; i++) + { + sinTable[i] = sinf(slice * i); + cosTable[i] = cosf(slice * i); + } + lastNumSlices = nvals; + return true; +} + + +void MillSim::Shape::RotateProfile(float* profPoints, int nPoints, float distance, float deltaHeight, int nSlices, bool isHalfTurn) +{ + int vidx = 0; + int iidx = 0; + int numVerts, numIndices; + int vstart; + + numVerts = nPoints * 2 * (nSlices + 1); + numIndices = (nPoints - 1) * nSlices * 6; + + float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); + if (vbuffer == nullptr) + return; + GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); + if (ibuffer == nullptr) + { + free(vbuffer); + return; + } + int nsinvals = nSlices; + if (isHalfTurn) + nsinvals *= 2; + if (GenerateSinTable(nsinvals) == false) + { + free(vbuffer); + free(ibuffer); + return; + } + + for (int i = 0; i < nPoints; i++) + { + int i2 = i * 2; + + float prevy = i > 0 ? profPoints[i2 - 2] : 0; + float prevz = i > 0 ? profPoints[i2 - 1] : profPoints[i2 + 1]; + float prevrad = fabsf(prevy); + float rad = fabsf(profPoints[i2]); + float z2 = profPoints[i2 + 1]; + float diffy = profPoints[i2] - prevy; + float diffz = z2 - prevz; + float len = sqrtf(diffy * diffy + diffz * diffz); + float nz = diffy / len; + vstart = i * 2 * (nSlices + 1); + + for (int j = 0; j <= nSlices; j++) + { + // generate vertices + float sx = sinTable[j]; + float sy = cosTable[j]; + float x1 = prevrad * sx + distance; + float y1 = prevrad * sy; + float x2 = rad * sx + distance; + float y2 = rad * sy; + + // generate normals + float ny = -diffz / len; + float nx = ny * sx; + ny *= sy; + + SET_TRIPLE(vbuffer, vidx, x1, y1, prevz); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + SET_TRIPLE(vbuffer, vidx, x2, y2, z2); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + + if (j != nSlices) + { + // generate indices { 0, 3, 1, 0, 2, 3 } + int pos = vstart + 2 * j; + if (i < (nPoints - 1)) + SET_TRIPLE(ibuffer, iidx, pos, pos + 3, pos + 1); + if (i > 0) + SET_TRIPLE(ibuffer, iidx, pos, pos + 2, pos + 3); + } + } + } + + GenerateModel(vbuffer, ibuffer, numVerts, numIndices); + + free(vbuffer); + free(ibuffer); +} + +void MillSim::Shape::CalculateExtrudeBufferSizes(int nProfilePoints, bool capStart, bool capEnd, + int* numVerts, int* numIndices, int* vc1idx, int* vc2idx, int* ic1idx, int* ic2idx) +{ + *numVerts = nProfilePoints * 4; // one face per profile point times 4 vertex per face + *numIndices = nProfilePoints * 2 * 3; // 2 triangles per face times 3 indices per triangle + if (capStart) + { + *vc1idx = *numVerts * 6; + *numVerts += nProfilePoints; + *ic1idx = *numIndices; + *numIndices += (nProfilePoints - 2) * 3; + } + if (capEnd) + { + *vc2idx = *numVerts * 6; + *numVerts += nProfilePoints; + *ic2idx = *numIndices; + *numIndices += (nProfilePoints - 2) * 3; + } +} + +void MillSim::Shape::ExtrudeProfileRadial(float* profPoints, int nPoints, float radius, float angleRad, float deltaHeight, bool capStart, bool capEnd) +{ + int vidx = 0, vc1idx, vc2idx; + int iidx = 0, ic1idx, ic2idx; + int numVerts, numIndices; + + CalculateExtrudeBufferSizes(nPoints, capStart, capEnd, &numVerts, &numIndices, &vc1idx, &vc2idx, &ic1idx, &ic2idx); + int vc1start = vc1idx / 6; + int vc2start = vc2idx / 6; + + float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); + if (!vbuffer) + return; + GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); + if (!ibuffer) + { + free(vbuffer); + return; + } + + bool is_clockwise = angleRad > 0; + angleRad = (float)fabs(angleRad); + float dir = is_clockwise ? 1.0f : -1.0f; + int offs1 = is_clockwise ? -1 : 0; + int offs2 = is_clockwise ? 0 : -1; + + float cosAng = cosf(angleRad); + float sinAng = sinf(angleRad); + for (int i = 0; i < nPoints; i++) + { + int p1 = i * 2; + float y1 = profPoints[p1] + radius; + float z1 = profPoints[p1 + 1]; + int p2 = (p1 + 2) % (nPoints * 2); + float y2 = profPoints[p2] + radius; + float z2 = profPoints[p2 + 1]; + + // normals + float ydiff = y2 - y1; + float zdiff = z2 - z1; + float len = sqrtf(ydiff * ydiff + zdiff * zdiff); + float ny = -zdiff / len; + float nz = ydiff / len; + float nx = -sinAng * ny; + ny *= cosAng; + + // start verts + SET_TRIPLE(vbuffer, vidx, 0, y1, z1); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + SET_TRIPLE(vbuffer, vidx, 0, y2, z2); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + + if (capStart) { + SET_TRIPLE(vbuffer, vc1idx, 0, y1, z1); + SET_TRIPLE(vbuffer, vc1idx, -1 * dir, 0, 0); + if (i > 1) + SET_TRIPLE(ibuffer, ic1idx, vc1start, vc1start + i + offs1, vc1start + i + offs2); + } + + float x1 = y1 * sinAng * dir; + float x2 = y2 * sinAng * dir; + y1 *= cosAng; + y2 *= cosAng; + z1 += deltaHeight; + z2 += deltaHeight; + SET_TRIPLE(vbuffer, vidx, x1, y1, z1); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + SET_TRIPLE(vbuffer, vidx, x2, y2, z2); + SET_TRIPLE(vbuffer, vidx, nx, ny, nz); + + // face have 2 triangles { 0, 2, 3, 0, 3, 1 }; + GLushort vistart = i * 4; + if (is_clockwise) + { + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 2, vistart + 3); + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 1); + } + else + { + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 2); + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 1, vistart + 3); + } + + if (capEnd) + { + SET_TRIPLE(vbuffer, vc2idx, x1, y1, z1); + SET_TRIPLE(vbuffer, vc2idx, cosAng * dir, -sinAng, 0); + if (i > 1) + SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i + offs2, vc2start + i + offs1); + } + } + + GenerateModel(vbuffer, ibuffer, numVerts, numIndices); + + free(vbuffer); + free(ibuffer); +} + +void MillSim::Shape::ExtrudeProfileLinear(float* profPoints, int nPoints, float fromX, float toX, float fromZ, float toZ, bool capStart, bool capEnd) +{ + int vidx = 0, vc1idx, vc2idx; + int iidx = 0, ic1idx, ic2idx; + int numVerts, numIndices; + + CalculateExtrudeBufferSizes(nPoints, capStart, capEnd, &numVerts, &numIndices, &vc1idx, &vc2idx, &ic1idx, &ic2idx); + int vc1start = vc1idx / 6; + int vc2start = vc2idx / 6; + + float* vbuffer = (float*)malloc(numVerts * sizeof(Vertex)); + if (!vbuffer) + return; + GLushort* ibuffer = (GLushort*)malloc(numIndices * sizeof(GLushort)); + if (!ibuffer) + { + free(vbuffer); + return; + } + + for (int i = 0; i < nPoints; i++) + { + // hollow pipe verts + int p1 = i * 2; + float y1 = profPoints[p1]; + float z1 = profPoints[p1 + 1]; + int p2 = (p1 + 2) % (nPoints * 2); + float y2 = profPoints[p2]; + float z2 = profPoints[p2 + 1]; + + // nornal + float ydiff = y2 - y1; + float zdiff = z2 - z1; + float len = sqrtf(ydiff * ydiff + zdiff * zdiff); + float ny = -zdiff / len; + float nz = ydiff / len; + + SET_TRIPLE(vbuffer, vidx, fromX, y1, z1 + fromZ); + SET_TRIPLE(vbuffer, vidx, 0, ny, nz); + SET_TRIPLE(vbuffer, vidx, fromX, y2, z2 + fromZ); + SET_TRIPLE(vbuffer, vidx, 0, ny, nz); + SET_TRIPLE(vbuffer, vidx, toX, y1, z1 + toZ); + SET_TRIPLE(vbuffer, vidx, 0, ny, nz); + SET_TRIPLE(vbuffer, vidx, toX, y2, z2 + toZ); + SET_TRIPLE(vbuffer, vidx, 0, ny, nz); + + // face have 2 triangles { 0, 2, 3, 0, 3, 1 }; + GLushort vistart = i * 4; + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 2, vistart + 3); + SET_TRIPLE(ibuffer, iidx, vistart, vistart + 3, vistart + 1); + + if (capStart) + { + SET_TRIPLE(vbuffer, vc1idx, fromX, profPoints[p1], profPoints[p1 + 1] + fromZ); + SET_TRIPLE(vbuffer, vc1idx, -1, 0, 0); + if (i > 1) + SET_TRIPLE(ibuffer, ic1idx, vc1start, vc1start + i - 1, vc1start + i); + } + if (capEnd) + { + SET_TRIPLE(vbuffer, vc2idx, toX, profPoints[p1], profPoints[p1 + 1] + toZ); + SET_TRIPLE(vbuffer, vc2idx, 1, 0, 0); + if (i > 1) + SET_TRIPLE(ibuffer, ic2idx, vc2start, vc2start + i, vc2start + i - 1); + } + } + + GenerateModel(vbuffer, ibuffer, numVerts, numIndices); + + free(vbuffer); + free(ibuffer); +} + +void MillSim::Shape::GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int nIndices) +{ + //GLuint vbo, ibo, vao; + + // vertex buffer + glGenBuffers(1, &vbo); + GLClearError(); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + GLLogError(); + glBufferData(GL_ARRAY_BUFFER, numVerts * sizeof(Vertex), vbuffer, GL_STATIC_DRAW); + + // index buffer + glGenBuffers(1, &ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, nIndices * sizeof(GLushort), ibuffer, GL_STATIC_DRAW); + + // vertex array + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, x)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, nx)); + + numIndices = nIndices; +} + +void MillSim::Shape::Render() +{ + glBindVertexArray(vao); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, nullptr); +} + +void MillSim::Shape::Render(mat4x4 modelMat, mat4x4 normallMat) // normals are rotated only +{ + CurrentShader->UpdateModelMat(modelMat, normallMat); + Render(); +} + +void MillSim::Shape::FreeResources() +{ + if (vao > 0) + { + glBindVertexArray(vao); + if (vbo > 0) glDeleteBuffers(1, &vbo); + if (ibo > 0) glDeleteBuffers(1, &ibo); + glDeleteVertexArrays(1, &vao); + vbo = ibo = vao = 0; + } +} + +MillSim::Shape::~Shape() +{ + FreeResources(); +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h new file mode 100644 index 0000000000..5e048d808a --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/SimShapes.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __sim_shapes_h__ +#define __sim_shapes_h__ +#include "OpenGlWrapper.h" +#include "linmath.h" + +#define SET_DUAL(var, idx, y, z) \ + { \ + var[idx++] = y; \ + var[idx++] = z; \ + } +#define SET_TRIPLE(var, idx, x, y, z) \ + { \ + var[idx++] = x; \ + var[idx++] = y; \ + var[idx++] = z; \ + } + +// #define SET_TRIPLE(var, idx, x, y, z) {var[idx] = x; var[idx+1] = y; var[idx+2] = z;} +// #define SET_TRIPLE_OFFS(var, idx, offs, x, y, z) {var[idx] = x + offs; var[idx+1] = y + offs; +// var[idx+2] = z + offs;} + +#define SET_TRIPLE_OFFS(var, idx, offs, x, y, z) \ + { \ + var[idx++] = x + offs; \ + var[idx++] = y + offs; \ + var[idx++] = z + offs; \ + } + +namespace MillSim +{ +typedef unsigned int uint; + +struct Vertex +{ + float x, y, z; + float nx, ny, nz; +}; + +class Shape +{ +public: + Shape() + {} + ~Shape(); + +public: + uint vao = 0; + uint vbo = 0; + uint ibo = 0; + int numIndices = 0; + +public: + void Render(); + void Render(mat4x4 modelMat, mat4x4 normallMat); + void FreeResources(); + void RotateProfile(float* profPoints, + int nPoints, + float distance, + float deltaHeight, + int nSlices, + bool isHalfTurn); + void ExtrudeProfileRadial(float* profPoints, + int nPoints, + float radius, + float angleRad, + float deltaHeight, + bool capStart, + bool capEnd); + void ExtrudeProfileLinear(float* profPoints, + int nPoints, + float fromX, + float toX, + float fromZ, + float toZ, + bool capStart, + bool capEnd); + +protected: + void GenerateModel(float* vbuffer, GLushort* ibuffer, int numVerts, int numIndices); + void CalculateExtrudeBufferSizes(int nProfilePoints, + bool capStart, + bool capEnd, + int* numVerts, + int* numIndices, + int* vc1idx, + int* vc2idx, + int* ic1idx, + int* ic2idx); +}; + +} // namespace MillSim +#endif diff --git a/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp b/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp new file mode 100644 index 0000000000..56db4f709a --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/StockObject.cpp @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "StockObject.h" +#include "Shader.h" +#include + +#define NUM_PROFILE_POINTS 4 + +MillSim::StockObject::StockObject() +{ + mat4x4_identity(mModelMat); + vec3_set(center, 0, 0, 0); +} + +MillSim::StockObject::~StockObject() +{ + shape.FreeResources(); +} + +void MillSim::StockObject::render() +{ + // glCallList(mDisplayListId); + // UpdateObjColor(color); + shape.Render(mModelMat, mModelMat); // model is not rotated hence both are identity matrix +} + +void MillSim::StockObject::SetPosition(vec3 position) +{ + mat4x4_translate(mModelMat, position[0], position[1], position[2]); +} + +void MillSim::StockObject::GenerateBoxStock(float x, float y, float z, float l, float w, float h) +{ + int idx = 0; + SET_DUAL(mProfile, idx, y + w, z + h); + SET_DUAL(mProfile, idx, y + w, z); + SET_DUAL(mProfile, idx, y, z); + SET_DUAL(mProfile, idx, y, z + h); + + vec3_set(center, x + l / 2, y + w / 2, z + h / 2); + vec3_set(size, l, w, h); + + shape.ExtrudeProfileLinear(mProfile, NUM_PROFILE_POINTS, x, x + l, 0, 0, true, true); +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/StockObject.h b/src/Mod/CAM/PathSimulator/AppGL/StockObject.h new file mode 100644 index 0000000000..0bca595669 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/StockObject.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __stock_object_h__ +#define __stock_object_h__ +#include "SimShapes.h" +#include "linmath.h" + +namespace MillSim +{ + +class StockObject +{ +public: + StockObject(); + virtual ~StockObject(); + + + /// Calls the display list. + virtual void render(); + Shape shape; + void SetPosition(vec3 position); + void GenerateBoxStock(float x, float y, float z, float l, float w, float h); + vec3 center = {}; + vec3 size = {}; + +private: + float mProfile[8] = {}; + mat4x4 mModelMat; +}; +} // namespace MillSim + +#endif \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp b/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp new file mode 100644 index 0000000000..2a5572a4b2 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/Texture.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "Texture.h" +#include "GlUtils.h" + +namespace MillSim +{ +Texture::~Texture() +{ + glDeleteTextures(1, &mTextureId); +} + +bool Texture::LoadImage(unsigned int* image, int _width, int _height) +{ + width = _width; + height = _height; + glGenTextures(1, &mTextureId); + glBindTexture(GL_TEXTURE_2D, mTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); + glBindTexture(GL_TEXTURE_2D, 0); + return true; +} + +bool Texture::Activate() +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, mTextureId); + return true; +} + +bool Texture::unbind() +{ + glBindTexture(GL_TEXTURE_2D, 0); + return true; +} + +} // namespace MillSim \ No newline at end of file diff --git a/src/Mod/CAM/PathSimulator/AppGL/Texture.h b/src/Mod/CAM/PathSimulator/AppGL/Texture.h new file mode 100644 index 0000000000..c08c4c9f68 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/Texture.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __texture_h__ +#define __texture_h__ +#include "OpenGlWrapper.h" + +namespace MillSim +{ + +class Texture +{ +public: + Texture() + {} + ~Texture(); + bool LoadImage(unsigned int* image, int x, int y); + bool Activate(); + bool unbind(); + float getTexX(int imgX) + { + return (float)imgX / (float)width; + } + float getTexY(int imgY) + { + return (float)imgY / (float)height; + } + +public: + int width = 0; + int height = 0; + + +protected: + unsigned int mTextureId = -1; +}; + + +} // namespace MillSim + +#endif // !__texture_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp new file mode 100644 index 0000000000..cdc0b56e6d --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.cpp @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#include "TextureLoader.h" + +using namespace MillSim; + +TextureItem texItems[] = { + {1, 36, 0, 0}, + {1, 1, 0, 0}, + {70, 1, 0, 0}, + {100, 1, 0, 0}, + {134, 1, 0, 0}, + {172, 1, 0, 0}, + {210, 1, 0, 0}, + {2, 50, 0, 0}, + {70, 50, 0, 0}, + {27, 50, 0, 0}, + {44, 50, 0, 0}, +}; + + +#define NUM_TEX_ITEMS (sizeof(texItems) / sizeof(TextureItem)) + +int sssize = -1; + +TextureLoader::TextureLoader(std::string imgFolder, std::vector fileNames, int textureSize) + : mImageFolder(imgFolder) +{ + int buffsize = textureSize * textureSize * sizeof(unsigned int); + mRawData = (unsigned int*)malloc(buffsize); + if (mRawData == nullptr) { + return; + } + memset(mRawData, 0x80, buffsize); + for (int i = 0; i < fileNames.size(); i++) { + QImage pixmap((imgFolder + fileNames[i]).c_str()); + AddImage(&(texItems[i]), pixmap, mRawData, textureSize); + } +} + +// parse compressed image into a texture buffer +bool TextureLoader::AddImage(TextureItem* texItem, + QImage& pixmap, + unsigned int* buffPos, + int stride) +{ + int width = pixmap.width(); + int height = pixmap.height(); + int buffLen = width * height; + buffPos += stride * texItem->ty + texItem->tx; + for (int i = 0; i < height; i++) { + unsigned int* line = reinterpret_cast(pixmap.scanLine(i)); + for (int j = 0; j < width; j++) { + buffPos[j] = line[j]; + } + buffPos += stride; + } + texItem->w = width; + texItem->h = height; + return true; +} + +MillSim::TextureLoader::~TextureLoader() +{ + if (mRawData != nullptr) { + free(mRawData); + } +} + +unsigned int* MillSim::TextureLoader::GetRawData() +{ + return mRawData; +} + +TextureItem* MillSim::TextureLoader::GetTextureItem(int i) +{ + return texItems + i; +} diff --git a/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.h b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.h new file mode 100644 index 0000000000..56ea6ab6de --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/TextureLoader.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (c) 2024 Shai Seger * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library 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 Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ***************************************************************************/ + +#ifndef __texture_loader_h__ +#define __texture_loader_h__ +#include +#include +#include + +namespace MillSim +{ + +struct TextureItem +{ + int tx, ty; // texture location + int w, h; // item size +}; + +class TextureLoader +{ +public: + TextureLoader(std::string imgFolder, std::vector fileNames, int textureSize); + ~TextureLoader(); + unsigned int* GetRawData(); + TextureItem* GetTextureItem(int i); + +protected: + bool AddImage(TextureItem* guiItem, QImage& pixmap, unsigned int* buffPos, int stride); + +protected: + unsigned int* mRawData = nullptr; + std::string mImageFolder; +}; + +} // namespace MillSim +#endif // !__texture_loader_h__ diff --git a/src/Mod/CAM/PathSimulator/AppGL/linmath.h b/src/Mod/CAM/PathSimulator/AppGL/linmath.h new file mode 100644 index 0000000000..2346ff2a47 --- /dev/null +++ b/src/Mod/CAM/PathSimulator/AppGL/linmath.h @@ -0,0 +1,634 @@ +/*************************************************************************************************/ +/* Taken from https://github.com/datenwolf/linmath.h under 'Do whatever you want' license */ +/*************************************************************************************************/ +#ifndef LINMATH_H +#define LINMATH_H + +#include +#include +#include + +#ifdef LINMATH_NO_INLINE +#define LINMATH_H_FUNC static +#else +#define LINMATH_H_FUNC static inline +#endif + +#define LINMATH_H_DEFINE_VEC(n) \ + typedef float vec##n[n]; \ + LINMATH_H_FUNC void vec##n##_add(vec##n r, vec##n const a, vec##n const b) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = a[i] + b[i]; \ + } \ + LINMATH_H_FUNC void vec##n##_sub(vec##n r, vec##n const a, vec##n const b) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = a[i] - b[i]; \ + } \ + LINMATH_H_FUNC void vec##n##_scale(vec##n r, vec##n const v, float const s) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = v[i] * s; \ + } \ + LINMATH_H_FUNC float vec##n##_mul_inner(vec##n const a, vec##n const b) \ + { \ + float p = 0.f; \ + int i; \ + for (i = 0; i < n; ++i) \ + p += b[i] * a[i]; \ + return p; \ + } \ + LINMATH_H_FUNC float vec##n##_len(vec##n const v) \ + { \ + return sqrtf(vec##n##_mul_inner(v, v)); \ + } \ + LINMATH_H_FUNC void vec##n##_norm(vec##n r, vec##n const v) \ + { \ + float k = 1.f / vec##n##_len(v); \ + vec##n##_scale(r, v, k); \ + } \ + LINMATH_H_FUNC void vec##n##_min(vec##n r, vec##n const a, vec##n const b) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = a[i] < b[i] ? a[i] : b[i]; \ + } \ + LINMATH_H_FUNC void vec##n##_max(vec##n r, vec##n const a, vec##n const b) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = a[i] > b[i] ? a[i] : b[i]; \ + } \ + LINMATH_H_FUNC void vec##n##_dup(vec##n r, vec##n const src) \ + { \ + int i; \ + for (i = 0; i < n; ++i) \ + r[i] = src[i]; \ + } + +LINMATH_H_DEFINE_VEC(2) +LINMATH_H_DEFINE_VEC(3) +LINMATH_H_DEFINE_VEC(4) + +LINMATH_H_FUNC void vec3_set(vec3 r, float x, float y, float z) +{ + r[0] = x; + r[1] = y; + r[2] = z; +} + +LINMATH_H_FUNC void vec3_mul_cross(vec3 r, vec3 const a, vec3 const b) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; +} + +LINMATH_H_FUNC void vec3_reflect(vec3 r, vec3 const v, vec3 const n) +{ + float p = 2.f * vec3_mul_inner(v, n); + int i; + for (i = 0; i < 3; ++i) { + r[i] = v[i] - p * n[i]; + } +} + +LINMATH_H_FUNC void vec4_mul_cross(vec4 r, vec4 const a, vec4 const b) +{ + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; + r[3] = 1.f; +} + +LINMATH_H_FUNC void vec4_reflect(vec4 r, vec4 const v, vec4 const n) +{ + float p = 2.f * vec4_mul_inner(v, n); + int i; + for (i = 0; i < 4; ++i) { + r[i] = v[i] - p * n[i]; + } +} + +typedef vec4 mat4x4[4]; +LINMATH_H_FUNC void mat4x4_identity(mat4x4 M) +{ + int i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + M[i][j] = i == j ? 1.f : 0.f; + } + } +} +LINMATH_H_FUNC void mat4x4_dup(mat4x4 M, mat4x4 const N) +{ + int i; + for (i = 0; i < 4; ++i) { + vec4_dup(M[i], N[i]); + } +} +LINMATH_H_FUNC void mat4x4_row(vec4 r, mat4x4 const M, int i) +{ + int k; + for (k = 0; k < 4; ++k) { + r[k] = M[k][i]; + } +} +LINMATH_H_FUNC void mat4x4_col(vec4 r, mat4x4 const M, int i) +{ + int k; + for (k = 0; k < 4; ++k) { + r[k] = M[i][k]; + } +} +LINMATH_H_FUNC void mat4x4_transpose(mat4x4 M, mat4x4 const N) +{ + // Note: if M and N are the same, the user has to + // explicitly make a copy of M and set it to N. + int i, j; + for (j = 0; j < 4; ++j) { + for (i = 0; i < 4; ++i) { + M[i][j] = N[j][i]; + } + } +} +LINMATH_H_FUNC void mat4x4_add(mat4x4 M, mat4x4 const a, mat4x4 const b) +{ + int i; + for (i = 0; i < 4; ++i) { + vec4_add(M[i], a[i], b[i]); + } +} +LINMATH_H_FUNC void mat4x4_sub(mat4x4 M, mat4x4 const a, mat4x4 const b) +{ + int i; + for (i = 0; i < 4; ++i) { + vec4_sub(M[i], a[i], b[i]); + } +} +LINMATH_H_FUNC void mat4x4_scale(mat4x4 M, mat4x4 const a, float k) +{ + int i; + for (i = 0; i < 4; ++i) { + vec4_scale(M[i], a[i], k); + } +} +LINMATH_H_FUNC void mat4x4_scale_aniso(mat4x4 M, mat4x4 const a, float x, float y, float z) +{ + vec4_scale(M[0], a[0], x); + vec4_scale(M[1], a[1], y); + vec4_scale(M[2], a[2], z); + vec4_dup(M[3], a[3]); +} +LINMATH_H_FUNC void mat4x4_mul(mat4x4 M, mat4x4 const a, mat4x4 const b) +{ + mat4x4 temp; + int k, r, c; + for (c = 0; c < 4; ++c) { + for (r = 0; r < 4; ++r) { + temp[c][r] = 0.f; + for (k = 0; k < 4; ++k) { + temp[c][r] += a[k][r] * b[c][k]; + } + } + } + mat4x4_dup(M, temp); +} +LINMATH_H_FUNC void mat4x4_mul_vec4(vec4 r, mat4x4 const M, vec4 const v) +{ + int i, j; + for (j = 0; j < 4; ++j) { + r[j] = 0.f; + for (i = 0; i < 4; ++i) { + r[j] += M[i][j] * v[i]; + } + } +} +LINMATH_H_FUNC void mat4x4_translate(mat4x4 T, float x, float y, float z) +{ + mat4x4_identity(T); + T[3][0] = x; + T[3][1] = y; + T[3][2] = z; +} +LINMATH_H_FUNC void mat4x4_translate_in_place(mat4x4 M, float x, float y, float z) +{ + vec4 t = {x, y, z, 0}; + vec4 r; + int i; + for (i = 0; i < 4; ++i) { + mat4x4_row(r, M, i); + M[3][i] += vec4_mul_inner(r, t); + } +} +LINMATH_H_FUNC void mat4x4_translate_in_place_v(mat4x4 M, vec3 v) +{ + vec4 t = {v[0], v[1], v[2], 0}; + vec4 r; + int i; + for (i = 0; i < 4; ++i) { + mat4x4_row(r, M, i); + M[3][i] += vec4_mul_inner(r, t); + } +} +LINMATH_H_FUNC void mat4x4_from_vec3_mul_outer(mat4x4 M, vec3 const a, vec3 const b) +{ + int i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + M[i][j] = i < 3 && j < 3 ? a[i] * b[j] : 0.f; + } + } +} +LINMATH_H_FUNC void mat4x4_rotate(mat4x4 R, mat4x4 const M, float x, float y, float z, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + vec3 u = {x, y, z}; + + if (vec3_len(u) > 1e-4) { + vec3_norm(u, u); + mat4x4 T; + mat4x4_from_vec3_mul_outer(T, u, u); + + mat4x4 S = {{0, u[2], -u[1], 0}, {-u[2], 0, u[0], 0}, {u[1], -u[0], 0, 0}, {0, 0, 0, 0}}; + mat4x4_scale(S, S, s); + + mat4x4 C; + mat4x4_identity(C); + mat4x4_sub(C, C, T); + + mat4x4_scale(C, C, c); + + mat4x4_add(T, T, C); + mat4x4_add(T, T, S); + + T[3][3] = 1.f; + mat4x4_mul(R, M, T); + } + else { + mat4x4_dup(R, M); + } +} +LINMATH_H_FUNC void mat4x4_rotate_X(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = {{1.f, 0.f, 0.f, 0.f}, {0.f, c, s, 0.f}, {0.f, -s, c, 0.f}, {0.f, 0.f, 0.f, 1.f}}; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_rotate_Y(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = {{c, 0.f, -s, 0.f}, {0.f, 1.f, 0.f, 0.f}, {s, 0.f, c, 0.f}, {0.f, 0.f, 0.f, 1.f}}; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_rotate_Z(mat4x4 Q, mat4x4 const M, float angle) +{ + float s = sinf(angle); + float c = cosf(angle); + mat4x4 R = {{c, s, 0.f, 0.f}, {-s, c, 0.f, 0.f}, {0.f, 0.f, 1.f, 0.f}, {0.f, 0.f, 0.f, 1.f}}; + mat4x4_mul(Q, M, R); +} +LINMATH_H_FUNC void mat4x4_invert(mat4x4 T, mat4x4 const M) +{ + float s[6]; + float c[6]; + s[0] = M[0][0] * M[1][1] - M[1][0] * M[0][1]; + s[1] = M[0][0] * M[1][2] - M[1][0] * M[0][2]; + s[2] = M[0][0] * M[1][3] - M[1][0] * M[0][3]; + s[3] = M[0][1] * M[1][2] - M[1][1] * M[0][2]; + s[4] = M[0][1] * M[1][3] - M[1][1] * M[0][3]; + s[5] = M[0][2] * M[1][3] - M[1][2] * M[0][3]; + + c[0] = M[2][0] * M[3][1] - M[3][0] * M[2][1]; + c[1] = M[2][0] * M[3][2] - M[3][0] * M[2][2]; + c[2] = M[2][0] * M[3][3] - M[3][0] * M[2][3]; + c[3] = M[2][1] * M[3][2] - M[3][1] * M[2][2]; + c[4] = M[2][1] * M[3][3] - M[3][1] * M[2][3]; + c[5] = M[2][2] * M[3][3] - M[3][2] * M[2][3]; + + /* Assumes it is invertible */ + float idet = + 1.0f / (s[0] * c[5] - s[1] * c[4] + s[2] * c[3] + s[3] * c[2] - s[4] * c[1] + s[5] * c[0]); + + T[0][0] = (M[1][1] * c[5] - M[1][2] * c[4] + M[1][3] * c[3]) * idet; + T[0][1] = (-M[0][1] * c[5] + M[0][2] * c[4] - M[0][3] * c[3]) * idet; + T[0][2] = (M[3][1] * s[5] - M[3][2] * s[4] + M[3][3] * s[3]) * idet; + T[0][3] = (-M[2][1] * s[5] + M[2][2] * s[4] - M[2][3] * s[3]) * idet; + + T[1][0] = (-M[1][0] * c[5] + M[1][2] * c[2] - M[1][3] * c[1]) * idet; + T[1][1] = (M[0][0] * c[5] - M[0][2] * c[2] + M[0][3] * c[1]) * idet; + T[1][2] = (-M[3][0] * s[5] + M[3][2] * s[2] - M[3][3] * s[1]) * idet; + T[1][3] = (M[2][0] * s[5] - M[2][2] * s[2] + M[2][3] * s[1]) * idet; + + T[2][0] = (M[1][0] * c[4] - M[1][1] * c[2] + M[1][3] * c[0]) * idet; + T[2][1] = (-M[0][0] * c[4] + M[0][1] * c[2] - M[0][3] * c[0]) * idet; + T[2][2] = (M[3][0] * s[4] - M[3][1] * s[2] + M[3][3] * s[0]) * idet; + T[2][3] = (-M[2][0] * s[4] + M[2][1] * s[2] - M[2][3] * s[0]) * idet; + + T[3][0] = (-M[1][0] * c[3] + M[1][1] * c[1] - M[1][2] * c[0]) * idet; + T[3][1] = (M[0][0] * c[3] - M[0][1] * c[1] + M[0][2] * c[0]) * idet; + T[3][2] = (-M[3][0] * s[3] + M[3][1] * s[1] - M[3][2] * s[0]) * idet; + T[3][3] = (M[2][0] * s[3] - M[2][1] * s[1] + M[2][2] * s[0]) * idet; +} +LINMATH_H_FUNC void mat4x4_orthonormalize(mat4x4 R, mat4x4 const M) +{ + mat4x4_dup(R, M); + float s = 1.f; + vec3 h; + + vec3_norm(R[2], R[2]); + + s = vec3_mul_inner(R[1], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[1], R[1], h); + vec3_norm(R[1], R[1]); + + s = vec3_mul_inner(R[0], R[2]); + vec3_scale(h, R[2], s); + vec3_sub(R[0], R[0], h); + + s = vec3_mul_inner(R[0], R[1]); + vec3_scale(h, R[1], s); + vec3_sub(R[0], R[0], h); + vec3_norm(R[0], R[0]); +} + +LINMATH_H_FUNC void mat4x4_frustum(mat4x4 M, float l, float r, float b, float t, float n, float f) +{ + M[0][0] = 2.f * n / (r - l); + M[0][1] = M[0][2] = M[0][3] = 0.f; + + M[1][1] = 2.f * n / (t - b); + M[1][0] = M[1][2] = M[1][3] = 0.f; + + M[2][0] = (r + l) / (r - l); + M[2][1] = (t + b) / (t - b); + M[2][2] = -(f + n) / (f - n); + M[2][3] = -1.f; + + M[3][2] = -2.f * (f * n) / (f - n); + M[3][0] = M[3][1] = M[3][3] = 0.f; +} +LINMATH_H_FUNC void mat4x4_ortho(mat4x4 M, float l, float r, float b, float t, float n, float f) +{ + M[0][0] = 2.f / (r - l); + M[0][1] = M[0][2] = M[0][3] = 0.f; + + M[1][1] = 2.f / (t - b); + M[1][0] = M[1][2] = M[1][3] = 0.f; + + M[2][2] = -2.f / (f - n); + M[2][0] = M[2][1] = M[2][3] = 0.f; + + M[3][0] = -(r + l) / (r - l); + M[3][1] = -(t + b) / (t - b); + M[3][2] = -(f + n) / (f - n); + M[3][3] = 1.f; +} +LINMATH_H_FUNC void mat4x4_perspective(mat4x4 m, float y_fov, float aspect, float n, float f) +{ + /* NOTE: Degrees are an unhandy unit to work with. + * linmath.h uses radians for everything! */ + float const a = 1.f / tanf(y_fov / 2.f); + + m[0][0] = a / aspect; + m[0][1] = 0.f; + m[0][2] = 0.f; + m[0][3] = 0.f; + + m[1][0] = 0.f; + m[1][1] = a; + m[1][2] = 0.f; + m[1][3] = 0.f; + + m[2][0] = 0.f; + m[2][1] = 0.f; + m[2][2] = -((f + n) / (f - n)); + m[2][3] = -1.f; + + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = -((2.f * f * n) / (f - n)); + m[3][3] = 0.f; +} +LINMATH_H_FUNC void mat4x4_look_at(mat4x4 m, vec3 const eye, vec3 const center, vec3 const up) +{ + /* Adapted from Android's OpenGL Matrix.java. */ + /* See the OpenGL GLUT documentation for gluLookAt for a description */ + /* of the algorithm. We implement it in a straightforward way: */ + + /* TODO: The negation of of can be spared by swapping the order of + * operands in the following cross products in the right way. */ + vec3 f; + vec3_sub(f, center, eye); + vec3_norm(f, f); + + vec3 s; + vec3_mul_cross(s, f, up); + vec3_norm(s, s); + + vec3 t; + vec3_mul_cross(t, s, f); + + m[0][0] = s[0]; + m[0][1] = t[0]; + m[0][2] = -f[0]; + m[0][3] = 0.f; + + m[1][0] = s[1]; + m[1][1] = t[1]; + m[1][2] = -f[1]; + m[1][3] = 0.f; + + m[2][0] = s[2]; + m[2][1] = t[2]; + m[2][2] = -f[2]; + m[2][3] = 0.f; + + m[3][0] = 0.f; + m[3][1] = 0.f; + m[3][2] = 0.f; + m[3][3] = 1.f; + + mat4x4_translate_in_place(m, -eye[0], -eye[1], -eye[2]); +} + +typedef float quat[4]; +#define quat_add vec4_add +#define quat_sub vec4_sub +#define quat_norm vec4_norm +#define quat_scale vec4_scale +#define quat_mul_inner vec4_mul_inner + +LINMATH_H_FUNC void quat_identity(quat q) +{ + q[0] = q[1] = q[2] = 0.f; + q[3] = 1.f; +} +LINMATH_H_FUNC void quat_mul(quat r, quat const p, quat const q) +{ + vec3 w, tmp; + + vec3_mul_cross(tmp, p, q); + vec3_scale(w, p, q[3]); + vec3_add(tmp, tmp, w); + vec3_scale(w, q, p[3]); + vec3_add(tmp, tmp, w); + + vec3_dup(r, tmp); + r[3] = p[3] * q[3] - vec3_mul_inner(p, q); +} +LINMATH_H_FUNC void quat_conj(quat r, quat const q) +{ + int i; + for (i = 0; i < 3; ++i) { + r[i] = -q[i]; + } + r[3] = q[3]; +} +LINMATH_H_FUNC void quat_rotate(quat r, float angle, vec3 const axis) +{ + vec3 axis_norm; + vec3_norm(axis_norm, axis); + float s = sinf(angle / 2); + float c = cosf(angle / 2); + vec3_scale(r, axis_norm, s); + r[3] = c; +} +LINMATH_H_FUNC void quat_mul_vec3(vec3 r, quat const q, vec3 const v) +{ + /* + * Method by Fabian 'ryg' Giessen (of Farbrausch) + t = 2 * cross(q.xyz, v) + v' = v + q.w * t + cross(q.xyz, t) + */ + vec3 t; + vec3 q_xyz = {q[0], q[1], q[2]}; + vec3 u = {q[0], q[1], q[2]}; + + vec3_mul_cross(t, q_xyz, v); + vec3_scale(t, t, 2); + + vec3_mul_cross(u, q_xyz, t); + vec3_scale(t, t, q[3]); + + vec3_add(r, v, t); + vec3_add(r, r, u); +} +LINMATH_H_FUNC void mat4x4_from_quat(mat4x4 M, quat const q) +{ + float a = q[3]; + float b = q[0]; + float c = q[1]; + float d = q[2]; + float a2 = a * a; + float b2 = b * b; + float c2 = c * c; + float d2 = d * d; + + M[0][0] = a2 + b2 - c2 - d2; + M[0][1] = 2.f * (b * c + a * d); + M[0][2] = 2.f * (b * d - a * c); + M[0][3] = 0.f; + + M[1][0] = 2 * (b * c - a * d); + M[1][1] = a2 - b2 + c2 - d2; + M[1][2] = 2.f * (c * d + a * b); + M[1][3] = 0.f; + + M[2][0] = 2.f * (b * d + a * c); + M[2][1] = 2.f * (c * d - a * b); + M[2][2] = a2 - b2 - c2 + d2; + M[2][3] = 0.f; + + M[3][0] = M[3][1] = M[3][2] = 0.f; + M[3][3] = 1.f; +} + +LINMATH_H_FUNC void mat4x4o_mul_quat(mat4x4 R, mat4x4 const M, quat const q) +{ + /* XXX: The way this is written only works for orthogonal matrices. */ + /* TODO: Take care of non-orthogonal case. */ + quat_mul_vec3(R[0], q, M[0]); + quat_mul_vec3(R[1], q, M[1]); + quat_mul_vec3(R[2], q, M[2]); + + R[3][0] = R[3][1] = R[3][2] = 0.f; + R[0][3] = M[0][3]; + R[1][3] = M[1][3]; + R[2][3] = M[2][3]; + R[3][3] = M[3][3]; // typically 1.0, but here we make it general +} +LINMATH_H_FUNC void quat_from_mat4x4(quat q, mat4x4 const M) +{ + float r = 0.f; + int i; + + int perm[] = {0, 1, 2, 0, 1}; + int* p = perm; + + for (i = 0; i < 3; i++) { + float m = M[i][i]; + if (m < r) { + continue; + } + m = r; + p = &perm[i]; + } + + r = sqrtf(1.f + M[p[0]][p[0]] - M[p[1]][p[1]] - M[p[2]][p[2]]); + + if (r < 1e-6) { + q[0] = 1.f; + q[1] = q[2] = q[3] = 0.f; + return; + } + + q[0] = r / 2.f; + q[1] = (M[p[0]][p[1]] - M[p[1]][p[0]]) / (2.f * r); + q[2] = (M[p[2]][p[0]] - M[p[0]][p[2]]) / (2.f * r); + q[3] = (M[p[2]][p[1]] - M[p[1]][p[2]]) / (2.f * r); +} + +LINMATH_H_FUNC void mat4x4_arcball(mat4x4 R, mat4x4 const M, vec2 const _a, vec2 const _b, float s) +{ + vec2 a; + memcpy(a, _a, sizeof(a)); + vec2 b; + memcpy(b, _b, sizeof(b)); + + float z_a = 0.; + float z_b = 0.; + + if (vec2_len(a) < 1.) { + z_a = sqrtf(1.f - vec2_mul_inner(a, a)); + } + else { + vec2_norm(a, a); + } + + if (vec2_len(b) < 1.f) { + z_b = sqrtf(1.f - vec2_mul_inner(b, b)); + } + else { + vec2_norm(b, b); + } + + vec3 a_ = {a[0], a[1], z_a}; + vec3 b_ = {b[0], b[1], z_b}; + + vec3 c_; + vec3_mul_cross(c_, a_, b_); + + float const angle = acosf(vec3_mul_inner(a_, b_)) * s; + mat4x4_rotate(R, M, c_[0], c_[1], c_[2], angle); +} + +#endif diff --git a/src/Mod/CAM/PathSimulator/CMakeLists.txt b/src/Mod/CAM/PathSimulator/CMakeLists.txt index 2e75b6a147..6f537ca40d 100644 --- a/src/Mod/CAM/PathSimulator/CMakeLists.txt +++ b/src/Mod/CAM/PathSimulator/CMakeLists.txt @@ -1,8 +1,9 @@ add_subdirectory(App) -# if(BUILD_GUI) - # add_subdirectory(Gui) -# endif(BUILD_GUI) +add_subdirectory(AppGL) +#if(BUILD_GUI) +# add_subdirectory(Gui) +#endif(BUILD_GUI) # install( # FILES