From df71b67cc0e14b56e6418d2d907e13eb8e511f5f Mon Sep 17 00:00:00 2001 From: amirghadiri1987 <144431373+amirghadiri1987@users.noreply.github.com> Date: Fri, 7 Feb 2025 19:03:59 +0330 Subject: [PATCH] Add files via upload --- Addons/Indicators/ZigZag/ZigZagModule.mqh | Bin 0 -> 83634 bytes Arrays/Array.mqh | 182 + Arrays/ArrayChar.mqh | 771 ++++ Arrays/ArrayDouble.mqh | 777 ++++ Arrays/ArrayFloat.mqh | 778 ++++ Arrays/ArrayInt.mqh | 770 ++++ Arrays/ArrayLong.mqh | 770 ++++ Arrays/ArrayObj.mqh | 759 +++ Arrays/ArrayShort.mqh | 770 ++++ Arrays/ArrayString.mqh | 780 ++++ Arrays/List.mqh | 657 +++ Arrays/Tree.mqh | 415 ++ Arrays/TreeNode.mqh | 174 + Canvas/Canvas.mqh | 4861 ++++++++++++++++++++ Canvas/Canvas3D.mqh | Bin 0 -> 34652 bytes Canvas/Charts/ChartCanvas.mqh | 1041 +++++ Canvas/Charts/HistogramChart.mqh | 347 ++ Canvas/Charts/LineChart.mqh | 383 ++ Canvas/Charts/PieChart.mqh | 414 ++ Canvas/CustomCanvas.mqh | 2596 +++++++++++ Canvas/DX/DXBox.mqh | 77 + Canvas/DX/DXBuffers.mqh | 92 + Canvas/DX/DXData.mqh | Bin 0 -> 4048 bytes Canvas/DX/DXDispatcher.mqh | 373 ++ Canvas/DX/DXHandle.mqh | Bin 0 -> 8568 bytes Canvas/DX/DXInput.mqh | Bin 0 -> 4968 bytes Canvas/DX/DXMath.mqh | 3352 ++++++++++++++ Canvas/DX/DXMesh.mqh | 375 ++ Canvas/DX/DXObject.mqh | Bin 0 -> 1744 bytes Canvas/DX/DXObjectBase.mqh | Bin 0 -> 2106 bytes Canvas/DX/DXShader.mqh | Bin 0 -> 16984 bytes Canvas/DX/DXSurface.mqh | 162 + Canvas/DX/DXTexture.mqh | Bin 0 -> 6904 bytes Canvas/DX/DXUtils.mqh | 942 ++++ Canvas/DX/Shaders/DefaultShaderPixel.hlsl | 71 + Canvas/DX/Shaders/DefaultShaderVertex.hlsl | 72 + Canvas/FlameCanvas.mqh | 751 +++ 37 files changed, 23512 insertions(+) create mode 100644 Addons/Indicators/ZigZag/ZigZagModule.mqh create mode 100644 Arrays/Array.mqh create mode 100644 Arrays/ArrayChar.mqh create mode 100644 Arrays/ArrayDouble.mqh create mode 100644 Arrays/ArrayFloat.mqh create mode 100644 Arrays/ArrayInt.mqh create mode 100644 Arrays/ArrayLong.mqh create mode 100644 Arrays/ArrayObj.mqh create mode 100644 Arrays/ArrayShort.mqh create mode 100644 Arrays/ArrayString.mqh create mode 100644 Arrays/List.mqh create mode 100644 Arrays/Tree.mqh create mode 100644 Arrays/TreeNode.mqh create mode 100644 Canvas/Canvas.mqh create mode 100644 Canvas/Canvas3D.mqh create mode 100644 Canvas/Charts/ChartCanvas.mqh create mode 100644 Canvas/Charts/HistogramChart.mqh create mode 100644 Canvas/Charts/LineChart.mqh create mode 100644 Canvas/Charts/PieChart.mqh create mode 100644 Canvas/CustomCanvas.mqh create mode 100644 Canvas/DX/DXBox.mqh create mode 100644 Canvas/DX/DXBuffers.mqh create mode 100644 Canvas/DX/DXData.mqh create mode 100644 Canvas/DX/DXDispatcher.mqh create mode 100644 Canvas/DX/DXHandle.mqh create mode 100644 Canvas/DX/DXInput.mqh create mode 100644 Canvas/DX/DXMath.mqh create mode 100644 Canvas/DX/DXMesh.mqh create mode 100644 Canvas/DX/DXObject.mqh create mode 100644 Canvas/DX/DXObjectBase.mqh create mode 100644 Canvas/DX/DXShader.mqh create mode 100644 Canvas/DX/DXSurface.mqh create mode 100644 Canvas/DX/DXTexture.mqh create mode 100644 Canvas/DX/DXUtils.mqh create mode 100644 Canvas/DX/Shaders/DefaultShaderPixel.hlsl create mode 100644 Canvas/DX/Shaders/DefaultShaderVertex.hlsl create mode 100644 Canvas/FlameCanvas.mqh diff --git a/Addons/Indicators/ZigZag/ZigZagModule.mqh b/Addons/Indicators/ZigZag/ZigZagModule.mqh new file mode 100644 index 0000000000000000000000000000000000000000..14f14da305fdc1eb37755119b6074baff2a387c3 GIT binary patch literal 83634 zcmeHQ`*R$}ao(R(mH*+al;wj7iF(+nltj`cf|A7)DKP}?#G+LwlHdb2NYMZ#QlXtc zJxPDl_|V<+*xS7abO)iX033E_XL|bGJw3Dk_rI6B%iX2!y*2n;>i)U=CxPa2_xEnH z^3ONjLHD%#rh6d2Z+G8y``!25VfVOuzkA;Ot$QZp|9t^?cBIFb-H+W-8s%B{O8Wb# z`=I-<`?qdezTcLSUdh=1A)|gL-yV0za&=d}Kau}l%bg>+HtEpMk@WPwd|w2f;rIls zuVlpI?vq4|*8}faDJ?rP-v0wWvhs5LmJ=f3u?%VED z*PD%QBVbI7-uJ->kXz`aJsIcC$#~C2N}db6pPoLA5hmSN>H0%yc_jZqD@~-;gYE~p z0*IfEGVtGv)N))9q(4UH%WA}XBHpRXy-@OF~2;aYi(2;8Eq*8pJ%)@ z5gvopdn8!(at@Ck`k)tF# z6X87c`@qd(9RCA)=w;C8vC#Tu!F%KSBaQldR9ujXHGCTnu~i#FqdNHXrRYv*5NH>V z?b;~$wYK%2idFkg`rPlfg00PaDF6Q-vF+ZY@;*h`t4W&8s%~jZ`)qteD-ON!)bR%A zP(o=}c4VIL0CJsu+)a~}c_G{aPl7rLKA~FUh?!aNPbJRTl74FQc#w4JL`M85waI+| zmEe~G13V4G8DX;7l;8U1NBjV;xAFt4|4R7jNZ^?!4~&?t`A3-lr2EH6c%q&F4pV>t43`F)ySO^Xl{CI0M+KJ9t?#)&qHoDwz2yC>HBS<*Dyfv4&% z534P69rCWs+^kIG#bwThJceiymLI;#_Ng>H6mEGUANAZ`$bZmerWI$kq(0tuTx1xZ z?;CiQQVZFI=cSR}vB>#z(U!1$krJPacmD^;KBI z_vK0BuK6Ier#!5scL3<&AH7QUN~s;;u0I2fW?l$+!=Uyu@ds(kUM4cqGnpyk8sP1F z;a1fL-X|KuUEd^peHSc&Neg=*WQq@i3Xrz!nzfpAw*)(?ougiWgy%7g&7K@>R4Bid zaZS`to^Ex`Swi6Tr9tE8b2CX8ECl0A$XsbNCMSL~{{ATUk(oG7Jo-rH{X~8=BE?LR z?|I`mcF62+37^02t|#7$x{Xg;&yj|_Ut-j*d@1lDPM)WKkF7gNvJq1eTQ%D{9z#(p z8{GbzV#pvEhovDS5KHrp;W2ejFpju}cv?PJQG?JXd0g?k;*7A2#aM3t*sJ{|#>S3N zm&NF(K)w#dk^_gU;ix#??#XCFvDBIa?6Fb(Q0yLN(#jAG;mZi;vE=(OhS!eu*btdP z4^0k5)Y+=P=reyWx=r){`vN`m9rT*{%Ygf}T(dTYk708HWckcs8C&z3zt>vf23jyNdC9?Fj@m3|&~h3gnwL_b449HXjce9gvbrnP0`TC;_h z2S3mBL-{SvR_#$M?lX&LKJB~0jpm_gE-SyQzL?&NwsJA-Xr4tgit}>BcpRBE8iJ=N zB@tT&|F+07GQiA(YCFZ7wOS_b%u1aVZ_y?V!&+|ZBIXD^@K$eG_y3n6lN@^-$7%io zF&Q#Sh!r7CSbKw1YYeEXD(wG>w0V$x7SmtjxpfxSV@&IPdg@AxWRYF=v98w#wI_8} z$P2cgv$>VJY0MQrzD91u@3JCZKfXo z|GgOdnm^?-RZ~Q6&$M`25!NX&okQ4i?QcRYg2Mi-fzS;eVGzU?a4!kyy#KF z3gL^qF&})f*GRWEbcO>DVLsPVKdbC7!m;@nQ8MSjW7Ip#kt$EA@et;7oCq&NKjc+P z(eKDDN(;+FwCOF8Jm^E#*+Cq-!gAuJN(lgEyl2bYDAb zJr9}hu6XOnTShs_GX!}&r!nY(*!rv8JMxJ>*pP9-8=OHoHhW;dHYblQQ0TdQH1dI8 z#;D_w%u1szdy;(`t;=t*$NDNeVbZ6^_o^7jdg_q=ZN8X^W};@*HDMO0CV@RePAjil z$|Jp(Np~I#9+A%m-dGhi9(x$~hV+3*G|CM%-t2rwJ68eO8?vfyaF;Y5a>b7`E7PD_ zlR1Xc$AauAFWPE&Eo+8$G45Y{jX&yaR)H9WQ5#r;#p*it96z9S^r)eT{m(f0Vgv^m0?UPG>WfzcEkj zYe|n&)l9_Em{H*&j~Uf6gPt}vV^5zQ!5F-x#chV~DeCUJ#6z=KHThn$u!_xCM>5XJ z__;+Wp!c{=8gp8H1jCZQ+Tx8k0#`=w2`}slMwv^l&mSD1^NpNgGXN|@{F^(8exM+# z<0ewgXR{c>XYu(DGYuU%8(gjkmOw{Ntp46Ej|?T_}JD zd)i0naa*Qd?ww;^7E1Wd?-I3xVeHOK9Rac`kBzJujGH! zai9-Wjv)fUT%&chdi3q2b&*-*EEIQRdo~NY{Jxptn$@#bFR!a^ypgD3m6&I(8U6m+ zb+R38?FSwlx&wjs+vI<}xxwe6xDryUc;sH!*P_dgy1$ABSk`AfEv4lF`uU?4?-=e({s%7;@=xCFc2jAj7!nSC= zo!{xVo$P;~_vWd+-da<64>FkV^hYia9?yf5$}_B!uHv)VTC?AerS7e3NNP3Psm)!? zitDT5o94O<-m!;aTrY%fJ9*jj_d^|_ zD|3tXMyO1KEjCL+XZmKb{tUiWkL#^dG*bzE@{Mq%=ABV-Wtq`dMs!&^X0vsQ;;+62 z<2|vM=VA_h)jKKamAyU95NPZT3-V0jV)J>Rk)pm}c5-^U>oaN>`j&?y`#H?C6#Mb8 zz;<2DAiN~H&qJ&l%Cx7h9l+A7eFQ(3c99qX$0JM7+SkXq`u?$!jw}XNx1dL~eu_B` zP@DUjDNh(buUulCFj^Zl8tF_$yX$E$2PYN+^t$5cgtgxT$$nl6nGoR1(^zf4hyA8V zwB=)vW7T?loceNc*Nu8pu1K`2yz_AL+iaLEsM_J3+Yws zbpf|gkn~VlQ(pJ0zi7!PIky`3p+cVC5_!r?R2>VN*Mk;4kX6NN;<@xuoUk5?M9t!y zI=)I7Q<|VR!lFAu@1Ks~dCnmCRv@*{)zhbSCM7qu#-U#{Y`m+2r!jrgry0^aqI%t% z**}Ma(*!U#3%g@erV15?A1pzdgvKVJOio4`zE7=k2>Wn zxE^OK4B>jhv^t*G5$Zg`(d+!qIn=45dfVsj`?)=iHH_Ew8Tb|CU1|m|x4!4I;B$^Q zoXh#da2~g~gnW)1y{^@2RfU&F+r1Wdx1wU@h4W*8y?w!@+J9gb_pgR{l~U}n{z1~; zrbkP5_->7aqmaHKEGU>)wW&#oagUAQC8wRsG!N5yD>Z~d-=C9<`O`6ZIm>RvoPqKy z&+=q!SC`QD(8>4cua{|}rV#{>Zo#%{e<#G#V_TuGy zl{s~#Lw#+gUk`Xc&Uk@-oWI-e<(bs2V@!HW==fT+wyjbOKM_nhVbR~~8IyY73{~Bp zgTSwRmk9h4#i8xNo?W|7-Riv^wFr=3)W5z-x&78^oN=yl=2pdm3V zq>qr)PRsRKH|R@37WeL~H-CS5;#L229KQMdO&tC-98Mo?l|0TdHkHe%#kJlhk6?$M z@VJO3Ur#P)WH3a3>yG}j*WFyt7{cGB_cT<0bCgZxaOxmCM={nate(d=nZt>_v*&SY zR`TDfxI7kq^ z`P(>~%JI}qrrC+Pv*P*l!STf7Pr>u1eM!4jaeX<`rtjb>Wx#aRB3^oY zc3t@%D+)Y4xnIj;m#>`%Vb#jZ()P%+^zR!o|66ezvo5Gk>GRoRmWed4uZG+d`-O9& za5@>Pa(LD=uIkQeeDhwh!ItNiekuFrQ1cr1Qey_*{^^X&TB4eYIiSnZ!>7dxp3MN6 zt0w~MLxI-sQ=$AqYQL50ojjAJ*NHRZSIaVSm8uUP$j{*fqB=HkUKDU>`01bdVZzk5 ztYI2u3^HFQ7<)twD^^l#=gx1XN>%eae19bk^e)DqIvOZEjZh=D`Bl=HWkt_y!oubn}@0XET2Kno5lrE8xR7|-S+G{ zt`5X^TwtxRep<8VmgRmwZ#Js-h7t0Mm!;e?VD%8%2U&dOzZ$3auU5~pX6+SL_$Pqi zlEBcixd*+LE|&!KwmEVTeKoG>Ql8Bgt9~!-EWk@_ zty8yU!Aq%9ay~2XWop^p56aGd zs&~95@LKHWqF>7B)BF`^k6CZt@Hm_A`+DjZ<~7D-m`YsEw_Y5*VIQ6Q%nbH9)kefA z;kx&!e%EunS!cPiLkb3N6^)(DvWT9kv=lQ-?%U}LybS9kLh*U9IWyR#im z@l9-6J(Azm-7R|0@>Sk`=GjH9T3;-4irk@ku;gZocPH{r>-Eot8ocy5s6G8o9L1~d z6RAtHdgIUqd9IXe>Y6jP(@|qZd2ID*mi2`Ea0e4%84C?_x^bi&9s)F)SW+EX)10E7=e;Jubv?VoSD6|IA=6s#EBD*xi*?(%^!ljt z<);_NaxOJ(ytE{FK8od7&Lz`yTJ&XXcbDPOo;@Evi0y+%Hg9oHNaMA$Fk^MxPg>#u?T`PWd*55oz8 ztXn@wX8`bBfLH~C)T8o4-;71B!3pg+H5c$K=VS1!Q?72l^{{*5@#q;)^;Oz^Ca-Hm*fF(pbmdo z=OlV4NFPsya);n@J$tjEc9JLMa3Ye4Q{V6gY`p8TEy=7M#i>?!YaQP>&7Mr6jXTPj z;bf$i&HhTFf?M+OHm%)*uE}t8@ZnX7B+4E%kNS(Oe$z{sr$r0CZP8*1c^r54Zm}|c zFnV}Q`MpD!jbB~Tcj`vG)Z5cu)}F}Y$2fww+0hR#X%N?GHS$u8g?I2qIP+1B`Yno; zA*X~b)9$<`UbTc`spXaH1xV7gg zR$1?h{6U%?$sF_Zh?RF^<#8?QF%GWp&Ux$B6%<2$E;qyyqizKm8dN7x(#cCL%l#|C zB;K_G_(37oxENRKSdrqCqx?it&o%a(QN8utr9i8k(q*qZiIe;r+M_icV*jFj0`_yu zVRP2=KQWK(Os4byO#;br#?9V0M{U(26vLT>Gjmwr4E2C zhvj=D9ye^KzDXFimpq5JN9)PC@Tjo!$e#d*3R<)FW@&K^7TC_*We)x7$?5&KW!yL2 zALX~c=jNt7q38A4JF<9=GwqM}655a}?iFspF(2G`5+_g-DuWx>`rsz*YfyU(VMnvR z8^Vu+n$Om0g)I*mRM58-mH3|BA1Uqq=EdR&Z(IDDh-TQ-(<&>H89Nz{%xf{r1}}Y@ zMNh+gP*i@lFo^f9BLU=D-uh$+d9~I zE!R`CfeYcE_U{VPGtc`x)w-+^$C~x5OAjGXER~iUUII_3&^B=_KH<+B&!6BJuwIB# zunODnTN>n^#~B$+laB6528x~j-b!p4RrGoPyETTkVPB@n zK&yk0XZF3try{-TvseGC>;qR+-{kl5tEQFu_xP7{@pQLdHbb{D^7E9jcW*CSvmxe! zzX4slPWYkJ57TXEU5{p?zTsQ88AHH}Fd6a^!|}=Y_}@pkvo)JO5hdlOtft+Qer|W4 zb^jG)jpGg1a-4(b_0{V*j=BsD7yH7yq;i}YZE{^vJDbk8VPEJ`Gcv>UTMhx9QvDX8 zJxlGTFxgBz#WzX2neM{s7ot=6FMJ2K?wn#9J*~p(o98`CX+5=D)Jpw2PFGZkt=*Kp zjH{Q6oST&Qr2S9BXkmpmHTuDA%DVZ^Uf-{3uMl1tkacyi{pVJNT_i(liV7YuU z_OB~%di!}v061sdi9bIN2{3M9zQ;J!+w~}U%F+2Lc6KTsc~~vWPay?=kTve}C;?b& zWv&HwKy#HtY{0SD4ZlM{a{!Z*iUsOPS`|?4=OO_mMrWOM74 zHW|grjF+uV(&3T+kY7ix9knA`X+(b1lPyrqqi-WI|IPcU!@jR42V+LF^H0&kxSq1I zv-f7zwYO?u!z9m^LC(8%cH^(*L}WcR!~74$dgOj>)Am_7Thi6?ROaDq#$F|xfzD}~ z@@XddyJasn!uaIe`Q9IZFt%pjl zx!Vg_vUq>1i|3VbeI~YQ?Hg6DEYD$xSm}rI1Cj4OC~-fBQuBQVBiiIJmNP-CHfD3J zK|H^ga$)-$@^7P7#3$maAgHVl-}Cz zo${F|OSNZA&zyyJVZDM%Jyr&C{SsT`vTJe~+vW008IP@0epQ=B%+$4vJq_vahqh1C z#C~#EYjYZ-ZOEC^6J7J3#HG|f(f2W5eVtYW;TfVDIKhf?2g};Qq{m1 zp8Hbp_f#tKZ;OX)xSW^ks0~ZLi@h74@`=sjJU!({v*XAz!nau7{{A7_ar3pE;qdM$ z2FLMTi+7RvuItiGvZ%YM<{m5TkHv1nYHIa_={tHRL-=K$HtWh``9AA@tD566+Q56s zems`dy#CBUzn!%j<5Kg%M<>lT-QwO#c@*e6a-`Sm*u{XEu2K`851x#8jJ9`Cj-*BH z<4I37&y#$1v#fD0;TmVyYV_(Okaguse{#k+Eg3T*v$SLMIMq(!e1ZeXKRzz9qpNxY z;#;?GOzj<4ChPCf)i&i*dcB-Q#L4c-?564MP8KsoORG5}=taEe_Hn2#wU%R6Tq3Rw z$1Ug%LjICtma{`8nbON@K6ynmIsA_~5X{M_oMXUZ$60mav7gOZt%+K^rFE=!a{0NB z&DT2-hmnWJY8_ZLd6IK8uKx-3`||`Rq*VU?nKtt!tgwW`3;```H|=Q+hl8 z4eDIqITl;vJs?{}hPJi+#%MP-BfFkj2lJX;_jF68~RaU8W*0o}oyxAXL-Ilc-Wb<~QHD>qf^Th+Mx1($~oh$rWJnL`dljj1o z?h1Z4svvM8nx0LvEFMKa>&d5FRheCRg>#u-XB?96NPq?P=kw^vFb5L%UzN9|yd#{$ zTHv>*-*AQ~YON9HJg)Tbb>Xa?+E}-aK8~Jj#yV5HujN2i;V(HzNaG!W9I;0U#Z;cb zi8VGG50mNmb@U4F%AB4H<$jRAHLmCESNC_b`M#ewDLr+*KD&%ol)D$g#aQKo%;_%t zcZB188{NCIbE@ZViVR}E;nZl4t=1UZVz(4NFGb3K zDogmj-csdth1!g$b(XPvtzRNHTdF+%qGwX~bs~2Ci6Yx4RlneF=(l<7kFG+s$KZas z*dkgM(*UHK_rEkQsO{2;ej=WICVTP_C%!K~IP2g3^U+0Fjr>f!L3{52p7j6c5G-TQ zUBri{$}eU2V0}%`+kM3p<8z!V)$Z}K^yufW&jMH2-BH%7=}9Z)6ToMDLMtmR7X$G~K6BV?V9%eh`e~v&{6eTJLyjQvo~Jl<)Ff1*wHIEo!H( z*3bLbr^@k2Kb+{=ruvw@enWaGX#_aT>L^q#tSxIUbtw2wvLW+mzuByuOHJz#`?)NT zw_9%*ZA(x6-CDz>r~VE!=&vYwrS#NBEv08yyc1Z5@0_P{?BWmftGiF|&L}-6uk?*P z)uNrDKhx)Lq35=D)uuU~@sWo@VKjwqcp`H{jcc4CA|1rF*pH}@&AyxfiW493A4bG_ z%(h%X-N19Xewgm#oglO>=;KxD1vq*neW6l9-_2nnNi9nfIu4ydmR%FO)>YnD@Oc-stu2-{o9|yYlUhc(V9*Q_gz)Tp)m6gT*NQ zPOKqBPj=C`-!h@UDf*}--<`&Au2Jf`~hACm#7ET zrsDiiTo*Zd)csW`eUx}vrKEg26KROQ^N;qQN&ZmfG8G@H#*g#&y(92LGk{;wi&0`P z`Q>_Vxnu4M?SGV!ApN_thxkuYOa8fxy({t@Z7` zd34>U&r58W%{CcY$6OM;KD?*#Q1};?Z z#1p2e`zZ!E^kf-$(r-?5wZ1{*SnMp`q@7C)#>aeG=h4gy3GBGp16$F6|3OG#BfVFF za|xhjn90uHIT3r=7RVtP*!vG(36KuQ6mY9i-gqp3nMQpkJ`NzoZYQmE9X_vC54AbH z(~XDlmSFPr0`S$JuR(XV7>5Oi>I3WpmmQv%clLJgivoJ(0q3%(C6t^P=sJ@VJ45 z@lGjS#1&g8Ubp25^>86ypUHop%Nm+pyDfLI=N{L0lhn=6p>bqlw;yW7c6@R?%xA;5 zQ49KW6O^m4j%WHXa@DYB=jH8o@3;et0PlD{_{tuZ59MvqyvV#^)o@u*Uhe)iX%?)- zjMuqsjD9KDgnzJHI9CNNe4-y$0xNRKzd1uYt(rnP#daE+yv_DMT0PV(;XFJ;4IFp6 zd7UsD#&#deg3|mPzax1J9$P)sApB`Gv!3ca4)eLhTOXIjH@zcL4;_VEA8hh+w2F?w zb9TMOoaIU5J%TyJ>}S>R0ydtGy(rASuR7dk!$GlnY*lp0DP}yzTCnYuU7C-(YIE@T zT`sN8axRDr^AnrLNp}r5`3zd4C{6M;1LI50+|P==ucewldkWEinU#&%h)a^=S4-rh zTZP1D4;HJD(G&UFHC;MVgzReaDlHQQjy(=p0^6f@zmlmyB@CYV_B4MGsln_(8Rzgx zRG}e%nCC!ZKjxX^IUN(ZL(QJdw#Sigkj0F3x>MoIEwKs631dE!?w2RtW=SPmZ;D)i zT1lVNQmVf%nfCXk$GjiQZsyg{n9WY;N$Wt2N8y)5zjdCHP%-R_ZiFBkbz zpOKuUvmw*Dtdc+SzoniY^K8^w7rBM8x$-vtE^-SOxrJO-3TzLPr=+Z|L z`5qVl)|%x}*~qtLfID?5Pm0S?p3JTk};%5gDIzK6c-s~&OBllzMM zX3LR=(`r^v%Y=FCE_ut6#q9q0{q~GS#%HF-?y+sbJiZ@B54AYvJjEW7d$qC;Cp_hE zvG+U>Ey|+*t!L~J=i9#saSXx_sP23Z6C9SeW@5A?^G=u)tbs4Nt zOa;!p?h9Gh|4K4KSndB>zTw(-%28btPPr*C9?QItS4G{+fxw_=%jD2-zbLBH9;eng z>r-pJvC|iCx_>1f<-=ddmCxiy&x+wn9dL*W>)*)FwEL|{S#2NXwEI}DKa<{ZTJvS` zg0Rw1tAE1tsLA@9%s{KN_EWaZOE0+@?OsVcNJ;%1JCpM~r=~P&=@IPW)_0O(XEIKR zZ?!~za9GQaPg@$-%$iBfB0m(|p{`W3i_oa3tkSyk4^Qgz?Y&zqxUtrid;8Sp)3A#j zS>zAW+So*D(`zzgdKEkS+R$Ow6Zm;HbiisGI$)B~$nv23crbL}jy80Fy$0PC!Ck3A z6n8Z`n-&3uW1ZraM2}gzqmLH9k=Y_k=HEkoUwW;*YvR6e0Nzvcu6(rCIzIJL&r_Z^ zl-p}_)jK+4)z*=#Id*yPqf@xbd(b%wb9oOH4iSF}L#cuF`WQXTXzrTG3nb>c=<7ep z-(AtozZZS|nN(t9w||Kn9V_ZJa9h2lYBzlbzy1H*EdZXwo;1+9NsQ*+I^; literal 0 HcmV?d00001 diff --git a/Arrays/Array.mqh b/Arrays/Array.mqh new file mode 100644 index 0000000..ea1e040 --- /dev/null +++ b/Arrays/Array.mqh @@ -0,0 +1,182 @@ +//+------------------------------------------------------------------+ +//| Array.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include +//+------------------------------------------------------------------+ +//| Class CArray | +//| Purpose: Base class of dynamic arrays. | +//| Derives from class CObject. | +//+------------------------------------------------------------------+ +class CArray : public CObject + { +protected: + int m_step_resize; // increment size of the array + int m_data_total; // number of elements + int m_data_max; // maximmum size of the array without memory reallocation + int m_sort_mode; // mode of array sorting + +public: + CArray(void); + ~CArray(void); + //--- methods of access to protected data + int Step(void) const { return(m_step_resize); } + bool Step(const int step); + int Total(void) const { return(m_data_total); } + int Available(void) const { return(m_data_max-m_data_total); } + int Max(void) const { return(m_data_max); } + bool IsSorted(const int mode=0) const { return(m_sort_mode==mode); } + int SortMode(void) const { return(m_sort_mode); } + //--- cleaning method + void Clear(void) { m_data_total=0; } + //--- methods for working with files + virtual bool Save(const int file_handle); + virtual bool Load(const int file_handle); + //--- sorting method + void Sort(const int mode=0); + +protected: + virtual void QuickSort(int beg,int end,const int mode=0) { m_sort_mode=-1; } + //--- templates for methods of searching for minimum and maximum + template + int Minimum(const T &data[],const int start,const int count) const; + template + int Maximum(const T &data[],const int start,const int count) const; + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CArray::CArray(void) : m_step_resize(16), + m_data_total(0), + m_data_max(0), + m_sort_mode(-1) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CArray::~CArray(void) + { + } +//+------------------------------------------------------------------+ +//| Method Set for variable m_step_resize | +//+------------------------------------------------------------------+ +bool CArray::Step(const int step) + { +//--- check + if(step>0) + { + m_step_resize=step; + return(true); + } +//--- failure + return(false); + } +//+------------------------------------------------------------------+ +//| Sorting an array in ascending order | +//+------------------------------------------------------------------+ +void CArray::Sort(const int mode) + { +//--- check + if(IsSorted(mode)) + return; + m_sort_mode=mode; + if(m_data_total<=1) + return; +//--- sort + QuickSort(0,m_data_total-1,mode); + } +//+------------------------------------------------------------------+ +//| Writing header of array to file | +//+------------------------------------------------------------------+ +bool CArray::Save(const int file_handle) + { +//--- check handle + if(file_handle!=INVALID_HANDLE) + { + //--- write start marker - 0xFFFFFFFFFFFFFFFF + if(FileWriteLong(file_handle,-1)==sizeof(long)) + { + //--- write array type + if(FileWriteInteger(file_handle,Type(),INT_VALUE)==INT_VALUE) + return(true); + } + } +//--- failure + return(false); + } +//+------------------------------------------------------------------+ +//| Reading header of array from file | +//+------------------------------------------------------------------+ +bool CArray::Load(const int file_handle) + { +//--- check handle + if(file_handle!=INVALID_HANDLE) + { + //--- read and check start marker - 0xFFFFFFFFFFFFFFFF + if(FileReadLong(file_handle)==-1) + { + //--- read and check array type + if(FileReadInteger(file_handle,INT_VALUE)==Type()) + return(true); + } + } +//--- failure + return(false); + } +//+------------------------------------------------------------------+ +//| Find minimum of array | +//+------------------------------------------------------------------+ +template +int CArray::Minimum(const T &data[],const int start,const int count) const + { + int real_count; +//--- check for empty array + if(m_data_total<1) + { + SetUserError(ERR_USER_ARRAY_IS_EMPTY); + return(-1); + } + //--- check for start is out of range + if(start<0 || start>=m_data_total) + { + SetUserError(ERR_USER_ITEM_NOT_FOUND); + return(-1); + } +//--- compute count of elements + real_count=(count==WHOLE_ARRAY || start+count>m_data_total) ? m_data_total-start : count; +#ifdef __MQL5__ + return(ArrayMinimum(data,start,real_count)); +#else + return(ArrayMinimum(data,real_count,start)); +#endif + } +//+------------------------------------------------------------------+ +//| Find maximum of array | +//+------------------------------------------------------------------+ +template +int CArray::Maximum(const T &data[],const int start,const int count) const + { + int real_count; +//--- check for empty array + if(m_data_total<1) + { + SetUserError(ERR_USER_ARRAY_IS_EMPTY); + return(-1); + } + //--- check for start is out of range + if(start<0 || start>=m_data_total) + { + SetUserError(ERR_USER_ITEM_NOT_FOUND); + return(-1); + } +//--- compute count of elements + real_count=(count==WHOLE_ARRAY || start+count>m_data_total) ? m_data_total-start : count; +#ifdef __MQL5__ + return(ArrayMaximum(data,start,real_count)); +#else + return(ArrayMaximum(data,real_count,start)); +#endif + } +//+------------------------------------------------------------------+ diff --git a/Arrays/ArrayChar.mqh b/Arrays/ArrayChar.mqh new file mode 100644 index 0000000..a034300 --- /dev/null +++ b/Arrays/ArrayChar.mqh @@ -0,0 +1,771 @@ +//+------------------------------------------------------------------+ +//| ArrayChar.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include "Array.mqh" +//+------------------------------------------------------------------+ +//| Class CArrayChar. | +//| Purpose: Class of dynamic array of variables | +//| of char or uchar type. | +//| Derives from class CArray. | +//+------------------------------------------------------------------+ +class CArrayChar : public CArray + { +protected: + char m_data[]; // data array + +public: + CArrayChar(void); + ~CArrayChar(void); + //--- method of identifying the object + virtual int Type(void) const { return(TYPE_CHAR); } + //--- methods for working with files + virtual bool Save(const int file_handle); + virtual bool Load(const int file_handle); + //--- methods of managing dynamic memory + bool Reserve(const int size); + bool Resize(const int size); + bool Shutdown(void); + //--- methods of filling the array + bool Add(const char element); + bool AddArray(const char &src[]); + bool AddArray(const CArrayChar *src); + bool Insert(const char element,const int pos); + bool InsertArray(const char &src[],const int pos); + bool InsertArray(const CArrayChar *src,const int pos); + bool AssignArray(const char &src[]); + bool AssignArray(const CArrayChar *src); + //--- method of access to the array + char At(const int index) const; + char operator[](const int index) const { return(At(index)); } + //--- methods of searching for minimum and maximum + int Minimum(const int start,const int count) const { return(CArray::Minimum(m_data,start,count)); } + int Maximum(const int start,const int count) const { return(CArray::Maximum(m_data,start,count)); } + //--- methods of changing + bool Update(const int index,const char element); + bool Shift(const int index,const int shift); + //--- methods of deleting + bool Delete(const int index); + bool DeleteRange(int from,int to); + //--- methods for comparing arrays + bool CompareArray(const char &array[]) const; + bool CompareArray(const CArrayChar *array) const; + //--- methods for working with the sorted array + bool InsertSort(const char element); + int Search(const char element) const; + int SearchGreat(const char element) const; + int SearchLess(const char element) const; + int SearchGreatOrEqual(const char element) const; + int SearchLessOrEqual(const char element) const; + int SearchFirst(const char element) const; + int SearchLast(const char element) const; + int SearchLinear(const char element) const; + +protected: + virtual void QuickSort(int beg,int end,const int mode=0); + int QuickSearch(const char element) const; + int MemMove(const int dest,const int src,int count); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CArrayChar::CArrayChar(void) + { +//--- initialize protected data + m_data_max=ArraySize(m_data); + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CArrayChar::~CArrayChar(void) + { + if(m_data_max!=0) + Shutdown(); + } +//+------------------------------------------------------------------+ +//| Moving the memory within a single array | +//+------------------------------------------------------------------+ +int CArrayChar::MemMove(const int dest,const int src,int count) + { + int i; +//--- check parameters + if(dest<0 || src<0 || count<0) + return(-1); +//--- check count + if(src+count>m_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayChar::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayChar::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayChar::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayChar::Add(const char element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayChar::AddArray(const char &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(CHAR_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayChar::Update(const int index,const char element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayChar::Shift(const int index,const int shift) + { + char tmp_char; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_char=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_char; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayChar::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayChar::CompareArray(const char &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_char=m_data[(beg+end)>>1]; + while(ip_char) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_char=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_char; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchLinear(const char element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_char=m_data[m]; + if(t_char==element) + break; + if(t_char>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::Search(const char element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchGreat(const char element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]<=element) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchLess(const char element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]>=element) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchGreatOrEqual(const char element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchLessOrEqual(const char element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchFirst(const char element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayChar::SearchLast(const char element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayChar::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayDouble::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayDouble::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayDouble::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayDouble::Add(const double element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayDouble::AddArray(const double &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(DBL_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayDouble::Update(const int index,const double element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayDouble::Shift(const int index,const int shift) + { + double tmp_double; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_double=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_double; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayDouble::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayDouble::CompareArray(const double &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_double=m_data[(beg+end)>>1]; + while(ip_double) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_double=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_double; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchLinear(const double element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_double=m_data[m]; + //--- compare with delta + if(MathAbs(t_double-element)<=m_delta) + break; + if(t_double>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::Search(const double element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + if(MathAbs(m_data[pos]-element)<=m_delta) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchGreat(const double element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + while(m_data[pos]<=element+m_delta) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchLess(const double element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + while(m_data[pos]>=element-m_delta) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchGreatOrEqual(const double element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchLessOrEqual(const double element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchFirst(const double element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + //--- compare with delta + while(MathAbs(m_data[pos]-element)<=m_delta) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayDouble::SearchLast(const double element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + //--- compare with delta + while(MathAbs(m_data[pos]-element)<=m_delta) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayDouble::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayFloat::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayFloat::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayFloat::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayFloat::Add(const float element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayFloat::AddArray(const float &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(FLT_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayFloat::Update(const int index,const float element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayFloat::Shift(const int index,const int shift) + { + float tmp_float; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_float=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_float; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayFloat::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayFloat::CompareArray(const float &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_float=m_data[(beg+end)>>1]; + while(ip_float) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_float=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_float; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchLinear(const float element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_float=m_data[m]; + //--- compare with delta + if(MathAbs(t_float-element)<=m_delta) + break; + if(t_float>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::Search(const float element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + if(MathAbs(m_data[pos]-element)<=m_delta) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchGreat(const float element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + while(m_data[pos]<=element+m_delta) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchLess(const float element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); +//--- compare with delta + while(m_data[pos]>=element-m_delta) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchGreatOrEqual(const float element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchLessOrEqual(const float element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchFirst(const float element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + //--- compare with delta + while(MathAbs(m_data[pos]-element)<=m_delta) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayFloat::SearchLast(const float element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + //--- compare with delta + while(MathAbs(m_data[pos]-element)<=m_delta) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayFloat::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayInt::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayInt::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayInt::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayInt::Add(const int element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayInt::AddArray(const int &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(INT_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayInt::Update(const int index,const int element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayInt::Shift(const int index,const int shift) + { + int tmp_int; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_int=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_int; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayInt::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayInt::CompareArray(const int &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_int=m_data[(beg+end)>>1]; + while(ip_int) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_int=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_int; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchLinear(const int element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_int=m_data[m]; + if(t_int==element) + break; + if(t_int>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::Search(const int element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchGreat(const int element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]<=element) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchLess(const int element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]>=element) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchGreatOrEqual(const int element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchLessOrEqual(const int element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchFirst(const int element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayInt::SearchLast(const int element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayInt::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayLong::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayLong::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayLong::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayLong::Add(const long element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayLong::AddArray(const long &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(LONG_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayLong::Update(const int index,const long element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayLong::Shift(const int index,const int shift) + { + long tmp_long; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_long=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_long; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayLong::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayLong::CompareArray(const long &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_long=m_data[(beg+end)>>1]; + while(ip_long) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_long=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_long; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchLinear(const long element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_long=m_data[m]; + if(t_long==element) + break; + if(t_long>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::Search(const long element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchGreat(const long element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]<=element) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchLess(const long element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]>=element) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchGreatOrEqual(const long element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchLessOrEqual(const long element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchFirst(const long element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayLong::SearchLast(const long element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayLong::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + { + //--- "physical" removal of the object (if necessary and possible) + if(m_free_mode && CheckPointer(m_data[dest+i])==POINTER_DYNAMIC) + delete m_data[dest+i]; + //--- + m_data[dest+i]=m_data[src+i]; + m_data[src+i]=NULL; + } + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayObj::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayObj::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_total>size) + { + //--- "physical" removal of the object (if necessary and possible) + if(m_free_mode) + for(int i=size;i=m_data_total) + return(NULL); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayObj::Update(const int index,CObject *element) + { +//--- check + if(index<0 || !CheckPointer(element) || index>=m_data_total) + return(false); +//--- "physical" removal of the object (if necessary and possible) + if(m_free_mode && CheckPointer(m_data[index])==POINTER_DYNAMIC) + delete m_data[index]; +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayObj::Shift(const int index,const int shift) + { + CObject *tmp_node; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_node=m_data[index]; + m_data[index]=NULL; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_node; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayObj::Delete(const int index) + { +//--- check + if(index>=m_data_total) + return(false); +//--- delete + if(index=0 && MemMove(index,index+1,m_data_total-index-1)<0) + return(false); + } + else + if(m_free_mode && CheckPointer(m_data[index])==POINTER_DYNAMIC) + delete m_data[index]; + m_data_total--; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Detach element from the specified position | +//+------------------------------------------------------------------+ +CObject *CArrayObj::Detach(const int index) + { + CObject *result; +//--- check + if(index>=m_data_total) + return(NULL); +//--- detach + result=m_data[index]; +//--- reset the array element, so as not remove the method MemMove + m_data[index]=NULL; + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + for(int i=to-from+1;i>0;i--,m_data_total--) + if(m_free_mode && CheckPointer(m_data[m_data_total-1])==POINTER_DYNAMIC) + delete m_data[m_data_total-1]; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Clearing of array without the release of memory | +//+------------------------------------------------------------------+ +void CArrayObj::Clear(void) + { +//--- "physical" removal of the object (if necessary and possible) + if(m_free_mode) + { + for(int i=0;i>1" is quick division by 2 + p_node=m_data[(beg+end)>>1]; + while(i0) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_node=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_node; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(beg0) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=mode; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Quick search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::QuickSearch(const CObject *element) const + { + int i,j,m=-1; + CObject *t_node; +//--- search + i=0; + j=m_data_total-1; + while(j>=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m==m_data_total-1) + break; + t_node=m_data[m]; + if(t_node.Compare(element,m_sort_mode)==0) + break; + if(t_node.Compare(element,m_sort_mode)>0) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::Search(const CObject *element) const + { + int pos; +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos].Compare(element,m_sort_mode)==0) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchGreat(const CObject *element) const + { + int pos; +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos].Compare(element,m_sort_mode)<=0) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchLess(const CObject *element) const + { + int pos; +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos].Compare(element,m_sort_mode)>=0) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchGreatOrEqual(const CObject *element) const + { +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=0) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchLessOrEqual(const CObject *element) const + { +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos].Compare(element,m_sort_mode)<=0) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchFirst(const CObject *element) const + { + int pos; +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos].Compare(element,m_sort_mode)==0) + { + while(m_data[pos].Compare(element,m_sort_mode)==0) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayObj::SearchLast(const CObject *element) const + { + int pos; +//--- check + if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos].Compare(element,m_sort_mode)==0) + { + while(m_data[pos].Compare(element,m_sort_mode)==0) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayObj::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayShort::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayShort::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayShort::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayShort::Add(const short element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayShort::AddArray(const short &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(SHORT_MAX); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayShort::Update(const int index,const short element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayShort::Shift(const int index,const int shift) + { + short tmp_short; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_short=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_short; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayShort::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayShort::CompareArray(const short &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_short=m_data[(beg+end)>>1]; + while(ip_short) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_short=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_short; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchLinear(const short element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_short=m_data[m]; + if(t_short==element) + break; + if(t_short>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::Search(const short element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchGreat(const short element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]<=element) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchLess(const short element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]>=element) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchGreatOrEqual(const short element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchLessOrEqual(const short element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchFirst(const short element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayShort::SearchLast(const short element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayShort::Save(const int file_handle) + { + int i=0; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;im_data_total) + count=m_data_total-src; + if(count<0) + return(-1); +//--- no need to copy + if(dest==src || count==0) + return(dest); +//--- check data total + if(dest+count>m_data_total) + { + if(m_data_max=0;i--) + m_data[dest+i]=m_data[src+i]; + } +//--- successful + return(dest); + } +//+------------------------------------------------------------------+ +//| Request for more memory in an array. Checks if the requested | +//| number of free elements already exists; allocates additional | +//| memory with a given step | +//+------------------------------------------------------------------+ +bool CArrayString::Reserve(const int size) + { + int new_size; +//--- check + if(size<=0) + return(false); +//--- resize array + if(Available()=size); + } +//+------------------------------------------------------------------+ +//| Resizing (with removal of elements on the right) | +//+------------------------------------------------------------------+ +bool CArrayString::Resize(const int size) + { + int new_size; +//--- check + if(size<0) + return(false); +//--- resize array + new_size=m_step_resize*(1+size/m_step_resize); + if(m_data_max!=new_size) + { + if((m_data_max=ArrayResize(m_data,new_size))==-1) + { + m_data_max=ArraySize(m_data); + return(false); + } + } + if(m_data_total>size) + m_data_total=size; +//--- result + return(m_data_max==new_size); + } +//+------------------------------------------------------------------+ +//| Complete cleaning of the array with the release of memory | +//+------------------------------------------------------------------+ +bool CArrayString::Shutdown(void) + { +//--- check + if(m_data_max==0) + return(true); +//--- clean + if(ArrayResize(m_data,0)==-1) + return(false); + m_data_total=0; + m_data_max=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array | +//+------------------------------------------------------------------+ +bool CArrayString::Add(const string element) + { +//--- check/reserve elements of array + if(!Reserve(1)) + return(false); +//--- add + m_data[m_data_total++]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Adding an element to the end of the array from another array | +//+------------------------------------------------------------------+ +bool CArrayString::AddArray(const string &src[]) + { + int num=ArraySize(src); +//--- check/reserve elements of array + if(!Reserve(num)) + return(false); +//--- add + for(int i=0;i=m_data_total) + return(""); +//--- result + return(m_data[index]); + } +//+------------------------------------------------------------------+ +//| Updating element in the specified position | +//+------------------------------------------------------------------+ +bool CArrayString::Update(const int index,const string element) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- update + m_data[index]=element; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Moving element from the specified position | +//| on the specified shift | +//+------------------------------------------------------------------+ +bool CArrayString::Shift(const int index,const int shift) + { + string tmp_string; +//--- check + if(index<0 || index+shift<0 || index+shift>=m_data_total) + return(false); + if(shift==0) + return(true); +//--- move + tmp_string=m_data[index]; + if(shift>0) + { + if(MemMove(index,index+1,shift)<0) + return(false); + } + else + { + if(MemMove(index+shift+1,index+shift,-shift)<0) + return(false); + } + m_data[index+shift]=tmp_string; + m_sort_mode=-1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Deleting element from the specified position | +//+------------------------------------------------------------------+ +bool CArrayString::Delete(const int index) + { +//--- check + if(index<0 || index>=m_data_total) + return(false); +//--- delete + if(indexto || from>=m_data_total) + return(false); +//--- delete + if(to>=m_data_total-1) + to=m_data_total-1; + if(MemMove(from,to+1,m_data_total-to-1)<0) + return(false); + m_data_total-=to-from+1; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Equality comparison of two arrays | +//+------------------------------------------------------------------+ +bool CArrayString::CompareArray(const string &array[]) const + { +//--- compare + if(m_data_total!=ArraySize(array)) + return(false); + for(int i=0;i>1" is quick division by 2 + p_string=m_data[(beg+end)>>1]; + while(ip_string) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + if(i<=j) + { + t_string=m_data[i]; + m_data[i++]=m_data[j]; + m_data[j]=t_string; + //--- control the output of the array bounds + if(j==0) + break; + j--; + } + } + if(begelement) + Insert(element,pos); + else + Insert(element,pos+1); +//--- restore the sorting flag after Insert(...) + m_sort_mode=0; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a array | +//+------------------------------------------------------------------+ +int CArrayString::SearchLinear(const string element) const + { +//--- check + if(m_data_total==0) + return(-1); +//--- + for(int i=0;i=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_string=m_data[m]; + if(t_string==element) + break; + if(t_string>element) + j=m-1; + else + i=m+1; + } +//--- position + return(m); + } +//+------------------------------------------------------------------+ +//| Search of position of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::Search(const string element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than | +//| specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchGreat(const string element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]<=element) + if(++pos==m_data_total) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than | +//| specified in the sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchLess(const string element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + while(m_data[pos]>=element) + if(pos--==0) + return(-1); +//--- position + return(pos); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is greater than or | +//| equal to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchGreatOrEqual(const string element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Search position of the first element which is less than or equal | +//| to the specified in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchLessOrEqual(const string element) const + { +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + for(int pos=QuickSearch(element);pos>=0;pos--) + if(m_data[pos]<=element) + return(pos); +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of first appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchFirst(const string element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(pos--==0) + break; + return(pos+1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Find position of last appearance of element in a sorted array | +//+------------------------------------------------------------------+ +int CArrayString::SearchLast(const string element) const + { + int pos; +//--- check + if(m_data_total==0 || !IsSorted()) + return(-1); +//--- search + pos=QuickSearch(element); + if(m_data[pos]==element) + { + while(m_data[pos]==element) + if(++pos==m_data_total) + break; + return(pos-1); + } +//--- not found + return(-1); + } +//+------------------------------------------------------------------+ +//| Writing array to file | +//+------------------------------------------------------------------+ +bool CArrayString::Save(const int file_handle) + { + int i=0,len; +//--- check + if(!CArray::Save(file_handle)) + return(false); +//--- write array length + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- write array + for(i=0;i +//+------------------------------------------------------------------+ +//| Class CList. | +//| Purpose: Provides the possibility of working with the list of | +//| CObject instances and its dervivatives | +//| Derives from class CObject. | +//+------------------------------------------------------------------+ +class CList : public CObject + { +protected: + CObject *m_first_node; // pointer to the first element of the list + CObject *m_last_node; // pointer to the last element of the list + CObject *m_curr_node; // pointer to the current element of the list + int m_curr_idx; // index of the current list item + int m_data_total; // number of elements + bool m_free_mode; // flag of the necessity of "physical" deletion of object + bool m_data_sort; // flag if the list is sorted or not + int m_sort_mode; // mode of sorting of array + +public: + CList(void); + ~CList(void); + //--- methods of access to protected data + bool FreeMode(void) const { return(m_free_mode); } + void FreeMode(bool mode) { m_free_mode=mode; } + int Total(void) const { return(m_data_total); } + bool IsSorted(void) const { return(m_data_sort); } + int SortMode(void) const { return(m_sort_mode); } + //--- method of identifying the object + virtual int Type(void) const { return(0x7779); } + //--- methods for working with files + virtual bool Save(const int file_handle); + virtual bool Load(const int file_handle); + //--- method of creating an element of the list + virtual CObject *CreateElement(void) { return(NULL); } + //--- methods of filling the list + int Add(CObject *new_node); + int Insert(CObject *new_node,int index); + //--- methods for navigating + int IndexOf(CObject *node); + CObject *GetNodeAtIndex(int index); + CObject *GetFirstNode(void); + CObject *GetPrevNode(void); + CObject *GetCurrentNode(void); + CObject *GetNextNode(void); + CObject *GetLastNode(void); + //--- methods for deleting + CObject *DetachCurrent(void); + bool DeleteCurrent(void); + bool Delete(int index); + void Clear(void); + //--- method for comparing lists + bool CompareList(CList *List); + //--- methods for changing + void Sort(int mode); + bool MoveToIndex(int index); + bool Exchange(CObject *node1,CObject *node2); + //--- method for searching + CObject *Search(CObject *element); +protected: + void QuickSort(int beg,int end,int mode); + CObject *QuickSearch(CObject *element); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CList::CList(void) : m_first_node(NULL), + m_last_node(NULL), + m_curr_node(NULL), + m_curr_idx(-1), + m_data_total(0), + m_free_mode(true), + m_data_sort(false), + m_sort_mode(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CList::~CList(void) + { + Clear(); + } +//+------------------------------------------------------------------+ +//| Method QuickSort | +//+------------------------------------------------------------------+ +void CList::QuickSort(int beg,int end,int mode) + { + int i,j,k; + CObject *i_ptr,*j_ptr,*k_ptr; +//--- + i_ptr=GetNodeAtIndex(i=beg); + j_ptr=GetNodeAtIndex(j=end); + while(i>1" is quick division by 2 + k_ptr=GetNodeAtIndex(k=(beg+end)>>1); + while(i0) + { + //--- control the output of the array bounds + if(j==0) + break; + j--; + j_ptr=j_ptr.Prev(); + } + if(i<=j) + { + Exchange(i_ptr,j_ptr); + i++; + i_ptr=GetNodeAtIndex(i); + //--- control the output of the array bounds + if(j==0) + break; + else + { + j--; + j_ptr=GetNodeAtIndex(j); + } + } + } + if(begm_data_total || index<0) + return(-1); +//--- adjust + if(index==-1) + { + if(m_curr_node==NULL) + return(Add(new_node)); + } + else + { + if(GetNodeAtIndex(index)==NULL) + return(Add(new_node)); + } +//--- no need to check m_curr_node + tmp_node=m_curr_node.Prev(); + new_node.Prev(tmp_node); + if(tmp_node!=NULL) + tmp_node.Next(new_node); + else + m_first_node=new_node; + new_node.Next(m_curr_node); + m_curr_node.Prev(new_node); + m_data_total++; + m_data_sort=false; + m_curr_node=new_node; +//--- result + return(index); + } +//+------------------------------------------------------------------+ +//| Get a pointer to the position of element in the list | +//+------------------------------------------------------------------+ +CObject *CList::GetNodeAtIndex(int index) + { + int i; + bool revers; + CObject *result; +//--- check + if(index>=m_data_total) + return(NULL); + if(index==m_curr_idx) + return(m_curr_node); +//--- optimize bust list + if(indexindex;i--) + { + result=result.Prev(); + if(result==NULL) + return(NULL); + } + } + else + { + //--- search from left to right + for(;i=m_data_total || !CheckPointer(m_curr_node)) + return(false); +//--- tune + if(m_curr_idx==index) + return(true); + if(m_curr_idx=i) + { + //--- ">>1" is quick division by 2 + m=(j+i)>>1; + if(m<0 || m>=m_data_total) + break; + t_node=GetNodeAtIndex(m); + if(t_node.Compare(element,m_sort_mode)==0) + break; + if(t_node.Compare(element,m_sort_mode)>0) + j=m-1; + else + i=m+1; + t_node=NULL; + } +//--- result + return(t_node); + } +//+------------------------------------------------------------------+ +//| Search position of an element in a sorted list | +//+------------------------------------------------------------------+ +CObject *CList::Search(CObject *element) + { + CObject *result; +//--- check + if(!CheckPointer(element) || !m_data_sort) + return(NULL); +//--- search + result=QuickSearch(element); +//--- result + return(result); + } +//+------------------------------------------------------------------+ +//| Writing list to file | +//+------------------------------------------------------------------+ +bool CList::Save(const int file_handle) + { + CObject *node; + bool result=true; +//--- check + if(!CheckPointer(m_curr_node) || file_handle==INVALID_HANDLE) + return(false); +//--- write start marker - 0xFFFFFFFFFFFFFFFF + if(FileWriteLong(file_handle,-1)!=sizeof(long)) + return(false); +//--- write type + if(FileWriteInteger(file_handle,Type(),INT_VALUE)!=INT_VALUE) + return(false); +//--- write list size + if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE) + return(false); +//--- sequential scannning of elements in the list using the call of method Save() + node=m_first_node; + while(node!=NULL) + { + result&=node.Save(file_handle); + node=node.Next(); + } +//--- successful + return(result); + } +//+------------------------------------------------------------------+ +//| Reading list from file | +//+------------------------------------------------------------------+ +bool CList::Load(const int file_handle) + { + uint i,num; + CObject *node; + bool result=true; +//--- check + if(file_handle==INVALID_HANDLE) + return(false); +//--- read and checking begin marker - 0xFFFFFFFFFFFFFFFF + if(FileReadLong(file_handle)!=-1) + return(false); +//--- read and checking type + if(FileReadInteger(file_handle,INT_VALUE)!=Type()) + return(false); +//--- read list size + num=FileReadInteger(file_handle,INT_VALUE); +//--- sequential creation of list items using the call of method Load() + Clear(); + for(i=0;i0) + p_node.Left(new_node); + else + p_node.Right(new_node); + new_node.Parent(p_node); + Balance(p_node); + } + else + m_root_node=new_node; +//--- result + return(result); + } +//+------------------------------------------------------------------+ +//| Method of removing a node from the tree | +//+------------------------------------------------------------------+ +bool CTree::Delete(CTreeNode *node) + { +//--- check + if(!CheckPointer(node)) + return(false); +//--- delete + if(Detach(node) && CheckPointer(node)==POINTER_DYNAMIC) + delete node; +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Method of detaching node from the tree | +//+------------------------------------------------------------------+ +bool CTree::Detach(CTreeNode *node) + { + CTreeNode *curr_node,*tmp_node; + CTreeNode *nodeA,*nodeB; +//--- check + curr_node=node; + if(!CheckPointer(curr_node)) + return(false); +//--- detach + if(curr_node.BalanceL()>curr_node.BalanceR()) + { + nodeA=curr_node.Left(); + while(nodeA.Right()!=NULL) + nodeA=nodeA.Right(); + nodeB=nodeA.Parent(); + if(nodeB!=curr_node) + { + nodeB.Right(nodeA.Left()); + tmp_node=nodeB.Right(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeB); + tmp_node=curr_node.Left(); + nodeA.Left(tmp_node); + tmp_node.Parent(nodeA); + } + //--- left link of curr_node is already installed as it should be + curr_node.Left(NULL); + //--- transferring the right link of curr_node to nodeA + nodeA.Right(curr_node.Right()); + tmp_node=curr_node.Right(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + curr_node.Right(NULL); + //--- transferring the root link of curr_node to nodeA + tmp_node=curr_node.Parent(); + nodeA.Parent(tmp_node); + if(tmp_node!=NULL) + { + if(tmp_node.Left()==curr_node) + tmp_node.Left(nodeA); + else + tmp_node.Right(nodeA); + } + else + { + curr_node.Parent(NULL); + m_root_node=nodeA; + tmp_node=nodeA; + } + Balance(tmp_node); + } + else + { + if(curr_node.BalanceR()>0) + { + nodeA=curr_node.Right(); + while(nodeA.Left()!=NULL) + nodeA=nodeA.Left(); + nodeB=nodeA.Parent(); + if(nodeB!=curr_node) + { + nodeB.Left(nodeA.Right()); + tmp_node=nodeB.Left(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeB); + tmp_node=curr_node.Right(); + nodeA.Right(tmp_node); + tmp_node.Parent(nodeA); + } + //--- right link of curr_node is already installed as it should be + curr_node.Right(NULL); + //--- transferring the left link of curr_node to nodeA + nodeA.Left(curr_node.Left()); + tmp_node=curr_node.Left(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + curr_node.Left(NULL); + //--- transferring the root link of curr_node to nodeA + tmp_node=curr_node.Parent(); + nodeA.Parent(tmp_node); + if(tmp_node!=NULL) + { + if(tmp_node.Left()==curr_node) + tmp_node.Left(nodeA); + else + tmp_node.Right(nodeA); + } + else + { + curr_node.Parent(NULL); + m_root_node=nodeA; + tmp_node=nodeA; + } + Balance(tmp_node); + } + else + { + //--- node list + if(curr_node.Parent()==NULL) + m_root_node=NULL; + else + { + tmp_node=curr_node.Parent(); + if(tmp_node.Left()==curr_node) + tmp_node.Left(NULL); + else + tmp_node.Right(NULL); + curr_node.Parent(NULL); + } + Balance(curr_node.Parent()); + } + } +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Method of cleaning the tree | +//+------------------------------------------------------------------+ +void CTree::Clear(void) + { + if(CheckPointer(m_root_node)==POINTER_DYNAMIC) + delete m_root_node; + m_root_node=NULL; + } +//+------------------------------------------------------------------+ +//| Method of searching for a node in the tree | +//+------------------------------------------------------------------+ +CTreeNode *CTree::Find(const CTreeNode *node) + { + CTreeNode *result=m_root_node; +//--- find + while(result!=NULL && result.Compare(node)!=0) + result=result.GetNext(node); +//--- result + return(result); + } +//+------------------------------------------------------------------+ +//| Method of balancing the tree | +//+------------------------------------------------------------------+ +void CTree::Balance(CTreeNode *node) + { + CTreeNode *nodeA,*nodeB,*nodeC,*curr_node,*tmp_node; +//--- + curr_node=node; + while(curr_node!=NULL) + { + curr_node.RefreshBalance(); + if(MathAbs(curr_node.BalanceL()-curr_node.BalanceR())<=1) + curr_node=curr_node.Parent(); + else + { + if(curr_node.BalanceR()>curr_node.BalanceL()) + { + //--- rotation to the right + tmp_node=curr_node.Right(); + if(tmp_node.BalanceL()>tmp_node.BalanceR()) + { + //--- great rotation to the right + nodeA=curr_node; + nodeB=nodeA.Right(); + nodeC=nodeB.Left(); + nodeC.Parent(nodeA.Parent()); + tmp_node=nodeC.Parent(); + if(tmp_node!=NULL) + { + if(tmp_node.Right()==nodeA) + tmp_node.Right(nodeC); + else + tmp_node.Left(nodeC); + } + else + m_root_node=nodeC; + nodeA.Parent(nodeC); + nodeB.Parent(nodeC); + nodeA.Right(nodeC.Left()); + tmp_node=nodeA.Right(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + nodeC.Left(nodeA); + nodeB.Left(nodeC.Right()); + tmp_node=nodeB.Left(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeB); + nodeC.Right(nodeB); + if(m_root_node==nodeA) + m_root_node=nodeC; + curr_node=nodeC.Parent(); + } + else + { + //--- slight rotation to the right + nodeA=curr_node; + nodeB=nodeA.Right(); + nodeB.Parent(nodeA.Parent()); + tmp_node=nodeB.Parent(); + if(tmp_node!=NULL) + { + if(tmp_node.Right()==nodeA) + tmp_node.Right(nodeB); + else + tmp_node.Left(nodeB); + } + else + m_root_node=nodeB; + nodeA.Parent(nodeB); + nodeA.Right(nodeB.Left()); + tmp_node=nodeA.Right(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + nodeB.Left(nodeA); + if(m_root_node==nodeA) + m_root_node=nodeB; + curr_node=nodeB.Parent(); + } + } + else + { + //--- rotation to the left + tmp_node=curr_node.Left(); + if(tmp_node.BalanceR()>tmp_node.BalanceL()) + { + //--- great rotation to the left + nodeA=curr_node; + nodeB=nodeA.Left(); + nodeC=nodeB.Right(); + nodeC.Parent(nodeA.Parent()); + tmp_node=nodeC.Parent(); + if(tmp_node!=NULL) + { + if(tmp_node.Right()==nodeA) + tmp_node.Right(nodeC); + else + tmp_node.Left(nodeC); + } + else + m_root_node=nodeC; + nodeA.Parent(nodeC); + nodeB.Parent(nodeC); + nodeA.Left(nodeC.Right()); + tmp_node=nodeA.Left(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + nodeC.Right(nodeA); + nodeB.Right(nodeC.Left()); + tmp_node=nodeB.Right(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeB); + nodeC.Left(nodeB); + if(m_root_node==nodeA) + m_root_node=nodeC; + curr_node=nodeC.Parent(); + } + else + { + //--- small rotation to the left + nodeA=curr_node; + nodeB=nodeA.Left(); + nodeB.Parent(nodeA.Parent()); + tmp_node=nodeB.Parent(); + if(tmp_node!=NULL) + { + if(tmp_node.Right()==nodeA) + tmp_node.Right(nodeB); + else + tmp_node.Left(nodeB); + } + else + m_root_node=nodeB; + nodeA.Parent(nodeB); + nodeA.Left(nodeB.Right()); + tmp_node=nodeA.Left(); + if(tmp_node!=NULL) + tmp_node.Parent(nodeA); + nodeB.Right(nodeA); + if(m_root_node==nodeA) + m_root_node=nodeB; + curr_node=nodeB.Parent(); + } + } + } + } + } +//+------------------------------------------------------------------+ +//| Writing tree to file | +//+------------------------------------------------------------------+ +bool CTree::Save(const int file_handle) + { +//--- check + if(file_handle==INVALID_HANDLE) + return(false); + if(m_root_node==NULL) + return(true); +//--- result + return(m_root_node.SaveNode(file_handle)); + } +//+------------------------------------------------------------------+ +//| Reading tree from file | +//+------------------------------------------------------------------+ +bool CTree::Load(const int file_handle) + { +//--- check + if(file_handle==INVALID_HANDLE) + return(false); +//--- create root node only + Clear(); + Insert(CreateElement()); +//--- result + return(m_root_node.LoadNode(file_handle,m_root_node)); + } +//+------------------------------------------------------------------+ diff --git a/Arrays/TreeNode.mqh b/Arrays/TreeNode.mqh new file mode 100644 index 0000000..51dbdf0 --- /dev/null +++ b/Arrays/TreeNode.mqh @@ -0,0 +1,174 @@ +//+------------------------------------------------------------------+ +//| TreeNode.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include +//+------------------------------------------------------------------+ +//| Class CTreeNode. | +//| Purpose: Base class of node of binary tree CTree. | +//| Derives from class CObject. | +//+------------------------------------------------------------------+ +class CTreeNode : public CObject + { +private: + CTreeNode *m_p_node; // link to node up + CTreeNode *m_l_node; // link to node left + CTreeNode *m_r_node; // link to node right + //--- variables + int m_balance; // balance of node + int m_l_balance; // balance of the left branch + int m_r_balance; // balance of the right branch + +public: + CTreeNode(void); + ~CTreeNode(void); + //--- methods of access to protected data + CTreeNode* Parent(void) const { return(m_p_node); } + void Parent(CTreeNode *node) { m_p_node=node; } + CTreeNode* Left(void) const { return(m_l_node); } + void Left(CTreeNode *node) { m_l_node=node; } + CTreeNode* Right(void) const { return(m_r_node); } + void Right(CTreeNode *node) { m_r_node=node; } + int Balance(void) const { return(m_balance); } + int BalanceL(void) const { return(m_l_balance); } + int BalanceR(void) const { return(m_r_balance); } + //--- method of identifying the object + virtual int Type(void) const { return(0x8888); } + //--- methods for controlling + int RefreshBalance(void); + CTreeNode *GetNext(const CTreeNode *node); + //--- methods for working with files + bool SaveNode(const int file_handle); + bool LoadNode(const int file_handle,CTreeNode *main); + +protected: + //--- method for creating an instance of class + virtual CTreeNode *CreateSample(void) { return(NULL); } + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CTreeNode::CTreeNode(void) : m_p_node(NULL), + m_l_node(NULL), + m_r_node(NULL), + m_balance(0), + m_l_balance(0), + m_r_balance(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CTreeNode::~CTreeNode(void) + { +//--- delete nodes of the next level + if(m_l_node!=NULL) + delete m_l_node; + if(m_r_node!=NULL) + delete m_r_node; + } +//+------------------------------------------------------------------+ +//| Calculating the balance of the node | +//+------------------------------------------------------------------+ +int CTreeNode::RefreshBalance(void) + { +//--- calculate the balance of the left branch + if(m_l_node==NULL) + m_l_balance=0; + else + m_l_balance=m_l_node.RefreshBalance(); +//--- calculate the balance of the right branch + if(m_r_node==NULL) + m_r_balance=0; + else + m_r_balance=m_r_node.RefreshBalance(); +//--- calculate the balance of the node + if(m_r_balance>m_l_balance) + m_balance=m_r_balance+1; + else + m_balance=m_l_balance+1; +//--- result + return(m_balance); + } +//+------------------------------------------------------------------+ +//| Selecting next node | +//+------------------------------------------------------------------+ +CTreeNode *CTreeNode::GetNext(const CTreeNode *node) + { + if(Compare(node)>0) + return(m_l_node); +//--- result + return(m_r_node); + } +//+------------------------------------------------------------------+ +//| Writing node data to file | +//+------------------------------------------------------------------+ +bool CTreeNode::SaveNode(const int file_handle) + { + bool result=true; +//--- check + if(file_handle==INVALID_HANDLE) + return(false); +//--- write left node (if it is available) + if(m_l_node!=NULL) + { + FileWriteInteger(file_handle,'L',SHORT_VALUE); + result&=m_l_node.SaveNode(file_handle); + } + else + FileWriteInteger(file_handle,'X',SHORT_VALUE); +//--- write data of current node + result&=Save(file_handle); +//--- write right node (if it is available) + if(m_r_node!=NULL) + { + FileWriteInteger(file_handle,'R',SHORT_VALUE); + result&=m_r_node.SaveNode(file_handle); + } + else + FileWriteInteger(file_handle,'X',SHORT_VALUE); +//--- successful + return(true); + } +//+------------------------------------------------------------------+ +//| Reading node data from file | +//+------------------------------------------------------------------+ +bool CTreeNode::LoadNode(const int file_handle,CTreeNode *main) + { + bool result=true; + short s_val; + CTreeNode *node; +//--- check + if(file_handle==INVALID_HANDLE) + return(false); +//--- read directions + s_val=(short)FileReadInteger(file_handle,SHORT_VALUE); + if(s_val=='L') + { + //--- read left node (if there is data) + node=CreateSample(); + if(node==NULL) + return(false); + m_l_node=node; + node.Parent(main); + result&=node.LoadNode(file_handle,node); + } +//--- read data of current node + result&=Load(file_handle); +//--- read directions + s_val=(short)FileReadInteger(file_handle,SHORT_VALUE); + if(s_val=='R') + { + //--- read right node (if there is data) + node=CreateSample(); + if(node==NULL) + return(false); + m_r_node=node; + node.Parent(main); + result&=node.LoadNode(file_handle,node); + } +//--- result + return(result); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/Canvas.mqh b/Canvas/Canvas.mqh new file mode 100644 index 0000000..e1cd647 --- /dev/null +++ b/Canvas/Canvas.mqh @@ -0,0 +1,4861 @@ +//+------------------------------------------------------------------+ +//| Canvas.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include +#include + +#define SIGN(i) ((i<0) ? -1 : 1) + +//+------------------------------------------------------------------+ +//| Macro to generate color | +//+------------------------------------------------------------------+ +#define XRGB(r,g,b) (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) +#define ARGB(a,r,g,b) ((uchar(a)<<24)|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) +#define TRGB(a,rgb) ((uchar(a)<<24)|(rgb)) +#define GETRGB(clr) ((clr)&0xFFFFFF) +#define GETRGBA(clr) uchar((clr)>>24) +#define GETRGBR(clr) uchar((clr)>>16) +#define GETRGBG(clr) uchar((clr)>>8) +#define GETRGBB(clr) uchar(clr) +#define COLOR2RGB(clr) (0xFF000000|(uchar(clr)<<16)|(uchar((clr)>>8)<<8)|uchar((clr)>>16)) +#define RGB2COLOR(rgb) ((uchar(rgb)<<16)|(uchar((rgb)>>8)<<8)|uchar((rgb)>>16)) + +//+------------------------------------------------------------------+ +//| Line end style (round, butt, square) | +//+------------------------------------------------------------------+ +enum ENUM_LINE_END + { + LINE_END_ROUND, + LINE_END_BUTT, + LINE_END_SQUARE, + }; + +//+------------------------------------------------------------------+ +//| Class CCanvas | +//| Usage: class for working with a dynamic resource | +//+------------------------------------------------------------------+ +class CCanvas + { +private: + uint m_style; // line style template + uint m_style_idx; // variable - current index of bit in line style template + static uint m_default_colors[9]; // default colors + +protected: + long m_chart_id; // chart ID + string m_objname; // object name + ENUM_OBJECT m_objtype; // object type + string m_rcname; // resource name + int m_width; // canvas width + int m_height; // canvas height + ENUM_COLOR_FORMAT m_format; // method of color processing + //--- for text + string m_fontname; // font name + int m_fontsize; // font size + uint m_fontflags; // font flags + uint m_fontangle; // angle of text tilt to the X axis in 0.1 degrees + //--- data + uint m_pixels[]; // array of pixels + +public: + CCanvas(void); + ~CCanvas(void); + //--- create/attach/destroy + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmap(const string name,const datetime time,const double price, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmap(const long chart_id,const int subwin,const string name, + const datetime time,const double price,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmapLabel(const string name,const int x,const int y, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmapLabel(const long chart_id,const int subwin,const string name, + const int x,const int y,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + virtual bool Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + virtual bool Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + virtual void Destroy(void); + //--- properties + string ChartObjectName(void) const { return(m_objname); } + string ResourceName(void) const { return(m_rcname); } + int Width(void) const { return(m_width); } + int Height(void) const { return(m_height); } + //--- update object on screen + void Update(const bool redraw=true); + bool Resize(const int width,const int height); + //--- clear/fill color + void Erase(const uint clr=0); + //--- data access + uint PixelGet(const int x,const int y) const; + void PixelSet(const int x,const int y,const uint clr); + //--- draw primitives + void LineVertical(int x,int y1,int y2,const uint clr); + void LineHorizontal(int x1,int x2,int y,const uint clr); + void Line(int x1,int y1,int x2,int y2,const uint clr); + void Polyline(int &x[],int &y[],const uint clr); + void Polygon(int &x[],int &y[],const uint clr); + void Rectangle(int x1,int y1,int x2,int y2,const uint clr); + void Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); + void Circle(int x,int y,int r,const uint clr); + void Ellipse(int x1,int y1,int x2,int y2,const uint clr); + void Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr); + void Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr); + void Arc(int x,int y,int rx,int ry,double fi3,double fi4,int &x3,int &y3,int &x4,int &y4,const uint clr); + void Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr); + void Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr); + //--- draw filled primitives + void FillRectangle(int x1,int y1,int x2,int y2,const uint clr); + void FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); + void FillPolygon(int &x[],int &y[],const uint clr); + void FillCircle(int x,int y,int r,const uint clr); + void FillEllipse(int x1,int y1,int x2,int y2,const uint clr); + void Fill(int x,int y,const uint clr); + void Fill(int x,int y,const uint clr,const uint threshould); + //--- draw primitives with antialiasing + void PixelSetAA(const double x,const double y,const uint clr); + void LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX); + void PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); + void PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); + void TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3, + const uint clr,const uint style=UINT_MAX); + void CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX); + void EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX); + //--- draw primitives with antialiasing by Wu's algorithm + void LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX); + void PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX); + void PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX); + void TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX); + void CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX); + void EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX); + //--- draw primitives with prefiltered antialiasing + void LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); + void LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); + void LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style); + void PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style); + void PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style); + //--- draw primitives smoothing polyline and polygon + void PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size, + ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND, + double tension=0.5,double step=10); + void PolygonSmooth(int &x[],int &y[],const uint clr,const int size, + ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND, + double tension=0.5,double step=10); + //--- BitBlt + void BitBlt(int dst_x,int dst_y,const uint &src[],int src_width,int src_height,int src_x,int src_y,int src_dx,int src_dy,uint mode=0); + //--- for text + bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); + bool FontNameSet(string name); + bool FontSizeSet(int size); + bool FontFlagsSet(uint flags); + bool FontAngleSet(uint angle); + void FontGet(string &name,int &size,uint &flags,uint &angle); + string FontNameGet(void) const { return(m_fontname); } + int FontSizeGet(void) const { return(m_fontsize); } + uint FontFlagsGet(void) const { return(m_fontflags); } + uint FontAngleGet(void) const { return(m_fontangle); } + void TextOut(int x,int y,string text,const uint clr,uint alignment=0); + int TextWidth(const string text); + int TextHeight(const string text); + void TextSize(const string text,int &width,int &height); + //--- services + static uint GetDefaultColor(const int i); + void TransparentLevelSet(const uchar value); + //--- load bitmap from file + bool LoadFromFile(const string filename); + //--- line style property + uint LineStyleGet(void) const; + void LineStyleSet(const uint style); + //--- load bitmap from file to buffer + static bool LoadBitmap(const string filename,uint &data[],int &width,int &height); + +private: + bool FontSet(void); + void TextOutFast(int x,int y,string text,const uint clr,uint alignment=0); + bool PixelsSimilar(const uint clr0,const uint clr1,const uint threshould); + //--- for Wu's algorithm + void PixelTransform(const int x,const int y,const uint clr,const double alpha); + //--- for circle and ellipse + void PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha); + void PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr); + //--- for thick line + void SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style); + void SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style); + void Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign, + const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style); + double DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2); + //--- for pie + double AngleCalc(int x1,int y1,int x2,int y2); + //--- for polygon + int PointClassify(const CPoint &p0,const CPoint &p1,const CPoint &p2); + int PolygonClassify(const CPoint &p[]); + bool IsPolygonConvex(CPoint &p[]); + void PolygonNormalize(CPoint &p[]); + void PolygonIntersect(CPoint &p[],CPoint &add[]); + void PolygonFill(CPoint &p[],const uint clr); + //--- for smoothing polyline and polygon + void CalcCurveBezierEndp(const double xend,const double yend,const double xadj,const double yadj,const double tension,double &x,double &y); + void CalcCurveBezier(const int &x[],const int &y[],const int i,const double tension,double &x1,double &y1,double &x2,double &y2); + double CalcBezierX(const double t,const double x0,const double x1,const double x2,const double x3); + double CalcBezierY(const double t,const double y0,const double y1,const double y2,const double y3); + +protected: + //--- method for prefiltered antialiasing + virtual double FilterFunction(const double x); + }; +//+------------------------------------------------------------------+ +//| Initialize static array | +//+------------------------------------------------------------------+ +uint CCanvas::m_default_colors[9]= + { + XRGB(0,0,255), // blue + XRGB(255,0,0), // red + XRGB(0,128,0), // green + XRGB(255,242,0), // yellow + XRGB(255,0,128), // pink + XRGB(0,255,0), // lime + XRGB(185,0,61), // crimson + XRGB(0,183,239), // sky blue + XRGB(255,128,0) // orange + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CCanvas::CCanvas(void) : m_chart_id(0), + m_objname(NULL), + m_objtype(WRONG_VALUE), + m_rcname(NULL), + m_width(0), + m_height(0), + m_format(COLOR_FORMAT_XRGB_NOALPHA), + m_fontname("arial"), + m_fontsize(-120), + m_fontflags(0), + m_fontangle(0), + m_style(UINT_MAX), + m_style_idx(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CCanvas::~CCanvas(void) + { + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + Destroy(); +//--- prepare data array + if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) + { + //--- generate uniq resource name + string uniq=(string)ChartID()+(string)GetTickCount()+"."+(string)(GetMicrosecondCount()&0x3FF); + + m_rcname="::" + StringSubstr(name,0,63-StringLen(uniq))+uniq; + //--- initialize data with zeros + ArrayInitialize(m_pixels,0); + //--- create dynamic resource + if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt)) + { + //--- successfully created + //--- complete initialization + m_width =width; + m_height=height; + m_format=clrfmt; + //--- succeed + return(true); + } + } +//--- error - destroy object + Destroy(); + return(false); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCanvas::CreateBitmap(const string name,const datetime time,const double price, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + return(CreateBitmap(0,0,name,time,price,width,height,clrfmt)); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCanvas::CreateBitmap(const long chart_id,const int subwin,const string name, + const datetime time,const double price,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt) + { +//--- create canvas + if(Create(name,width,height,clrfmt)) + { + //--- create attached object + if(ObjectCreate(chart_id,name,OBJ_BITMAP,subwin,time,price)) + { + //--- bind object with resource + if(ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) + { + //--- successfully created + //--- complete initialization + m_chart_id=chart_id; + m_objname =name; + m_objtype =OBJ_BITMAP; + //--- succeed + return(true); + } + } + } +//--- error + return(false); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt)); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCanvas::CreateBitmapLabel(const long chart_id,const int subwin,const string name, + const int x,const int y,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt) + { +//--- create canvas + if(Create(name,width,height,clrfmt)) + { + //--- create attached object + if(ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwin,0,0)) + { + //--- set x,y and bind object with resource + if(ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x) && + ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y) && + ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) + { + //--- successfully created + //--- complete initialization + m_chart_id=chart_id; + m_objname =name; + m_objtype =OBJ_BITMAP_LABEL; + //--- succeed + return(true); + } + } + } +//--- error + return(false); + } +//+------------------------------------------------------------------+ +//| Attach new object with bitmap resource | +//+------------------------------------------------------------------+ +bool CCanvas::Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) + { + if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE)) + { + string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE); + rcname=StringSubstr(rcname,StringFind(rcname,"::")); + if(ResourceReadImage(rcname,m_pixels,m_width,m_height)) + { + m_chart_id=chart_id; + m_objname=objname; + m_rcname=rcname; + m_format=clrfmt; + m_objtype=OBJ_BITMAP_LABEL; + //--- success + return(true); + } + } +//--- failed + return(false); + } +//+------------------------------------------------------------------+ +//| Attach new object without bitmap resource | +//+------------------------------------------------------------------+ +bool CCanvas::Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA) + { + if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE)) + { + string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE); + if(StringLen(rcname)==0 && width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) + { + ZeroMemory(m_pixels); + if(ResourceCreate("::"+objname,m_pixels,width,height,0,0,0,clrfmt) && + ObjectSetString(chart_id,objname,OBJPROP_BMPFILE,"::"+objname)) + { + m_chart_id=chart_id; + m_width=width; + m_height=height; + m_objname=objname; + m_rcname="::"+objname; + m_format=clrfmt; + m_objtype=OBJ_BITMAP_LABEL; + //--- success + return(true); + } + } + } +//--- failed + return(false); + } +//+------------------------------------------------------------------+ +//| Remove object from chart and deallocate data array | +//+------------------------------------------------------------------+ +void CCanvas::Destroy(void) + { +//--- delete object + if(m_objname!=NULL) + { + ObjectDelete(m_chart_id,m_objname); + m_chart_id=0; + m_objname =NULL; + m_objtype =WRONG_VALUE; + } +//--- deallocate array + ArrayFree(m_pixels); +//--- free resource + if(m_rcname!=NULL) + { + ResourceFree(m_rcname); + m_rcname=NULL; + } +//--- zeroize data + m_width =0; + m_height=0; + } +//+------------------------------------------------------------------+ +//| Update object on screen (redraw) | +//+------------------------------------------------------------------+ +void CCanvas::Update(const bool redraw) + { +//--- check + if(m_rcname==NULL) + return; +//--- update resource and redraw + if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format) && redraw) + ChartRedraw(this.m_chart_id); + } +//+------------------------------------------------------------------+ +//| Resize | +//+------------------------------------------------------------------+ +bool CCanvas::Resize(const int width,const int height) + { +//--- check + if(m_rcname!=NULL && width>0 && height>0) + if(ArrayResize(m_pixels,width*height)>0) + { + m_width =width; + m_height=height; + //--- initialize data with zeros + ArrayInitialize(m_pixels,0); + //--- create dynamic resource + if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format)) + { + //--- bind object with resource + if(m_objname!=NULL && ObjectSetString(m_chart_id,m_objname,OBJPROP_BMPFILE,m_rcname)) + return(true); + } + } +//--- error + return(false); + } +//+------------------------------------------------------------------+ +//| Clear/Fill color | +//+------------------------------------------------------------------+ +void CCanvas::Erase(const uint clr) + { + ArrayInitialize(m_pixels,clr); + } +//+------------------------------------------------------------------+ +//| Get pixel color | +//+------------------------------------------------------------------+ +uint CCanvas::PixelGet(const int x,const int y) const + { +//--- check coordinates + if(x>=0 && x=0 && y=0 && x=0 && y=m_width || y<0 || y>=m_height) + return; +//--- + int index=y*m_width+x; + uint old_clr=m_pixels[index]; +//--- check if replacement is necessary + if(old_clr==clr) + return; +//--- use pseudo stack to emulate deeply-nested recursive calls + int stack[]; + uint count=1; + int idx; + int total=ArraySize(m_pixels); +//--- allocate memory for stack + if(ArrayResize(stack,total)==-1) + return; + stack[0]=index; + m_pixels[index]=clr; + for(uint i=0; i0 && m_pixels[idx]==old_clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- top adjacent point + idx=index-m_width; + if(idx>=0 && m_pixels[idx]==old_clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- right adjacent point + idx=index+1; + if(x=m_width || y<0 || y>=m_height || threshould>255) + return; +//--- + int index=y*m_width+x; + uint old_clr=m_pixels[index]; +//--- check if replacement is necessary + if(old_clr==clr) + return; +//--- use pseudo stack to emulate deeply-nested recursive calls + int stack[]; + uint count=1; + int idx; + int total=ArraySize(m_pixels); +//--- allocate memory for stack + if(ArrayResize(stack,total)==-1) + return; + stack[0]=index; + m_pixels[index]=clr; + for(uint i=0; i0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- top adjacent point + idx=index-m_width; + if(idx>=0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- right adjacent point + idx=index+1; + if(xy2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } +//--- line is out of image boundaries + if(y2<0 || y1>=m_height || x<0 || x>=m_width) + return; +//--- stay withing image boundaries + if(y1<0) + y1=0; + if(y2>=m_height) + y2=m_height-1; +//--- draw line + int index=y1*m_width+x; + for(int i=y1; i<=y2; i++,index+=m_width) + m_pixels[index]=clr; + } +//+------------------------------------------------------------------+ +//| Draw horizontal line | +//+------------------------------------------------------------------+ +void CCanvas::LineHorizontal(int x1,int x2,int y,const uint clr) + { + int tmp; +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- line is out of image boundaries + if(x2<0 || x1>=m_width || y<0 || y>=m_height) + return; +//--- stay withing image boundaries + if(x1<0) + x1=0; + if(x2>=m_width) + x2=m_width-1; +//--- draw line + ArrayFill(m_pixels,y*m_width+x1,(x2-x1)+1,clr); + } +//+------------------------------------------------------------------+ +//| Draw line according to Bresenham's algorithm | +//+------------------------------------------------------------------+ +void CCanvas::Line(int x1,int y1,int x2,int y2,const uint clr) + { +//--- line is out of image boundaries + if((x1<0 && x2<0) || (y1<0 && y2<0)) + return; + if(x1>=m_width && x2>=m_width) + return; + if(y1>=m_height && y2>=m_height) + return; +//--- get length by X and Y + int dx=(x2>x1)? x2-x1 : x1-x2; + int dy=(y2>y1)? y2-y1 : y1-y2; + if(dx==0) + { + //--- vertical line + LineVertical(x1,y1,y2,clr); + return; + } + if(dy==0) + { + //--- horizontal line + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- get direction by X and Y + int sx=(x1=m_width || + y1<0 || y1>=m_height) + { + if(draw) + return; + } + else + { + //--- draw pixel + m_pixels[y1*m_width+x1]=clr; + draw=true; + } + //--- get coordinates of next pixel + int er2=er<<1; + if(er2>-dy) + { + er-=dy; + x1+=sx; + } + if(er2ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0; iArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0; i=dx) + { + xx=x+dx; + if(xx>=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0) + { + dy--; + dd_y+=2; + f+=dd_y; + } + dx++; + dd_x+=2; + f+=dd_x; + } + } +//+------------------------------------------------------------------+ +//| Draw ellipse according to Bresenham's algorithm | +//+------------------------------------------------------------------+ +void CCanvas::Ellipse(int x1,int y1,int x2,int y2,const uint clr) + { + int x,y; + int rx,ry; + int dx,dy; + int xx,yy; + int rx_sq,ry_sq; + int f; + int tmp; +//--- handle extreme conditions + if(x1==x2) + { + if(y1==y2) + PixelSet(x1,y1,clr); + else + LineVertical(x1,y1,y2,clr); + return; + } + if(y1==y2) + { + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; + rx=(x2-x1)>>1; + ry=(y2-y1)>>1; + dx=0; + dy=ry; + rx_sq=rx*rx; + ry_sq=ry*ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>ry_sq*dx) + { + yy=y+dy; + if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xx=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; + while(dy>=0) + { + yy=y+dy; + if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xxx2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; +//--- check rays + if(x3==x && y3==y) + return; + if(x4==x && y4==y) + return; +//--- calculate parameters of ray x3,y3 + fi3=AngleCalc(x,y,x3,y3); +//--- calculate parameters of ray x4,y4 + fi4=AngleCalc(x,y,x4,y4); +//--- draw arc + Arc(x,y,x2-x,y2-y,fi3,fi4,clr); + } +//+------------------------------------------------------------------+ +//| Draws ellipse arc | +//+------------------------------------------------------------------+ +void CCanvas::Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr) + { + int x3,y3,x4,y4; +//--- check + if(rx<10 || ry<10) + return; + if(rx<0) + rx=-rx; + if(ry<0) + ry=-ry; +//--- check rays + if(fi3==fi4) + return; +//--- adjustment for passing through 0 + if(fi40) || // ray 3 is in the 1st or 2nd quadrant + (fi0) || // ray 4 is in the 1st or 2nd quadrant + (fi4-fi3>=M_PI)) // arc will pass through the top of the ellipse + { + dx=0; + dy=ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>=ry_sq*dx) + { + yy=y-dy; + if(dx==0) + { + //--- central point + fi=AngleCalc(0,0,0,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(x,yy,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=x; + yy_c=yy; + fi_c=fi; + xx_a=x; + yy_a=yy; + fi_a=fi; + } + else + { + //--- iterate clockwise + xx=x+dx; + fi=AngleCalc(0,0,dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + xx=x-dx; + fi=AngleCalc(0,0,-dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,dx,-dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x+dx,y3=y-dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,-dx,-dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x-dx,y4=y-dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 2 left +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi3>M_PI_2 && fi3<3*M_PI_2) || // ray 3 is in the 2nd or 3rd quadrant + (fi>M_PI_2 && fi<3*M_PI_2) || // ray 4 is in the 2nd or 3rd quadrant + (fi4-fi3>=M_PI)) // arc will pass through the left part of the ellipse + { + dx=rx; + dy=0; + f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); + while(ry_sq*dx>=rx_sq*dy) + { + xx=x-dx; + if(dy==0) + { + //--- central point + fi=AngleCalc(0,0,-dx,0); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,y,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=xx; + yy_c=y; + fi_c=fi; + xx_a=xx; + yy_a=y; + fi_a=fi; + } + else + { + //--- iterate clockwise + yy=y-dy; + fi=AngleCalc(0,0,-dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + yy=y+dy; + fi=AngleCalc(0,0,-dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dx--; + f-=(ry_sq<<2)*dx; + } + f+=(rx_sq<<1)*(3+(dy<<1)); + dy++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,-dx,-dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x-dx,y3=y-dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,-dx,dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x-dx,y4=y+dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 3 bottom +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi3>M_PI && fi3<2*M_PI) || // ray 3 is in the 3rd or 4th quadrant + (fi>M_PI && fi<2*M_PI) || // ray 4 is in the 3rd or 4th quadrant + (fi4-fi3>=M_PI)) // arc will pass through the bottom of the ellipse + { + dx=0; + dy=ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>=ry_sq*dx) + { + yy=y+dy; + if(dx==0) + { + //--- central point + fi=AngleCalc(0,0,0,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(x,yy,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=x; + yy_c=yy; + fi_c=fi; + xx_a=x; + yy_a=yy; + fi_a=fi; + } + else + { + //--- iterate clockwise + xx=x-dx; + fi=AngleCalc(0,0,-dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + xx=x+dx; + fi=AngleCalc(0,0,dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,-dx,dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x-dx,y3=y+dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,dx,dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x+dx,y4=y+dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 4 right +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi33*M_PI_2) || // ray 3 is 1 or 4 quadrant + (fi3*M_PI_2) || // ray 4 is 1 or 4 quadrant + (fi4-fi3>=M_PI)) // arc will pass through the right side of the ellipse + { + dx=rx; + dy=0; + f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); + while(ry_sq*dx>=rx_sq*dy) + { + xx=x+dx; + if(dy==0) + { + //--- central point + fi=AngleCalc(0,0,dx,0); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,y,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=xx; + yy_c=y; + fi_c=fi; + xx_a=xx; + yy_a=y; + fi_a=fi; + } + else + { + //--- iterate clockwise + yy=y+dy; + fi=AngleCalc(0,0,dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + yy=y-dy; + fi=AngleCalc(0,0,dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(MathMod(fi_a,2*M_PI)-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dx--; + f-=(ry_sq<<2)*dx; + } + f+=(rx_sq<<1)*(3+(dy<<1)); + dy++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,dx,dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x+dx,y3=y+dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,dx,-dy); + if(MathAbs(MathMod(fi_a,2*M_PI)-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x+dx,y4=y-dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } + } +//+------------------------------------------------------------------+ +//| Draws ellipse pie | +//+------------------------------------------------------------------+ +void CCanvas::Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr) + { + int tmp; + int x,y; +//--- + double fi3; + double fi4; +//--- check + if(x1==x2 || y1==y2) + return; +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; +//--- check rays + if(x3==x && y3==y) + return; + if(x4==x && y4==y) + return; +//--- calculate parameters of ray x3,y3 + fi3=AngleCalc(x,y,x3,y3); +//--- calculate parameters of ray x4,y4 + fi4=AngleCalc(x,y,x4,y4); +//--- draw pie + Pie(x,y,x2-x,y2-y,fi3,fi4,clr,fill_clr); + } +//+------------------------------------------------------------------+ +//| Draws ellipse pie | +//+------------------------------------------------------------------+ +void CCanvas::Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr) + { + int x3=x; + int y3=y; + int x4=x; + int y4=y; +//--- check + if(rx==0 || ry==0) + return; + if(rx<0) + rx=-rx; + if(ry<0) + ry=-ry; +//--- check rays + if(fi3==fi4) + return; +//--- adjustment for passing through 0 + if(fi4ry) + rx=ry; + double fi=(fi3+fi4)/2; + int xf=x+(int)(0.9*rx*cos(fi)); + int yf=y-(int)(0.9*rx*sin(fi)); + Fill(xf,yf,fill_clr); + } +//+------------------------------------------------------------------+ +//| Draw filled circle | +//+------------------------------------------------------------------+ +void CCanvas::FillCircle(int x,int y,int r,const uint clr) + { + int f =1-r; + int dd_x=1; + int dd_y=-2*r; + int dx =0; + int dy =r; +//--- draw + while(dy>=dx) + { + LineHorizontal(x-dy,x+dy,y-dx,clr); + LineHorizontal(x-dy,x+dy,y+dx,clr); + //--- + if(f>=0) + { + LineHorizontal(x-dx,x+dx,y-dy,clr); + LineHorizontal(x-dx,x+dx,y+dy,clr); + dy--; + dd_y+=2; + f+=dd_y; + } + dx++; + dd_x+=2; + f+=dd_x; + } + } +//+------------------------------------------------------------------+ +//| Draw filled ellipse | +//+------------------------------------------------------------------+ +void CCanvas::FillEllipse(int x1,int y1,int x2,int y2,const uint clr) + { + int x,y; + int rx,ry; + int dx,dy; + int rx_sq,ry_sq; + int f; + int tmp; +//--- handle extreme conditions + if(x1==x2) + { + if(y1==y2) + PixelSet(x1,y1,clr); + else + LineVertical(x1,y1,y2,clr); + return; + } + if(y1==y2) + { + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; + rx=(x2-x1)>>1; + ry=(y2-y1)>>1; + dx=0; + dy=ry; + rx_sq=rx*rx; + ry_sq=ry*ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>ry_sq*(dx)) + { + LineHorizontal(x-dx,x+dx,y+dy,clr); + LineHorizontal(x-dx,x+dx,y-dy,clr); + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; + while(dy>=0) + { + LineHorizontal(x-dx,x+dx,y+dy,clr); + LineHorizontal(x-dx,x+dx,y-dy,clr); + if(f<=0) + { + dx++; + f+=(ry_sq<<2)*dx; + } + dy--; + f+=(rx_sq<<1)*(3-(dy<<1)); + } + } +//+------------------------------------------------------------------+ +//| Draw filled rectangle | +//+------------------------------------------------------------------+ +void CCanvas::FillRectangle(int x1,int y1,int x2,int y2,const uint clr) + { + int tmp; +//--- sort vertexes + if(x2=m_width || y1>=m_height) + return; +//--- stay withing screen boundaries + if(x1<0) + x1=0; + if(y1<0) + y1=0; + if(x2>=m_width) + x2=m_width -1; + if(y2>=m_height) + y2=m_height-1; + int len=(x2-x1)+1; +//--- set pixels + for(; y1<=y2; y1++) + ArrayFill(m_pixels,y1*m_width+x1,len,clr); + } +//+------------------------------------------------------------------+ +//| Draw filled triangle | +//+------------------------------------------------------------------+ +void CCanvas::FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr) + { + int xx1,xx2,tmp; + double k1=0,k2=0,xd1,xd2; +//--- sort vertexes from lesser to greater + if(y1>y2) + { + tmp=y2; + y2 =y1; + y1 =tmp; + tmp=x2; + x2 =x1; + x1=tmp; + } + if(y1>y3) + { + tmp=y1; + y1 =y3; + y3 =tmp; + tmp=x1; + x1 =x3; + x3 =tmp; + } + if(y2>y3) + { + tmp=y2; + y2 =y3; + y3 =tmp; + tmp=x2; + x2 =x3; + x3 =tmp; + } +//--- all vertexes are out of image boundaries + if(y3<0 || y1>m_height) + return; + if(x1<0 && x2<0 && x3<0) + return; + if(x1>m_width && x2>m_width && x3>m_width) + return; +//--- find coefficients of lines + if((tmp=y1-y2)!=0) + k1=(x1-x2)/(double)tmp; + if((tmp=y1-y3)!=0) + k2=(x1-x3)/(double)tmp; +//--- + xd1=x1; + xd2=x1; +//--- + for(int i=y1; i<=y3; i++) + { + if(i==y2) + { + if((tmp=y2-y3)!=0) + k1=(x2-x3)/(double)tmp; + xd1=x2; + } + //--- calculate new boundaries of triangle line + xx1 =(int)xd1; + xd1+=k1; + xx2 =(int)xd2; + xd2+=k2; + //--- triangle line is out of screen boundaries + if(i<0 || i>=m_height) + continue; + //--- sort + if(xx1>xx2) + { + tmp=xx1; + xx1=xx2; + xx2=tmp; + } + //--- line is out of screen boundaries + if(xx2<0 || xx1>=m_width) + continue; + //--- draw only what is within screen boundaries + if(xx1<0) + xx1=0; + if(xx2>=m_width) + xx2=m_width-1; + //--- draw horizontal line of triangle + ArrayFill(m_pixels,i*m_width+xx1,xx2-xx1,clr); + } + } +//+------------------------------------------------------------------+ +//| Draw filled poligon | +//+------------------------------------------------------------------+ +void CCanvas::FillPolygon(int &x[],int &y[],const uint clr) + { + static CPoint p[]; + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<3) + return; +//--- resize array of points + ArrayResize(p,total); +//--- find top-left point + int imin=0; + int xmin=x[0]; + int ymin=y[0]; + for(int i=1; iymin) + continue; + if(y[i]==ymin) + { + if(x[i]0.0) + xx[1]=xx[3]=ix+1; + if(dy<0.0) + yy[2]=yy[3]=iy-1; + if(dy==0.0) + yy[2]=yy[3]=iy; + if(dy>0.0) + yy[2]=yy[3]=iy+1; +//--- calculate radii and sum of their squares + for(int i=0; i<4; i++) + { + dx=xx[i]-x; + dy=yy[i]-y; + rr[i]=1/(dx*dx+dy*dy); + rrr+=rr[i]; + } +//--- draw pixels + for(int i=0; i<4; i++) + { + k=rr[i]/rrr; + c=PixelGet(xx[i],yy[i]); + a=(uchar)(k*GETRGBA(clr)+(1-k)*GETRGBA(c)); + r=(uchar)(k*GETRGBR(clr)+(1-k)*GETRGBR(c)); + g=(uchar)(k*GETRGBG(clr)+(1-k)*GETRGBG(c)); + b=(uchar)(k*GETRGBB(clr)+(1-k)*GETRGBB(c)); + PixelSet(xx[i],yy[i],ARGB(a,r,g,b)); + } + } +//+------------------------------------------------------------------+ +//| Get line style | +//+------------------------------------------------------------------+ +uint CCanvas::LineStyleGet(void) const + { + switch(m_style) + { + case 0xFFFFFF: + return(STYLE_SOLID); + break; + case 0x3FFFF: + return(STYLE_DASH); + break; + case 0x1C71C7: + return(STYLE_DOT); + break; + case 0x381FF: + return(STYLE_DASHDOT); + break; + case 0x1C71FF: + return(STYLE_DASHDOTDOT); + break; + default: + return (m_style); + break; + } + } +//+------------------------------------------------------------------+ +//| Set line style | +//+------------------------------------------------------------------+ +void CCanvas::LineStyleSet(const uint style) + { + switch(style) + { + case STYLE_SOLID: + m_style=0xFFFFFF; + break; + case STYLE_DASH: + m_style=0x3FFFF; + break; + case STYLE_DOT: + m_style=0x1C71C7; + break; + case STYLE_DASHDOT: + m_style=0x381FF; + break; + case STYLE_DASHDOTDOT: + m_style=0x1C71FF; + break; + default: + //--- high-order bit must be set then custom style + if((style&0x80000000)!=0) + { + m_style=style; + } + break; + } + m_style_idx=0; + } +//+------------------------------------------------------------------+ +//| Draw line with antialiasing (with style) | +//+------------------------------------------------------------------+ +void CCanvas::LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style) + { +//--- line is out of image boundaries + if((x1<0 && x2<0) || (y1<0 && y2<0)) + return; + if(x1>=m_width && x2>=m_width) + return; + if(y1>=m_height && y2>=m_height) + return; +//--- check + if(x1==x2 && y1==y2) + { + PixelSet(x1,y1,clr); + return; + } +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); +//--- preliminary calculations + double dx=x2-x1; + double dy=y2-y1; + double xy=sqrt(dx*dx+dy*dy); + double xx=x1; + double yy=y1; + uint mask=1<=fabs(dx) && fabs(y2-yy)>=fabs(dy)); +//--- set last pixel + if((m_style&mask)==mask) + { + PixelSetAA(x2,y2,clr); + } +//--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + } +//+------------------------------------------------------------------+ +//| Draw polyline with antialiasing (with style) | +//+------------------------------------------------------------------+ +void CCanvas::PolylineAA(int &x[],int &y[],const uint clr,const uint style) + { +//--- check arrays + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<=m_width && x2>=m_width) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + if(y1>=m_height && y2>=m_height) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- check + if(x1==x2 && y1==y2) + { + PixelSet(x1,y1,clr); + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- preliminary calculations + double dx=x2-x1; + double dy=y2-y1; + double xy=sqrt(dx*dx+dy*dy); + double xx=x1; + double yy=y1; + //--- set pixels + dx/=xy; + dy/=xy; + do + { + if((m_style&mask)==mask) + { + PixelSetAA(xx,yy,clr); + } + xx+=dx; + yy+=dy; + mask<<=1; + if(mask==0x1000000) + mask=1; + } + while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy)); + //--- set last pixel + if((m_style&mask)==mask) + { + PixelSetAA(x2,y2,clr); + } + mask<<=1; + if(mask==0x1000000) + mask=1; + } +//--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + } +//+------------------------------------------------------------------+ +//| Draw polygon with antialiasing (with style) | +//+------------------------------------------------------------------+ +void CCanvas::PolygonAA(int &x[],int &y[],const uint clr,const uint style) + { +//--- check arrays + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<=m_width && x2>=m_width) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + if(y1>=m_height && y2>=m_height) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- check + if(x1==x2 && y1==y2) + { + PixelSet(x1,y1,clr); + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- preliminary calculations + double dx=x2-x1; + double dy=y2-y1; + double xy=sqrt(dx*dx+dy*dy); + double xx=x1; + double yy=y1; + //--- set pixels + dx/=xy; + dy/=xy; + do + { + if((m_style&mask)==mask) + { + PixelSetAA(xx,yy,clr); + } + xx+=dx; + yy+=dy; + mask<<=1; + if(mask==0x1000000) + mask=1; + } + while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy)); + //--- set last pixel + if((m_style&mask)==mask) + { + PixelSetAA(x2,y2,clr); + } + } +//--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + } +//+------------------------------------------------------------------+ +//| Draw triangle with antialiasing | +//+------------------------------------------------------------------+ +void CCanvas::TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style) + { +//--- draw + int x[3]; + int y[3]; + x[0] = x1; + x[1] = x2; + x[2] = x3; + y[0] = y1; + y[1] = y2; + y[2] = y3; + PolygonAA(x,y,clr,style); + } +//+------------------------------------------------------------------+ +//| Draw circle with antialiasing | +//+------------------------------------------------------------------+ +void CCanvas::CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX) + { + if(r<=0) + return; +//--- preliminary calculations + double xx=x+r; + double yy=y; + double fi=0; + double df=M_PI_2/MathCeil(r); +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<M_PI) + df/=2; + do + { + xx=x+r*cos(fi); + yy=y-r*sin(fi); + if((m_style&mask)==mask) + PixelSetAA(xx,yy,clr); + mask<<=1; + if(mask==0x1000000) + mask=1; + fi+=df; + } + while(fabs(2*M_PI-fi)>=df/2); +//--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + } +//+------------------------------------------------------------------+ +//| Draw ellipse with antialiasing | +//+------------------------------------------------------------------+ +void CCanvas::EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX) + { + double rx = (x2-x1)/2; + double ry = (y2-y1)/2; +//--- preliminary calculations + double x=(x2>x1) ? x1+rx : x2+rx; + double y=(y2>y1) ? y1+ry : y2+ry; + double rx2=rx*rx; + double ry2=ry*ry; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<ym) + continue; + if((p[i].y==ym) && (p[i].x>xm)) + continue; + im=i; + xm=p[i].x; + ym=p[i].y; + } +//--- check the orientation of triangle + return PointClassify(p[(im-1+total)%total],p[im],p[(im+1)%total]); + } +//+------------------------------------------------------------------+ +//| Checks convexity of polygon | +//+------------------------------------------------------------------+ +bool CCanvas::IsPolygonConvex(CPoint &p[]) + { + int total=ArraySize(p); +//--- triangle - always convex + if(total==3) + return(true); + int res=SIGN(PointClassify(p[0],p[1],p[2])); + for(int i=1; iymin) + continue; + if(p[i].y==ymin) + { + if(p[i].xp[il].y) + return; + if(yy!=p[il].y) + { + dl=(p[il].x-xl)/(p[il].y-yy); + //--- make adjustment for half of left increment dl/2 + LineHorizontal((int)MathCeil(xl+dl/2),(int)MathFloor(xl),yy,clr); + xl+=dl/2; + } + else + LineHorizontal((int)MathCeil(xl),(int)MathFloor(p[il].x),yy,clr); + } + while(yy==p[ir].y) + { + ir=(ir+1)%total; + if(yy>p[ir].y) + return; + if(yy!=p[ir].y) + { + dr=(p[ir].x-xr)/(p[ir].y-yy); + //--- make adjustment for half of right increment dr/2 + LineHorizontal((int)MathCeil(xr),(int)MathFloor(xr+dr/2),yy,clr); + xr+=dr/2; + } + else + LineHorizontal((int)MathCeil(p[ir].x),(int)MathFloor(xr),yy,clr); + } + yy++; + if(yy==p[il].y) + xl=p[il].x; + else + xl+=dl; + if(yy==p[ir].y) + xr=p[ir].x; + else + xr+=dr; + LineHorizontal((int)MathCeil(xl),(int)MathFloor(xr),yy,clr); + } + while(il>=ir && ir!=0); + } +//+------------------------------------------------------------------+ +//| Draw line according to Wu's algorithm | +//+------------------------------------------------------------------+ +void CCanvas::LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX) + { +//--- calculating the variation of the coordinates + int dx = (x2 > x1) ? (x2 - x1) : (x1 - x2); + int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + //--- line is out of image boundaries + if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- stay withing image boundaries + if(y1<0) + y1=0; + if(y2>=m_height-1) + y2=m_height-1; + //--- draw line + int index=y1*m_width+x1; + for(int i=y1; i<=y2; i++,index+=m_width) + { + if((m_style&mask)==mask) + m_pixels[index]=clr; + + mask<<=1; + if(mask==0x1000000) + mask=1; + } + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + //--- success + return; + } +//--- check if dy==0 then draw horizontal line + if(dy==0) + { + //--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } + //--- line is out of image boundaries + if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) + { + //--- set the previous line style + if(style!=UINT_MAX) + m_style=prev_style; + return; + } + //--- stay withing image boundaries + if(x1<0) + x1=0; + if(x2>=m_width) + x2=m_width-1; + //--- draw line + for(int i=0,index=y1*m_width+x1; i 1) + else + { + //--- first point has to have a smaller Y coordinate + if(y2ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1< x1) ? (x2 - x1) : (x1 - x2); + int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); + int tmp; + //--- check if dx==0 then draw vertical line + if(dx==0) + { + //--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + //--- line is out of image boundaries + if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) + continue; + //--- stay withing image boundaries + if(y1<0) + y1=0; + if(y2>=m_height-1) + y2=m_height-1; + //--- draw line + int index=y1*m_width+x1; + for(int j=y1; j<=y2; j++,index+=m_width) + { + if((m_style&mask)==mask) + m_pixels[index]=clr; + + mask<<=1; + if(mask==0x1000000) + mask=1; + } + continue; + } + //--- check if dy==0 then draw horizontal line + if(dy==0) + { + //--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } + //--- line is out of image boundaries + if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) + continue; + //--- stay withing image boundaries + if(x1<0) + x1=0; + if(x2>=m_width) + x2=m_width-1; + //--- draw line + for(int j=0,index=y1*m_width+x1; j 1) + else + { + //--- first point has to have a smaller Y coordinate + if(y2ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1< x1) ? (x2 - x1) : (x1 - x2); + int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2); + int tmp; + //--- check if dx==0 then draw vertical line + if(dx==0) + { + //--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + //--- line is out of image boundaries + if(y2<0 || y1>=m_height || x1<0 || x1>=m_width) + continue; + //--- stay withing image boundaries + if(y1<0) + y1=0; + if(y2>=m_height-1) + y2=m_height-1; + //--- draw line + int index=y1*m_width+x1; + for(int j=y1; j<=y2; j++,index+=m_width) + { + if((m_style&mask)==mask) + m_pixels[index]=clr; + + mask<<=1; + if(mask==0x1000000) + mask=1; + } + continue; + } + //--- check if dy==0 then draw horizontal line + if(dy==0) + { + //--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } + //--- line is out of image boundaries + if(x2<0 || x1>=m_width || y1<0 || y1>=m_height) + continue; + //--- stay withing image boundaries + if(x1<0) + x1=0; + if(x2>=m_width) + x2=m_width-1; + //--- draw line + for(int j=0,index=y1*m_width+x1; j 1) + else + { + //--- first point has to have a smaller Y coordinate + if(y2x1) ? x1+rx : x2+rx; + int y=(y2>y1) ? y1+ry : y2+ry; + if(rx<=0 || ry<=0) + return; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<0) + LineWu(x,y1,x,y2,clr,style); + return; + } +//--- r be the filter radius (and also the half-width of the wide line) + double r=(size/2.0); +//--- primary calculate + int dy=MathAbs(y2-y1); + int sign=(y10) + LineWu(x1,y,x2,y,clr,style); + return; + } +//--- r be the filter radius (and also the half-width of the wide line) + double r=(size/2.0); +//--- primary calculate + int dx=MathAbs(x2-x1); + int sign=(x10) + LineWu(x1,y1,x2,y2,clr,style); + return; + } +//--- r be the filter radius (and also the half-width of the wide line) + double r=(size/2.0); +//--- compute x and y deltas + double dx=MathAbs(x2-x1); + double dy=MathAbs(y2-y1); + if(dx==0) + { + LineThickVertical(x1,y1,y2,clr,size,style,end_style); + return; + } + if(dy==0) + { + LineThickHorizontal(x1,x2,y1,clr,size,style,end_style); + return; + } +//--- compute the linear coefficients of the two (scaled) edge functions + double k=MathArctan(dx/dy); + double rcos_k=r*cos(k); + double rsin_k=r*sin(k); +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<0) + PolylineWu(x,y,clr,style); + return; + } +//--- check arrays + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- r be the filter radius (and also the half-width of the wide line) + double r=(size/2.0); +//--- + double gap=1.0; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<0) + PolylineWu(x,y,clr,style); + return; + } +//--- check arrays + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; +//--- r be the filter radius (and also the half-width of the wide line) + double r=(size/2.0); +//--- + double gap=1.0; +//--- set the line style + uint prev_style=m_style; + if(style!=UINT_MAX) + LineStyleSet(style); + uint mask=1<>16) &0xff) - + uint((clr1>>16) &0xff)); + uint dg=MathAbs(uint((clr0>>8) &0xff) - + uint((clr1>>8) &0xff)); + uint db=MathAbs(uint((clr0>>0) &0xff) - + uint((clr1>>0) &0xff)); +//--- return + return (dr<=threshould || dg<=threshould || db<=threshould); + } +//+------------------------------------------------------------------+ +//| Calculate and set new color | +//+------------------------------------------------------------------+ +void CCanvas::PixelTransform(const int x,const int y,const uint clr,const double alpha) + { + int index=y*m_width+x; +//--- check + if(x<0 || y<0 || x>m_width || y>m_height || index>=ArraySize(m_pixels)) + return; +//--- check alpha + if(alpha==1) + { + m_pixels[index]=clr; + return; + } +//--- get pixel color + uint clr0=m_pixels[index]; +//--- transform of color component for the background + double r0 = ((clr0>>16) & 0xFF) * (1.0-alpha); + double g0 = ((clr0>>8) & 0xFF) * (1.0-alpha); + double b0 = ((clr0>>0) & 0xFF) * (1.0-alpha); +//--- transform of color component + double r1 = ((clr>>16) & 0xFF) * (alpha); + double g1 = ((clr>>8) & 0xFF) * (alpha); + double b1 = ((clr>>0) & 0xFF) * (alpha); +//--- components of the new color + int r = (int)(r0+r1); + int g = (int)(g0+g1); + int b = (int)(b0+b1); +//--- set new color + m_pixels[y*m_width+x]=((r<<16)|(g<<8)|(b<<0)|(255<<24)); + } +//+------------------------------------------------------------------+ +//| Draw 4 pixel with PixelTransform method | +//+------------------------------------------------------------------+ +void CCanvas::PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha) + { + PixelTransform(x+dx,y+dy,clr,alpha); + PixelTransform(x-dx,y+dy,clr,alpha); + PixelTransform(x+dx,y-dy,clr,alpha); + PixelTransform(x-dx,y-dy,clr,alpha); + } +//+------------------------------------------------------------------+ +//| Draw 4 pixel with antialiasing | +//+------------------------------------------------------------------+ +void CCanvas::PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr) + { + PixelSetAA(x+dx,y+dy,clr); + PixelSetAA(x-dx,y+dy,clr); + PixelSetAA(x+dx,y-dy,clr); + PixelSetAA(x-dx,y-dy,clr); + } +//+------------------------------------------------------------------+ +//| Draw solid segment for vertical thick line | +//+------------------------------------------------------------------+ +void CCanvas::SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style) + { +//--- compute the constol points of the solid segment + int ye1,ye2; + int ys1,ys2; + switch(end_style) + { + case LINE_END_ROUND: + { + ye1=y1; + ye2=y2; + ys1=y1-(int)(ysign*r); + ys2=y2+(int)(ysign*r); + break; + } + case LINE_END_BUTT: + { + ye1=y1; + ye2=y2; + ys1=y1; + ys2=y2; + break; + } + case LINE_END_SQUARE: + { + ye1=y1-(int)(ysign*r); + ye2=y2+(int)(ysign*r); + ys1=ye1; + ys2=ye2; + break; + } + default: + return; + }; +//--- darw solid segment + for(int i=0; i<=MathAbs(ys2-ys1); i++) + { + double yi=ys1+(ysign*i); + for(int j=0; j<2*r; j++) + { + double xi=x-r+j; + double dist=DistancePointSegment(xi,yi,x,ye1,x,ye2); + double val=MathAbs(dist/r); + PixelTransform((int)xi,(int)yi,clr,FilterFunction(val)); + } + } + } +//+------------------------------------------------------------------+ +//| Draw solid segment for horizontal thick line | +//+------------------------------------------------------------------+ +void CCanvas::SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style) + { +//--- compute the constol points of the solid segment + int xe1,xe2; + int xs1,xs2; + switch(end_style) + { + case LINE_END_ROUND: + { + xe1=x1; + xe2=x2; + xs1=x1-(int)(xsign*r); + xs2=x2+(int)(xsign*r); + break; + } + case LINE_END_BUTT: + { + xe1=x1; + xe2=x2; + xs1=x1; + xs2=x2; + break; + } + case LINE_END_SQUARE: + { + xe1=x1-(int)(xsign*r); + xe2=x2+(int)(xsign*r); + xs1=xe1; + xs2=xe2; + break; + } + default: + return; + }; +//--- draw solid segment + for(int i=0; i<=MathAbs(xs2-xs1); i++) + { + double xi=xs1+(xsign*i); + for(int j=0; j<2*r; j++) + { + double yi=y-r+j; + double dist=DistancePointSegment(xi,yi,xe1,y,xe2,y); + double val=MathAbs(dist/r); + PixelTransform((int)xi,(int)yi,clr,FilterFunction(val)); + } + } + } +//+------------------------------------------------------------------+ +//| Draw solid segment for thick line | +//+------------------------------------------------------------------+ +void CCanvas::Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign, + const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style) + { + if(x1==x2 && y1==y2) + return; + if(x1==x2) + { + SegmentVertical(x1,y1,y2,ysign,r,clr,end_style); + return; + } + if(y1==y2) + { + SegmentHorizontal(x1,x2,y1,xsign,r,clr,end_style); + return; + } +//--- compute the constol points of the solid segment + int xe1,ye1,xe2,ye2; + int xs1,ys1,xs2,ys2; + switch(end_style) + { + case LINE_END_ROUND: + { + xe1=x1; + ye1=y1; + xe2=x2; + ye2=y2; + xs1=x1-(xsign)*(int)(rsin_k); + ys1=y1-(ysign)*(int)(rcos_k); + xs2=x2+(xsign)*(int)(rsin_k); + ys2=y2+(ysign)*(int)(rcos_k); + break; + } + case LINE_END_BUTT: + { + xe1=x1; + ye1=y1; + xe2=x2; + ye2=y2; + xs1=x1; + ys1=y1; + xs2=x2; + ys2=y2; + break; + } + case LINE_END_SQUARE: + { + xe1=x1-(xsign)*(int)(rsin_k); + ye1=y1-(ysign)*(int)(rcos_k); + xe2=x2+(xsign)*(int)(rsin_k); + ye2=y2+(ysign)*(int)(rcos_k); + xs1=xe1; + ys1=ye1; + xs2=xe2; + ys2=ye2; + break; + } + default: + return; + }; +//--- compute the four corners of the wide line + double p0x=xs1+(xsign)*(rcos_k); + double p0y=ys1-(ysign)*(rsin_k); + double p1x=xs1-(xsign)*(rcos_k); + double p1y=ys1+(ysign)*(rsin_k); + double p2x=xs2+(xsign)*(rcos_k); + double p2y=ys2-(ysign)*(rsin_k); + double p3x=xs2-(xsign)*(rcos_k); + double p3y=ys2+(ysign)*(rsin_k); +//--- draw solid segment + if(MathAbs(kp0)>=1) + { + double xi0,xi1; + double height=MathAbs(p3y-p0y); + for(int i=0; i<=height; i++) + { + double y=p0y+(ysign*i); + double xi00 = MathRound(p0x + (y-p0y)/kp0); + double xi01 = MathRound(p1x + (y-p1y)/kp1); + double xi02 = MathRound(p2x + (y-p2y)/kp1); + double xi03 = MathRound(p3x + (y-p3y)/kp0); + if(xsign==1) + { + xi0 = MathMax(xi00,xi01); + xi1 = MathMin(xi02,xi03); + } + else + { + xi0 = MathMin(xi00,xi01); + xi1 = MathMax(xi02,xi03); + } + double width=MathAbs(MathRound(xi1-xi0)); + for(int j=0; j<=width; j++) + { + double xi=xi0+(xsign*j); + double dist=DistancePointSegment(xi,y,xe1,ye1,xe2,ye2); + double val = MathAbs(dist/r); + PixelTransform((int)xi,(int)y,clr,FilterFunction(val)); + } + } + } + else + { + double yi0,yi1; + double width=MathAbs(p2x-p1x); + for(int i=0; i<=width; i++) + { + double x=p1x+(xsign*i); + double yi00 = MathRound(p0y + (x-p0x)*kp0); + double yi01 = MathRound(p1y + (x-p1x)*kp1); + double yi02 = MathRound(p2y + (x-p2x)*kp1); + double yi03 = MathRound(p3y + (x-p3x)*kp0); + if(ysign==1) + { + yi0 = MathMax(yi00,yi02); + yi1 = MathMin(yi01,yi03); + } + else + { + yi0 = MathMin(yi00,yi02); + yi1 = MathMax(yi01,yi03); + } + double height=MathAbs(yi1-yi0); + for(int j=0; j<=height; j++) + { + double yi=yi0+(ysign*j); + double dist=DistancePointSegment(x,yi,xe1,ye1,xe2,ye2); + double val=MathAbs(dist/r); + PixelTransform((int)x,(int)yi,clr,FilterFunction(val)); + } + } + } + } +//+------------------------------------------------------------------+ +//| Filter function for calculating alpha channel | +//+------------------------------------------------------------------+ +double CCanvas::FilterFunction(const double x) + { + if(x<=0.8) + return(1.0); + else + return MathExp(-(x-0.8)*(x-0.8)*50); + } +//+------------------------------------------------------------------+ +//| Calculate distance between point and segment | +//+------------------------------------------------------------------+ +double CCanvas::DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2) + { +//--- primary calculate + double a=(px-x1)*(px-x1)+(py-y1)*(py-y1); + double b=(px-x2)*(px-x2)+(py-y2)*(py-y2); + double c=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1); +//--- check + if(a>=b+c) + return (MathSqrt(b)); + if(b>=a+c) + return (MathSqrt(a)); +//--- calculate distance + a=MathSqrt(a); + b=MathSqrt(b); + c=MathSqrt(c); + double p=(a+b+c)/2; + double s=MathSqrt((p-a)*(p-b)*(p-c)*p); +//--- check distance + if(MathIsValidNumber(s)) + return(s*2.0/c); + else + return(0); + } +//+------------------------------------------------------------------+ +//| Draw smothing polyline | +//+------------------------------------------------------------------+ +void CCanvas::PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,ENUM_LINE_STYLE style=STYLE_SOLID, + ENUM_LINE_END end_style=LINE_END_ROUND,double tension=0.5,double step=10) + { +//--- + int arr_size= ArraySize(x); + if(arr_size!=ArraySize(y)) + return; +//--- + double x1,x2,y1,y2; + tension*=0.3; +//--- coordinates of Bezier curve + int xc[]; + int yc[]; +//--- initialize control points + double ptX[]; + double ptY[]; + int size_pt=arr_size*3-2; + + ArrayResize(ptX,size_pt); + ArrayResize(ptY,size_pt); +//--- calculation of control points + CalcCurveBezierEndp(x[0],y[0],x[1],y[1],tension,x1,y1); + + ptX[0] = x[0]; + ptY[0] = y[0]; + ptX[1] = x1; + ptY[1] = y1; + + for(int i=0; i0.0) ?(int)(distance/step) : 1; + if(size_i<1) + size_i=2; + ArrayResize(xc,ArraySize(xc)+size_i,1024); + ArrayResize(yc,ArraySize(yc)+size_i,1024); + for(int t=0; t0.0) ?(int)(distance/step) : 1; + if(size_i<1) + size_i=2; + ArrayResize(xc,ArraySize(xc)+size_i,1024); + ArrayResize(yc,ArraySize(yc)+size_i,1024); + for(int t=0; t0.0) ?(int)(distance/step) : 1; + if(size_i<1) + size_i=2; + ArrayResize(xc,ArraySize(xc)+size_i,1024); + ArrayResize(yc,ArraySize(yc)+size_i,1024); + for(int t=0; t69V}qi6a?92oedLF(@ZRvWy>u3x1r}=9%1|es|y3 z<#JU`ckj-6*JL)aR?_0NLshitTp`Q4*p-mZ{Gq#SDJgxjTW&0j@c3&p`JsK9oV)JZYP_!QPD$ zj^I(V-h77=q~!C{lHF;^DR~D=U+Vw2=Bq?YU$tO0$^9_#Z#5t?t#)^goWG=L0sWIF zpoDt313M{mO%H;zyPM{DfVuU0D^xzmubV)jpMgeOsWfr=2sKWUcR+c3k!sRr{5W!4 zMo*u#l2%taQJN=m>ky-ThH~V=0CVYWv+9HB=wadweIfE=2PpWZ(->NL`ctDmd(f$E zSf~xOaFEK2z51$BOEiYMNXmST0rZf*qS#}llciX?WM7Ofqhh;YhjLmx6_lf4l8_^e zf!<81jW#{ro~!RsqUXT*C_`X?mGM`eo?)V5KT#5E55S)zd^X|lw&}SK&T-z4n~w{9 zkUyMFDY1d^a-{5|Op(y@_^i~x)gFLXyXb?nV!isZ$Dr>JZ5xd&D_#@*^fv_x2e337 zh!b9*-fLX#VU))hD@%`}@#$#|*0&n5=cj0s@=g12v(R_!`4HuXXq8?Nb_Dn4HYM7hk8w{%*S7%f;B{Qs88g!a4%BP zMFs?u)UTITYVx2nL4=Pry2`_?iaZcKC7x71s0`&2Ayhm#t1+GrL_YCXJPOz~CDKzs zY?bn(W`h_Rxxa~81J@EQskRhnrVB-z(6MLegUE3V3azc2D&fgWs4g(f zGe(JV%I=yKD$&ClIZfmbXCb$ZIW>N^w-TReb9NvD{3a4J_Pq{%)2E>QItaF!7UBrM zpP(c)^BL|_A8jm@8_u7!yO4BD=T=%ItD7y%l8?fE5a?N zcupcbi_RF?F-rR$aoajtr001A%fE;AmqDdQak1P6WOWVE;B9dA2b5Vsy!8m4-3qjj zp2I8jOCN@MN$ z_s5YawFh3??m!AnQzL4OlGZvBP_CI#BrXh6&U*kouuSF9$$UxwJ$1bHbypzI+rV@| zYJX<{zh9(T{FG?(Jhem@pYJ-sU*@k(O5K`kZ2`EZ(qehEMfdA7Xy#V)3-C)~a?;`b zh-KvMBjsIC!P9UX^gK^g^nseYiPH-X=gIwTjD8acy$m#^9T?)bIBGv%1n&(C%=cDjv-%pymqw`n?<@y8s_`tb*bOSr~P`|`4BWP zhV!vrjgEE%c@^G*)MT7UtUNy-)=-}|O(Ul6G*E|p&GXZ+j{Y_3?A|~7)>`N9`w4iN zM=sr|@Ug0oJR?P3qe>CxcG~3@HYKmh%Zs-> z)Ef0F+g3$=NLe2e{+7!wr(90?p2hW&U+Sy035PDF&Gx6sF0u1STMZ|tk-WGvvzNRQ zSrb~WNbtN!p5mOX*XX*MYLu+y_~>M|XgA+DH+|%$@z%Aoe2ie8_d|y%F3o2`QL?>i zsZpV}y?hoY>MtUnz7NJP+exySFS6P9(r*2=qv8kf(GHMNVP^CJ@~o2Y*H$9BFSZc7 zO>O(WkMG*|eAMMVoPOi|rPdkJ%hhCfKg(Q&%5<)E&rh{ZrI;&PG1C#fLTyP_-lDyc znr0R9GVL+_6T0{sYZlDxa4muNU*fZHO2uME%@WplO257-{`&4L?)503W9F(DbA*q?-E=<`*Lt`Iv&NffOTBI{VUGnfuS@vnIW_wlH?ls6XA;xi z#3~c>qFi^7G3}*QPmYs0?>aSYZ1Q8%c~Oii@jU0N?J}E;s~#1*aW&R>i+RX-6i1%d z$Y<`nT=i|Yxf<_@UX{4Ig*ki)2|dC8vWiJ=GT$?R)LO=HlvVZgb4`~m?o1&AH`4O} z!Y8F!yeZ$4uC7FWdz#xknZ$x>zg_~#(@Wdmwn?-4J3jHEw%@FZpIN_iYPNp1wKm@# z_qx+v;bmNbH%+pgc2|SOck#6SoS0p&Yvgk1;nKUFmF{$FXPkx+-%7jTOebc0HQyTc z1l*ctO~PAf??HCayV^|EbkO&;oIKOgI>@CycGII-m5TbP+0D{wVvG`ItA*CueCp#x zuiUCmy+6x5(fgoB9aoEE_D1sUF6I#;I6j}Jdk0>mOvbl~GRs_!mv8PSkkQn4BE;PX{43vaFdy2H=YwSDu&uRU zlz%m!*ga|=g=)qY^M$b3J8I`233MYnQf}lN#6d+jF-4u0z}Jif#KW%66L1zk{>P zFli3avQOl=tgUc&BlC%qjGy~7+ZncgV~6^EUQT3R(j|8AiGZ4AeQ>saDUZU0r?NA4 z@;z88Z{Fh@ty3O5G@l|l@LIk0F4TU@sAl83$SCnz_Mus1G5yuN+qX$~sCl_Dj+Sj% z+u~UHXbbFjvnlU6Ze6LcI78pkEtEz9h@$eLnY+?`o_ z3f3fKc31tAHI9u^Fm8ugZ4YeChmZ0XIUaW-mt>H5SFf`?;@-T-qT*g0<)2#?r;)=t z_C*elm%Vy zcRk1)b!%yS4p3VyjqDw0V@A1@7i-7A%BS&pkUSo{g)ENkD3S5i+cVEbN=kcXwY06p z(>B}Rqejf zn(@>yp18qGqdxObjkgn9MC&K}zn@mi&MYWPiRW9~Ar#Nvuv0gLQ@ktZjPd(o&o3Ee zA^o{7JQI?qA94qxo?@(0PFi;%(N|h_oSG7OU?cUTiYKu6(^alH3$7AFd9yQp@^b^c zC5$7GlM$md+8An{gkiqPa2!=_EFS%pN3~wAz_#4SIWbSuIWhOa>y30)3{My3Ih{67 z$_L@Owa5Hne(r3f)JjJ#l=#ekJbr1v{AN2m-y%OLNIZ4%$jTJmKda|y`#M=xyJzPY zebd59PlxAfnI7_$XSP1TC+DHHRU4_4sE>CE^Tm@$)*zMCfjH;ZzFtM?rI&evnxkup z=b$aFW`o*Hha zAK7yQ~XwzfCaC*1O^3s8+s8 zUY#F9TdqinXqxwod?YF8y_(izc@7Yt;v<5Q3rDBpDI?l7*x6;2f0*pWI&1^al8&~c zJ#J>J zV>!-4%reMWb8d57ZLZolVymhn#VDl%93K}~c)rEBf@{Mc{_HA5pW+Ar;350v*#OLT zDyO5I=?rNpldni~&J|f>dwklu_c9`8%hPb9Ip%rx$H8U3Pk@p$7v&!b2Pe~7wt3Hv4@yT;yVNC`5RxeOU8z+`8vztuh_HAxN3~AxfEv6vNzr` z?I!<$kq0AhjmOHIm-U5SVg81_+WYk+ zYe>CN)V!Kj8saMID1HOY| z4LRlU$bB8FykDeuuYLyV%PsRMDBtqEnYGMkpnS`>XVx;GT=Ffi%%o+R=PPM$+u0PA z7yd3!Yz)z-$zk+%7cq||yD2jo5!j<`dxi{gHN);Dm)Z8ig!a=(uDva?Cb{NOcTBPD$shd+^4?jLOLn~g1$c0Cw!^>N3+x4~cjJ-o&F z_Z2l-u+=R}g}wpCd>`?8yiCPtg|#$B%{3smH}ZT!S367%I>c~;~68Wx+C(hdOtWM8m@feJ|))&iJoHu_uD*Lc_HYo9uXDo`I z`q)@`b}`@csiP0w{wY28vCCVvRC02C-GmG?KVviD=RoV3k7&_Xdl!taH=QP0uld;Q zt;HWh=b3%td;R +#include +#include +//--- enumerations +enum ENUM_SHOW_FLAGS + { + FLAG_SHOW_NONE =0, + FLAG_SHOW_LEGEND =1, + FLAG_SHOW_SCALE_LEFT =2, + FLAG_SHOW_SCALE_RIGHT =4, + FLAG_SHOW_SCALE_TOP =8, + FLAG_SHOW_SCALE_BOTTOM=16, + FLAG_SHOW_GRID =32, + FLAG_SHOW_DESCRIPTORS =64, + FLAG_SHOW_VALUE =128, + FLAG_SHOW_PERCENT =256, + FLAGS_SHOW_SCALES =(FLAG_SHOW_SCALE_LEFT+FLAG_SHOW_SCALE_RIGHT+ + FLAG_SHOW_SCALE_TOP+FLAG_SHOW_SCALE_BOTTOM), + FLAGS_SHOW_ALL =(FLAG_SHOW_LEGEND+FLAGS_SHOW_SCALES+FLAG_SHOW_GRID+ + FLAG_SHOW_DESCRIPTORS+FLAG_SHOW_VALUE+FLAG_SHOW_PERCENT) + }; +enum ENUM_ALIGNMENT + { + ALIGNMENT_LEFT = 1, // align by left border + ALIGNMENT_TOP = 2, // align by top border + ALIGNMENT_RIGHT = 4, // align by right border + ALIGNMENT_BOTTOM = 8 // align by bottom border + }; +//--- macro +#define IS_SHOW_LEGEND ((m_show_flags&FLAG_SHOW_LEGEND) !=0) +#define IS_SHOW_SCALES ((m_show_flags&FLAGS_SHOW_SCALES) !=0) +#define IS_SHOW_SCALE_LEFT ((m_show_flags&FLAG_SHOW_SCALE_LEFT) !=0) +#define IS_SHOW_SCALE_RIGHT ((m_show_flags&FLAG_SHOW_SCALE_RIGHT) !=0) +#define IS_SHOW_SCALE_TOP ((m_show_flags&FLAG_SHOW_SCALE_TOP) !=0) +#define IS_SHOW_SCALE_BOTTOM ((m_show_flags&FLAG_SHOW_SCALE_BOTTOM)!=0) +#define IS_SHOW_GRID ((m_show_flags&FLAG_SHOW_GRID) !=0) +#define IS_SHOW_DESCRIPTORS ((m_show_flags&FLAG_SHOW_DESCRIPTORS) !=0) +#define IS_SHOW_VALUE ((m_show_flags&FLAG_SHOW_VALUE) !=0) +#define IS_SHOW_PERCENT ((m_show_flags&FLAG_SHOW_PERCENT) !=0) +//+------------------------------------------------------------------+ +//| Class CChartCanvas | +//| Usage: base class for graphical charts | +//+------------------------------------------------------------------+ +class CChartCanvas : public CCanvas + { +protected: + //--- colors + uint m_color_background; + uint m_color_border; + uint m_color_text; + uint m_color_grid; + //--- adjusted parameters + uint m_max_data; + uint m_max_descr_len; + uint m_allowed_show_flags; + uint m_show_flags; + ENUM_ALIGNMENT m_legend_alignment; + uint m_threshold_drawing; + bool m_accumulative; + //--- parameters for scales and grid + double m_v_scale_min; + double m_v_scale_max; + uint m_num_grid; + int m_scale_digits; + //--- data + int m_data_offset; + uint m_data_total; + CArray *m_data; + CArrayInt m_colors; + CArrayString m_descriptors; + //--- + CArrayInt m_index; + uint m_index_size; + double m_sum; + double m_others; + uint m_max_descr_width; + uint m_max_value_width; + //--- variables + CRect m_data_area; + //--- variables for scaling and scales + double m_scale_x; + int m_x_min; + int m_x_0; + int m_x_max; + int m_dx_grid; + double m_scale_y; + int m_y_min; + int m_y_0; + int m_y_max; + int m_dy_grid; + string m_scale_text[]; + +public: + CChartCanvas(void); + ~CChartCanvas(void); + //--- create + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + //--- colors + uint ColorBackground(void) const { return(m_color_background); } + void ColorBackground(const uint value); + uint ColorBorder(void) const { return(m_color_border); } + void ColorBorder(const uint value); + uint ColorText(void) const { return(m_color_text); } + void ColorText(const uint value); + uint ColorGrid(void) const { return(m_color_grid); } + void ColorGrid(const uint value) { m_color_grid=value; } + //--- adjusted parameters + uint MaxData(void) const { return(m_max_data); } + void MaxData(const uint value); + uint MaxDescrLen(void) const { return(m_max_descr_len); } + void MaxDescrLen(const uint value); + //--- show flags + void AllowedShowFlags(const uint flags); + uint ShowFlags(void) const { return(m_show_flags); } + void ShowFlags(const uint flags); + bool IsShowLegend(void) const { return(IS_SHOW_LEGEND); } + bool IsShowScaleLeft(void) const { return(IS_SHOW_SCALE_LEFT); } + bool IsShowScaleRight(void) const { return(IS_SHOW_SCALE_RIGHT); } + bool IsShowScaleTop(void) const { return(IS_SHOW_SCALE_TOP); } + bool IsShowScaleBottom(void) const { return(IS_SHOW_SCALE_BOTTOM); } + bool IsShowGrid(void) const { return(IS_SHOW_GRID); } + bool IsShowDescriptors(void) const { return(IS_SHOW_DESCRIPTORS); } + bool IsShowPercent(void) const { return(IS_SHOW_PERCENT); } + void ShowLegend(const bool flag=true); + void ShowScaleLeft(const bool flag=true); + void ShowScaleRight(const bool flag=true); + void ShowScaleTop(const bool flag=true); + void ShowScaleBottom(const bool flag=true); + void ShowGrid(const bool flag=true); + void ShowDescriptors(const bool flag=true); + void ShowValue(const bool flag=true); + void ShowPercent(const bool flag=true); + void LegendAlignment(const ENUM_ALIGNMENT value); + void Accumulative(const bool flag=true); + //--- for scales and grid + double VScaleMin(void) const { return(m_v_scale_min); } + void VScaleMin(const double value); + double VScaleMax(void) const { return(m_v_scale_max); } + void VScaleMax(const double value); + uint NumGrid(void) const { return(m_num_grid); } + void NumGrid(const uint value); + void VScaleParams(const double max,const double min,const uint grid); + //--- state + int DataOffset(void) const { return(m_data_offset); } + void DataOffset(const int value); + //--- data + uint DataTotal(void) const { return(m_data_total); } + bool DescriptorUpdate(const uint pos,const string descr); + bool ColorUpdate(const uint pos,const uint clr); + +protected: + virtual void ValuesCheck(void); + virtual void Redraw(void); + virtual void DrawBackground(void); + virtual void DrawLegend(void); + int DrawLegendVertical(const int w,const int h); + int DrawLegendHorizontal(const int w,const int h); + virtual void CalcScales(void); + virtual void DrawScales(void); + virtual int DrawScaleLeft(const bool draw=true); + virtual int DrawScaleRight(const bool draw=true); + virtual int DrawScaleTop(const bool draw=true); + virtual int DrawScaleBottom(const bool draw=true); + virtual void DrawGrid(void); + virtual void DrawDescriptors(void) {} + virtual void DrawChart(void); + virtual void DrawData(const uint idx=0) {} + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CChartCanvas::CChartCanvas(void) : m_color_background(XRGB(0xFF,0xFF,0xFF)), + m_color_border(XRGB(0x9F,0x9F,0x9F)), + m_color_text(XRGB(0x3F,0x3F,0x3F)), + m_color_grid(XRGB(0xCF,0xCF,0xCF)), + m_max_data(10), + m_max_descr_len(10), + m_allowed_show_flags(FLAGS_SHOW_ALL), + m_show_flags(FLAG_SHOW_NONE), + m_legend_alignment(ALIGNMENT_BOTTOM), + m_threshold_drawing(2), + m_accumulative(false), + m_data_offset(0), + m_data_total(0), + m_data(NULL), + m_v_scale_min(0.0), + m_v_scale_max(10.0), + m_num_grid(5), + m_scale_digits(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CChartCanvas::~CChartCanvas(void) + { + if(m_data!=NULL) + delete m_data; + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CChartCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { +//--- call method of parent class + if(!CCanvas::Create(name,width,height,clrfmt)) + return(false); +//--- set font + FontSet("Tahoma",-100); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Sets background color | +//+------------------------------------------------------------------+ +void CChartCanvas::ColorBackground(const uint value) + { + m_color_background=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets border color | +//+------------------------------------------------------------------+ +void CChartCanvas::ColorBorder(const uint value) + { + m_color_border=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets text color | +//+------------------------------------------------------------------+ +void CChartCanvas::ColorText(const uint value) + { + m_color_text=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets maximum amount of data | +//+------------------------------------------------------------------+ +void CChartCanvas::MaxData(const uint value) + { +//--- check + if((value==0) || (m_data_total==value)) + return; +//--- save + m_max_data=value; + if(m_data_total>m_max_data) + { + m_data_total=value; + m_colors.Resize(value); + m_descriptors.Resize(value); + } + } +//+------------------------------------------------------------------+ +//| Sets maximum length of descriptor | +//+------------------------------------------------------------------+ +void CChartCanvas::MaxDescrLen(const uint value) + { + m_max_descr_len=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets allowed visibility flags | +//+------------------------------------------------------------------+ +void CChartCanvas::AllowedShowFlags(const uint flags) + { + m_allowed_show_flags=flags; + m_show_flags&=m_allowed_show_flags; + } +//+------------------------------------------------------------------+ +//| Sets visibility flags | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowFlags(const uint flags) + { + m_show_flags=flags&m_allowed_show_flags; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for "legend" | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowLegend(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_LEGEND)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_LEGEND; + else + m_show_flags&=~FLAG_SHOW_LEGEND; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for left scale | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowScaleLeft(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_SCALE_LEFT)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_SCALE_LEFT; + else + m_show_flags&=~FLAG_SHOW_SCALE_LEFT; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for right scale | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowScaleRight(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_SCALE_RIGHT)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_SCALE_RIGHT; + else + m_show_flags&=~FLAG_SHOW_SCALE_RIGHT; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for top scale | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowScaleTop(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_SCALE_TOP)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_SCALE_TOP; + else + m_show_flags&=~FLAG_SHOW_SCALE_TOP; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for bottom scale | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowScaleBottom(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_SCALE_BOTTOM)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_SCALE_BOTTOM; + else + m_show_flags&=~FLAG_SHOW_SCALE_BOTTOM; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for grid | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowGrid(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_GRID)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_GRID; + else + m_show_flags&=~FLAG_SHOW_GRID; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for descriptors | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowDescriptors(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_DESCRIPTORS)!=0) + { + if(flag) + m_show_flags|=FLAG_SHOW_DESCRIPTORS; + else + m_show_flags&=~FLAG_SHOW_DESCRIPTORS; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for value | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowValue(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_VALUE)!=0) + { + if(flag) + { + m_show_flags|=FLAG_SHOW_VALUE; + m_show_flags&=~FLAG_SHOW_PERCENT; + } + else + m_show_flags&=~FLAG_SHOW_VALUE; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets visibility flag for percentage | +//+------------------------------------------------------------------+ +void CChartCanvas::ShowPercent(const bool flag) + { + if((m_allowed_show_flags&FLAG_SHOW_PERCENT)!=0) + { + if(flag) + { + m_show_flags|=FLAG_SHOW_PERCENT; + m_show_flags&=~FLAG_SHOW_VALUE; + } + else + m_show_flags&=~FLAG_SHOW_PERCENT; + //--- redraw + if(m_data_total>0) + Redraw(); + } + } +//+------------------------------------------------------------------+ +//| Sets legend alignment | +//+------------------------------------------------------------------+ +void CChartCanvas::LegendAlignment(const ENUM_ALIGNMENT value) + { + m_legend_alignment=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets accumulative flag | +//+------------------------------------------------------------------+ +void CChartCanvas::Accumulative(const bool flag=true) + { + m_accumulative=flag; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets lower limit for vertical scale | +//+------------------------------------------------------------------+ +void CChartCanvas::VScaleMin(const double value) + { +//--- check + if(value==m_v_scale_max) + return; +//--- save + m_v_scale_min=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets upper limit for vertical scale | +//+------------------------------------------------------------------+ +void CChartCanvas::VScaleMax(const double value) + { + if(value==m_v_scale_min) + return; +//--- save + m_v_scale_max=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets number of vertical scale divisions | +//+------------------------------------------------------------------+ +void CChartCanvas::NumGrid(const uint value) + { +//--- check + if(value==0) + return; +//--- save + m_num_grid=value; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets parameters for vertical scale | +//+------------------------------------------------------------------+ +void CChartCanvas::VScaleParams(const double max,const double min,const uint grid) + { +//--- check + if(grid==0) + return; + if(max<=min) + return; +//--- save + m_v_scale_max=max; + m_v_scale_min=min; + m_num_grid =grid; +//--- redraw + if(m_data_total>0) + Redraw(); + } +//+------------------------------------------------------------------+ +//| Sets data offset | +//+------------------------------------------------------------------+ +void CChartCanvas::DataOffset(const int value) + { + m_data_offset=value; +//--- redraw + Redraw(); + } +//+------------------------------------------------------------------+ +//| Updates parameter descriptor only (in specified position) | +//+------------------------------------------------------------------+ +bool CChartCanvas::DescriptorUpdate(const uint pos,const string descr) + { +//--- update + if(descr!=NULL && !m_descriptors.Update(pos,descr)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates parameter color only (in specified position) | +//+------------------------------------------------------------------+ +bool CChartCanvas::ColorUpdate(const uint pos,const uint clr) + { +//--- update + if(clr!=0 && !m_colors.Update(pos,clr)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Checks values for insignificance | +//+------------------------------------------------------------------+ +void CChartCanvas::ValuesCheck(void) + { + string text; + uint w,h; +//--- clear + m_max_value_width=0; + m_sum =0; + m_others =0; + m_index_size =0; + m_index.Clear(); +//--- check + if(m_data==NULL) + return; + if(m_data.Type()==TYPE_DOUBLE) + { + //--- single-series chart + //--- calculate sum of all values + for(uint i=0;im_height) + { + cols++; + rows=(int)m_index_size/cols; + if((int)m_index_size%cols!=0) + rows++; + } +//--- draw + int x0=(m_legend_alignment==ALIGNMENT_RIGHT) ? width-w*cols+h : h; + int x=0; + int y =-h/2; + int i; + if(m_data_total==m_index_size) + { + for(i=0;i<(int)m_data_total;i++,x+=w) + { + if(i%cols==0) + { + x=x0; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,(uint)m_colors[i]); + TextOut(x+h,y," - "+m_descriptors[i],m_color_text); + } + } + else + { + for(i=0;i<(int)m_index_size;i++,x+=w) + { + int index=m_index[i]; + if(i%cols==0) + { + x=x0; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,(uint)m_colors[index]); + TextOut(x+h,y," - "+m_descriptors[index],m_color_text); + } + if(i%cols==0) + { + x=x0; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,COLOR2RGB(clrBlack)); + TextOut(x+h,y," - Others",m_color_text); + } +//--- width + return(w*cols); + } +//+------------------------------------------------------------------+ +//| Draw horizontal "legend" | +//+------------------------------------------------------------------+ +int CChartCanvas::DrawLegendHorizontal(const int w,const int h) + { + int width =m_data_area.Width(); + int height=m_data_area.Height(); + int rows =1; + int cols =(int)m_index_size; +//--- calculate + while(w*cols>m_width) + { + rows++; + cols=(int)m_index_size/rows; + if((int)m_index_size%rows!=0) + cols++; + } +//--- draw + int dx=width/(cols+1); + int x =dx-w/2+h; + int dy=(int)(1.5*h); + int y =(m_legend_alignment==ALIGNMENT_BOTTOM) ? height-dy*(rows+1) : -h/2; + int i; + if(m_data_total==m_index_size) + { + for(i=0;i<(int)m_data_total;i++,x+=dx) + { + if(i%cols==0) + { + x=dx-w/2+h; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,(uint)m_colors[i]); + TextOut(x+h,y," - "+m_descriptors[i],m_color_text); + } + } + else + { + for(i=0;i<(int)m_index_size;i++,x+=dx) + { + int index=m_index[i]; + if(i%cols==0) + { + x=dx-w/2+h; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,(uint)m_colors[index]); + TextOut(x+h,y," - "+m_descriptors[index],m_color_text); + } + if(i%cols==0) + { + x=dx-w/2+h; + y+=dy; + } + FillRectangle(x,y,x+h,y+h,COLOR2RGB(clrBlack)); + TextOut(x+h,y," - Others",m_color_text); + } +//--- height + return(dy*(rows+1)); + } +//+------------------------------------------------------------------+ +//| Calculates coordinates of scales | +//+------------------------------------------------------------------+ +void CChartCanvas::CalcScales(void) + { + int width =m_data_area.Width(); + int height=m_data_area.Height(); +//--- limits + m_y_max=m_data_area.top+DrawScaleTop(false); + m_y_min=m_data_area.bottom-DrawScaleBottom(false); +//--- additional + m_dy_grid=(int)((m_y_min-m_y_max)/m_num_grid); + m_y_max+=(int)(((m_y_min-m_y_max)-m_dy_grid*m_num_grid)/2); + m_y_min=(int)(m_y_max+m_dy_grid*m_num_grid); +//--- normalize + if(m_v_scale_min>=0.0) + m_y_0=m_y_min; + else + { + if(m_v_scale_max<=0.0) + m_y_0=m_y_max; + else + m_y_0=(int)(m_y_max+(m_y_min-m_y_max)*m_v_scale_max/(m_v_scale_max-m_v_scale_min)); + } +//--- scale + m_scale_y=(m_v_scale_max!=m_v_scale_min) ? (m_y_min-m_y_max)/(m_v_scale_max-m_v_scale_min) : 1; +//--- labels on scale + if(ArraySize(m_scale_text)!=m_num_grid+1 && ArrayResize(m_scale_text,m_num_grid+1)==-1) + return; + double val=m_v_scale_min; + double dval=(m_v_scale_max-m_v_scale_min)/m_num_grid; + for(uint i=0;i<=m_num_grid;i++,val+=dval) + m_scale_text[i]=DoubleToString(val,m_scale_digits); + } +//+------------------------------------------------------------------+ +//| Redraws scales | +//+------------------------------------------------------------------+ +void CChartCanvas::DrawScales(void) + { +//--- recalculate + CalcScales(); +//--- redraw scales + if(IS_SHOW_SCALE_LEFT) + DrawScaleLeft(); + if(IS_SHOW_SCALE_RIGHT) + DrawScaleRight(); + if(IS_SHOW_SCALE_TOP) + DrawScaleTop(); + if(IS_SHOW_SCALE_BOTTOM) + DrawScaleBottom(); + } +//+------------------------------------------------------------------+ +//| Redraws left scale | +//+------------------------------------------------------------------+ +int CChartCanvas::DrawScaleLeft(const bool draw) + { +//--- check flag + if(!IS_SHOW_SCALE_LEFT) + return(0); +//--- variables + int x1=m_data_area.left; + int x2; + int y=m_y_min; +//--- calculate scale width + int size=0; + for(uint i=0;i<=m_num_grid;i++) + { + if(size +//+------------------------------------------------------------------+ +//| Class CHistogramChart | +//| Usage: generates histogram chart | +//+------------------------------------------------------------------+ +class CHistogramChart : public CChartCanvas + { +private: + //--- colors + uint m_fill_brush[]; + //--- adjusted parameters + bool m_gradient; + uint m_bar_gap; + uint m_bar_min_size; + uint m_bar_border; + //--- data + CArrayObj *m_values; + +public: + CHistogramChart(void); + ~CHistogramChart(void); + //--- create + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_ARGB_NORMALIZE); + //--- adjusted parameters + void Gradient(const bool flag=true) { m_gradient=flag; } + void BarGap(const uint value) { m_bar_gap=value; } + void BarMinSize(const uint value) { m_bar_min_size=value; } + void BarBorder(const uint value) { m_bar_border=value; } + //--- data + bool SeriesAdd(const double &value[],const string descr="",const uint clr=0); + bool SeriesInsert(const uint pos,const double &value[],const string descr="",const uint clr=0); + bool SeriesUpdate(const uint pos,const double &value[],const string descr=NULL,const uint clr=0); + bool SeriesDelete(const uint pos); + bool ValueUpdate(const uint series,const uint pos,double value); + +protected: + virtual void DrawData(const uint idx); + void DrawBar(const int x,const int y,const int w,const int h,const uint clr); + void GradientBrush(const int size,const uint fill_clr); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CHistogramChart::CHistogramChart(void) : m_gradient(true), + m_bar_gap(3), + m_bar_min_size(5), + m_bar_border(0) + { + ShowFlags(FLAG_SHOW_LEGEND|FLAGS_SHOW_SCALES|FLAG_SHOW_GRID); + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CHistogramChart::~CHistogramChart(void) + { + if(ArraySize(m_fill_brush)!=0) + ArrayFree(m_fill_brush); + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CHistogramChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { +//--- create object to store data + if((m_values=new CArrayObj)==NULL) + return(false); +//--- pass responsibility for its destruction to the parent class + m_data=m_values; +//--- call method of parent class + if(!CChartCanvas::Create(name,width,height,clrfmt)) + return(false); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Adds data series | +//+------------------------------------------------------------------+ +bool CHistogramChart::SeriesAdd(const double &value[],const string descr,const uint clr) + { +//--- check + if(m_data_total==m_max_data) + return(false); +//--- add + CArrayDouble *arr=new CArrayDouble; + if(!m_values.Add(arr)) + return(false); + if(!arr.AssignArray(value)) + return(false); + if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr)) + return(false); + if(!m_descriptors.Add(descr)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Inserts data series | +//+------------------------------------------------------------------+ +bool CHistogramChart::SeriesInsert(const uint pos,const double &value[],const string descr,const uint clr) + { +//--- check + if(m_data_total==m_max_data) + return(false); + if(pos>=m_data_total) + return(false); +//--- insert + CArrayDouble *arr=new CArrayDouble; + if(!m_values.Insert(arr,pos)) + return(false); + if(!arr.AssignArray(value)) + return(false); + if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos)) + return(false); + if(!m_descriptors.Insert(descr,pos)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates data series | +//+------------------------------------------------------------------+ +bool CHistogramChart::SeriesUpdate(const uint pos,const double &value[],const string descr,const uint clr) + { +//--- check + if(pos>=m_data_total) + return(false); + CArrayDouble *data=m_values.At(pos); + if(data==NULL) + return(false); +//--- update + if(!data.AssignArray(value)) + return(false); + if(clr!=0 && !m_colors.Update(pos,clr)) + return(false); + if(descr!=NULL && !m_descriptors.Update(pos,descr)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Deletes data series | +//+------------------------------------------------------------------+ +bool CHistogramChart::SeriesDelete(const uint pos) + { +//--- check + if(pos>=m_data_total && m_data_total!=0) + return(false); +//--- delete + if(!m_values.Delete(pos)) + return(false); + m_data_total--; + if(!m_colors.Delete(pos)) + return(false); + if(!m_descriptors.Delete(pos)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates element in data series | +//+------------------------------------------------------------------+ +bool CHistogramChart::ValueUpdate(const uint series,const uint pos,double value) + { + CArrayDouble *data=m_values.At(series); +//--- check + if(data==NULL) + return(false); +//--- update + if(!data.Update(pos,value)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Draws histogram | +//+------------------------------------------------------------------+ +void CHistogramChart::DrawData(const uint idx) + { + double value=0.0; +//--- check + CArrayDouble *data=m_values.At(idx); + if(data==NULL) + return; + int total=data.Total(); + if(total==0 || (int)idx>=total) + return; +//--- calculate + int x1=m_data_area.left; + int x2=m_data_area.right; + int dx=(x2-x1)/total; + uint clr=m_colors[idx]; + uint w=dx/m_data_total; + if(w0) + { + y=(m_y_0-(int)(value*m_scale_y)); + h=m_y_0-y; + } + else + { + y=m_y_0; + h=-(int)(value*m_scale_y); + } + DrawBar(x,y,w,h,clr); + //--- draw text of value + if(IS_SHOW_VALUE) + { + string text =DoubleToString(value,2); + int width=(int)(TextWidth(text)+w); + if(value>0) + { + if(width>y-m_y_max) + TextOut(x+w/2,y+w,text,m_color_text,TA_RIGHT|TA_VCENTER); + else + TextOut(x+w/2,y-w,text,m_color_text,TA_LEFT|TA_VCENTER); + } + else + { + if(width>m_y_min-y-h) + TextOut(x+w/2,y+h-w,text,m_color_text,TA_LEFT|TA_VCENTER); + else + TextOut(x+w/2,y+h+w,text,m_color_text,TA_RIGHT|TA_VCENTER); + } + } + } + if(IS_SHOW_VALUE) + FontSet(fontname,fontsize,fontflags,fontangle); + } +//+------------------------------------------------------------------+ +//| Draws bar | +//+------------------------------------------------------------------+ +void CHistogramChart::DrawBar(const int x,const int y,const int w,const int h,const uint clr) + { +//--- draw bar + if(!m_gradient || ArraySize(m_fill_brush)>1; + if((r&1)==0) + i1--; + //--- calculate + while(dy>=dx) + { + clr=fill_clr; + dclr=GETRGB(XRGB((r-dy)*GETRGBR(clr)/r,(r-dy)*GETRGBG(clr)/r,(r-dy)*GETRGBB(clr)/r)); + clr-=dclr; + m_fill_brush[i1]=clr; + m_fill_brush[i2]=clr; + //--- + if(f>=0) + { + dy--; + dd_y+=2; + f+=dd_y; + } + dx++; + if(--i1<0) + break; + i2++; + dd_x+=2; + f+=dd_x; + } + } + else + ArrayFree(m_fill_brush); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/Charts/LineChart.mqh b/Canvas/Charts/LineChart.mqh new file mode 100644 index 0000000..16a8441 --- /dev/null +++ b/Canvas/Charts/LineChart.mqh @@ -0,0 +1,383 @@ +//+------------------------------------------------------------------+ +//| LineChart.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include "ChartCanvas.mqh" +#include +//+------------------------------------------------------------------+ +//| Class CLineChart | +//| Usage: generates line chart | +//+------------------------------------------------------------------+ +class CLineChart : public CChartCanvas + { +private: + //--- data + CArrayObj *m_values; + //--- adjusted parameters + bool m_filled; + +public: + CLineChart(void); + ~CLineChart(void); + //--- create + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_ARGB_NORMALIZE); + //--- adjusted parameters + void Filled(const bool flag=true) { m_filled=flag; } + //--- set up + bool SeriesAdd(const double &value[],const string descr="",const uint clr=0); + bool SeriesInsert(const uint pos,const double &value[],const string descr="",const uint clr=0); + bool SeriesUpdate(const uint pos,const double &value[],const string descr=NULL,const uint clr=0); + bool SeriesDelete(const uint pos); + bool ValueUpdate(const uint series,const uint pos,double value); + +protected: + virtual void DrawChart(void); + virtual void DrawData(const uint index=0); + +private: + double CalcArea(const uint index); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CLineChart::CLineChart(void) : m_filled(false) + { + ShowFlags(FLAG_SHOW_LEGEND|FLAGS_SHOW_SCALES|FLAG_SHOW_GRID); + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CLineChart::~CLineChart(void) + { + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CLineChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { +//--- create object to store data + if((m_values=new CArrayObj)==NULL) + return(false); +//--- pass responsibility for its destruction to the parent class + m_data=m_values; +//--- call method of parent class + if(!CChartCanvas::Create(name,width,height,clrfmt)) + return(false); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Adds data series | +//+------------------------------------------------------------------+ +bool CLineChart::SeriesAdd(const double &value[],const string descr,const uint clr) + { +//--- check + if(m_data_total==m_max_data) + return(false); +//--- add + CArrayDouble *arr=new CArrayDouble; + if(!m_values.Add(arr)) + return(false); + if(!arr.AssignArray(value)) + return(false); + if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr)) + return(false); + if(!m_descriptors.Add(descr)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Inserts data series | +//+------------------------------------------------------------------+ +bool CLineChart::SeriesInsert(const uint pos,const double &value[],const string descr,const uint clr) + { +//--- check + if(m_data_total==m_max_data) + return(false); + if(pos>=m_data_total) + return(false); +//--- insert + CArrayDouble *arr=new CArrayDouble; + if(!m_values.Insert(arr,pos)) + return(false); + if(!arr.AssignArray(value)) + return(false); + if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos)) + return(false); + if(!m_descriptors.Insert(descr,pos)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates data series | +//+------------------------------------------------------------------+ +bool CLineChart::SeriesUpdate(const uint pos,const double &value[],const string descr,const uint clr) + { +//--- check + if(pos>=m_data_total) + return(false); + CArrayDouble *data=m_values.At(pos); + if(data==NULL) + return(false); +//--- update + if(!data.AssignArray(value)) + return(false); + if(clr!=0 && !m_colors.Update(pos,clr)) + return(false); + if(descr!=NULL && !m_descriptors.Update(pos,descr)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Deletes data series | +//+------------------------------------------------------------------+ +bool CLineChart::SeriesDelete(const uint pos) + { +//--- check + if(pos>=m_data_total && m_data_total!=0) + return(false); +//--- delete + if(!m_values.Delete(pos)) + return(false); + m_data_total--; + if(!m_colors.Delete(pos)) + return(false); + if(!m_descriptors.Delete(pos)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates element in data series | +//+------------------------------------------------------------------+ +bool CLineChart::ValueUpdate(const uint series,const uint pos,double value) + { + CArrayDouble *data=m_values.At(series); +//--- check + if(data==NULL) + return(false); +//--- update + if(!data.Update(pos,value)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Redraws data | +//+------------------------------------------------------------------+ +void CLineChart::DrawChart(void) + { + if(m_filled) + { + //--- calculate areas of filling + double s[]; + ArrayResize(s,m_data_total); + ArrayInitialize(s,0); + for(uint i=0;im_y_0 && y2m_y_0)) + { + //--- draw two triangles + int x3; + if(y1>y2) + { + x3=x+dx*(y1-m_y_0)/(y1-y2); + FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]); + FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]); + } + else + { + x3=x+dx*(m_y_0-y1)/(y2-y1); + FillTriangle(x,y1,x3,m_y_0,x,m_y_0,(uint)m_colors[index]); + FillTriangle(x+dx,y2,x3,m_y_0,x+dx,m_y_0,(uint)m_colors[index]); + } + continue; + } + if(y1y2) + FillTriangle(x,y1,x+dx,y2,x+dx,y1,(uint)m_colors[index]); + if(y1m_y_0 || y2>m_y_0) + { + if(y1y2) + { + FillTriangle(x,y1,x+dx,y2,x,y2,(uint)m_colors[index]); + y1=y2; + } + } + FillRectangle(x,m_y_0,x+dx,y1,(uint)m_colors[index]); + } + else + LineAA(x,y1,x+dx,y2,(uint)m_colors[index],STYLE_SOLID); + } + } +//+------------------------------------------------------------------+ +//| Area of filling | +//+------------------------------------------------------------------+ +double CLineChart::CalcArea(const uint index) + { + double area =0; + double value=0; + int dx =100; +//--- + CArrayDouble *data=m_values.At(index); + if(data==NULL) + return(0); + int total=data.Total(); + if(total<=1) + return(0); + int y1=0; + int y2=(int)(m_y_0-data[0]*m_scale_y); + for(int i=0;im_y_0 && y2m_y_0)) + { + //--- line of values crosses the Y axis + int x; + if(y1>y2) + { + //--- from the bottom up + x=dx*(y1-m_y_0)/(y1-y2); + //--- add area of lower triangle + area+=x*(y1-m_y_0)/2; + //--- add area of upper triangle + area+=(dx-x)*(m_y_0-y2)/2; + } + else + { + //--- from top down + x=dx*(m_y_0-y1)/(y2-y1); + //--- add area of upper triangle + area+=x*(m_y_0-y1)/2; + //--- add area of lower triangle + area+=(dx-x)*(y2-m_y_0)/2; + } + continue; + } + if(y1y2) + { + //--- add area of triangle + area+=dx*(y1-y2)/2; + //--- add area of rectangle + area+=dx*(m_y_0-y2); + } + if(y1m_y_0 || y2>m_y_0) + { + //--- both values are less than zero + if(y1y2) + { + //--- add area of triangle + area+=dx*(y1-y2)/2; + //--- add area of rectangle + area+=dx*(y2-m_y_0); + } + } + } +//--- + return(area); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/Charts/PieChart.mqh b/Canvas/Charts/PieChart.mqh new file mode 100644 index 0000000..d4b61ea --- /dev/null +++ b/Canvas/Charts/PieChart.mqh @@ -0,0 +1,414 @@ +//+------------------------------------------------------------------+ +//| PieChart.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include "ChartCanvas.mqh" +//+------------------------------------------------------------------+ +//| Class CPieChart | +//| Usage: generates pie chart | +//+------------------------------------------------------------------+ +class CPieChart : public CChartCanvas + { +private: + //--- data + CArrayDouble *m_values; + //--- for draw + int m_x0; + int m_y0; + int m_r; + +public: + CPieChart(void); + ~CPieChart(void); + //--- create + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + //--- data + bool SeriesSet(const double &value[],const string &text[],const uint &clr[]); + bool ValueAdd(const double value,const string descr="",const uint clr=0); + bool ValueInsert(const uint pos,const double value,const string descr="",const uint clr=0); + bool ValueUpdate(const uint pos,const double value,const string descr=NULL,const uint clr=0); + bool ValueDelete(const uint pos); + +protected: + virtual void DrawChart(void); + void DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr); + string LabelMake(const string text,const double value,const bool to_left); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CPieChart::CPieChart(void) + { + uint flags=FLAG_SHOW_LEGEND|FLAG_SHOW_DESCRIPTORS|FLAG_SHOW_VALUE|FLAG_SHOW_PERCENT; + AllowedShowFlags(flags); + ShowFlags(flags); + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CPieChart::~CPieChart(void) + { + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CPieChart::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { +//--- create object to store data + if((m_values=new CArrayDouble)==NULL) + return(false); +//--- pass responsibility for its destruction to the parent class + m_data=m_values; +//--- call method of parent class + if(!CChartCanvas::Create(name,width,height,clrfmt)) + return(false); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Sets displayed parameters | +//+------------------------------------------------------------------+ +bool CPieChart::SeriesSet(const double &value[],const string &text[],const uint &clr[]) + { +//--- !!! user is responsible for correct filling of arrays !!! +//--- check + if(m_values==NULL) + return(false); +//--- set + if(!m_values.AssignArray(value)) + return(false); + if(!m_descriptors.AssignArray(text)) + return(false); + if(!m_colors.AssignArray(clr)) + return(false); + m_data_total=m_values.Total(); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Adds displayed parameter (to the end) | +//+------------------------------------------------------------------+ +bool CPieChart::ValueAdd(const double value,const string descr,const uint clr) + { +//--- check + if((value<=0)) + return(false); +//--- add + if(!m_values.Add(value)) + return(false); + if(!m_descriptors.Add(descr)) + return(false); + if(!m_colors.Add((clr==0) ? GetDefaultColor(m_data_total) : clr)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Inserts displayed parameter (to specified position) | +//+------------------------------------------------------------------+ +bool CPieChart::ValueInsert(const uint pos,const double value,const string descr,const uint clr) + { +//--- check + if((value<=0)) + return(false); +//--- insert + if(!m_values.Insert(value,pos)) + return(false); + if(!m_descriptors.Insert(descr,pos)) + return(false); + if(!m_colors.Insert((clr==0) ? GetDefaultColor(m_data_total) : clr,pos)) + return(false); + m_data_total++; +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Updates displayed parameter (in specified position) | +//+------------------------------------------------------------------+ +bool CPieChart::ValueUpdate(const uint pos,const double value,const string descr,const uint clr) + { +//--- check + if((value<=0)) + return(false); +//--- update + if(!m_values.Update(pos,value)) + return(false); + if(descr!=NULL && !m_descriptors.Update(pos,descr)) + return(false); + if(clr!=0 && !m_colors.Update(pos,clr)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Deletes displayed parameter (from specified position) | +//+------------------------------------------------------------------+ +bool CPieChart::ValueDelete(const uint pos) + { +//--- delete + if(!m_values.Delete(pos)) + return(false); + m_data_total--; + if(!m_descriptors.Delete(pos)) + return(false); + if(!m_colors.Delete(pos)) + return(false); +//--- redraw + Redraw(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Draws | +//+------------------------------------------------------------------+ +void CPieChart::DrawChart(void) + { +//--- check + if(m_data_total==0) + return; +//--- variables + string text=""; + double angle=M_PI*(m_data_offset%360)/180; + int width,height; + int dw=0; + int dh=0; + int index; + CPoint p0[]; + CPoint p1[]; +//--- calculate geometry + width =(m_data_area.Width()<<3)/10; + height=(m_data_area.Height()<<3)/10; + if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) + { + if(IS_SHOW_VALUE) + dw=(int)m_max_value_width; + else + { + if(IS_SHOW_PERCENT) + dw=TextWidth("100.00%"); + } + } + else + { + if(IS_SHOW_DESCRIPTORS) + { + if(IS_SHOW_VALUE) + dw=(int)m_max_value_width+TextWidth(" ()"); + else + { + if(IS_SHOW_PERCENT) + dw=TextWidth(" (100.00%)"); + } + dw+=(int)m_max_descr_width; + } + } +//--- pie chart will always be round + width -=2*dw+10; + height-=20; + m_x0=m_data_area.left+(m_data_area.Width()>>1); + m_y0=m_data_area.top+(m_data_area.Height()>>1); + m_r =(((width>height) ? height : width)>>1); +//--- draw pie chart + if(ArrayResize(p0,m_data_total+1)==-1) + return; + if(m_data_total==1) + { + FillCircle(m_x0,m_y0,m_r,m_colors[0]); + Circle(m_x0,m_y0,m_r,m_color_border); + } + else + { + Circle(m_x0,m_y0,m_r,m_color_border); + for(uint i=0;i=m_x0) + { + x=p1[i].x+10; + y=p1[i].y; + index=m_index[i]; + text=LabelMake(m_descriptors[index],m_values[index],false); + if(text!="") + { + Line(p0[i].x,p0[i].y,p1[i].x,p1[i].y,m_color_border); + Line(p1[i].x,p1[i].y,x,y,m_color_border); + TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER); + } + } + if(m_data_total!=m_index_size) + { + index=(int)m_data_total-1; + if(p0[index].x>=m_x0) + { + x=p1[index].x+10; + y=p1[index].y; + text=LabelMake("Others",m_others,true); + TextOut(x+5,y,text,m_color_text,TA_LEFT|TA_VCENTER); + } + else + { + x=p1[index].x-10; + y=p1[index].y; + text=LabelMake("Others",m_others,false); + TextOut(x-5,y,text,m_color_text,TA_RIGHT|TA_VCENTER); + } + if(text!="") + { + Line(p0[index].x,p0[index].y,p1[index].x,p1[index].y,m_color_border); + Line(p1[index].x,p1[index].y,x,y,m_color_border); + } + } + ArrayFree(p1); + ArrayFree(p0); + } +//+------------------------------------------------------------------+ +//| Draw pie | +//+------------------------------------------------------------------+ +void CPieChart::DrawPie(double fi3,double fi4,int idx,CPoint &p[],const uint clr) + { +//--- draw arc + Arc(m_x0,m_y0,m_r,m_r,fi3,fi4,p[idx].x,p[idx].y,p[idx+1].x,p[idx+1].y,clr); +//--- variables + int x3=p[idx].x; + int y3=p[idx].y; + int x4=p[idx+1].x; + int y4=p[idx+1].y; +//--- draw radii + if(idx==0) + Line(m_x0,m_y0,x3,y3,clr); + if(idx!=m_data_total-1) + Line(m_x0,m_y0,x4,y4,clr); +//--- fill + double fi=(fi3+fi4)/2; + int xf=m_x0+(int)(0.99*m_r*cos(fi)); + int yf=m_y0-(int)(0.99*m_r*sin(fi)); + Fill(xf,yf,clr); +//--- for small pie + if(fi4-fi3<=M_PI_4) + Line(m_x0,m_y0,xf,yf,clr); + } +//+------------------------------------------------------------------+ +//| Make label for pie | +//+------------------------------------------------------------------+ +string CPieChart::LabelMake(const string text,const double value,const bool to_left) + { + string label=""; +//--- + if(to_left) + { + if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) + { + if(IS_SHOW_VALUE) + label=DoubleToString(value,2); + else + { + if(IS_SHOW_PERCENT) + label=DoubleToString(100*value/m_sum,2)+"%"; + } + } + else + { + label=text; + if(IS_SHOW_VALUE) + label+=" ("+DoubleToString(value,2)+")"; + else + { + if(IS_SHOW_PERCENT) + label+=" ("+DoubleToString(100*value/m_sum,2)+"%)"; + } + } + } + else + { + if(IS_SHOW_LEGEND || !IS_SHOW_DESCRIPTORS) + { + if(IS_SHOW_VALUE) + label=DoubleToString(value,2); + else + { + if(IS_SHOW_PERCENT) + label=DoubleToString(100*value/m_sum,2)+"%"; + } + } + else + { + if(IS_SHOW_VALUE) + label="("+DoubleToString(value,2)+") "; + else + { + if(IS_SHOW_PERCENT) + label="("+DoubleToString(100*value/m_sum,2)+"%) "; + } + label+=text; + } + } +//--- + return(label); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/CustomCanvas.mqh b/Canvas/CustomCanvas.mqh new file mode 100644 index 0000000..00fc14f --- /dev/null +++ b/Canvas/CustomCanvas.mqh @@ -0,0 +1,2596 @@ +//+------------------------------------------------------------------+ +//| CustomCanvas.mqh | +//| Copyright 2015, MetaQuotes Software Corp. | +//| http://www.mql5.com | +//+------------------------------------------------------------------+ +#include +#include +#include +#include +//+------------------------------------------------------------------+ +#define SIGN(i) ((i<0) ? -1 : 1) +//+------------------------------------------------------------------+ +//| Macro to generate color | +//+------------------------------------------------------------------+ +#define XRGB(r,g,b) (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) +#define ARGB(a,r,g,b) ((uchar(a)<<24)|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b)) +#define TRGB(a,rgb) ((uchar(a)<<24)|(rgb)) +#define GETRGB(clr) ((clr)&0xFFFFFF) +#define GETRGBA(clr) uchar((clr)>>24) +#define GETRGBR(clr) uchar((clr)>>16) +#define GETRGBG(clr) uchar((clr)>>8) +#define GETRGBB(clr) uchar(clr) +#define COLOR2RGB(clr) (0xFF000000|(uchar(clr)<<16)|(uchar((clr)>>8)<<8)|uchar((clr)>>16)) +#define RGB2COLOR(rgb) ((uchar(rgb)<<16)|(uchar((rgb)>>8)<<8)|uchar((rgb)>>16)) +//+------------------------------------------------------------------+ +//| Class CCustomCanvas | +//| Usage: class for working with a dynamic resource | +//+------------------------------------------------------------------+ +class CCustomCanvas : public CChartObjectBmpLabel + { +private: + uint m_style; // line style template + uint m_style_idx; // variable - current index of bit in line style template + static uint m_default_colors[9]; // default colors +protected: + string m_objname; // object name + ENUM_OBJECT m_objtype; // object type + string m_rcname; // resource name + int m_width; // canvas width + int m_height; // canvas height + ENUM_COLOR_FORMAT m_format; // method of color processing + //--- for text + string m_fontname; // font name + int m_fontsize; // font size + uint m_fontflags; // font flags + uint m_fontangle; // angle of text tilt to the X axis in 0.1 degrees + //--- data + uint m_pixels[]; // array of pixels + +public: + CCustomCanvas(void); + ~CCustomCanvas(void); + //--- create/destroy + virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmap(const string name,const datetime time,const double price, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmap(const long chart_id,const int subwin,const string name, + const datetime time,const double price,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmapLabel(const string name,const int x,const int y, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + bool CreateBitmapLabel(const long chart_id,const int subwin,const string name, + const int x,const int y,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); + void Destroy(void); + //--- properties + string ChartObjectName(void) const { return(m_objname); } + string ResourceName(void) const { return(m_rcname); } + int Width(void) const { return(m_width); } + int Height(void) const { return(m_height); } + //--- update object on screen + void Update(const bool redraw=true); + bool Resize(const int width,const int height); + //--- clear/fill color + void Erase(const uint clr=0); + //--- data access + uint PixelGet(const int x,const int y) const; + void PixelSet(const int x,const int y,const uint clr); + //--- draw primitives + void LineVertical(int x,int y1,int y2,const uint clr); + void LineHorizontal(int x1,int x2,int y,const uint clr); + void Line(int x1,int y1,int x2,int y2,const uint clr); + void Polyline(int &x[],int &y[],const uint clr); + void Polygon(int &x[],int &y[],const uint clr); + void Rectangle(int x1,int y1,int x2,int y2,const uint clr); + void Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); + void Circle(int x,int y,int r,const uint clr); + void Ellipse(int x1,int y1,int x2,int y2,const uint clr); + void Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr); + void Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr); + void Arc(int x,int y,int rx,int ry,double fi3,double fi4,int &x3,int &y3,int &x4,int &y4,const uint clr); + void Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr); + void Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr); + //--- draw filled primitives + void FillRectangle(int x1,int y1,int x2,int y2,const uint clr); + void FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); + void FillPolygon(int &x[],int &y[],const uint clr); + void FillCircle(int x,int y,int r,const uint clr); + void FillEllipse(int x1,int y1,int x2,int y2,const uint clr); + void Fill(int x,int y,const uint clr); + //--- draw primitives with antialiasing + void PixelSetAA(const double x,const double y,const uint clr); + void LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX); + void PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); + void PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX); + void TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3, + const uint clr,const uint style=UINT_MAX); + void CircleAA(const int x,const int y,const double r,const uint clr); + //--- for text + bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); + bool FontNameSet(string name); + bool FontSizeSet(int size); + bool FontFlagsSet(uint flags); + bool FontAngleSet(uint angle); + void FontGet(string &name,int &size,uint &flags,uint &angle); + string FontNameGet(void) const { return(m_fontname); } + int FontSizeGet(void) const { return(m_fontsize); } + uint FontFlagsGet(void) const { return(m_fontflags); } + uint FontAngleGet(void) const { return(m_fontangle); } + void TextOut(int x,int y,string text,const uint clr,uint alignment=0); + int TextWidth(const string text); + int TextHeight(const string text); + void TextSize(const string text,int &width,int &height); + //--- services + static uint GetDefaultColor(const int i); + void TransparentLevelSet(const uchar value); + //--- load bitmap from file + bool LoadFromFile(const string filename); + +private: + void LineStyleSet(const uint style); + bool FontSet(void); + void TextOutFast(int x,int y,string text,const uint clr,uint alignment=0); + //--- for pie + double AngleCalc(int x1,int y1,int x2,int y2); + //--- for polygon + int PointClassify(const CPoint &p0,const CPoint &p1,const CPoint &p2); + int PolygonClassify(const CPoint &p[]); + bool IsPolygonConvex(CPoint &p[]); + void PolygonNormalize(CPoint &p[]); + void PolygonIntersect(CPoint &p[],CPoint &add[]); + void PolygonFill(CPoint &p[],const uint clr); + }; +//+------------------------------------------------------------------+ +//| Initialize static array | +//+------------------------------------------------------------------+ +uint CCustomCanvas::m_default_colors[9]= + { + XRGB(0,0,255), // blue + XRGB(255,0,0), // red + XRGB(0,128,0), // green + XRGB(255,242,0), // yellow + XRGB(255,0,128), // pink + XRGB(0,255,0), // lime + XRGB(185,0,61), // crimson + XRGB(0,183,239), // sky blue + XRGB(255,128,0) // orange + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CCustomCanvas::CCustomCanvas(void) : m_objname(NULL), + m_objtype(WRONG_VALUE), + m_rcname(NULL), + m_width(0), + m_height(0), + m_format(COLOR_FORMAT_XRGB_NOALPHA), + m_fontname("arial"), + m_fontsize(-120), + m_fontflags(0), + m_fontangle(0), + m_style(UINT_MAX), + m_style_idx(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CCustomCanvas::~CCustomCanvas(void) + { + } +//+------------------------------------------------------------------+ +//| Create dynamic resource | +//+------------------------------------------------------------------+ +bool CCustomCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + Destroy(); +//--- prepare data array + if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0) + { + //--- generate resource name + m_rcname="::"+name+(string)(GetTickCount()+MathRand()); + //--- initialize data with zeros + ArrayInitialize(m_pixels,0); + //--- create dynamic resource + if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt)) + { + //--- successfully created + //--- complete initialization + m_width =width; + m_height=height; + m_format=clrfmt; + //--- succeed + return(true); + } + } +//--- error - destroy object + Destroy(); + return(false); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCustomCanvas::CreateBitmap(const string name,const datetime time,const double price, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + return(CreateBitmap(0,0,name,time,price,width,height,clrfmt)); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCustomCanvas::CreateBitmap(const long chart_id,const int subwin,const string name, + const datetime time,const double price,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt) + { +//--- create canvas + if(Create(name,width,height,clrfmt)) + { + //--- create attached object + if(ObjectCreate(chart_id,name,OBJ_BITMAP,subwin,time,price)) + { + //--- bind object with resource + if(ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) + { + //--- successfully created + //--- complete initialization + m_chart_id=chart_id; + m_objname =name; + m_objtype =OBJ_BITMAP; + //--- succeed + return(true); + } + } + } +//--- error - destroy object + Destroy(); + return(false); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCustomCanvas::CreateBitmapLabel(const string name,const int x,const int y, + const int width,const int height,ENUM_COLOR_FORMAT clrfmt) + { + return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt)); + } +//+------------------------------------------------------------------+ +//| Create object on chart with attached dynamic resource | +//+------------------------------------------------------------------+ +bool CCustomCanvas::CreateBitmapLabel(const long chart_id,const int subwin,const string name, + const int x,const int y,const int width,const int height, + ENUM_COLOR_FORMAT clrfmt) + { +//--- create canvas + if(Create(name,width,height,clrfmt)) + { + //--- create attached object + if(ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwin,0,0)) + { + //--- set x,y and bind object with resource + if(ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x) && + ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y) && + ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname)) + { + //--- successfully created + //--- complete initialization + m_chart_id=chart_id; + m_objname =name; + m_objtype =OBJ_BITMAP_LABEL; + //--- succeed + return(true); + } + } + } +//--- error - destroy object + Destroy(); + return(false); + } +//+------------------------------------------------------------------+ +//| Remove object from chart and deallocate data array | +//+------------------------------------------------------------------+ +void CCustomCanvas::Destroy(void) + { +//--- delete object + if(m_objname!=NULL) + { + ObjectDelete(m_chart_id,m_objname); + m_chart_id=0; + m_objname =NULL; + m_objtype =WRONG_VALUE; + } +//--- deallocate array + ArrayFree(m_pixels); +//--- free resource + if(m_rcname!=NULL) + { + ResourceFree(m_rcname); + m_rcname=NULL; + } +//--- zeroize data + m_width =0; + m_height=0; + m_rcname=NULL; + } +//+------------------------------------------------------------------+ +//| Update object on screen (redraw) | +//+------------------------------------------------------------------+ +void CCustomCanvas::Update(const bool redraw) + { +//--- check + if(m_rcname==NULL) + return; +//--- update resource and redraw + if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format) && redraw) + ChartRedraw(); + } +//+------------------------------------------------------------------+ +//| Resize | +//+------------------------------------------------------------------+ +bool CCustomCanvas::Resize(const int width,const int height) + { +//--- check + if(m_rcname!=NULL && width>0 && height>0) + if(ArrayResize(m_pixels,width*height)>0) + { + m_width =width; + m_height=height; + //--- initialize data with zeros + ArrayInitialize(m_pixels,0); + //--- create dynamic resource + if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format)) + { + //--- bind object with resource + if(m_objname!=NULL && ObjectSetString(m_chart_id,m_objname,OBJPROP_BMPFILE,m_rcname)) + return(true); + } + } +//--- error - destroy object + Destroy(); + return(false); + } +//+------------------------------------------------------------------+ +//| Clear/Fill color | +//+------------------------------------------------------------------+ +void CCustomCanvas::Erase(const uint clr) + { + ArrayInitialize(m_pixels,clr); + } +//+------------------------------------------------------------------+ +//| Get pixel color | +//+------------------------------------------------------------------+ +uint CCustomCanvas::PixelGet(const int x,const int y) const + { +//--- check coordinates + if(x>=0 && x=0 && y=0 && x=0 && y=m_width || y<0 || y>=m_height) + return; +//--- + int index=y*m_width+x; + uint old_clr=m_pixels[index]; +//--- check if replacement is necessary + if(old_clr==clr) + return; +//--- use pseudo stack to emulate deeply-nested recursive calls + int stack[]; + uint count=1; + int idx; + int total=ArraySize(m_pixels); +//--- allocate memory for stack + if(ArrayResize(stack,total)==-1) + return; + stack[0]=index; + m_pixels[index]=clr; + for(uint i=0;i0 && m_pixels[idx]==old_clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- top adjacent point + idx=index-m_width; + if(idx>=0 && m_pixels[idx]==old_clr) + { + m_pixels[idx]=clr; + stack[count++]=idx; + } + //--- right adjacent point + idx=index+1; + if(xy2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } +//--- line is out of image boundaries + if(y2<0 || y1>=m_height || x<0 || x>=m_width) + return; +//--- stay withing image boundaries + if(y1<0) + y1=0; + if(y2>=m_height-1) + y2=m_height-1; +//--- draw line + int index=y1*m_width+x; + for(int i=y1;i<=y2;i++,index+=m_width) + m_pixels[index]=clr; + } +//+------------------------------------------------------------------+ +//| Draw horizontal line | +//+------------------------------------------------------------------+ +void CCustomCanvas::LineHorizontal(int x1,int x2,int y,const uint clr) + { + int tmp; +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- line is out of image boundaries + if(x2<0 || x1>=m_width || y<0 || y>=m_height) + return; +//--- stay withing image boundaries + if(x1<0) + x1=0; + if(x2>=m_width) + x2=m_width-1; +//--- draw line + ArrayFill(m_pixels,y*m_width+x1,x2-x1,clr); + } +//+------------------------------------------------------------------+ +//| Draw line according to Bresenham's algorithm | +//+------------------------------------------------------------------+ +void CCustomCanvas::Line(int x1,int y1,int x2,int y2,const uint clr) + { +//--- line is out of image boundaries + if((x1<0 && x2<0) || (y1<0 && y2<0)) + return; + if(x1>=m_width && x2>=m_width) + return; + if(y1>=m_height && y2>=m_height) + return; +//--- get length by X and Y + int dx=(x2>x1)? x2-x1 : x1-x2; + int dy=(y2>y1)? y2-y1 : y1-y2; + if(dx==0) + { + //--- vertical line + LineVertical(x1,y1,y2,clr); + return; + } + if(dy==0) + { + //--- horizontal line + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- get direction by X and Y + int sx=(x1=m_width || + y1<0 || y1>=m_height) + { + if(draw) + return; + } + else + { + //--- draw pixel + m_pixels[y1*m_width+x1]=clr; + draw=true; + } + //--- get coordinates of next pixel + int er2=er<<1; + if(er2>-dy) + { + er-=dy; + x1+=sx; + } + if(er2ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0;iArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0;i=dx) + { + xx=x+dx; + if(xx>=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0 && xx=0 && yy=0 && yy=0) + { + dy--; + dd_y+=2; + f+=dd_y; + } + dx++; + dd_x+=2; + f+=dd_x; + } + } +//+------------------------------------------------------------------+ +//| Draw ellipse according to Bresenham's algorithm | +//+------------------------------------------------------------------+ +void CCustomCanvas::Ellipse(int x1,int y1,int x2,int y2,const uint clr) + { + int x,y; + int rx,ry; + int dx,dy; + int xx,yy; + int rx_sq,ry_sq; + int f; + int tmp; +//--- handle extreme conditions + if(x1==x2) + { + if(y1==y2) + PixelSet(x1,y1,clr); + else + LineVertical(x1,y1,y2,clr); + return; + } + if(y1==y2) + { + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; + rx=(x2-x1)>>1; + ry=(y2-y1)>>1; + dx=0; + dy=ry; + rx_sq=rx*rx; + ry_sq=ry*ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>ry_sq*dx) + { + yy=y+dy; + if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xx=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; + while(dy>=0) + { + yy=y+dy; + if(yy>=0 && yy=0 && xx=0 && xx=0 && yy=0 && xx=0 && xxx2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; +//--- check rays + if(x3==x && y3==y) + return; + if(x4==x && y4==y) + return; +//--- calculate parameters of ray x3,y3 + fi3=AngleCalc(x,y,x3,y3); +//--- calculate parameters of ray x4,y4 + fi4=AngleCalc(x,y,x4,y4); +//--- draw arc + Arc(x,y,x2-x,y2-y,fi3,fi4,clr); + } +//+------------------------------------------------------------------+ +//| Draws ellipse arc | +//+------------------------------------------------------------------+ +void CCustomCanvas::Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr) + { + int x3,y3,x4,y4; +//--- check + if(rx<10 || ry<10) + return; + if(rx<0) + rx=-rx; + if(ry<0) + ry=-ry; +//--- check rays + if(fi3==fi4) + return; +//--- adjustment for passing through 0 + if(fi40) || // ray 3 is in the 1st or 2nd quadrant + (fi0) || // ray 4 is in the 1st or 2nd quadrant + fi4-fi3>=M_PI) // arc will pass through the top of the ellipse + { + dx=0; + dy=ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>=ry_sq*dx) + { + yy=y-dy; + if(dx==0) + { + //--- central point + fi=AngleCalc(0,0,0,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(x,yy,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=x; + yy_c=yy; + fi_c=fi; + xx_a=x; + yy_a=yy; + fi_a=fi; + } + else + { + //--- iterate clockwise + xx=x+dx; + fi=AngleCalc(0,0,dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + xx=x-dx; + fi=AngleCalc(0,0,-dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,dx,-dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x+dx,y3=y-dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,-dx,-dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x-dx,y4=y-dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 2 left +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi3>M_PI_2 && fi3<3*M_PI_2) || // ray 3 is in the 2nd or 3rd quadrant + (fi>M_PI_2 && fi<3*M_PI_2) || // ray 4 is in the 2nd or 3rd quadrant + fi4-fi3>=M_PI) // arc will pass through the left part of the ellipse + { + dx=rx; + dy=0; + f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); + while(ry_sq*dx>=rx_sq*dy) + { + xx=x-dx; + if(dy==0) + { + //--- central point + fi=AngleCalc(0,0,-dx,0); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,y,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=xx; + yy_c=y; + fi_c=fi; + xx_a=xx; + yy_a=y; + fi_a=fi; + } + else + { + //--- iterate clockwise + yy=y-dy; + fi=AngleCalc(0,0,-dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + yy=y+dy; + fi=AngleCalc(0,0,-dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dx--; + f-=(ry_sq<<2)*dx; + } + f+=(rx_sq<<1)*(3+(dy<<1)); + dy++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,-dx,-dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x-dx,y3=y-dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,-dx,dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x-dx,y4=y+dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 3 bottom +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi3>M_PI && fi3<2*M_PI) || // ray 3 is in the 3rd or 4th quadrant + (fi>M_PI && fi<2*M_PI) || // ray 4 is in the 3rd or 4th quadrant + fi4-fi3>=M_PI) // arc will pass through the bottom of the ellipse + { + dx=0; + dy=ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>=ry_sq*dx) + { + yy=y+dy; + if(dx==0) + { + //--- central point + fi=AngleCalc(0,0,0,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(x,yy,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=x; + yy_c=yy; + fi_c=fi; + xx_a=x; + yy_a=yy; + fi_a=fi; + } + else + { + //--- iterate clockwise + xx=x-dx; + fi=AngleCalc(0,0,-dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + xx=x+dx; + fi=AngleCalc(0,0,dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(fi_a-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,-dx,dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x-dx,y3=y+dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,dx,dy); + if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x+dx,y4=y+dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } +//--- 4 right +//--- +//--- if arc is obviously not within the rays range, don't draw + fi=MathMod(fi4,2*M_PI); + if((fi33*M_PI_2) || // ��� 3 �������� � 1 ��� 4 �������� + (fi3*M_PI_2) || // ��� 4 �������� � 1 ��� 4 �������� + fi4-fi3>=M_PI) // ���� ������� ����� ������ ����� ������� + { + dx=rx; + dy=0; + f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq); + while(ry_sq*dx>=rx_sq*dy) + { + xx=x+dx; + if(dy==0) + { + //--- central point + fi=AngleCalc(0,0,dx,0); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,y,clr); + ckw=ackw=true; + } + else + ckw=ackw=false; + xx_c=xx; + yy_c=y; + fi_c=fi; + xx_a=xx; + yy_a=y; + fi_a=fi; + } + else + { + //--- iterate clockwise + yy=y+dy; + fi=AngleCalc(0,0,dx,dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4 + if(!ckw) + { + ckw=true; + if(!ray4) + { + if(MathAbs(fi_c-MathMod(fi4,2*M_PI))MathAbs(fi-fi3)) + PixelSet(x3=xx,y3=yy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + ray3=true; + } + ckw=false; + } + //--- save parameters of the last iteration + xx_c=xx; + yy_c=yy; + fi_c=fi; + //--- iterate counterclockwise + yy=y-dy; + fi=AngleCalc(0,0,dx,-dy); + if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI)) + { + PixelSet(xx,yy,clr); + //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined + //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3 + if(!ackw) + { + ackw=true; + if(!ray3) + { + if(MathAbs(MathMod(fi_a,2*M_PI)-fi3)MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=xx,y4=yy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + ray4=true; + } + ackw=false; + } + //--- save parameters of the last iteration + xx_a=xx; + yy_a=yy; + fi_a=fi; + } + //--- calculate coordinates of the next point + if(f>=0) + { + dx--; + f-=(ry_sq<<2)*dx; + } + f+=(rx_sq<<1)*(3+(dy<<1)); + dy++; + } + //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found + if(ckw && !ray3) + { + fi=AngleCalc(0,0,dx,dy); + if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3)) + PixelSet(x3=x+dx,y3=y+dy,clr); + else + { + x3=xx_c; + y3=yy_c; + } + } + //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found + if(ackw && !ray4) + { + fi=AngleCalc(0,0,dx,-dy); + if(MathAbs(MathMod(fi_a,2*M_PI)-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI))) + PixelSet(x4=x+dx,y4=y-dy,clr); + else + { + x4=xx_a; + y4=yy_a; + } + } + } + } +//+------------------------------------------------------------------+ +//| Draws ellipse pie | +//+------------------------------------------------------------------+ +void CCustomCanvas::Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr) + { + int tmp; + int x,y; +//--- + double fi3; + double fi4; +//--- check + if(x1==x2 || y1==y2) + return; +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; +//--- check rays + if(x3==x && y3==y) + return; + if(x4==x && y4==y) + return; +//--- calculate parameters of ray x3,y3 + fi3=AngleCalc(x,y,x3,y3); +//--- calculate parameters of ray x4,y4 + fi4=AngleCalc(x,y,x4,y4); +//--- draw pie + Pie(x,y,x2-x,y2-y,fi3,fi4,clr,fill_clr); + } +//+------------------------------------------------------------------+ +//| Draws ellipse pie | +//+------------------------------------------------------------------+ +void CCustomCanvas::Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr) + { + int x3=x; + int y3=y; + int x4=x; + int y4=y; +//--- check + if(rx==0 || ry==0) + return; + if(rx<0) + rx=-rx; + if(ry<0) + ry=-ry; +//--- check rays + if(fi3==fi4) + return; +//--- adjustment for passing through 0 + if(fi4ry) + rx=ry; + double fi=(fi3+fi4)/2; + int xf=x+(int)(0.9*rx*cos(fi)); + int yf=y-(int)(0.9*rx*sin(fi)); + Fill(xf,yf,fill_clr); + } +//+------------------------------------------------------------------+ +//| Draw filled circle | +//+------------------------------------------------------------------+ +void CCustomCanvas::FillCircle(int x,int y,int r,const uint clr) + { + int f =1-r; + int dd_x=1; + int dd_y=-2*r; + int dx =0; + int dy =r; +//--- draw + while(dy>=dx) + { + LineHorizontal(x-dx,x+dx,y-dy,clr); + LineHorizontal(x-dx,x+dx,y+dy,clr); + LineHorizontal(x-dy,x+dy,y-dx,clr); + LineHorizontal(x-dy,x+dy,y+dx,clr); + //--- + if(f>=0) + { + dy--; + dd_y+=2; + f+=dd_y; + } + dx++; + dd_x+=2; + f+=dd_x; + } + } +//+------------------------------------------------------------------+ +//| Draw filled ellipse | +//+------------------------------------------------------------------+ +void CCustomCanvas::FillEllipse(int x1,int y1,int x2,int y2,const uint clr) + { + int x,y; + int rx,ry; + int dx,dy; + int rx_sq,ry_sq; + int f; + int tmp; +//--- handle extreme conditions + if(x1==x2) + { + if(y1==y2) + PixelSet(x1,y1,clr); + else + LineVertical(x1,y1,y2,clr); + return; + } + if(y1==y2) + { + LineHorizontal(x1,x2,y1,clr); + return; + } +//--- sort by X + if(x1>x2) + { + tmp=x1; + x1 =x2; + x2 =tmp; + } +//--- sort by Y + if(y1>y2) + { + tmp=y1; + y1 =y2; + y2 =tmp; + } + x =(x2+x1)>>1; + y =(y2+y1)>>1; + rx=(x2-x1)>>1; + ry=(y2-y1)>>1; + dx=0; + dy=ry; + rx_sq=rx*rx; + ry_sq=ry*ry; + f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq); + while(rx_sq*dy>ry_sq*(dx)) + { + LineHorizontal(x-dx,x+dx,y+dy,clr); + LineHorizontal(x-dx,x+dx,y-dy,clr); + if(f>=0) + { + dy--; + f-=(rx_sq<<2)*dy; + } + f+=(ry_sq<<1)*(3+(dx<<1)); + dx++; + } + f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq; + while(dy>=0) + { + LineHorizontal(x-dx,x+dx,y+dy,clr); + LineHorizontal(x-dx,x+dx,y-dy,clr); + if(f<=0) + { + dx++; + f+=(ry_sq<<2)*dx; + } + dy--; + f+=(rx_sq<<1)*(3-(dy<<1)); + } + } +//+------------------------------------------------------------------+ +//| Draw filled rectangle | +//+------------------------------------------------------------------+ +void CCustomCanvas::FillRectangle(int x1,int y1,int x2,int y2,const uint clr) + { + int tmp; +//--- sort vertexes + if(x2=m_width || y1>=m_height) + return; +//--- stay withing screen boundaries + if(x1<0) + x1=0; + if(y1<0) + y1=0; + if(x2>=m_width) + x2=m_width -1; + if(y2>=m_height) + y2=m_height-1; +//--- set pixels + for(;y1<=y2;y1++) + ArrayFill(m_pixels,y1*m_width+x1,x2-x1,clr); + } +//+------------------------------------------------------------------+ +//| Draw filled triangle | +//+------------------------------------------------------------------+ +void CCustomCanvas::FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr) + { + int xx1,xx2,tmp; + double k1=0,k2=0,xd1,xd2; +//--- sort vertexes from lesser to greater + if(y1>y2) + { + tmp=y2; + y2 =y1; + y1 =tmp; + tmp=x2; + x2 =x1; + x1=tmp; + } + if(y1>y3) + { + tmp=y1; + y1 =y3; + y3 =tmp; + tmp=x1; + x1 =x3; + x3 =tmp; + } + if(y2>y3) + { + tmp=y2; + y2 =y3; + y3 =tmp; + tmp=x2; + x2 =x3; + x3 =tmp; + } +//--- all vertexes are out of image boundaries + if(y3<0 || y1>m_height) + return; + if(x1<0 && x2<0 && x3<0) + return; + if(x1>m_width && x2>m_width && x3>m_width) + return; +//--- find coefficients of lines + if((tmp=y1-y2)!=0) + k1=(x1-x2)/(double)tmp; + if((tmp=y1-y3)!=0) + k2=(x1-x3)/(double)tmp; +//--- + xd1=x1; + xd2=x1; +//--- + for(int i=y1;i<=y3;i++) + { + if(i==y2) + { + if((tmp=y2-y3)!=0) + k1=(x2-x3)/(double)tmp; + xd1=x2; + } + //--- calculate new boundaries of triangle line + xx1 =(int)xd1; + xd1+=k1; + xx2 =(int)xd2; + xd2+=k2; + //--- triangle line is out of screen boundaries + if(i<0 || i>=m_height) + continue; + //--- sort + if(xx1>xx2) + { + tmp=xx1; + xx1=xx2; + xx2=tmp; + } + //--- line is out of screen boundaries + if(xx2<0 || xx1>=m_width) + continue; + //--- draw only what is within screen boundaries + if(xx1<0) + xx1=0; + if(xx2>=m_width) + xx2=m_width-1; + //--- draw horizontal line of triangle + ArrayFill(m_pixels,i*m_width+xx1,xx2-xx1,clr); + } + } +//+------------------------------------------------------------------+ +//| Draw filled poligon | +//+------------------------------------------------------------------+ +void CCustomCanvas::FillPolygon(int &x[],int &y[],const uint clr) + { + static CPoint p[]; + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<3) + return; +//--- resize array of points + ArrayResize(p,total); +//--- find top-left point + int imin=0; + int xmin=x[0]; + int ymin=y[0]; + for(int i=1;iymin) + continue; + if(y[i]==ymin) + { + if(x[i]0.0) + xx[1]=xx[3]=ix+1; + if(dy<0.0) + yy[2]=yy[2]=iy-1; + if(dy==0.0) + yy[2]=yy[2]=iy; + if(dy>0.0) + yy[2]=yy[2]=iy+1; +//--- calculate radii and sum of their squares + for(int i=0;i<4;i++) + { + dx=xx[i]-x; + dy=yy[i]-y; + rr[i]=1/(dx*dx+dy*dy); + rrr+=rr[i]; + } +//--- draw pixels + for(int i=0;i<4;i++) + { + k=rr[i]/rrr; + c=PixelGet(xx[i],yy[i]); + a=(uchar)(k*GETRGBA(clr)+(1-k)*GETRGBA(c)); + r=(uchar)(k*GETRGBR(clr)+(1-k)*GETRGBR(c)); + g=(uchar)(k*GETRGBG(clr)+(1-k)*GETRGBG(c)); + b=(uchar)(k*GETRGBB(clr)+(1-k)*GETRGBB(c)); + PixelSet(xx[i],yy[i],ARGB(a,r,g,b)); + } + } +//+------------------------------------------------------------------+ +//| Set line style | +//+------------------------------------------------------------------+ +void CCustomCanvas::LineStyleSet(const uint style) + { + switch(style) + { + case STYLE_SOLID: + m_style=0xFFFFFF; + break; + case STYLE_DASH: + m_style=0x3FFFF; + break; + case STYLE_DOT: + m_style=0x1C71C7; + break; + case STYLE_DASHDOT: + m_style=0x381FF; + break; + case STYLE_DASHDOTDOT: + m_style=0x1C71FF; + break; + default: + //--- high-order bit must be set then custom style + if((style&0x80000000)!=0) + m_style=style; + break; + } + m_style_idx=0; + } +//+------------------------------------------------------------------+ +//| Draw line with antialiasing (with style) | +//+------------------------------------------------------------------+ +void CCustomCanvas::LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style) + { +//--- line is out of image boundaries + if((x1<0 && x2<0) || (y1<0 && y2<0)) + return; + if(x1>=m_width && x2>=m_width) + return; + if(y1>=m_height && y2>=m_height) + return; +//--- check + if(x1==x2 && y1==y2) + { + PixelSet(x1,y1,clr); + return; + } +//--- set the line style + if(style!=UINT_MAX) + LineStyleSet(style); +//--- preliminary calculations + double dx=x2-x1; + double dy=y2-y1; + double xy=sqrt(dx*dx+dy*dy); + double xx=x1; + double yy=y1; + uint mask=1<=fabs(dx) && fabs(y2-yy)>=fabs(dy)); +//--- set last pixel + if((m_style&mask)==mask) + PixelSetAA(x2,y2,clr); + } +//+------------------------------------------------------------------+ +//| Draw polyline with antialiasing (with style) | +//+------------------------------------------------------------------+ +void CCustomCanvas::PolylineAA(int &x[],int &y[],const uint clr,const uint style) + { +//--- set the line style + LineStyleSet(style); +//--- check arrays + int total=ArraySize(x); + if(total>ArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0;iArraySize(y)) + total=ArraySize(y); +//--- check + if(total<2) + return; + total--; +//--- draw + for(int i=0;iM_PI) + df/=2; + do + { + xx=x+r*cos(fi); + yy=y-r*sin(fi); + PixelSetAA(xx,yy,clr); + fi+=df; + } + while(fabs(2*M_PI-fi)>=df/2); + } +//+------------------------------------------------------------------+ +//| Gets default color | +//+------------------------------------------------------------------+ +static uint CCustomCanvas::GetDefaultColor(const int i) + { + if(i<0) + return(0); + if(iym) + continue; + if((p[i].y==ym) && (p[i].x>xm)) + continue; + im=i; + xm=p[i].x; + ym=p[i].y; + } +//--- check the orientation of triangle + return PointClassify(p[(im-1+total)%total],p[im],p[(im+1)%total]); + } +//+------------------------------------------------------------------+ +//| Checks convexity of polygon | +//+------------------------------------------------------------------+ +bool CCustomCanvas::IsPolygonConvex(CPoint &p[]) + { + int total=ArraySize(p); +//--- triangle - always convex + if(total==3) + return(true); + int res=SIGN(PointClassify(p[0],p[1],p[2])); + for(int i=1;iymin) + continue; + if(p[i].y==ymin) + { + if(p[i].xp[il].y) + return; + if(yy!=p[il].y) + { + dl=(p[il].x-xl)/(p[il].y-yy); + //--- make adjustment for half of left increment dl/2 + LineHorizontal((int)MathCeil(xl+dl/2),(int)MathFloor(xl),yy,clr); + xl+=dl/2; + } + else + LineHorizontal((int)MathCeil(xl),(int)MathFloor(p[il].x),yy,clr); + } + while(yy==p[ir].y) + { + ir=(ir+1)%total; + if(yy>p[ir].y) + return; + if(yy!=p[ir].y) + { + dr=(p[ir].x-xr)/(p[ir].y-yy); + //--- make adjustment for half of right increment dr/2 + LineHorizontal((int)MathCeil(xr),(int)MathFloor(xr+dr/2),yy,clr); + xr+=dr/2; + } + else + LineHorizontal((int)MathCeil(p[ir].x),(int)MathFloor(xr),yy,clr); + } + yy++; + if(yy==p[il].y) + xl=p[il].x; + else + xl+=dl; + if(yy==p[ir].y) + xr=p[ir].x; + else + xr+=dr; + LineHorizontal((int)MathCeil(xl),(int)MathFloor(xr),yy,clr); + } + while(il>=ir && ir!=0); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/DX/DXBox.mqh b/Canvas/DX/DXBox.mqh new file mode 100644 index 0000000..7ed6149 --- /dev/null +++ b/Canvas/DX/DXBox.mqh @@ -0,0 +1,77 @@ +//+------------------------------------------------------------------+ +//| DXBox.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2000-2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +//--- +#include "DXMesh.mqh" +#include "DXMath.mqh" +#include "DXUtils.mqh" +//+------------------------------------------------------------------+ +//| 3D Box object | +//+------------------------------------------------------------------+ +class CDXBox : public CDXMesh + { +public: + CDXBox(); + ~CDXBox(); + //--- create bon in specified context + bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVector3 &from,const DXVector3 &to); + //--- update box + bool Update(const DXVector3 &from,const DXVector3 &to); + +private: + //--- + void PrepareVertices(const DXVector3 &from,const DXVector3 &to); + }; +//+------------------------------------------------------------------+ +//| Class constructor | +//+------------------------------------------------------------------+ +void CDXBox::CDXBox() : CDXMesh() + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +void CDXBox::~CDXBox(void) + { + } +//+------------------------------------------------------------------+ +//| Create box in specified context | +//+------------------------------------------------------------------+ +bool CDXBox::Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,const DXVector3 &from,const DXVector3 &to) + { +//--- release previous buffers + Shutdown(); +//--- + DXVertex vertices[]; + uint indices[]; +//--- prepare box vertices and indices + DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f); + if(!DXComputeBox(from,to,vertices,indices)) + return(false); + for(int i=0; i + bool Create(int context_handle,const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY) + { + Shutdown(); + m_context=context_handle; + m_handle=DXBufferCreate(m_context,DX_BUFFER_VERTEX,vertices,start,count); + return(m_handle!=INVALID_HANDLE); + } + //+------------------------------------------------------------------+ + //| Render | + //+------------------------------------------------------------------+ + bool Render(uint start=0,uint count=WHOLE_ARRAY) + { + return(DXBufferSet(m_context,m_handle,start,count)); + } + //+------------------------------------------------------------------+ + //| Shutdown | + //+------------------------------------------------------------------+ + void Shutdown(void) + { + //--- relase handle + if(m_handle!=INVALID_HANDLE) + DXRelease(m_handle); + m_handle=INVALID_HANDLE; + } + }; +//+------------------------------------------------------------------+ +//| DX index buffer | +//+------------------------------------------------------------------+ +class CDXIndexBuffer : public CDXHandleShared + { +public: + //+------------------------------------------------------------------+ + //| Destructor | + //+------------------------------------------------------------------+ + virtual ~CDXIndexBuffer(void) + { + Shutdown(); + } + //+------------------------------------------------------------------+ + //| Create index buffer in specified context | + //+------------------------------------------------------------------+ + bool Create(int context_handle,const uint &indices[],uint start=0,uint count=WHOLE_ARRAY) + { + Shutdown(); + m_context=context_handle; + m_handle=DXBufferCreate(m_context,DX_BUFFER_INDEX,indices,start,count); + return(m_handle!=INVALID_HANDLE); + } + //+------------------------------------------------------------------+ + //| Render | + //+------------------------------------------------------------------+ + bool Render(uint start=0,uint count=WHOLE_ARRAY) + { + return(DXBufferSet(m_context,m_handle,start,count)); + } + //+------------------------------------------------------------------+ + //| Shutdown | + //+------------------------------------------------------------------+ + void Shutdown(void) + { + //--- relase handle + if(m_handle!=INVALID_HANDLE) + DXRelease(m_handle); + m_handle=INVALID_HANDLE; + } + }; +//+------------------------------------------------------------------+ diff --git a/Canvas/DX/DXData.mqh b/Canvas/DX/DXData.mqh new file mode 100644 index 0000000000000000000000000000000000000000..be69aa94233384e6cd6803f4902531d49569678d GIT binary patch literal 4048 zcmeHJ+fLg+5IxUG{DYM~atTW53#bp2v=vfHC?JG{Dk~)pq;f8`9UuaJ9XK;)lMUJ6 zfI?&>w6f#aJ2Pj_Tz2m7iYQ_UD>+z8Si}O+6mbJ@uB(h=lu<*%YZWd0$2sla%mmLi zI_RR02u++(Z$ix_tm#_OrxMn&%(uS~D1zVUQ6}NPm~Sh5suR_$@MJM72ooZXv7xcJ zgsha65MTIoN_4FeJbB8WPvp5xj3HU9lS2;$#`c)(N_@`;@huFZnBVuGv`Ri$1ZI+~e2v64<+mtcGq)2oqyY67J%P%qWCgIg#_H4dT zzO9;D%&_oT*^SMCfJ3`g%j{b$praNrR!jfcrlixkv)24qe#wy&`r-hz= zjp5VY@XZO?`eUKGeSDSsOUGQ( zyV&sYgYWlQrcIZ)BBd*WnYCmWtWFr%A1@?8NR0G-GQdcqaFG7HntA& zo#?k|QP7uP{{4xxh?8xgxA$hXHyzOeXAFwV0{*x18UhBQ| zR2NGo%64@Mqv^RS(~bL-y5N_96NKHLrjhHiBX;nQveLO<*Ox=er8Ae$?%Jfr=d@*Y zTU(oS!12B-er0K?ad!HvoV4keuig2nruS7zRr+J%^ifHp)5)-sY?fYIOEUY^^yFVpmdfnu^bt+e)zF%g j$!2n%jq8%>?vnbW5w@n$RVShrS05*w9$b@9-p;xQ4U51{ literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXDispatcher.mqh b/Canvas/DX/DXDispatcher.mqh new file mode 100644 index 0000000..e27f26b --- /dev/null +++ b/Canvas/DX/DXDispatcher.mqh @@ -0,0 +1,373 @@ +//+------------------------------------------------------------------+ +//| DXDispatcher.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2000-2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +//--- +#include "DXObjectBase.mqh" +#include "DXData.mqh" +#include "DXBuffers.mqh" +#include "DXInput.mqh" +#include "DXTexture.mqh" +#include "DXShader.mqh" +//--- default shaders +#resource "Shaders/DefaultShaderVertex.hlsl" as string ExtDefaultShaderVertex; +#resource "Shaders/DefaultShaderPixel.hlsl" as string ExtDefaultShaderPixel; +//+------------------------------------------------------------------+ +//| DX dispatcher which holds all resources | +//+------------------------------------------------------------------+ +class CDXDispatcher : public CDXObjectBase + { +protected: + CDXObjectBase m_dx_resources; // DX resources list (Textures, Shaders, etc.) + //--- default shaders + CDXShader* m_default_vs; + CDXShader* m_default_ps; + +public: + CDXDispatcher(void); + ~CDXDispatcher(void); + //--- create/destroy + bool Create(int context); + void Destroy(void); + //--- check resources + void Check(void); + //--- get DX Context Handle + int DXContext(void) const { return(m_context); } + //--- create shaders + CDXShader* ShaderCreateDefault(ENUM_DX_SHADER_TYPE shader_type); + CDXShader* ShaderCreateFromFile(ENUM_DX_SHADER_TYPE shader_type,string path,string entry_point); + CDXShader* ShaderCreateFromSource(ENUM_DX_SHADER_TYPE shader_type,string source,string entry_point); + //--- create buffers + template + CDXVertexBuffer* VertexBufferCreate(const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY); + CDXIndexBuffer* IndexBufferCreate(const uint &indicies[],uint start=0,uint count=WHOLE_ARRAY); + //--- create shader inputs + template + CDXInput* InputCreate(void); + CDXTexture* TextureCreateFromFile(string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0); + CDXTexture* TextureCreateFromData(ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0); + +private: + //--- add resource to list + bool ResourceAdd(CDXObjectBase *resource); + //--- check resources + void ResourcesCheck(void); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CDXDispatcher::CDXDispatcher(void) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CDXDispatcher::~CDXDispatcher(void) + { + Destroy(); + } +//+------------------------------------------------------------------+ +//| Create | +//+------------------------------------------------------------------+ +bool CDXDispatcher::Create(int context) + { +//--- check if context already exist + if(m_context!=INVALID_HANDLE) + return(false); +//--- save context + m_context=context; +//--- + return(true); + } +//+------------------------------------------------------------------+ +//| Destroy | +//+------------------------------------------------------------------+ +void CDXDispatcher::Destroy(void) + { +//--- release default shaders + if(m_default_ps) + { + m_default_ps.Release(); + m_default_ps=NULL; + } + if(m_default_vs) + { + m_default_vs.Release(); + m_default_vs=NULL; + } +//--- release and delete all DX resources + while(m_dx_resources.Next()) + { + CDXHandleShared* resource=(CDXHandleShared*)m_dx_resources.Next(); + resource.Release(); + } +//--- forget context + m_context=INVALID_HANDLE; + } +//+------------------------------------------------------------------+ +//| Check resources | +//+------------------------------------------------------------------+ +void CDXDispatcher::Check(void) + { +//--- + CDXHandleShared* resource=(CDXHandleShared*)m_dx_resources.Next(); +//--- release and delete all DX resources + while(CheckPointer(resource)!=POINTER_INVALID) + { + CDXHandleShared* next=(CDXHandleShared*)resource.Next(); + //--- if references count 1 or less, then only we hold the resource + if(resource.References()<=1) + resource.Release(); + //--- + resource=next; + } + } +//+------------------------------------------------------------------+ +//| Create default shader of specified type | +//+------------------------------------------------------------------+ +CDXShader* CDXDispatcher::ShaderCreateDefault(ENUM_DX_SHADER_TYPE shader_type) + { + switch(shader_type) + { + //--- default pixel shader + case DX_SHADER_PIXEL: + { + if(m_default_ps==NULL) + m_default_ps=ShaderCreateFromSource(DX_SHADER_PIXEL,ExtDefaultShaderPixel,"PSMain"); + return(m_default_ps); + } + //--- default vertex shader + case DX_SHADER_VERTEX: + { + if(m_default_vs==NULL) + { + m_default_vs=ShaderCreateFromSource(DX_SHADER_VERTEX,ExtDefaultShaderVertex,"VSMain"); + if(m_default_vs && !m_default_vs.LayoutSet()) + { + m_default_vs.Release(); + m_default_vs=NULL; + } + } + return(m_default_vs); + } + } +//--- return result + return(NULL); + } +//+------------------------------------------------------------------+ +//| Create new shader of specified type from file | +//+------------------------------------------------------------------+ +CDXShader* CDXDispatcher::ShaderCreateFromFile(ENUM_DX_SHADER_TYPE shader_type,string path,string entry_point) + { +//--- open source file + int file=FileOpen(path,FILE_READ); + if(file==INVALID_HANDLE) + return(NULL); +//--- check file size + uint size=(uint)FileSize(file); + FileClose(file); + if(size>16*1024*1024) + return(NULL); +//--- prepare buffer + char buffer[]; + ArrayResize(buffer,size); +//--- read file into buffer + int read=(int)FileLoad(path,buffer); + if(read<=0) + return(NULL); +//--- convert vuffer to string + string source=CharArrayToString(buffer,0,WHOLE_ARRAY,CP_UTF8); +//--- add shader by source + return(ShaderCreateFromSource(shader_type,source,entry_point)); + } +//+------------------------------------------------------------------+ +//| Create new shader of specified type from source code | +//+------------------------------------------------------------------+ +CDXShader* CDXDispatcher::ShaderCreateFromSource(ENUM_DX_SHADER_TYPE shader_type,string source,string entry_point) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new shader + CDXShader* shader=new CDXShader(); + if(shader==NULL) + return(NULL); +//--- create shader + if(!shader.Create(m_context,shader_type,source,entry_point)) + { + shader.Release(); + return(NULL); + } +//--- add shader to resources list + if(!ResourceAdd(shader)) + { + shader.Release(); + return(NULL); + } +//--- return shader + return(shader); + } +//+------------------------------------------------------------------+ +//| Create vertex buffer | +//+------------------------------------------------------------------+ +template +CDXVertexBuffer* CDXDispatcher::VertexBufferCreate(const TVertex &vertices[],uint start=0,uint count=WHOLE_ARRAY) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new buffer + CDXVertexBuffer* buffer=new CDXVertexBuffer(); + if(buffer==NULL) + return(NULL); +//--- create buffer + if(!buffer.Create(m_context,vertices,start,count)) + { + buffer.Release(); + return(NULL); + } +//--- add buffer to resources list + if(!ResourceAdd(buffer)) + { + buffer.Release(); + return(NULL); + } +//--- return buffer + return(buffer); + } +//+------------------------------------------------------------------+ +//| Create vertex buffer | +//+------------------------------------------------------------------+ +CDXIndexBuffer* CDXDispatcher::IndexBufferCreate(const uint &indicies[],uint start=0,uint count=WHOLE_ARRAY) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new buffer + CDXIndexBuffer* buffer=new CDXIndexBuffer(); + if(buffer==NULL) + return(NULL); +//--- create buffer + if(!buffer.Create(m_context,indicies,start,count)) + { + buffer.Release(); + return(NULL); + } +//--- add buffer to resources list + if(!ResourceAdd(buffer)) + { + buffer.Release(); + return(NULL); + } +//--- return input buffer + return(buffer); + } +//+------------------------------------------------------------------+ +//| Create shader input buffer | +//+------------------------------------------------------------------+ +template +CDXInput* CDXDispatcher::InputCreate(void) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new input buffer + CDXInput* input_buffer=new CDXInput(); + if(input_buffer==NULL) + return(NULL); +//--- create buffer + if(!input_buffer.Create(m_context)) + { + input_buffer.Release(); + return(NULL); + } +//--- add buffer to resources list + if(!ResourceAdd(input_buffer)) + { + input_buffer.Release(); + return(NULL); + } +//--- return shader + return(input_buffer); + } +//+------------------------------------------------------------------+ +//| Create texture from bitmap file | +//+------------------------------------------------------------------+ +CDXTexture* CDXDispatcher::TextureCreateFromFile(string path,uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new texture + CDXTexture* texture=new CDXTexture(); + if(texture==NULL) + return(NULL); +//--- create texture + if(!texture.Create(m_context,path,data_x,data_y,data_width,data_height)) + { + texture.Release(); + return(NULL); + } +//--- add texture to resources list + if(!ResourceAdd(texture)) + { + texture.Release(); + return(NULL); + } +//--- return shader + return(texture); + } +//+------------------------------------------------------------------+ +//| Create texture from raw data, only 32-bit pixel formats supported| +//+------------------------------------------------------------------+ +CDXTexture* CDXDispatcher::TextureCreateFromData(ENUM_DX_FORMAT format,uint width,uint height,const uint &data[],uint data_x=0,uint data_y=0,uint data_width=0,uint data_height=0) + { +//--- check context + if(m_context==INVALID_HANDLE) + return(NULL); +//--- allocate new texture + CDXTexture* texture=new CDXTexture(); + if(texture==NULL) + return(NULL); +//--- create texture + if(!texture.Create(m_context,format,width,height,data,data_x,data_y,data_width,data_height)) + { + texture.Release(); + return(NULL); + } +//--- add texture to resources list + if(!ResourceAdd(texture)) + { + texture.Release(); + return(NULL); + } +//--- return shader + return(texture); + } +//+------------------------------------------------------------------+ +//| Add DX resource | +//+------------------------------------------------------------------+ +bool CDXDispatcher::ResourceAdd(CDXObjectBase *resource) + { +//--- add resource + if(!CheckPointer(resource)) + return(false); +//--- + CDXObjectBase *last=&m_dx_resources; + while(CheckPointer(last.Next())!=POINTER_INVALID) + { + if(last==resource) + return(false); + //--- + last=last.Next(); + } +//--- + resource.Next(NULL); + resource.Prev(last); + last.Next(resource); + return(true); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/DX/DXHandle.mqh b/Canvas/DX/DXHandle.mqh new file mode 100644 index 0000000000000000000000000000000000000000..5c35a53f973ed87026fecfb4f597475eb791477d GIT binary patch literal 8568 zcmd^EYflqF6uqBK{0~z|ObsGP^h*gJ$|FePBVsguBS0xs`@p`80{(RMoViSQwllk} zSjZN$X}jI-&fI(Mx%Zx#{{FQhE3zcZ1vr-Ep*(;!D{?93&W>&QBroMq+ESCIoXAsY z$yYhU{FgJaXG=PAF5jgq4LQZQeT=*&tM*v7XKV6Yp5W?h%-)A3?__{y`uNtv)mJ#H z!Kzu=GYMH**oVbEsn}S26CR36Q(oZg7}m8?_7sUfi@>=J8=F9}1`LfX2xI(Kj^s1G zAH%y%pxnc`i}iwyv}0e9ym8sE1%GHW8L?&k@i{vwrnC4x$%Z1~!#L;RQUPA)V8RjZ zYasRxy%-x9eH}G)hr}qPQay#y{=f2W6b2dqa_E(_oT9{NA6~Pjg6;C-Ah~e z%C{nKcK2Jzma+`tSpyzDmUYjkA+B}d)c`g(Beoj(8R1?yn%bRU{rw4rH>zkV?5+6^$CummbtZ{YXvNG>8tT~hC)A7c=iAAQ0+^E?~!g@5F zPK%AR5B$fY`LE;owQbxo$4G-1cW}h@qr;UeVlwUUTF;bHzWS;s8JEw7b(h@AR)8C* z-+S^#K1vnq=soD{1FZF{xU!A!J7fBnGt9GE^m*uGcv}i#UeuzGOI$hnclcRipe9sB zhIOw8&fX6#>3eTbeQsM<&MR+Xy3)taXO62|TCvzWb%&LC$=-M@U%;%8Rq1Y8MkaFrfgki`S$^PG?!d%Q;t1S@RMk;} z73rM}wS0So)}}@J5SW)EcWTXF#NFHjCVnn@#a8-}d}aJsvt;)*;<>v%C*0k&7{*Xb z=l1O}`95%WA1LOj{GxI=YrW}L@nY9rBHuzr5AhjeG4q|>i~p0k?%m5=4d0~x-WUGH zSCwhzDn7bLSIVh=?kR_7x^k|LdViwFk|(k_N!_MZHF+wnU-I@jRBbs9&>mGd20EyX ze}!Y*>5Xc*(`wWE9I7>HMyIrlJhE(T*u9;^_gJ4v&~j+H)v=F*kMQ01!mOATzmEI? D11s|$ literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXInput.mqh b/Canvas/DX/DXInput.mqh new file mode 100644 index 0000000000000000000000000000000000000000..b25ef3ad9fd8e1d342ef0bf534ce1877aace5c84 GIT binary patch literal 4968 zcmeHKU2oGs5S(Wu{(~tJa@CZe3SI(1suCnB0a7VKyhU!}f`#KiJ{nru{&ZmWu2n6iZ ziohtKM_(i6L0HgJG2|?*%i?;T=L%>NK9bvNTslNclzhx@CFFs@&d&c4br?~RG5H`h zN6`AIcJ`Z{n`ePiLX$Ai`87=Gt6DKedr#`1DruDGkHm7K&T&!)IL=^V1{hMWQxb zaeehIj4|+{>3Y|`eq4J)hoi=36+g?YOBXORLD%cBF8W1ZTSPO#IpdvB0il!W?Go(= z%3G;FQ`!v4kAdn!#{L6z$(48bh<(m{2lQsZnPs1M`n-Nsa^l^oCPzd`xNGNTq*No7 zWfSXvHOXg3osIBXYcrJ@yG{joWMyi=jwbajTCvs)$?ljozIu7LHRo$GQA+Za(XCa6 ze~xC9%y+roP8dN-&Bi54a_fqGr;4q(^b literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXMath.mqh b/Canvas/DX/DXMath.mqh new file mode 100644 index 0000000..79f4c36 --- /dev/null +++ b/Canvas/DX/DXMath.mqh @@ -0,0 +1,3352 @@ +//+------------------------------------------------------------------+ +//| DXMath.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2000-2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +//+------------------------------------------------------------------+ +//| DirectX Math Routines | +//+------------------------------------------------------------------+ +//| Ported from C++ code of ReactOS, written by David Adam | +//| and Tony Wasserka | +//| | +//| https://doxygen.reactos.org/de/d57/ | +//| dll_2directx_2wine_2d3dx9__36_2math_8c_source.html | +//| | +//| Copyright (C) 2007 David Adam | +//| Copyright (C) 2007 Tony Wasserka | +//+------------------------------------------------------------------+ +#define DX_PI 3.1415926535897932384626f +#define DX_PI_DIV2 1.5707963267948966192313f +#define DX_PI_DIV3 1.0471975511965977461542f +#define DX_PI_DIV4 0.7853981633974483096156f +#define DX_PI_DIV6 0.5235987755982988730771f +#define DX_PI_MUL2 6.2831853071795864769253f +#define DXSH_MINORDER 2 +#define DXSH_MAXORDER 6 +//+------------------------------------------------------------------+ +//| Preliminary declarations | +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| DXColor | +//+------------------------------------------------------------------+ +struct DXColor; +struct DXPlane; +struct DXVector2; +struct DXVector3; +struct DXVector4; +struct DXMatrix; +struct DXQuaternion; +struct DViewport; +//+------------------------------------------------------------------+ +//| DXColor | +//+------------------------------------------------------------------+ +struct DXColor + { + float r; + float g; + float b; + float a; + //--- constructors + DXColor(void) { r=0.0; g=0.0; b=0.0; a=1.0; } + DXColor(float red,float green,float blue, float alpha) { r=red; g=green; b=blue; a=alpha; } + DXColor(const DXVector4 &v) { r=v.x; g=v.y; b=v.z; a=v.w; } + DXColor(const DXVector3 &v) { r=v.x; g=v.y; b=v.z; a=1.0; } + }; +//+------------------------------------------------------------------+ +//| DXPlane | +//+------------------------------------------------------------------+ +struct DXPlane + { + float a; + float b; + float c; + float d; + }; +//+------------------------------------------------------------------+ +//| DXVector2 | +//+------------------------------------------------------------------+ +struct DXVector2 + { + float x; + float y; + //--- constructors + DXVector2(void) { x=0.0; y=0.0; } + DXVector2(float v) { x=v; y=v; } + DXVector2(float vx,float vy) { x=vx; y=vy; } + DXVector2(const DXVector3 &v) { x=v.x; y=v.y; } + DXVector2(const DXVector4 &v) { x=v.x; y=v.y; } + }; +//+------------------------------------------------------------------+ +//| DXVector3 | +//+------------------------------------------------------------------+ +struct DXVector3 + { + float x; + float y; + float z; + //--- constructors + DXVector3(void) { x=0.0; y=0.0; z=0.0; } + DXVector3(float v) { x=v; y=v; z=v; } + DXVector3(float vx,float vy,float vz) { x=vx; y=vy; z=vz; } + DXVector3(const DXVector2 &v) { x=v.x; y=v.y; z=0.0; } + DXVector3(const DXVector4 &v) { x=v.x; y=v.y; z=v.z; } + }; +//+------------------------------------------------------------------+ +//| DXVector4 | +//+------------------------------------------------------------------+ +struct DXVector4 + { + float x; + float y; + float z; + float w; + //--- constructors + DXVector4(void) { x=0.0; y=0.0; z=0.0; w=1.0; } + DXVector4(float v) { x=v; y=v; z=v; w=v; } + DXVector4(float vx,float vy,float vz,float vw) { x=vx; y=vy; z=vz; w=vw; } + DXVector4(const DXVector2 &v) { x=v.x; y=v.y; z=0.0; w=1.0; } + DXVector4(const DXVector3 &v) { x=v.x; y=v.y; z=v.z; w=1.0; } + }; +//+------------------------------------------------------------------+ +//| DXMatrix | +//+------------------------------------------------------------------+ +struct DXMatrix + { + float m[4][4]; + }; +//+------------------------------------------------------------------+ +//| DXQuaternion | +//+------------------------------------------------------------------+ +struct DXQuaternion + { + float x; + float y; + float z; + float w; + }; +//+------------------------------------------------------------------+ +//| DViewport | +//+------------------------------------------------------------------+ +struct DViewport + { + ulong x; + ulong y; + ulong width; + ulong height; + float minz; + float maxz; + }; + +/* +//--- DXColor functions +void DXColorAdd(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +void DXColorAdjustContrast(DXColor &pout,const DXColor &pc,float s); +void DXColorAdjustSaturation(DXColor &pout,const DXColor &pc,float s); +void DXColorLerp(DXColor &pout,const DXColor &pc1,const DXColor &pc2,float s); +void DXColorModulate(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +void DXColorNegative(DXColor &pout,const DXColor &pc); +void DXColorScale(DXColor &pout,const DXColor &pc,float s); +void DXColorSubtract(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +float DXFresnelTerm(float costheta,float refractionindex); + +//--- DXVector2 functions +void DXVec2Add(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2BaryCentric(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 &pv3,float f,float g); +void DXVec2CatmullRom(DXVector2 &pout,const DXVector2 &pv0,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 &pv3,float s); +float DXVec2CCW(const DXVector2 &pv1,const DXVector2 &pv2); +float DXVec2Dot(const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Hermite(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pt1,const DXVector2 &pv2,const DXVector2 &pt2,float s); +float DXVec2Length(const DXVector2 &v); +float DXVec2LengthSq(const DXVector2 &v); +void DXVec2Lerp(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,float s); +void DXVec2Maximize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Minimize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Normalize(DXVector2 &pout,const DXVector2 &pv); +void DXVec2Scale(DXVector2 &pout,const DXVector2 &pv,float s); +void DXVec2Subtract(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Transform(DXVector4 &pout,const DXVector2 &pv,const DXMatrix &pm); +void DXVec2TransformCoord(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm); +void DXVec2TransformNormal(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm); + +//--- DXVector3 functions +void DXVec3Add(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3BaryCentric(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3,float f,float g); +void DXVec3CatmullRom(DXVector3 &pout,const DXVector3 &pv0,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3,float s); +void DXVec3Cross(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +float DXVec3Dot(const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3Hermite(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pt1,const DXVector3 &pv2,const DXVector3 &pt2,float s); +float DXVec3Length(const DXVector3 &pv); +float DXVec3LengthSq(const DXVector3 &pv); +void DXVec3Lerp(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,float s); +void DXVec3Maximize(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3Minimize(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3Normalize(DXVector3 &pout,const DXVector3 &pv); +void DXVec3Project(DXVector3 &pout,const DXVector3 &pv,const DViewport &pviewport,const DXMatrix &pprojection,const DXMatrix &pview,const DXMatrix &pworld); +void DXVec3Scale(DXVector3 &pout,const DXVector3 &pv,float s); +void DXVec3Subtract(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3Transform(DXVector4 &pout,const DXVector3 &pv,const DXMatrix &pm); +void DXVec3TransformCoord(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm); +void DXVec3TransformNormal(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm); +void DXVec3Unproject(DXVector3 &out,const DXVector3 &v,const DViewport &viewport,const DXMatrix &projection,const DXMatrix &view,const DXMatrix &world); + +//--- DXVector4 vector functions +void DXVec4Add(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4BaryCentric(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3,float f,float g); +void DXVec4CatmullRom(DXVector4 &pout,const DXVector4 &pv0,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3,float s); +void DXVec4Cross(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3); +float DXVec4Dot(const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4Hermite(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pt1,const DXVector4 &pv2,const DXVector4 &pt2,float s); +float DXVec4Length(const DXVector4 &pv); +float DXVec4LengthSq(const DXVector4 &pv); +void DXVec4Lerp(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,float s); +void DXVec4Maximize(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4Minimize(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4Normalize(DXVector4 &pout,const DXVector4 &pv); +void DXVec4Scale(DXVector4 &pout,const DXVector4 &pv,float s); +void DXVec4Subtract(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4Transform(DXVector4 &pout,const DXVector4 &pv,const DXMatrix &pm); + +//---DXQuaternion functions +void DXQuaternionBaryCentric(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,float f,float g); +void DXQuaternionConjugate(DXQuaternion &pout,const DXQuaternion &pq); +float DXQuaternionDot(DXQuaternion &a,DXQuaternion &b); +void DXQuaternionExp(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionIdentity(DXQuaternion &out); +bool DXQuaternionIsIdentity(DXQuaternion &pq); +float DXQuaternionLength(const DXQuaternion &pq); +float DXQuaternionLengthSq(const DXQuaternion &pq); +void DXQuaternionInverse(DXQuaternion &pout,const DXQuaternion &pq); +void DXQuaternionLn(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionMultiply(DXQuaternion &pout,const DXQuaternion &pq1,const DXQuaternion &pq2); +void DXQuaternionNormalize(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionRotationAxis(DXQuaternion &out,const DXVector3 &v,float angle); +void DXQuaternionRotationMatrix(DXQuaternion &out,const DXMatrix &m); +void DXQuaternionRotationYawPitchRoll(DXQuaternion &out,float yaw,float pitch,float roll); +void DXQuaternionSlerp(DXQuaternion &out,DXQuaternion &q1,DXQuaternion &q2,float t); +void DXQuaternionSquad(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,DXQuaternion &pq4,float t); +void DXQuaternionSquadSetup(DXQuaternion &paout,DXQuaternion &pbout,DXQuaternion &pcout,DXQuaternion &pq0,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3); +void DXQuaternionToAxisAngle(const DXQuaternion &pq,DXVector3 &paxis,float &pangle); +DXQuaternion add_diff(const DXQuaternion &q1,const DXQuaternion &q2,const float add); + +//--- DXMatrix functions +void DXMatrixIdentity(DXMatrix &out); +bool DXMatrixIsIdentity(DXMatrix &pm); +void DXMatrixAffineTransformation(DXMatrix &out,float scaling,const DXVector3 &rotationcenter,const DXQuaternion &rotation,const DXVector3 &translation); +void DXMatrixAffineTransformation2D(DXMatrix &out,float scaling,const DXVector2 &rotationcenter,float rotation,const DXVector2 &translation); +int DXMatrixDecompose(DXVector3 &poutscale,DXQuaternion &poutrotation,DXVector3 &pouttranslation,const DXMatrix &pm); +float DXMatrixDeterminant(const DXMatrix &pm); +void DXMatrixInverse(DXMatrix &pout,float &pdeterminant,const DXMatrix &pm); +void DXMatrixLookAtLH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up); +void DXMatrixLookAtRH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up); +void DXMatrixMultiply(DXMatrix &pout,const DXMatrix &pm1,const DXMatrix &pm2); +void DXMatrixMultiplyTranspose(DXMatrix &pout,const DXMatrix &pm1,const DXMatrix &pm2); +void DXMatrixOrthoLH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixOrthoOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixOrthoOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixOrthoRH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixPerspectiveFovLH(DXMatrix &pout,float fovy,float aspect,float zn,float zf); +void DXMatrixPerspectiveFovRH(DXMatrix &pout,float fovy,float aspect,float zn,float zf); +void DXMatrixPerspectiveLH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixPerspectiveOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixPerspectiveOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixPerspectiveRH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixReflect(DXMatrix &pout,const DXPlane &pplane); +void DXMatrixRotationAxis(DXMatrix &out,const DXVector3 &v,float angle); +void DXMatrixRotationQuaternion(DXMatrix &pout,const DXQuaternion &pq); +void DXMatrixRotationX(DXMatrix &pout,float angle); +void DXMatrixRotationY(DXMatrix &pout,float angle); +void DXMatrixRotationYawPitchRoll(DXMatrix &out,float yaw,float pitch,float roll); +void DXMatrixRotationZ(DXMatrix &pout,float angle); +void DXMatrixScaling(DXMatrix &pout,float sx,float sy,float sz); +void DXMatrixShadow(DXMatrix &pout,const DXVector4 &plight,const DXPlane &pplane); +void DXMatrixTransformation(DXMatrix &pout,const DXVector3 &pscalingcenter,const DXQuaternion &pscalingrotation,const DXVector3 &pscaling,const DXVector3 &protationcenter,const DXQuaternion &protation,const DXVector3 &ptranslation); +void DXMatrixTransformation2D(DXMatrix &pout,const DXVector2 &pscalingcenter,float scalingrotation,const DXVector2 &pscaling,const DXVector2 &protationcenter,float rotation,const DXVector2 &ptranslation); +void DXMatrixTranslation(DXMatrix &pout,float x,float y,float z); +void DXMatrixTranspose(DXMatrix &pout,const DXMatrix &pm); + +//--- DXPlane functions | +float DXPlaneDot(const DXPlane &p1,const DXVector4 &p2); +float DXPlaneDotCoord(const DXPlane &pp,const DXVector4 &pv); +float DXPlaneDotNormal(const DXPlane &pp,const DXVector4 &pv); +void DXPlaneFromPointNormal(DXPlane &pout,const DXVector3 &pvpoint,const DXVector3 &pvnormal); +void DXPlaneFromPoints(DXPlane &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3); +void DXPlaneIntersectLine(DXVector3 &pout,const DXPlane &pp,const DXVector3 &pv1,const DXVector3 &pv2); +void DXPlaneNormalize(DXPlane &out,const DXPlane &p); +void DXPlaneScale(DXPlane &pout,const DXPlane &p,float s); +void DXPlaneTransform(DXPlane &pout,const DXPlane &pplane,const DXMatrix &pm); + +//---- spherical harmonic functions +void DXSHAdd(float &out[],int order,const float &a[],const float &b[]); +float DXSHDot(int order,const float &a[],const float &b[]); +int DXSHEvalConeLight(int order,const DXVector3 &dir,float radius,float Rintensity,float Gintensity,float Bintensity,float &rout[],float &gout[],float &bout[]); +void DXSHEvalDirection(float &out[],int order,const DXVector3 &dir); +int DXSHEvalDirectionalLight(int order,const DXVector3 &dir,float Rintensity,float Gintensity,float Bintensity,float &rout[],float &gout[],float &bout[]); +int DXSHEvalHemisphereLight(int order,const DXVector3 &dir,DXColor &top,DXColor &bottom,float &rout[],float &gout[],float &bout[]); +int DXSHEvalSphericalLight(int order,const DXVector3 &dir,float radius,float Rintensity,float Gintensity,float Bintensity,float &rout[],float &gout[],float &bout[]); +void DXSHMultiply2(float &out[],const float &a[],const float &b[]); +void DXSHMultiply3(float &out[],const float &a[],const float &b[]); +void DXSHMultiply4(float &out[],const float &a[],const float &b[]); +void DXSHRotate(float &out[],int order,const DXMatrix &matrix,const float &in[]); +void DXSHRotateZ(float &out[],int order,float angle,const float &in[]); +void DXSHScale(float &out[],int order,const float &a[],const float scale); + +//--- scalar functions +float DXScalarLerp(const float val1,const float val2,float s) +float DXScalarBiasScale(const float val,const float bias,const float scale) +*/ + +//+------------------------------------------------------------------+ +//| Adds two color values together to create a new color value. | +//+------------------------------------------------------------------+ +void DXColorAdd(DXColor &pout,const DXColor &pc1,const DXColor &pc2) + { + pout.r = pc1.r + pc2.r; + pout.g = pc1.g + pc2.g; + pout.b = pc1.b + pc2.b; + pout.a = pc1.a + pc2.a; + } +//+------------------------------------------------------------------+ +//| Adjusts the contrast value of a color. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, | +//| to the output alpha channel. | +//| | +//| This function interpolates the red,green,and blue color | +//| components of a DXColor structure between fifty percent gray | +//| and a specified contrast value,as shown in the following example.| +//| | +//| pout.r = 0.5f + s*(pc.r - 0.5f); | +//| | +//| If s is greater than 0 and less than 1,the contrast is decreased.| +//| If s is greater than 1, the contrast is increased. | +//+------------------------------------------------------------------+ +void DXColorAdjustContrast(DXColor &pout,const DXColor &pc,float s) + { + pout.r = 0.5f + s*(pc.r - 0.5f); + pout.g = 0.5f + s*(pc.g - 0.5f); + pout.b = 0.5f + s*(pc.b - 0.5f); + pout.a = pc.a; + } +//+------------------------------------------------------------------+ +//| Adjusts the saturation value of a color. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, | +//| to the output alpha channel. | +//| | +//| This function interpolates the red, green, and blue color | +//| components of a DXColor structure between an unsaturated color | +//| and a color, as shown in the following example. | +//| | +//| Approximate values for each component's contribution to | +//| luminance. Based upon the NTSC standard described in | +//| ITU-R Recommendation BT.709. | +//| float grey = pc.r*0.2125f + pc.g*0.7154f + pc.b*0.0721f; | +//| | +//| pout.r = grey + s*(pc.r - grey); | +//| If s is greater than 0 and less than 1, the saturation is | +//| decreased. If s is greater than 1, the saturation is increased. | +//| | +//| The grayscale color is computed as: | +//| r = g = b = 0.2125*r + 0.7154*g + 0.0721*b | +//+------------------------------------------------------------------+ +void DXColorAdjustSaturation(DXColor &pout,const DXColor &pc,float s) + { + float grey = pc.r*0.2125f + pc.g*0.7154f + pc.b*0.0721f; + pout.r = grey + s*(pc.r - grey); + pout.g = grey + s*(pc.g - grey); + pout.b = grey + s*(pc.b - grey); + pout.a = pc.a; + } +//+------------------------------------------------------------------+ +//| Uses linear interpolation to create a color value. | +//+------------------------------------------------------------------+ +//| This function interpolates the red, green, blue, and alpha | +//| components of a DXColor structure between two colors, as shown | +//| in the following example. | +//| | +//| pout.r = pC1.r + s * (pC2.r - pC1.r); | +//| | +//| If you are linearly interpolating between the colors A and B, | +//| and s is 0, the resulting color is A. | +//| If s is 1, the resulting color is color B. | +//+------------------------------------------------------------------+ +void DXColorLerp(DXColor &pout,const DXColor &pc1,const DXColor &pc2,float s) + { + pout.r = (1-s)*pc1.r + s*pc2.r; + pout.g = (1-s)*pc1.g + s*pc2.g; + pout.b = (1-s)*pc1.b + s*pc2.b; + pout.a = (1-s)*pc1.a + s*pc2.a; + } +//+------------------------------------------------------------------+ +//| Blends two colors. | +//+------------------------------------------------------------------+ +//| This function blends together two colors by multiplying matching | +//| color components, as shown in the following example. | +//| pout.r = pC1.r * pC2.r; | +//+------------------------------------------------------------------+ +void DXColorModulate(DXColor &pout,const DXColor &pc1,const DXColor &pc2) + { + pout.r = pc1.r*pc2.r; + pout.g = pc1.g*pc2.g; + pout.b = pc1.b*pc2.b; + pout.a = pc1.a*pc2.a; + } +//+------------------------------------------------------------------+ +//| Creates the negative color value of a color value. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, to the output | +//| alpha channel. | +//| This function returns the negative color value by subtracting 1.0| +//| from the color components of the DXColor structure, | +//| as shown in the following example. | +//| pout.r = 1.0f - pc.r; | +//+------------------------------------------------------------------+ +void DXColorNegative(DXColor &pout,const DXColor &pc) + { + pout.r = 1.0f - pc.r; + pout.g = 1.0f - pc.g; + pout.b = 1.0f - pc.b; + pout.a = pc.a; + } +//+------------------------------------------------------------------+ +//| Scales a color value. | +//+------------------------------------------------------------------+ +//| This function computes the scaled color value by multiplying | +//| the color components of the DXColor structure by the specified | +//| scale factor, as shown in the following example. | +//| pOut.r = pC.r*s; | +//+------------------------------------------------------------------+ +void DXColorScale(DXColor &pout,const DXColor &pc,float s) + { + pout.r = s*pc.r; + pout.g = s*pc.g; + pout.b = s*pc.b; + pout.a = s*pc.a; + } +//+------------------------------------------------------------------+ +//| Subtracts two color values to create a new color value. | +//+------------------------------------------------------------------+ +void DXColorSubtract(DXColor &pout,const DXColor &pc1,const DXColor &pc2) + { + pout.r = pc1.r - pc2.r; + pout.g = pc1.g - pc2.g; + pout.b = pc1.b - pc2.b; + pout.a = pc1.a - pc2.a; + } +//+------------------------------------------------------------------+ +//| Calculate the Fresnel term. | +//+------------------------------------------------------------------+ +//| To find the Fresnel term (F): | +//| If A is angle of incidence and B is the angle of refraction, | +//| then | +//| F = 0.5*[tan2(A - B)/tan2(A + B) + sin2(A - B)/sin2(A + B)] | +//| = 0.5*sin2(A - B)/sin2(A + B)*[cos2(A + B)/cos2(A - B) + 1] | +//| | +//| Let r = sina(A)/sin(B) (the relative refractive index) | +//| Let c = cos(A) | +//| Let g = (r2 + c2 - 1)1/2 | +//| | +//| Then,expanding using the trig identities and simplifying, | +//| you get: | +//| F = 0.5*(g + c)2/(g - c)2*([c(g + c)-1]2/[c(g - c) + 1]2 + 1) | +//+------------------------------------------------------------------+ +float DXFresnelTerm(float costheta,float refractionindex) + { + float g = (float)sqrt(refractionindex*refractionindex + costheta*costheta - 1.0f); + float a = g + costheta; + float d = g - costheta; + float result = (costheta*a - 1.0f)*(costheta*a - 1.0f)/((costheta*d + 1.0f)*(costheta*d + 1.0f)) + 1.0f; + result *= 0.5f*d*d/(a*a); +//--- + return(result); + } +//+------------------------------------------------------------------+ +//| Adds two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Add(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2) + { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; + } +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2BaryCentric(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 &pv3,float f,float g) + { + pout.x = (1.0f-f-g)*(pv1.x) + f*(pv2.x) + g*(pv3.x); + pout.y = (1.0f-f-g)*(pv1.y) + f*(pv2.y) + g*(pv3.y); + } +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2CatmullRom(DXVector2 &pout,const DXVector2 &pv0,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 &pv3,float s) + { + pout.x = 0.5f*(2.0f*pv1.x + (pv2.x-pv0.x)*s + (2.0f*pv0.x - 5.0f*pv1.x + 4.0f*pv2.x - pv3.x)*s*s + (pv3.x - 3.0f*pv2.x + 3.0f*pv1.x - pv0.x)*s*s*s); + pout.y = 0.5f*(2.0f*pv1.y + (pv2.y-pv0.y)*s + (2.0f*pv0.y - 5.0f*pv1.y + 4.0f*pv2.y - pv3.y)*s*s + (pv3.y - 3.0f*pv2.y + 3.0f*pv1.y - pv0.y)*s*s*s); + } +//+------------------------------------------------------------------+ +//| Returns the z-component by taking the cross product | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +float DXVec2CCW(const DXVector2 &pv1,const DXVector2 &pv2) + { + return(pv1.x*pv2.y - pv1.y*pv2.x); + } +//+------------------------------------------------------------------+ +//| Determines the dot product of two 2D vectors. | +//+------------------------------------------------------------------+ +float DXVec2Dot(const DXVector2 &pv1,const DXVector2 &pv2) + { + return(pv1.x*pv2.x + pv1.y*pv2.y); + } +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Hermite(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pt1,const DXVector2 &pv2,const DXVector2 &pt2,float s) + { +//--- prepare coefficients + float h1 = 2.0f*s*s*s - 3.0f*s*s + 1.0f; + float h2 = s*s*s - 2.0f*s*s + s; + float h3 = -2.0f*s*s*s + 3.0f*s*s; + float h4 = s*s*s - s*s; +//--- calculate interpolated point + pout.x = h1*pv1.x + h2*pt1.x + h3*pv2.x + h4*pt2.x; + pout.y = h1*pv1.y + h2*pt1.y + h3*pv2.y + h4*pt2.y; + } +//+------------------------------------------------------------------+ +//| Returns the length of a 2D vector. | +//+------------------------------------------------------------------+ +float DXVec2Length(const DXVector2 &v) + { + return((float)sqrt(v.x*v.x + v.y*v.y)); + } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 2D vector. | +//+------------------------------------------------------------------+ +float DXVec2LengthSq(const DXVector2 &v) + { + return((float)(v.x*v.x + v.y*v.y)); + } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Lerp(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,float s) + { + pout.x = (1.0f-s)*pv1.x + s*pv2.x; + pout.y = (1.0f-s)*pv1.y + s*pv2.y; + } +//+------------------------------------------------------------------+ +//| Returns a 2D vector that is made up of the largest components | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Maximize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2) + { + pout.x = (float)fmax(pv1.x,pv2.x); + pout.y = (float)fmax(pv1.y,pv2.y); + } +//+------------------------------------------------------------------+ +//| Returns a 2D vector that is made up of the smallest components | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Minimize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2) + { + pout.x = (float)fmin(pv1.x,pv2.x); + pout.y = (float)fmin(pv1.y,pv2.y); + } +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 2D vector. | +//+------------------------------------------------------------------+ +void DXVec2Normalize(DXVector2 &pout,const DXVector2 &pv) + { +//--- calculate length + float norm = DXVec2Length(pv); + if(!norm) + { + pout.x = 0.0f; + pout.y = 0.0f; + } + else + { + pout.x = pv.x/norm; + pout.y = pv.y/norm; + } + } +//+------------------------------------------------------------------+ +//| Scales a 2D vector. | +//+------------------------------------------------------------------+ +void DXVec2Scale(DXVector2 &pout,const DXVector2 &pv,float s) + { + pout.x = s*pv.x; + pout.y = s*pv.y; + } +//+------------------------------------------------------------------+ +//| DXVec3Subtract | +//+------------------------------------------------------------------+ +void DXVec2Subtract(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2) + { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; + } +//+------------------------------------------------------------------+ +//| Transforms a 2D vector by a given matrix. | +//| This function transforms the vector pv(x,y,0,1) by the matrix pm.| +//+------------------------------------------------------------------+ +void DXVec2Transform(DXVector4 &pout,const DXVector2 &pv,const DXMatrix &pm) + { + DXVector4 out; + out.x = pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[3][0]; + out.y = pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[3][1]; + out.z = pm.m[0][2]*pv.x + pm.m[1][2]*pv.y + pm.m[3][2]; + out.w = pm.m[0][3]*pv.x + pm.m[1][3]*pv.y + pm.m[3][3]; + pout = out; + } +//+------------------------------------------------------------------+ +//| Transforms a 2D vector by a given matrix, | +//| projecting the result back into w = 1. | +//| This function transforms the vector pv(x,y,0,1) by the matrix pm.| +//+------------------------------------------------------------------+ +void DXVec2TransformCoord(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm) + { + float norm = pm.m[0][3]*pv.x + pm.m[1][3]*pv.y + pm.m[3][3]; + if(norm) + { + pout.x = (pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[3][0])/norm; + pout.y = (pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[3][1])/norm; + } + else + { + pout.x = 0.0f; + pout.y = 0.0f; + } + } +//+------------------------------------------------------------------+ +//| Transforms the 2D vector normal by the given matrix. | +//+------------------------------------------------------------------+ +void DXVec2TransformNormal(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm) + { + pout.x = pm.m[0][0]*pv.x + pm.m[1][0]*pv.y; + pout.y = pm.m[0][1]*pv.x + pm.m[1][1]*pv.y; + } +//+------------------------------------------------------------------+ +//| Adds two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Add(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2) + { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; + pout.z = pv1.z + pv2.z; + } +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3BaryCentric(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3,float f,float g) + { + pout.x = (1.0f-f-g)*pv1.x + f*pv2.x + g*pv3.x; + pout.y = (1.0f-f-g)*pv1.y + f*pv2.y + g*pv3.y; + pout.z = (1.0f-f-g)*pv1.z + f*pv2.z + g*pv3.z; + } +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3CatmullRom(DXVector3 &pout,const DXVector3 &pv0,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3,float s) + { + pout.x = 0.5f*(2.0f*pv1.x + (pv2.x - pv0.x)*s + (2.0f*pv0.x - 5.0f*pv1.x + 4.0f*pv2.x - pv3.x)*s*s + (pv3.x - 3.0f*pv2.x + 3.0f*pv1.x - pv0.x)*s*s*s); + pout.y = 0.5f*(2.0f*pv1.y + (pv2.y - pv0.y)*s + (2.0f*pv0.y - 5.0f*pv1.y + 4.0f*pv2.y - pv3.y)*s*s + (pv3.y - 3.0f*pv2.y + 3.0f*pv1.y - pv0.y)*s*s*s); + pout.z = 0.5f*(2.0f*pv1.z + (pv2.z - pv0.z)*s + (2.0f*pv0.z - 5.0f*pv1.z + 4.0f*pv2.z - pv3.z)*s*s + (pv3.z - 3.0f*pv2.z + 3.0f*pv1.z - pv0.z)*s*s*s); + } +//+------------------------------------------------------------------+ +//| Determines the cross-product of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Cross(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2) + { + pout.x = pv1.y*pv2.z - pv1.z*pv2.y; + pout.y = pv1.z*pv2.x - pv1.x*pv2.z; + pout.z = pv1.x*pv2.y - pv1.y*pv2.x; + } +//+------------------------------------------------------------------+ +//| Determines the dot product of two 3D vectors. | +//+------------------------------------------------------------------+ +float DXVec3Dot(const DXVector3 &pv1,const DXVector3 &pv2) + { + return(pv1.x*pv2.x + pv1.y*pv2.y + pv1.z*pv2.z); + } +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Hermite(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pt1,const DXVector3 &pv2,const DXVector3 &pt2,float s) + { + float h1 = 2.0f*s*s*s - 3.0f*s*s + 1.0f; + float h2 = s*s*s - 2.0f*s*s + s; + float h3 = -2.0f*s*s*s + 3.0f*s*s; + float h4 = s*s*s - s*s; +//--- calculate interpolated coordinates + pout.x = h1*pv1.x + h2*pt1.x + h3*pv2.x + h4*pt2.x; + pout.y = h1*pv1.y + h2*pt1.y + h3*pv2.y + h4*pt2.y; + pout.z = h1*pv1.z + h2*pt1.z + h3*pv2.z + h4*pt2.z; + } +//+------------------------------------------------------------------+ +//| Returns the length of a 3D vector. | +//+------------------------------------------------------------------+ +float DXVec3Length(const DXVector3 &pv) + { + return((float)sqrt(pv.x*pv.x + pv.y*pv.y + pv.z*pv.z)); + } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 3D vector. | +//+------------------------------------------------------------------+ +float DXVec3LengthSq(const DXVector3 &pv) + { + return((float)(pv.x*pv.x + pv.y*pv.y + pv.z*pv.z)); + } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Lerp(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,float s) + { + pout.x = (1.0f-s)*pv1.x + s*pv2.x; + pout.y = (1.0f-s)*pv1.y + s*pv2.y; + pout.z = (1.0f-s)*pv1.z + s*pv2.z; + } +//+------------------------------------------------------------------+ +//| Returns a 3D vector that is made up of the largest components | +//| of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Maximize(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2) + { + pout.x = (float)fmax(pv1.x,pv2.x); + pout.y = (float)fmax(pv1.y,pv2.y); + pout.z = (float)fmax(pv1.z,pv2.z); + } +//+------------------------------------------------------------------+ +//| Returns a 3D vector that is made up of the smallest components | +//| of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Minimize(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2) + { + pout.x = (float)fmin(pv1.x,pv2.x); + pout.y = (float)fmin(pv1.y,pv2.y); + pout.z = (float)fmin(pv1.z,pv2.z); + } +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 3D vector. | +//+------------------------------------------------------------------+ +void DXVec3Normalize(DXVector3 &pout,const DXVector3 &pv) + { +//--- calculate length + float norm = DXVec3Length(pv); + if(!norm) + { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + } + else + { + pout.x = pv.x/norm; + pout.y = pv.y/norm; + pout.z = pv.z/norm; + } + } +//+------------------------------------------------------------------+ +//| Projects a 3D vector from object space into screen space. | +//+------------------------------------------------------------------+ +void DXVec3Project(DXVector3 &pout,const DXVector3 &pv,const DViewport &pviewport,const DXMatrix &pprojection,const DXMatrix &pview,const DXMatrix &pworld) + { + DXMatrix m; + DXMatrixIdentity(m); +//--- pworld + DXMatrixMultiply(m,m,pworld); +//--- pview + DXMatrixMultiply(m,m,pview); +//--- pprojection + DXMatrixMultiply(m,m,pprojection); + DXVec3TransformCoord(pout,pv,m); +//---pviewport + pout.x = pviewport.x + (1.0f + pout.x)*pviewport.width/2.0f; + pout.y = pviewport.y + (1.0f - pout.y)*pviewport.height/2.0f; + pout.z = pviewport.minz + pout.z*(pviewport.maxz - pviewport.minz); + } +//+------------------------------------------------------------------+ +//| Scales a 3D vector. | +//+------------------------------------------------------------------+ +void DXVec3Scale(DXVector3 &pout,const DXVector3 &pv,float s) + { + pout.x = s*pv.x; + pout.y = s*pv.y; + pout.z = s*pv.z; + } +//+------------------------------------------------------------------+ +//| Subtracts two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Subtract(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2) + { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; + pout.z = pv1.z - pv2.z; + } +//+------------------------------------------------------------------+ +//| Transforms vector (x,y,z,1) by a given matrix. | +//+------------------------------------------------------------------+ +void DXVec3Transform(DXVector4 &pout,const DXVector3 &pv,const DXMatrix &pm) + { + DXVector4 out; +//--- + out.x = pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[2][0]*pv.z + pm.m[3][0]; + out.y = pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[2][1]*pv.z + pm.m[3][1]; + out.z = pm.m[0][2]*pv.x + pm.m[1][2]*pv.y + pm.m[2][2]*pv.z + pm.m[3][2]; + out.w = pm.m[0][3]*pv.x + pm.m[1][3]*pv.y + pm.m[2][3]*pv.z + pm.m[3][3]; + pout = out; + } +//+------------------------------------------------------------------+ +//| Transforms a 3D vector by a given matrix, | +//| projecting the result back into w = 1. | +//+------------------------------------------------------------------+ +void DXVec3TransformCoord(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm) + { + float norm = pm.m[0][3]*pv.x + pm.m[1][3]*pv.y + pm.m[2][3]*pv.z + pm.m[3][3]; +//--- + if(norm) + { + pout.x = (pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[2][0]*pv.z + pm.m[3][0])/norm; + pout.y = (pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[2][1]*pv.z + pm.m[3][1])/norm; + pout.z = (pm.m[0][2]*pv.x + pm.m[1][2]*pv.y + pm.m[2][2]*pv.z + pm.m[3][2])/norm; + } + else + { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + } + } +//+------------------------------------------------------------------+ +//| Transforms the 3D vector normal by the given matrix. | +//+------------------------------------------------------------------+ +void DXVec3TransformNormal(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm) + { + pout.x = pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[2][0]*pv.z; + pout.y = pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[2][1]*pv.z; + pout.z = pm.m[0][2]*pv.x + pm.m[1][2]*pv.y + pm.m[2][2]*pv.z; + } +//+------------------------------------------------------------------+ +//| Projects a vector from screen space into object space. | +//+------------------------------------------------------------------+ +void DXVec3Unproject(DXVector3 &out,const DXVector3 &v,const DViewport &viewport,const DXMatrix &projection,const DXMatrix &view,const DXMatrix &world) + { + DXMatrix m; + DXMatrixIdentity(m); +//--- world + DXMatrixMultiply(m,m,world); +//--- view + DXMatrixMultiply(m,m,view); +//--- projection + DXMatrixMultiply(m,m,projection); +//--- calculate inverse matrix + float det=0.0f; + DXMatrixInverse(m,det,m); + out = v; +//--- viewport + out.x = 2.0f*(out.x - viewport.x)/viewport.width - 1.0f; + out.y = 1.0f - 2.0f*(out.y - viewport.y)/viewport.height; + out.z = (out.z - viewport.minz)/(viewport.maxz - viewport.minz); +//--- + DXVec3TransformCoord(out,out,m); + } +//+------------------------------------------------------------------+ +//| Adds two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Add(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2) + { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; + pout.z = pv1.z + pv2.z; + pout.w = pv1.w + pv2.w; + } +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4BaryCentric(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3,float f,float g) + { + pout.x = (1.0f-f-g)*(pv1.x) + f*(pv2.x) + g*(pv3.x); + pout.y = (1.0f-f-g)*(pv1.y) + f*(pv2.y) + g*(pv3.y); + pout.z = (1.0f-f-g)*(pv1.z) + f*(pv2.z) + g*(pv3.z); + pout.w = (1.0f-f-g)*(pv1.w) + f*(pv2.w) + g*(pv3.w); + } +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4CatmullRom(DXVector4 &pout,const DXVector4 &pv0,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3,float s) + { + pout.x = 0.5f*(2.0f*pv1.x + (pv2.x - pv0.x)*s + (2.0f*pv0.x - 5.0f*pv1.x + 4.0f*pv2.x - pv3.x)*s*s + (pv3.x -3.0f*pv2.x + 3.0f*pv1.x - pv0.x)*s*s*s); + pout.y = 0.5f*(2.0f*pv1.y + (pv2.y - pv0.y)*s + (2.0f*pv0.y - 5.0f*pv1.y + 4.0f*pv2.y - pv3.y)*s*s + (pv3.y -3.0f*pv2.y + 3.0f*pv1.y - pv0.y)*s*s*s); + pout.z = 0.5f*(2.0f*pv1.z + (pv2.z - pv0.z)*s + (2.0f*pv0.z - 5.0f*pv1.z + 4.0f*pv2.z - pv3.z)*s*s + (pv3.z -3.0f*pv2.z + 3.0f*pv1.z - pv0.z)*s*s*s); + pout.w = 0.5f*(2.0f*pv1.w + (pv2.w - pv0.w)*s + (2.0f*pv0.w - 5.0f*pv1.w + 4.0f*pv2.w - pv3.w)*s*s + (pv3.w -3.0f*pv2.w + 3.0f*pv1.w - pv0.w)*s*s*s); + } +//+------------------------------------------------------------------+ +//| Determines the cross-product in four dimensions. | +//+------------------------------------------------------------------+ +void DXVec4Cross(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3) + { + DXVector4 out; + out.x = pv1.y*(pv2.z*pv3.w - pv3.z*pv2.w) - pv1.z*(pv2.y*pv3.w - pv3.y*pv2.w) + pv1.w*(pv2.y*pv3.z - pv2.z*pv3.y); + out.y = -(pv1.x*(pv2.z*pv3.w - pv3.z*pv2.w) - pv1.z*(pv2.x*pv3.w - pv3.x*pv2.w) + pv1.w*(pv2.x*pv3.z - pv3.x*pv2.z)); + out.z = pv1.x*(pv2.y*pv3.w - pv3.y*pv2.w) - pv1.y*(pv2.x*pv3.w - pv3.x*pv2.w) + pv1.w*(pv2.x*pv3.y - pv3.x*pv2.y); + out.w = -(pv1.x*(pv2.y*pv3.z - pv3.y*pv2.z) - pv1.y*(pv2.x*pv3.z - pv3.x*pv2.z) + pv1.z*(pv2.x*pv3.y - pv3.x*pv2.y)); + pout = out; + } +//+------------------------------------------------------------------+ +//| Determines the dot product of two 4D vectors. | +//+------------------------------------------------------------------+ +float DXVec4Dot(const DXVector4 &pv1,const DXVector4 &pv2) + { + return(pv1.x*pv2.x + pv1.y*pv2.y + pv1.z*pv2.z+ pv1.w*pv2.w); + } +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Hermite(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pt1,const DXVector4 &pv2,const DXVector4 &pt2,float s) + { + float h1 = 2.0f*s*s*s - 3.0f*s*s + 1.0f; + float h2 = s*s*s - 2.0f*s*s + s; + float h3 = -2.0f*s*s*s + 3.0f*s*s; + float h4 = s*s*s - s*s; + pout.x = h1*pv1.x + h2*pt1.x + h3*pv2.x + h4*pt2.x; + pout.y = h1*pv1.y + h2*pt1.y + h3*pv2.y + h4*pt2.y; + pout.z = h1*pv1.z + h2*pt1.z + h3*pv2.z + h4*pt2.z; + pout.w = h1*pv1.w + h2*pt1.w + h3*pv2.w + h4*pt2.w; + } +//+------------------------------------------------------------------+ +//| Returns the length of a 4D vector. | +//+------------------------------------------------------------------+ +float DXVec4Length(const DXVector4 &pv) + { + return((float)sqrt(pv.x*pv.x + pv.y*pv.y + pv.z*pv.z+ pv.w*pv.w)); + } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 4D vector. | +//+------------------------------------------------------------------+ +float DXVec4LengthSq(const DXVector4 &pv) + { + return((float)(pv.x*pv.x + pv.y*pv.y + pv.z*pv.z)); + } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Lerp(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,float s) + { + pout.x = (1.0f-s)*pv1.x + s*pv2.x; + pout.y = (1.0f-s)*pv1.y + s*pv2.y; + pout.z = (1.0f-s)*pv1.z + s*pv2.z; + pout.w = (1.0f-s)*pv1.w + s*pv2.w; + } +//+------------------------------------------------------------------+ +//| Returns a 4D vector that is made up of the largest components | +//| of two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Maximize(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2) + { + pout.x = (float)fmax(pv1.x,pv2.x); + pout.y = (float)fmax(pv1.y,pv2.y); + pout.z = (float)fmax(pv1.z,pv2.z); + pout.w = (float)fmax(pv1.w,pv2.w); + } +//+------------------------------------------------------------------+ +//| Returns a 4D vector that is made up of the smallest components | +//| of two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Minimize(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2) + { + pout.x = (float)fmin(pv1.x,pv2.x); + pout.y = (float)fmin(pv1.y,pv2.y); + pout.z = (float)fmin(pv1.z,pv2.z); + pout.w = (float)fmin(pv1.w,pv2.w); + } +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 4D vector. | +//+------------------------------------------------------------------+ +void DXVec4Normalize(DXVector4 &pout,const DXVector4 &pv) + { +//--- calculate length + float norm = DXVec4Length(pv); + if(!norm) + { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + pout.w = 0.0f; + } + else + { + pout.x = pv.x/norm; + pout.y = pv.y/norm; + pout.z = pv.z/norm; + pout.w = pv.w/norm; + } + } +//+------------------------------------------------------------------+ +//| Scales a 4D vector. | +//+------------------------------------------------------------------+ +void DXVec4Scale(DXVector4 &pout,const DXVector4 &pv,float s) + { + pout.x = s*pv.x; + pout.y = s*pv.y; + pout.z = s*pv.z; + pout.w = s*pv.w; + } +//+------------------------------------------------------------------+ +//| Subtracts two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Subtract(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2) + { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; + pout.z = pv1.z - pv2.z; + pout.w = pv1.w - pv2.w; + } +//+------------------------------------------------------------------+ +//| Transforms a 4D vector by a given matrix. | +//+------------------------------------------------------------------+ +void DXVec4Transform(DXVector4 &pout,const DXVector4 &pv,const DXMatrix &pm) + { + DXVector4 temp; + temp.x = pm.m[0][0]*pv.x + pm.m[1][0]*pv.y + pm.m[2][0]*pv.z + pm.m[3][0]*pv.w; + temp.y = pm.m[0][1]*pv.x + pm.m[1][1]*pv.y + pm.m[2][1]*pv.z + pm.m[3][1]*pv.w; + temp.z = pm.m[0][2]*pv.x + pm.m[1][2]*pv.y + pm.m[2][2]*pv.z + pm.m[3][2]*pv.w; + temp.w = pm.m[0][3]*pv.x + pm.m[1][3]*pv.y + pm.m[2][3]*pv.z + pm.m[3][3]*pv.w; + pout=temp; + } +//+------------------------------------------------------------------+ +//| Returns a quaternion in barycentric coordinates. | +//+------------------------------------------------------------------+ +void DXQuaternionBaryCentric(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,float f,float g) + { + DXQuaternion temp1,temp2; + DXQuaternionSlerp(temp1,pq1,pq2,f+g); + DXQuaternionSlerp(temp2,pq1,pq3,f+g); + DXQuaternionSlerp(pout,temp1,temp2,g/(f+g)); + } +//+------------------------------------------------------------------+ +//| Returns the conjugate of a quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionConjugate(DXQuaternion &pout,const DXQuaternion &pq) + { + pout.x = -pq.x; + pout.y = -pq.y; + pout.z = -pq.z; + pout.w = pq.w; + } +//+------------------------------------------------------------------+ +//| Returns the dot product of two quaternions. | +//+------------------------------------------------------------------+ +float DXQuaternionDot(DXQuaternion &a,DXQuaternion &b) + { + return(a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w); + } +//+------------------------------------------------------------------+ +//| Calculates the exponential. | +//| This method converts a pure quaternion to a unit quaternion. | +//| DXQuaternionExp expects a pure quaternion, where w is ignored | +//| in the calculation (w == 0). | +//+------------------------------------------------------------------+ +void DXQuaternionExp(DXQuaternion &out,const DXQuaternion &q) + { + float norm = (float)sqrt(q.x*q.x + q.y*q.y + q.z*q.z); + if(norm) + { + out.x = (float)sin(norm)*q.x/norm; + out.y = (float)sin(norm)*q.y/norm; + out.z = (float)sin(norm)*q.z/norm; + out.w = (float)cos(norm); + } + else + { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 1.0f; + } + } +//+------------------------------------------------------------------+ +//| Returns the identity quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionIdentity(DXQuaternion &out) + { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 1.0f; + } +//+------------------------------------------------------------------+ +//| Determines if a quaternion is an identity quaternion. | +//+------------------------------------------------------------------+ +bool DXQuaternionIsIdentity(DXQuaternion &pq) + { + return ((pq.x == 0.0f) && (pq.y == 0.0f) && (pq.z == 0.0f) && (pq.w == 1.0f)); + } +//+------------------------------------------------------------------+ +//| Returns the length of a quaternion. | +//+------------------------------------------------------------------+ +float DXQuaternionLength(const DXQuaternion &pq) + { + return((float)sqrt(pq.x*pq.x + pq.y*pq.y + pq.z*pq.z + pq.w*pq.w)); + } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a quaternion. | +//+------------------------------------------------------------------+ +float DXQuaternionLengthSq(const DXQuaternion &pq) + { + return((float)(pq.x*pq.x + pq.y*pq.y + pq.z*pq.z + pq.w*pq.w)); + } +//+------------------------------------------------------------------+ +//| Conjugates and renormalizes a quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionInverse(DXQuaternion &pout,const DXQuaternion &pq) + { + float norm = DXQuaternionLengthSq(pq); + pout.x = -pq.x/norm; + pout.y = -pq.y/norm; + pout.z = -pq.z/norm; + pout.w = pq.w/norm; + } +//+------------------------------------------------------------------+ +//| Calculates the natural logarithm. | +//| The DXQuaternionLn function works only for unit quaternions. | +//+------------------------------------------------------------------+ +void DXQuaternionLn(DXQuaternion &out,const DXQuaternion &q) + { + float t; + if((q.w >= 1.0f) || (q.w == -1.0f)) + t = 1.0f; + else + t = (float)(acos(q.w)/sqrt(1.0f - q.w*q.w)); + out.x = t*q.x; + out.y = t*q.y; + out.z = t*q.z; + out.w = 0.0f; + } +//+------------------------------------------------------------------+ +//| Multiplies two quaternions. | +//+------------------------------------------------------------------+ +void DXQuaternionMultiply(DXQuaternion &pout,const DXQuaternion &pq1,const DXQuaternion &pq2) + { + DXQuaternion out; + out.x = pq2.w*pq1.x + pq2.x*pq1.w + pq2.y*pq1.z - pq2.z*pq1.y; + out.y = pq2.w*pq1.y - pq2.x*pq1.z + pq2.y*pq1.w + pq2.z*pq1.x; + out.z = pq2.w*pq1.z + pq2.x*pq1.y - pq2.y*pq1.x + pq2.z*pq1.w; + out.w = pq2.w*pq1.w - pq2.x*pq1.x - pq2.y*pq1.y - pq2.z*pq1.z; + pout = out; + } +//+------------------------------------------------------------------+ +//| Computes a unit length quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionNormalize(DXQuaternion &out,const DXQuaternion &q) + { + float norm = DXQuaternionLength(q); + if(!norm) + { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 0.0f; + } + else + { + out.x = q.x/norm; + out.y = q.y/norm; + out.z = q.z/norm; + out.w = q.w/norm; + } + } +//+------------------------------------------------------------------+ +//| Rotates a quaternion about an arbitrary axis. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationAxis(DXQuaternion &out,const DXVector3 &v,float angle) + { + DXVector3 temp; + DXVec3Normalize(temp,v); + out.x = (float)sin(angle/2.0f)*temp.x; + out.y = (float)sin(angle/2.0f)*temp.y; + out.z = (float)sin(angle/2.0f)*temp.z; + out.w = (float)cos(angle/2.0f); + } +//+------------------------------------------------------------------+ +//| Builds a quaternion from a rotation matrix. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationMatrix(DXQuaternion &out,const DXMatrix &m) + { + float s; + float trace = m.m[0][0] + m.m[1][1] + m.m[2][2] + 1.0f; + if(trace>1.0f) + { + s = 2.0f*(float)sqrt(trace); + out.x = (m.m[1][2] - m.m[2][1])/s; + out.y = (m.m[2][0] - m.m[0][2])/s; + out.z = (m.m[0][1] - m.m[1][0])/s; + out.w = 0.25f*s; + } + else + { + int maxi = 0; + for(int i=1; i<3; i++) + { + if(m.m[i][i] > m.m[maxi][maxi]) + maxi = i; + } + switch(maxi) + { + case 0: + s = 2.0f*(float)sqrt(1.0f + m.m[0][0] - m.m[1][1] - m.m[2][2]); + out.x = 0.25f*s; + out.y = (m.m[0][1] + m.m[1][0])/s; + out.z = (m.m[0][2] + m.m[2][0])/s; + out.w = (m.m[1][2] - m.m[2][1])/s; + break; + + case 1: + s = 2.0f*(float)sqrt(1.0f + m.m[1][1] - m.m[0][0] - m.m[2][2]); + out.x = (m.m[0][1] + m.m[1][0])/s; + out.y = 0.25f*s; + out.z = (m.m[1][2] + m.m[2][1])/s; + out.w = (m.m[2][0] - m.m[0][2])/s; + break; + + case 2: + s = 2.0f*(float)sqrt(1.0f + m.m[2][2] - m.m[0][0] - m.m[1][1]); + out.x = (m.m[0][2] + m.m[2][0])/s; + out.y = (m.m[1][2] + m.m[2][1])/s; + out.z = 0.25f*s; + out.w = (m.m[0][1] - m.m[1][0])/s; + break; + } + } + } +//+------------------------------------------------------------------+ +//| Builds a quaternion with the given yaw, pitch, and roll. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationYawPitchRoll(DXQuaternion &out,float yaw,float pitch,float roll) + { + float syaw = (float)sin(yaw/2.0f); + float cyaw = (float)cos(yaw/2.0f); + float spitch = (float)sin(pitch/2.0f); + float cpitch = (float)cos(pitch/2.0f); + float sroll = (float)sin(roll/2.0f); + float croll = (float)cos(roll/2.0f); +//--- + out.x = syaw*cpitch*sroll + cyaw*spitch*croll; + out.y = syaw*cpitch*croll - cyaw*spitch*sroll; + out.z = cyaw*cpitch*sroll - syaw*spitch*croll; + out.w = cyaw*cpitch*croll + syaw*spitch*sroll; + } +//+------------------------------------------------------------------+ +//| Interpolates between two quaternions, using spherical linear | +//| interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSlerp(DXQuaternion &out,DXQuaternion &q1,DXQuaternion &q2,float t) + { + float temp = 1.0f - t; + float dot = DXQuaternionDot(q1,q2); + if(dot<0.0f) + { + t = -t; + dot = -dot; + } + if(1.0f-dot>0.001f) + { + float theta = (float)acos(dot); + temp = (float)sin(theta*temp)/(float)sin(theta); + t = (float)sin(theta*t)/(float)sin(theta); + } + out.x = temp*q1.x + t*q2.x; + out.y = temp*q1.y + t*q2.y; + out.z = temp*q1.z + t*q2.z; + out.w = temp*q1.w + t*q2.w; + } +//+------------------------------------------------------------------+ +//| Interpolates between quaternions, using spherical quadrangle | +//| interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSquad(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,DXQuaternion &pq4,float t) + { + DXQuaternion temp1,temp2; + DXQuaternionSlerp(temp1,pq1,pq4,t); + DXQuaternionSlerp(temp2,pq2,pq3,t); + DXQuaternionSlerp(pout,temp1,temp2,2.0f*t*(1.0f - t)); + } +//+------------------------------------------------------------------+ +//| add_diff | +//+------------------------------------------------------------------+ +DXQuaternion add_diff(const DXQuaternion &q1,const DXQuaternion &q2,const float add) + { + DXQuaternion temp; + temp.x = q1.x + add * q2.x; + temp.y = q1.y + add * q2.y; + temp.z = q1.z + add * q2.z; + temp.w = q1.w + add * q2.w; +//--- + return(temp); + } +//+------------------------------------------------------------------+ +//| Sets up control points for spherical quadrangle interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSquadSetup(DXQuaternion &paout,DXQuaternion &pbout,DXQuaternion &pcout,DXQuaternion &pq0,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3) + { + DXQuaternion q,temp1,temp2,temp3,zero; + DXQuaternion aout,cout; + zero.x = 0.0f; + zero.y = 0.0f; + zero.z = 0.0f; + zero.w = 0.0f; +//--- + if(DXQuaternionDot(pq0,pq1) < 0.0f) + temp2 = add_diff(zero,pq0,-1.0f); + else + temp2 = pq0; +//--- + if(DXQuaternionDot(pq1,pq2) < 0.0f) + cout = add_diff(zero,pq2,-1.0f); + else + cout = pq2; +//--- + if(DXQuaternionDot(cout,pq3) < 0.0f) + temp3 = add_diff(zero,pq3,-1.0f); + else + temp3 = pq3; +//--- + DXQuaternionInverse(temp1,pq1); + DXQuaternionMultiply(temp2,temp1,temp2); + DXQuaternionLn(temp2,temp2); + DXQuaternionMultiply(q,temp1,cout); + DXQuaternionLn(q,q); + temp1 = add_diff(temp2,q,1.0f); + temp1.x *= -0.25f; + temp1.y *= -0.25f; + temp1.z *= -0.25f; + temp1.w *= -0.25f; + DXQuaternionExp(temp1,temp1); + DXQuaternionMultiply(aout,pq1,temp1); +//--- + DXQuaternionInverse(temp1,cout); + DXQuaternionMultiply(temp2,temp1,pq1); + DXQuaternionLn(temp2,temp2); + DXQuaternionMultiply(q,temp1,temp3); + DXQuaternionLn(q,q); + temp1 = add_diff(temp2,q,1.0f); + temp1.x *= -0.25f; + temp1.y *= -0.25f; + temp1.z *= -0.25f; + temp1.w *= -0.25f; + DXQuaternionExp(temp1,temp1); + DXQuaternionMultiply(pbout,cout,temp1); + paout = aout; + pcout = cout; + } +//+------------------------------------------------------------------+ +//| Computes a quaternion's axis and angle of rotation. | +//+------------------------------------------------------------------+ +void DXQuaternionToAxisAngle(const DXQuaternion &pq,DXVector3 &paxis,float &pangle) + { +//--- paxis + paxis.x = pq.x; + paxis.y = pq.y; + paxis.z = pq.z; +//--- pangle + pangle = 2.0f*(float)acos(pq.w); + } +//+------------------------------------------------------------------+ +//| DXMatrixIdentity creates an identity matrix | +//+------------------------------------------------------------------+ +void DXMatrixIdentity(DXMatrix &out) + { + for(int j=0; j<4; j++) + for(int i=0; i<4; i++) + { + if(i==j) + out.m[j,i]=1.0f; + else + out.m[j,i]=0.0f; + } + } +//+------------------------------------------------------------------+ +//| Determines if a matrix is an identity matrix. | +//+------------------------------------------------------------------+ +bool DXMatrixIsIdentity(DXMatrix &pm) + { + for(int j=0; j<4; j++) + for(int i=0; i<4; i++) + { + if(i==j) + { + if(fabs(pm.m[j,i]-1.0f)>1e-6f) + return(false); + } + else + if(fabs(pm.m[j,i])>1e-6f) + return(false); + } +//--- + return(true); + } +//+------------------------------------------------------------------+ +//| Builds a 3D affine transformation matrix. | +//+------------------------------------------------------------------+ +//| This function calculates the affine transformation matrix | +//| with the following formula, with matrix concatenation | +//| evaluated in left-to-right order: | +//| Mout = Ms * (Mrc)-1 * Mr * Mrc * Mt | +//| where: | +//| Mout = output matrix (pOut) | +//| Ms = scaling matrix (Scaling) | +//| Mrc = center of rotation matrix (pRotationCenter) | +//| Mr = rotation matrix (pRotation) | +//| Mt = translation matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixAffineTransformation(DXMatrix &out,float scaling,const DXVector3 &rotationcenter,const DXQuaternion &rotation,const DXVector3 &translation) + { + DXMatrixIdentity(out); +//--- rotation + float temp00 = 1.0f - 2.0f*(rotation.y*rotation.y + rotation.z*rotation.z); + float temp01 = 2.0f * (rotation.x*rotation.y + rotation.z*rotation.w); + float temp02 = 2.0f * (rotation.x*rotation.z - rotation.y*rotation.w); + float temp10 = 2.0f * (rotation.x*rotation.y - rotation.z*rotation.w); + float temp11 = 1.0f - 2.0f*(rotation.x*rotation.x + rotation.z*rotation.z); + float temp12 = 2.0f * (rotation.y*rotation.z + rotation.x*rotation.w); + float temp20 = 2.0f * (rotation.x*rotation.z + rotation.y*rotation.w); + float temp21 = 2.0f * (rotation.y*rotation.z - rotation.x*rotation.w); + float temp22 = 1.0f - 2.0f*(rotation.x*rotation.x + rotation.y*rotation.y); +//--- scaling + out.m[0][0] = scaling*temp00; + out.m[0][1] = scaling*temp01; + out.m[0][2] = scaling*temp02; + out.m[1][0] = scaling*temp10; + out.m[1][1] = scaling*temp11; + out.m[1][2] = scaling*temp12; + out.m[2][0] = scaling*temp20; + out.m[2][1] = scaling*temp21; + out.m[2][2] = scaling*temp22; +//--- rotationcenter + out.m[3][0] = rotationcenter.x*(1.0f - temp00) - rotationcenter.y*temp10 - rotationcenter.z*temp20; + out.m[3][1] = rotationcenter.y*(1.0f - temp11) - rotationcenter.x*temp01 - rotationcenter.z*temp21; + out.m[3][2] = rotationcenter.z*(1.0f - temp22) - rotationcenter.x*temp02 - rotationcenter.y*temp12; +//--- translation + out.m[3][0] += translation.x; + out.m[3][1] += translation.y; + out.m[3][2] += translation.z; + } +//+------------------------------------------------------------------+ +//| Builds a 2D affine transformation matrix in the xy plane. | +//+------------------------------------------------------------------+ +//| This function calculates the affine transformation matrix | +//| with the following formula, with matrix concatenation evaluated | +//| in left-to-right order: | +//| Mout = Ms * (Mrc)^(-1) * Mr * Mrc * Mt | +//| where: | +//| Mout = output matrix (pOut) | +//| Ms = scaling matrix (Scaling) | +//| Mrc = center of rotation matrix (pRotationCenter) | +//| Mr = rotation matrix (Rotation) | +//| Mt = translation matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixAffineTransformation2D(DXMatrix &out,float scaling,const DXVector2 &rotationcenter,float rotation,const DXVector2 &translation) + { + float s = (float)sin(rotation/2.0f); + float tmp1 = 1.0f - 2.0f*s*s; + float tmp2 = 2.0f*s*(float)cos(rotation/2.0f); +//--- + DXMatrixIdentity(out); + out.m[0][0] = scaling*tmp1; + out.m[0][1] = scaling*tmp2; + out.m[1][0] = -scaling*tmp2; + out.m[1][1] = scaling*tmp1; +//--- rotationcenter + float x = rotationcenter.x; + float y = rotationcenter.y; + out.m[3][0] = y*tmp2 - x*tmp1 + x; + out.m[3][1] = -x*tmp2 - y*tmp1 + y; +//--- translation + out.m[3][0] += translation.x; + out.m[3][1] += translation.y; + } +#define D3DERR_INVALIDCALL -2005530516 +//#define S_OK 0; +//+------------------------------------------------------------------+ +//| Breaks down a general 3D transformation matrix into its scalar, | +//| rotational, and translational components. | +//+------------------------------------------------------------------+ +int DXMatrixDecompose(DXVector3 &poutscale,DXQuaternion &poutrotation,DXVector3 &pouttranslation,const DXMatrix &pm) + { + DXMatrix normalized; + DXVector3 vec; +//--- Compute the scaling part + vec.x=pm.m[0][0]; + vec.y=pm.m[0][1]; + vec.z=pm.m[0][2]; + poutscale.x=DXVec3Length(vec); + vec.x=pm.m[1][0]; + vec.y=pm.m[1][1]; + vec.z=pm.m[1][2]; + poutscale.y=DXVec3Length(vec); + vec.x=pm.m[2][0]; + vec.y=pm.m[2][1]; + vec.z=pm.m[2][2]; + poutscale.z=DXVec3Length(vec); +//--- compute the translation part + pouttranslation.x=pm.m[3][0]; + pouttranslation.y=pm.m[3][1]; + pouttranslation.z=pm.m[3][2]; +//--- let's calculate the rotation now + if((poutscale.x == 0.0f) || (poutscale.y == 0.0f) || (poutscale.z == 0.0f)) + return(D3DERR_INVALIDCALL); +//--- + normalized.m[0][0]=pm.m[0][0]/poutscale.x; + normalized.m[0][1]=pm.m[0][1]/poutscale.x; + normalized.m[0][2]=pm.m[0][2]/poutscale.x; + normalized.m[1][0]=pm.m[1][0]/poutscale.y; + normalized.m[1][1]=pm.m[1][1]/poutscale.y; + normalized.m[1][2]=pm.m[1][2]/poutscale.y; + normalized.m[2][0]=pm.m[2][0]/poutscale.z; + normalized.m[2][1]=pm.m[2][1]/poutscale.z; + normalized.m[2][2]=pm.m[2][2]/poutscale.z; + DXQuaternionRotationMatrix(poutrotation,normalized); +//--- + return(0); + } +//+------------------------------------------------------------------+ +//| Returns the determinant of a matrix. | +//+------------------------------------------------------------------+ +float DXMatrixDeterminant(const DXMatrix &pm) + { + float t[3],v[4]; + t[0] = pm.m[2][2]*pm.m[3][3] - pm.m[2][3]*pm.m[3][2]; + t[1] = pm.m[1][2]*pm.m[3][3] - pm.m[1][3]*pm.m[3][2]; + t[2] = pm.m[1][2]*pm.m[2][3] - pm.m[1][3]*pm.m[2][2]; + v[0] = pm.m[1][1]*t[0] - pm.m[2][1] * t[1] + pm.m[3][1]*t[2]; + v[1] = -pm.m[1][0]*t[0] + pm.m[2][0] * t[1] - pm.m[3][0]*t[2]; +//--- + t[0] = pm.m[1][0]*pm.m[2][1] - pm.m[2][0]*pm.m[1][1]; + t[1] = pm.m[1][0]*pm.m[3][1] - pm.m[3][0]*pm.m[1][1]; + t[2] = pm.m[2][0]*pm.m[3][1] - pm.m[3][0]*pm.m[2][1]; + v[2] = pm.m[3][3]*t[0] - pm.m[2][3]*t[1] + pm.m[1][3]*t[2]; + v[3] = -pm.m[3][2]*t[0] + pm.m[2][2]*t[1] - pm.m[1][2]*t[2]; +//--- + return(pm.m[0][0]*v[0] + pm.m[0][1]*v[1] + pm.m[0][2]*v[2] + pm.m[0][3]*v[3]); + } +//+------------------------------------------------------------------+ +//| Calculates the inverse of a matrix. | +//+------------------------------------------------------------------+ +void DXMatrixInverse(DXMatrix &pout,float &pdeterminant,const DXMatrix &pm) + { + float t[3],v[16]; + t[0] = pm.m[2][2]*pm.m[3][3] - pm.m[2][3]*pm.m[3][2]; + t[1] = pm.m[1][2]*pm.m[3][3] - pm.m[1][3]*pm.m[3][2]; + t[2] = pm.m[1][2]*pm.m[2][3] - pm.m[1][3]*pm.m[2][2]; + v[0] = pm.m[1][1]*t[0] - pm.m[2][1]*t[1] + pm.m[3][1]*t[2]; + v[4] = -pm.m[1][0]*t[0] + pm.m[2][0]*t[1] - pm.m[3][0]*t[2]; +//--- + t[0] = pm.m[1][0]*pm.m[2][1] - pm.m[2][0]*pm.m[1][1]; + t[1] = pm.m[1][0]*pm.m[3][1] - pm.m[3][0]*pm.m[1][1]; + t[2] = pm.m[2][0]*pm.m[3][1] - pm.m[3][0]*pm.m[2][1]; + v[8] = pm.m[3][3]*t[0] - pm.m[2][3]*t[1] + pm.m[1][3]*t[2]; + v[12] = -pm.m[3][2]*t[0] + pm.m[2][2]*t[1] - pm.m[1][2]*t[2]; +//--- + float det = pm.m[0][0]*v[0] + pm.m[0][1]*v[4] + pm.m[0][2]*v[8] + pm.m[0][3]*v[12]; + if(det == 0.0f) + { + for(int j=0; j<4; j++) + for(int i=0; i<4; i++) + { + pout.m[j,i]=0.0; + } + //--- + return; + } + if(pdeterminant) + pdeterminant = det; +//--- + t[0] = pm.m[2][2]*pm.m[3][3] - pm.m[2][3]*pm.m[3][2]; + t[1] = pm.m[0][2]*pm.m[3][3] - pm.m[0][3]*pm.m[3][2]; + t[2] = pm.m[0][2]*pm.m[2][3] - pm.m[0][3]*pm.m[2][2]; + v[1] = -pm.m[0][1]*t[0] + pm.m[2][1]*t[1] - pm.m[3][1]*t[2]; + v[5] = pm.m[0][0]*t[0] - pm.m[2][0]*t[1] + pm.m[3][0]*t[2]; +//--- + t[0] = pm.m[0][0]*pm.m[2][1] - pm.m[2][0] * pm.m[0][1]; + t[1] = pm.m[3][0]*pm.m[0][1] - pm.m[0][0] * pm.m[3][1]; + t[2] = pm.m[2][0]*pm.m[3][1] - pm.m[3][0] * pm.m[2][1]; + v[9] = -pm.m[3][3]*t[0] - pm.m[2][3]*t[1]- pm.m[0][3]*t[2]; + v[13] = pm.m[3][2]*t[0] + pm.m[2][2]*t[1] + pm.m[0][2]*t[2]; +//--- + t[0] = pm.m[1][2]*pm.m[3][3] - pm.m[1][3] * pm.m[3][2]; + t[1] = pm.m[0][2]*pm.m[3][3] - pm.m[0][3] * pm.m[3][2]; + t[2] = pm.m[0][2]*pm.m[1][3] - pm.m[0][3] * pm.m[1][2]; + v[2] = pm.m[0][1]*t[0] - pm.m[1][1]*t[1] + pm.m[3][1]*t[2]; + v[6] = -pm.m[0][0]*t[0] + pm.m[1][0]*t[1] - pm.m[3][0]*t[2]; +//--- + t[0] = pm.m[0][0]*pm.m[1][1] - pm.m[1][0] * pm.m[0][1]; + t[1] = pm.m[3][0]*pm.m[0][1] - pm.m[0][0] * pm.m[3][1]; + t[2] = pm.m[1][0]*pm.m[3][1] - pm.m[3][0] * pm.m[1][1]; + v[10] = pm.m[3][3]*t[0] + pm.m[1][3]*t[1] + pm.m[0][3]*t[2]; + v[14] = -pm.m[3][2]*t[0] - pm.m[1][2]*t[1] - pm.m[0][2]*t[2]; +//--- + t[0] = pm.m[1][2]*pm.m[2][3] - pm.m[1][3] * pm.m[2][2]; + t[1] = pm.m[0][2]*pm.m[2][3] - pm.m[0][3] * pm.m[2][2]; + t[2] = pm.m[0][2]*pm.m[1][3] - pm.m[0][3] * pm.m[1][2]; + v[3] = -pm.m[0][1]*t[0] + pm.m[1][1]*t[1] - pm.m[2][1]*t[2]; + v[7] = pm.m[0][0]*t[0] - pm.m[1][0]*t[1] + pm.m[2][0]*t[2]; +//--- + v[11] = -pm.m[0][0]*(pm.m[1][1]*pm.m[2][3] - pm.m[1][3]*pm.m[2][1]) + + pm.m[1][0]*(pm.m[0][1]*pm.m[2][3] - pm.m[0][3]*pm.m[2][1]) - + pm.m[2][0]*(pm.m[0][1]*pm.m[1][3] - pm.m[0][3]*pm.m[1][1]); +//--- + v[15] = pm.m[0][0]*(pm.m[1][1]*pm.m[2][2] - pm.m[1][2]*pm.m[2][1]) - + pm.m[1][0]*(pm.m[0][1]*pm.m[2][2] - pm.m[0][2]*pm.m[2][1]) + + pm.m[2][0]*(pm.m[0][1]*pm.m[1][2] - pm.m[0][2]*pm.m[1][1]); +//--- + det = 1.0f/det; + for(int i=0; i<4; i++) + for(int j=0; j<4; j++) + pout.m[i][j] = v[4*i + j]*det; + } +//+------------------------------------------------------------------+ +//| Builds a left-handed,look-at matrix. | +//| This function uses the following formula to compute | +//| the returned matrix. | +//| | +//| zaxis = normal(At - Eye) | +//| xaxis = normal(cross(Up,zaxis)) | +//| yaxis = cross(zaxis,xaxis) | +//| | +//| xaxis.x yaxis.x zaxis.x 0 | +//| xaxis.y yaxis.y zaxis.y 0 | +//| xaxis.z yaxis.z zaxis.z 0 | +//| -dot(xaxis,eye) -dot(yaxis,eye) -dot(zaxis,eye) 1 | +//+------------------------------------------------------------------+ +void DXMatrixLookAtLH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up) + { + DXVector3 right,upn,vec; + DXVec3Subtract(vec,at,eye); + DXVec3Normalize(vec,vec); + DXVec3Cross(right,up,vec); + DXVec3Cross(upn,vec,right); + DXVec3Normalize(right,right); + DXVec3Normalize(upn,upn); +//--- + out.m[0][0] = right.x; + out.m[1][0] = right.y; + out.m[2][0] = right.z; + out.m[3][0] = -DXVec3Dot(right,eye); + out.m[0][1] = upn.x; + out.m[1][1] = upn.y; + out.m[2][1] = upn.z; + out.m[3][1] = -DXVec3Dot(upn,eye); + out.m[0][2] = vec.x; + out.m[1][2] = vec.y; + out.m[2][2] = vec.z; + out.m[3][2] = -DXVec3Dot(vec,eye); + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; + } +//+------------------------------------------------------------------+ +//| Builds a right-handed, look-at matrix. | +//+------------------------------------------------------------------+ +//| This function uses the following formula to compute | +//| the returned matrix. | +//| | +//| zaxis = normal(Eye - At) | +//| xaxis = normal(cross(Up,zaxis)) | +//| yaxis = cross(zaxis,xaxis) | +//| | +//| xaxis.x yaxis.x zaxis.x 0 | +//| xaxis.y yaxis.y zaxis.y 0 | +//| xaxis.z yaxis.z zaxis.z 0 | +//| dot(xaxis,eye) dot(yaxis,eye) dot(zaxis,eye) 1 | +//+------------------------------------------------------------------+ +void DXMatrixLookAtRH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up) + { + DXVector3 right,upn,vec; + DXVec3Subtract(vec,at,eye); + DXVec3Normalize(vec,vec); + DXVec3Cross(right,up,vec); + DXVec3Cross(upn,vec,right); + DXVec3Normalize(right,right); + DXVec3Normalize(upn,upn); +//--- + out.m[0][0] = -right.x; + out.m[1][0] = -right.y; + out.m[2][0] = -right.z; + out.m[3][0] = DXVec3Dot(right,eye); + out.m[0][1] = upn.x; + out.m[1][1] = upn.y; + out.m[2][1] = upn.z; + out.m[3][1] = -DXVec3Dot(upn,eye); + out.m[0][2] = -vec.x; + out.m[1][2] = -vec.y; + out.m[2][2] = -vec.z; + out.m[3][2] = DXVec3Dot(vec,eye); + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; + } +//+------------------------------------------------------------------+ +//| Determines the product of two matrices. | +//+------------------------------------------------------------------+ +void DXMatrixMultiply(DXMatrix &pout,const DXMatrix &pm1,const DXMatrix &pm2) + { + DXMatrix out= {}; + for(int i=0; i<4; i++) + { + for(int j=0; j<4; j++) + { + out.m[i][j] = pm1.m[i][0]*pm2.m[0][j] + pm1.m[i][1]*pm2.m[1][j] + pm1.m[i][2]*pm2.m[2][j] + pm1.m[i][3]*pm2.m[3][j]; + } + } + pout = out; + } +//+------------------------------------------------------------------+ +//| Calculates the transposed product of two matrices. | +//+------------------------------------------------------------------+ +void DXMatrixMultiplyTranspose(DXMatrix &pout,const DXMatrix &pm1,const DXMatrix &pm2) + { + DXMatrix temp= {}; + for(int i = 0; i < 4; i++) + for(int j = 0; j < 4; j++) + temp.m[j][i] = pm1.m[i][0]*pm2.m[0][j] + pm1.m[i][1]*pm2.m[1][j] + pm1.m[i][2]*pm2.m[2][j] + pm1.m[i][3]*pm2.m[3][j]; + pout = temp; + } +//+------------------------------------------------------------------+ +//| Builds a left-handed orthographic projection matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoLH(DXMatrix &pout,float w,float h,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f/w; + pout.m[1][1] = 2.0f/h; + pout.m[2][2] = 1.0f/(zf - zn); + pout.m[3][2] = zn/(zn - zf); + } +//+------------------------------------------------------------------+ +//| Builds a customized,left-handed orthographic projection matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f/(r - l); + pout.m[1][1] = 2.0f/(t - b); + pout.m[2][2] = 1.0f/(zf -zn); + pout.m[3][0] = -1.0f -2.0f*l/(r - l); + pout.m[3][1] = 1.0f + 2.0f*t/(b - t); + pout.m[3][2] = zn/(zn -zf); + } +//+------------------------------------------------------------------+ +//| Builds a customized,right-handed orthographic projection matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f/(r - l); + pout.m[1][1] = 2.0f/(t - b); + pout.m[2][2] = 1.0f/(zn -zf); + pout.m[3][0] = -1.0f -2.0f*l/(r - l); + pout.m[3][1] = 1.0f + 2.0f*t/(b - t); + pout.m[3][2] = zn/(zn -zf); + } +//+------------------------------------------------------------------+ +//| Builds a right-handed orthographic projection matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixOrthoRH function | +//| are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned matrix: | +//| 2/w 0 0 0 | +//| 0 2/h 0 0 | +//| 0 0 1/(zn-zf) 0 | +//| 0 0 zn/(zn-zf) 1 | +//+------------------------------------------------------------------+ +void DXMatrixOrthoRH(DXMatrix &pout,float w,float h,float zn,float zf) + { + DXMatrixIdentity(pout); + pout.m[0][0] = 2.0f/w; + pout.m[1][1] = 2.0f/h; + pout.m[2][2] = 1.0f/(zn - zf); + pout.m[3][2] = zn/(zn - zf); + } +//+------------------------------------------------------------------+ +//| Builds a left-handed perspective projection matrix | +//| based on a field of view. | +//+------------------------------------------------------------------+ +//| This function computes the returned matrix as shown: | +//| xScale 0 0 0 | +//| 0 yScale 0 0 | +//| 0 0 zf/(zf-zn) 1 | +//| 0 0 -zn*zf/(zf-zn) 0 | +//| where: | +//| yScale = cot(fovY/2) | +//| xScale = yScale / aspect ratio | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveFovLH(DXMatrix &pout,float fovy,float aspect,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 1.0f/(aspect*(float)tan(fovy/2.0f)); + pout.m[1][1] = 1.0f/(float)tan(fovy/2.0f); + pout.m[2][2] = zf/(zf - zn); + pout.m[2][3] = 1.0f; + pout.m[3][2] = (zf*zn)/(zn - zf); + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a right-handed perspective projection matrix | +//| based on a field of view. | +//+------------------------------------------------------------------+ +//| This function computes the returned matrix as shown. | +//| xScale 0 0 0 | +//| 0 yScale 0 0 | +//| 0 0 zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//| where: | +//| yScale = cot(fovY/2) | +//| xScale = yScale / aspect ratio | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveFovRH(DXMatrix &pout,float fovy,float aspect,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 1.0f/(aspect*(float)tan(fovy/2.0f)); + pout.m[1][1] = 1.0f/(float)tan(fovy/2.0f); + pout.m[2][2] = zf/(zn - zf); + pout.m[2][3] = -1.0f; + pout.m[3][2] = (zf*zn)/(zn - zf); + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a left-handed perspective projection matrix | +//+------------------------------------------------------------------+ +//| This function uses the following formula to compute | +//| the returned matrix. | +//| 2*zn/w 0 0 0 | +//| 0 2*zn/h 0 0 | +//| 0 0 zf/(zf-zn) 1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveLH(DXMatrix &pout,float w,float h,float zn,float zf) + { + DXMatrixIdentity(pout); + pout.m[0][0] = 2.0f*zn/w; + pout.m[1][1] = 2.0f*zn/h; + pout.m[2][2] = zf/(zf - zn); + pout.m[3][2] = (zn*zf)/(zn - zf); + pout.m[2][3] = 1.0f; + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a customized, left-handed perspective projection matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveOffCenterLH | +//| function are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned matrix. | +//| 2*zn/(r-l) 0 0 0 | +//| 0 2*zn/(t-b) 0 0 | +//| (l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f*zn/(r - l); + pout.m[1][1] = -2.0f*zn/(b - t); + pout.m[2][0] = -1.0f - 2.0f*l/(r - l); + pout.m[2][1] = 1.0f + 2.0f*t/(b - t); + pout.m[2][2] = - zf/(zn - zf); + pout.m[3][2] = (zn*zf)/(zn -zf); + pout.m[2][3] = 1.0f; + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a customized, right-handed perspective projection matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveOffCenterRH | +//| function are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned matrix. | +//| 2*zn/(r-l) 0 0 0 | +//| 0 2*zn/(t-b) 0 0 | +//| (l+r)/(r-l) (t+b)/(t-b) zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f*zn/(r - l); + pout.m[1][1] = -2.0f*zn/(b - t); + pout.m[2][0] = 1.0f + 2.0f*l/(r - l); + pout.m[2][1] = -1.0f -2.0f*t/(b - t); + pout.m[2][2] = zf/(zn - zf); + pout.m[3][2] = (zn*zf)/(zn -zf); + pout.m[2][3] = -1.0f; + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a right-handed perspective projection matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveRH function | +//| are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned matrix. | +//| 2*zn/w 0 0 0 | +//| 0 2*zn/h 0 0 | +//| 0 0 zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveRH(DXMatrix &pout,float w,float h,float zn,float zf) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 2.0f*zn/w; + pout.m[1][1] = 2.0f*zn/h; + pout.m[2][2] = zf/(zn - zf); + pout.m[3][2] = (zn*zf)/(zn - zf); + pout.m[2][3] = -1.0f; + pout.m[3][3] = 0.0f; + } +//+------------------------------------------------------------------+ +//| Builds a matrix that reflects the coordinate system about a plane| +//| This function normalizes the plane equation before it creates | +//| the reflected matrix. | +//| | +//| This function uses the following formula to compute | +//| the returned matrix. | +//| P = normalize(Plane); | +//| -2 * P.a * P.a + 1 -2 * P.b * P.a -2 * P.c * P.a 0 | +//| -2 * P.a * P.b -2 * P.b * P.b + 1 -2 * P.c * P.b 0 | +//| -2 * P.a * P.c -2 * P.b * P.c -2 * P.c * P.c + 1 0 | +//| -2 * P.a * P.d -2 * P.b * P.d -2 * P.c * P.d 1 | +//+------------------------------------------------------------------+ +void DXMatrixReflect(DXMatrix &pout,const DXPlane &pplane) + { + DXPlane Nplane; + DXPlaneNormalize(Nplane,pplane); + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 1.0f - 2.0f*Nplane.a*Nplane.a; + pout.m[0][1] = -2.0f*Nplane.a*Nplane.b; + pout.m[0][2] = -2.0f*Nplane.a*Nplane.c; + pout.m[1][0] = -2.0f*Nplane.a*Nplane.b; + pout.m[1][1] = 1.0f - 2.0f*Nplane.b*Nplane.b; + pout.m[1][2] = -2.0f*Nplane.b*Nplane.c; + pout.m[2][0] = -2.0f*Nplane.c*Nplane.a; + pout.m[2][1] = -2.0f*Nplane.c*Nplane.b; + pout.m[2][2] = 1.0f - 2.0f*Nplane.c*Nplane.c; + pout.m[3][0] = -2.0f*Nplane.d*Nplane.a; + pout.m[3][1] = -2.0f*Nplane.d*Nplane.b; + pout.m[3][2] = -2.0f*Nplane.d*Nplane.c; + } +//+------------------------------------------------------------------+ +//| Builds a matrix that rotates around an arbitrary axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationAxis(DXMatrix &out,const DXVector3 &v,float angle) + { + DXVector3 nv; + DXVec3Normalize(nv,v); +//--- + float sangle = (float)sin(angle); + float cangle = (float)cos(angle); + float cdiff = 1.0f - cangle; +//--- + out.m[0][0] = cdiff*nv.x*nv.x + cangle; + out.m[1][0] = cdiff*nv.x*nv.y - sangle*nv.z; + out.m[2][0] = cdiff*nv.x*nv.z + sangle*nv.y; + out.m[3][0] = 0.0f; + out.m[0][1] = cdiff*nv.y*nv.x + sangle*nv.z; + out.m[1][1] = cdiff*nv.y*nv.y + cangle; + out.m[2][1] = cdiff*nv.y*nv.z - sangle*nv.x; + out.m[3][1] = 0.0f; + out.m[0][2] = cdiff*nv.z*nv.x - sangle*nv.y; + out.m[1][2] = cdiff*nv.z*nv.y + sangle*nv.x; + out.m[2][2] = cdiff*nv.z*nv.z + cangle; + out.m[3][2] = 0.0f; + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; + } +//+------------------------------------------------------------------+ +//| Builds a rotation matrix from a quaternion. | +//+------------------------------------------------------------------+ +void DXMatrixRotationQuaternion(DXMatrix &pout,const DXQuaternion &pq) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = 1.0f - 2.0f*(pq.y*pq.y + pq.z*pq.z); + pout.m[0][1] = 2.0f*(pq.x*pq.y + pq.z*pq.w); + pout.m[0][2] = 2.0f*(pq.x*pq.z - pq.y*pq.w); + pout.m[1][0] = 2.0f*(pq.x*pq.y - pq.z*pq.w); + pout.m[1][1] = 1.0f - 2.0f * (pq.x*pq.x + pq.z*pq.z); + pout.m[1][2] = 2.0f*(pq.y*pq.z + pq.x*pq.w); + pout.m[2][0] = 2.0f*(pq.x*pq.z + pq.y*pq.w); + pout.m[2][1] = 2.0f*(pq.y*pq.z - pq.x*pq.w); + pout.m[2][2] = 1.0f - 2.0f*(pq.x*pq.x + pq.y*pq.y); + } +//+------------------------------------------------------------------+ +//| Builds a matrix that rotates around the x-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationX(DXMatrix &pout,float angle) + { + DXMatrixIdentity(pout); +//--- + pout.m[1][1] = (float)cos(angle); + pout.m[2][2] = (float)cos(angle); + pout.m[1][2] = (float)sin(angle); + pout.m[2][1] = -(float)sin(angle); + } +//+------------------------------------------------------------------+ +//| Builds a matrix that rotates around the y-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationY(DXMatrix &pout,float angle) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = (float)cos(angle); + pout.m[2][2] = (float)cos(angle); + pout.m[0][2] = -(float)sin(angle); + pout.m[2][0] = (float)sin(angle); + } +//+------------------------------------------------------------------+ +//| Builds a matrix with a specified yaw, pitch, and roll. | +//+------------------------------------------------------------------+ +//| The order of transformations is roll first, then pitch, then yaw.| +//| Relative to the object's local coordinate axis, this is | +//| equivalent to rotation around the z-axis, followed by rotation | +//| around the x-axis, followed by rotation around the y-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationYawPitchRoll(DXMatrix &out,float yaw,float pitch,float roll) + { + float sroll = (float)sin(roll); + float croll = (float)cos(roll); + float spitch = (float)sin(pitch); + float cpitch = (float)cos(pitch); + float syaw = (float)sin(yaw); + float cyaw = (float)cos(yaw); +//--- + out.m[0][0] = sroll * spitch * syaw + croll * cyaw; + out.m[0][1] = sroll * cpitch; + out.m[0][2] = sroll * spitch * cyaw - croll * syaw; + out.m[0][3] = 0.0f; + out.m[1][0] = croll * spitch * syaw - sroll * cyaw; + out.m[1][1] = croll * cpitch; + out.m[1][2] = croll * spitch * cyaw + sroll * syaw; + out.m[1][3] = 0.0f; + out.m[2][0] = cpitch * syaw; + out.m[2][1] = -spitch; + out.m[2][2] = cpitch * cyaw; + out.m[2][3] = 0.0f; + out.m[3][0] = 0.0f; + out.m[3][1] = 0.0f; + out.m[3][2] = 0.0f; + out.m[3][3] = 1.0f; + } +//+------------------------------------------------------------------+ +//| Builds a matrix that rotates around the z-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationZ(DXMatrix &pout,float angle) + { + DXMatrixIdentity(pout); +//--- + pout.m[0][0] = (float)cos(angle); + pout.m[1][1] = (float)cos(angle); + pout.m[0][1] = (float)sin(angle); + pout.m[1][0] = -(float)sin(angle); + } +//+------------------------------------------------------------------+ +//| Builds a matrix that scales along the x-axis, | +//| the y-axis,and the z-axis. | +//+------------------------------------------------------------------+ +void DXMatrixScaling(DXMatrix &pout,float sx,float sy,float sz) + { + DXMatrixIdentity(pout); + pout.m[0][0] = sx; + pout.m[1][1] = sy; + pout.m[2][2] = sz; + } +//+------------------------------------------------------------------+ +//| Builds a matrix that flattens geometry into a plane. | +//+------------------------------------------------------------------+ +//| The DXMatrixShadow function flattens geometry into a plane, as | +//| if casting a shadow from a light. | +//| This function uses the following formula to compute the returned | +//| matrix. | +//| | +//| P = normalize(Plane); | +//| L = Light; | +//| d = -dot(P,L) | +//| | +//| P.a * L.x + d P.a * L.y P.a * L.z P.a * L.w | +//| P.b * L.x P.b * L.y + d P.b * L.z P.b * L.w | +//| P.c * L.x P.c * L.y P.c * L.z + d P.c * L.w | +//| P.d * L.x P.d * L.y P.d * L.z P.d * L.w + d | +//| | +//| If the light's w-component is 0, the ray from the origin to the | +//| light represents a directional light. If it is 1,the light is | +//| a point light. | +//+------------------------------------------------------------------+ +void DXMatrixShadow(DXMatrix &pout,const DXVector4 &plight,const DXPlane &pplane) + { + DXPlane Nplane; + DXPlaneNormalize(Nplane,pplane); + float dot = DXPlaneDot(Nplane,plight); +//--- + pout.m[0][0] = dot - Nplane.a*plight.x; + pout.m[0][1] = -Nplane.a*plight.y; + pout.m[0][2] = -Nplane.a*plight.z; + pout.m[0][3] = -Nplane.a*plight.w; + pout.m[1][0] = -Nplane.b*plight.x; + pout.m[1][1] = dot - Nplane.b*plight.y; + pout.m[1][2] = -Nplane.b*plight.z; + pout.m[1][3] = -Nplane.b*plight.w; + pout.m[2][0] = -Nplane.c*plight.x; + pout.m[2][1] = -Nplane.c*plight.y; + pout.m[2][2] = dot - Nplane.c*plight.z; + pout.m[2][3] = -Nplane.c*plight.w; + pout.m[3][0] = -Nplane.d*plight.x; + pout.m[3][1] = -Nplane.d*plight.y; + pout.m[3][2] = -Nplane.d*plight.z; + pout.m[3][3] = dot - Nplane.d*plight.w; + } +//+------------------------------------------------------------------+ +//| Builds a transformation matrix. | +//+------------------------------------------------------------------+ +//| This function calculates the transformation matrix with the | +//| following formula, with matrix concatenation evaluated | +//| in left-to-right order: | +//| | +//| Mout = (Msc)^(-1)*(Msr)^(-1)*Ms*Msr*Msc*(Mrc)^(-1)*Mr*Mrc*Mt | +//| | +//| where: | +//| Mout = output matrix (pOut) | +//| Msc = scaling center matrix (pScalingCenter) | +//| Msr = scaling rotation matrix (pScalingRotation) | +//| Ms = scaling matrix (pScaling) | +//| Mrc = center of rotation matrix (pRotationCenter) | +//| Mr = rotation matrix (pRotation) | +//| Mt = translation matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixTransformation(DXMatrix &pout,const DXVector3 &pscalingcenter,const DXQuaternion &pscalingrotation,const DXVector3 &pscaling,const DXVector3 &protationcenter,const DXQuaternion &protation,const DXVector3 &ptranslation) + { + DXMatrix m1,m2,m3,m4,m5,m6,m7; + DXQuaternion prc; + DXVector3 psc,pt; +//--- pscalingcenter + psc.x = pscalingcenter.x; + psc.y = pscalingcenter.y; + psc.z = pscalingcenter.z; +//--- protationcenter + prc.x = protationcenter.x; + prc.y = protationcenter.y; + prc.z = protationcenter.z; +//--- ptranslation + pt.x = ptranslation.x; + pt.y = ptranslation.y; + pt.z = ptranslation.z; + DXMatrixTranslation(m1,-psc.x,-psc.y,-psc.z); +//--- + DXQuaternion temp; + DXMatrixRotationQuaternion(m4,pscalingrotation); + temp.w = pscalingrotation.w; + temp.x = -pscalingrotation.x; + temp.y = -pscalingrotation.y; + temp.z = -pscalingrotation.z; + DXMatrixRotationQuaternion(m2,temp); +//--- pscaling + DXMatrixScaling(m3,pscaling.x,pscaling.y,pscaling.z); +//--- protation + DXMatrixRotationQuaternion(m6,protation); +//--- + DXMatrixTranslation(m5,psc.x - prc.x,psc.y - prc.y,psc.z - prc.z); + DXMatrixTranslation(m7,prc.x + pt.x,prc.y + pt.y,prc.z + pt.z); + DXMatrixMultiply(m1,m1,m2); + DXMatrixMultiply(m1,m1,m3); + DXMatrixMultiply(m1,m1,m4); + DXMatrixMultiply(m1,m1,m5); + DXMatrixMultiply(m1,m1,m6); + DXMatrixMultiply(pout,m1,m7); + } +//+------------------------------------------------------------------+ +//| Builds a 2D transformation matrix that represents | +//| transformations in the xy plane. | +//+------------------------------------------------------------------+ +//| This function calculates the transformation matrix with the | +//| following formula, with matrix concatenation evaluated | +//| in left-to-right order: | +//| | +//| Mout = (Msc)^(-1)*(Msr)^(-1)*Ms*Msr*Msc*(Mrc)^(-1)*Mr*Mrc*Mt | +//| | +//| where: | +//| Mout = output matrix (pOut) | +//| Msc = scaling center matrix (pScalingCenter) | +//| Msr = scaling rotation matrix (pScalingRotation) | +//| Ms = scaling matrix (pScaling) | +//| Mrc = center of rotation matrix (pRotationCenter) | +//| Mr = rotation matrix (Rotation) | +//| Mt = translation matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixTransformation2D(DXMatrix &pout,const DXVector2 &pscalingcenter,float scalingrotation,const DXVector2 &pscaling,const DXVector2 &protationcenter,float rotation,const DXVector2 &ptranslation) + { + DXQuaternion rot,sca_rot; + DXVector3 rot_center,sca,sca_center,trans; +//--- pscalingcenter + sca_center.x=pscalingcenter.x; + sca_center.y=pscalingcenter.y; + sca_center.z=0.0f; +//--- pscaling + sca.x=pscaling.x; + sca.y=pscaling.y; + sca.z=1.0f; +//--- protationcenter + rot_center.x=protationcenter.x; + rot_center.y=protationcenter.y; + rot_center.z=0.0f; +//--- ptranslation + trans.x=ptranslation.x; + trans.y=ptranslation.y; + trans.z=0.0f; +//--- + rot.w=(float)cos(rotation/2.0f); + rot.x=0.0f; + rot.y=0.0f; + rot.z=(float)sin(rotation/2.0f); +//--- + sca_rot.w=(float)cos(scalingrotation/2.0f); + sca_rot.x=0.0f; + sca_rot.y=0.0f; + sca_rot.z=(float)sin(scalingrotation/2.0f); + DXMatrixTransformation(pout,sca_center,sca_rot,sca,rot_center,rot,trans); + } +//+------------------------------------------------------------------+ +//| Builds a matrix using the specified offsets. | +//+------------------------------------------------------------------+ +void DXMatrixTranslation(DXMatrix &pout,float x,float y,float z) + { + DXMatrixIdentity(pout); +//--- + pout.m[3][0] = x; + pout.m[3][1] = y; + pout.m[3][2] = z; + } +//+------------------------------------------------------------------+ +//| Returns the matrix transpose of a matrix. | +//+------------------------------------------------------------------+ +void DXMatrixTranspose(DXMatrix &pout,const DXMatrix &pm) + { + const DXMatrix m = pm; + for(int i=0; i<4; i++) + for(int j=0; j<4; j++) + pout.m[i][j] = m.m[j][i]; + } +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 4D vector. | +//+------------------------------------------------------------------+ +float DXPlaneDot(const DXPlane &p1,const DXVector4 &p2) + { + return(p1.a*p2.x + p1.b*p2.y + p1.c*p2.z + p1.d*p2.w); + } +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 3D vector. | +//| The w parameter of the vector is assumed to be 1. | +//+------------------------------------------------------------------+ +float DXPlaneDotCoord(const DXPlane &pp,const DXVector4 &pv) + { + return(pp.a*pv.x + pp.b*pv.y + pp.c*pv.z + pp.d); + } +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 3D vector. | +//| The w parameter of the vector is assumed to be 0. | +//+------------------------------------------------------------------+ +float DXPlaneDotNormal(const DXPlane &pp,const DXVector4 &pv) + { + return(pp.a*pv.x + pp.b*pv.y + pp.c*pv.z); + } +//+------------------------------------------------------------------+ +//| Constructs a plane from a point and a normal. | +//+------------------------------------------------------------------+ +void DXPlaneFromPointNormal(DXPlane &pout,const DXVector3 &pvpoint,const DXVector3 &pvnormal) + { + pout.a = pvnormal.x; + pout.b = pvnormal.y; + pout.c = pvnormal.z; + pout.d = -DXVec3Dot(pvpoint,pvnormal); + } +//+------------------------------------------------------------------+ +//| Constructs a plane from three points. | +//+------------------------------------------------------------------+ +void DXPlaneFromPoints(DXPlane &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3) + { + DXVector3 edge1,edge2,normal,Nnormal; +//--- + edge1.x = 0.0f; + edge1.y = 0.0f; + edge1.z = 0.0f; + edge2.x = 0.0f; + edge2.y = 0.0f; + edge2.z = 0.0f; +//--- + DXVec3Subtract(edge1,pv2,pv1); + DXVec3Subtract(edge2,pv3,pv1); + DXVec3Cross(normal,edge1,edge2); + DXVec3Normalize(Nnormal,normal); + DXPlaneFromPointNormal(pout,pv1,Nnormal); + } +//+------------------------------------------------------------------+ +//| Finds the intersection between a plane and a line. | +//| If the line is parallel to the plane, null vector is returned. | +//+------------------------------------------------------------------+ +void DXPlaneIntersectLine(DXVector3 &pout,const DXPlane &pp,const DXVector3 &pv1,const DXVector3 &pv2) + { + DXVector3 direction,normal; + normal.x = pp.a; + normal.y = pp.b; + normal.z = pp.c; + direction.x = pv2.x - pv1.x; + direction.y = pv2.y - pv1.y; + direction.z = pv2.z - pv1.z; +//--- + float dot = DXVec3Dot(normal,direction); + if(!dot) + { + pout.x=0.0f; + pout.y=0.0f; + pout.z=0.0f; + } + float temp = (pp.d + DXVec3Dot(normal,pv1))/dot; + pout.x = pv1.x - temp*direction.x; + pout.y = pv1.y - temp*direction.y; + pout.z = pv1.z - temp*direction.z; + } +//+------------------------------------------------------------------+ +//| Normalizes the plane coefficients so that the plane normal | +//| has unit length. | +//| This function normalizes a plane so that |a,b,c| == 1. | +//+------------------------------------------------------------------+ +void DXPlaneNormalize(DXPlane &out,const DXPlane &p) + { + float norm = (float)sqrt(p.a*p.a + p.b*p.b + p.c*p.c); + if(norm) + { + out.a = p.a/norm; + out.b = p.b/norm; + out.c = p.c/norm; + out.d = p.d/norm; + } + else + { + out.a = 0.0f; + out.b = 0.0f; + out.c = 0.0f; + out.d = 0.0f; + } + } +//+------------------------------------------------------------------+ +//| Scale the plane with the given scaling factor. | +//+------------------------------------------------------------------+ +void DXPlaneScale(DXPlane &pout,const DXPlane &p,float s) + { + pout.a = p.a*s; + pout.b = p.b*s; + pout.c = p.c*s; + pout.d = p.d*s; + }; +//+------------------------------------------------------------------+ +//| Transforms a plane by a matrix. | +//| The input matrix is the inverse transpose of the actual | +//| transformation. | +//+------------------------------------------------------------------+ +void DXPlaneTransform(DXPlane &pout,const DXPlane &pplane,const DXMatrix &pm) + { + DXPlane plane = pplane; +//--- + pout.a = pm.m[0][0]*plane.a + pm.m[1][0]*plane.b + pm.m[2][0]*plane.c + pm.m[3][0]*plane.d; + pout.b = pm.m[0][1]*plane.a + pm.m[1][1]*plane.b + pm.m[2][1]*plane.c + pm.m[3][1]*plane.d; + pout.c = pm.m[0][2]*plane.a + pm.m[1][2]*plane.b + pm.m[2][2]*plane.c + pm.m[3][2]*plane.d; + pout.d = pm.m[0][3]*plane.a + pm.m[1][3]*plane.b + pm.m[2][3]*plane.c + pm.m[3][3]*plane.d; + } +//+------------------------------------------------------------------+ +//| Adds two spherical harmonic (SH) vectors; in other words, | +//| out[i] = a[i] + b[i]. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) is stored | +//| at memory location l^2 + m + l,where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l, inclusive. | +//+------------------------------------------------------------------+ +void DXSHAdd(float &out[],int order,const float &a[],const float &b[]) + { + for(int i = 0; i DX_PI/2.0f) ? (DX_PI/2.0f) : radius; + float norm = (float)sin(clamped_angle)*(float)sin(clamped_angle); + if(order > DXSH_MAXORDER) + { + //--- order clamped at DXSH_MAXORDER + order = DXSH_MAXORDER; + } +//--- + weightedcapintegrale(cap,order,radius); + DXSHEvalDirection(rout,order,dir); +//--- + for(int i = 0; i DXSH_MAXORDER)) + return; + + out[0] = 0.5f/(float)sqrt(DX_PI); + out[1] = -0.5f/(float)sqrt(DX_PI/3.0f)*dir.y; + out[2] = 0.5f/(float)sqrt(DX_PI/3.0f)*dir.z; + out[3] = -0.5f/(float)sqrt(DX_PI/3.0f)*dir.x; + if(order == 2) + return; + + out[4] = 0.5f/(float)sqrt(DX_PI/15.0f)*dirxy; + out[5] = -0.5f/(float)sqrt(DX_PI/15.0f)*diryz; + out[6] = 0.25f/(float)sqrt(DX_PI/5.0f)*(3.0f*dirzz - 1.0f); + out[7] = -0.5f/(float)sqrt(DX_PI/15.0f)*dirxz; + out[8] = 0.25f/(float)sqrt(DX_PI/15.0f)*(dirxx - diryy); + if(order == 3) + return; + + out[9] = -(float)sqrt(70.0f/DX_PI)/8.0f*dir.y*(3.0f*dirxx - diryy); + out[10] = (float)sqrt(105.0f/DX_PI)/2.0f*dirxy*dir.z; + out[11] = -(float)sqrt(42.0f/DX_PI)/8.0f*dir.y*(-1.0f + 5.0f*dirzz); + out[12] = (float)sqrt(7.0f/DX_PI)/4.0f*dir.z*(5.0f*dirzz - 3.0f); + out[13] = (float)sqrt(42.0f/DX_PI)/8.0f*dir.x*(1.0f - 5.0f*dirzz); + out[14] = (float)sqrt(105.0f/DX_PI)/4.0f*dir.z*(dirxx - diryy); + out[15] = -(float)sqrt(70.0f/DX_PI)/8.0f*dir.x*(dirxx - 3.0f*diryy); + if(order == 4) + return; + + out[16] = 0.75f*float(sqrt(35.0f/DX_PI))*dirxy*(dirxx - diryy); + out[17] = 3.0f*dir.z*out[9]; + out[18] = 0.75f*(float)sqrt(5.0f/DX_PI)*dirxy*(7.0f*dirzz - 1.0f); + out[19] = 0.375f*(float)sqrt(10.0f/DX_PI)*diryz*(3.0f - 7.0f*dirzz); + out[20] = 3.0f/(16.0f*(float)sqrt(DX_PI))*(35.0f*dirzzzz - 30.f*dirzz + 3.0f); + out[21] = 0.375f*(float)sqrt(10.0f/DX_PI)*dirxz*(3.0f - 7.0f*dirzz); + out[22] = 0.375f*(float)sqrt(5.0f/DX_PI)*(dirxx - diryy)*(7.0f*dirzz - 1.0f); + out[23] = 3.0f*dir.z*out[15]; + out[24] = 3.0f / 16.0f*float(sqrt(35.0f/DX_PI))*(dirxxxx - 6.0f*dirxyxy + diryyyy); + if(order == 5) + return; + + out[25] = -3.0f/32.0f*(float)sqrt(154.0f/DX_PI)*dir.y*(5.0f*dirxxxx - 10.0f*dirxyxy + diryyyy); + out[26] = 0.75f*(float)sqrt(385.0f/DX_PI)*dirxy*dir.z*(dirxx - diryy); + out[27] = (float)sqrt(770.0f/DX_PI)/32.0f*dir.y*(3.0f*dirxx - diryy)*(1.0f - 9.0f*dirzz); + out[28] = (float)sqrt(1155.0f/DX_PI)/4.0f*dirxy*dir.z*(3.0f*dirzz - 1.0f); + out[29] = (float)sqrt(165.0f/DX_PI)/16.0f*dir.y*(14.0f*dirzz - 21.0f*dirzzzz - 1.0f); + out[30] = (float)sqrt(11.0f/DX_PI)/16.0f*dir.z*(63.0f*dirzzzz - 70.0f*dirzz + 15.0f); + out[31] = (float)sqrt(165.0f/DX_PI)/16.0f*dir.x*(14.0f*dirzz - 21.0f*dirzzzz - 1.0f); + out[32] = (float)sqrt(1155.0f/DX_PI)/8.0f*dir.z*(dirxx - diryy)*(3.0f*dirzz - 1.0f); + out[33] = (float)sqrt(770.0f/DX_PI)/32.0f*dir.x*(dirxx - 3.0f*diryy)*(1.0f - 9.0f*dirzz); + out[34] = 3.0f/16.0f*(float)sqrt(385.0f/DX_PI)*dir.z*(dirxxxx - 6.0f*dirxyxy + diryyyy); + out[35] = -3.0f/32.0f*(float)sqrt(154.0f/DX_PI)*dir.x*(dirxxxx - 10.0f*dirxyxy + 5.0f*diryyyy); + } +//+------------------------------------------------------------------+ +//| Evaluates a directional light and | +//| returns spectral spherical harmonic (SH) data. | +//+------------------------------------------------------------------+ +//| The output vector is computed so that if the intensity ratio | +//| R/G/B is equal to 1 ,the resulting exit radiance of a point | +//| directly under the light on a diffuse object with an albedo | +//| of 1 would be 1.0. This will compute three spectral samples; | +//| rout[], gout[] and bout[] will be returned. | +//+------------------------------------------------------------------+ +int DXSHEvalDirectionalLight(int order,const DXVector3 &dir,float Rintensity,float Gintensity,float Bintensity,float &rout[],float &gout[],float &bout[]) + { + float s = 0.75f; + if(order > 2) + s += 5.0f/16.0f; + if(order > 4) + s -= 3.0f/32.0f; + s /= DX_PI; + + DXSHEvalDirection(rout,order,dir); + for(int j=0; j DXSH_MAXORDER) + order = DXSH_MAXORDER; +//--- check radius + if(radius < 0.0f) + radius = -radius; + + float dist = DXVec3Length(dir); + float clamped_angle = (dist <= radius) ? DX_PI/2.0f : (float)asin(radius / dist); + + weightedcapintegrale(cap,order,clamped_angle); + DXVec3Normalize(normal,dir); + DXSHEvalDirection(rout,order,normal); + + for(int i=0; iDXSH_MAXORDER) || (order0; j--) + { + out[sum - j] = 0.0f; + out[sum - j] = c[j - 1]*in[sum - j]; + out[sum - j] += s[j - 1]*in[sum + j]; + } + out[sum] = in[sum]; + //--- + for(int j=1; j(); + if(m_buffer_object!=NULL) + { + m_buffer_object.AddRef(); + //--- use default vertex shader + m_shader_vertex=dispatcher.ShaderCreateDefault(DX_SHADER_VERTEX); + if(m_shader_vertex!=NULL) + { + m_shader_vertex.AddRef(); + //--- use default pixel shader + m_shader_pixel=dispatcher.ShaderCreateDefault(DX_SHADER_PIXEL); + if(m_shader_pixel!=NULL) + { + m_shader_pixel.AddRef(); + //--- create vertex buffer + m_buffer_vertex=dispatcher.VertexBufferCreate(vertices); + if(m_buffer_vertex!=NULL) + { + m_buffer_vertex.AddRef(); + //--- create index buffer + m_buffer_index=dispatcher.IndexBufferCreate(indices); + if(m_buffer_index!=NULL) + m_buffer_index.AddRef(); + } + } + } + } + //--- set object buffer to shaders + if(m_buffer_object==NULL || m_shader_vertex==NULL || m_shader_pixel==NULL || m_buffer_vertex==NULL || m_buffer_index==NULL) + { + Shutdown(); + return(false); + } + //--- save scene buffer + m_buffer_scene=buffer_scene; + m_buffer_scene.AddRef(); + //--- save topology + m_topology=topology; + //--- + return(true); + } + //+------------------------------------------------------------------+ + //| Create mesh object from OBJ mesh file | + //+------------------------------------------------------------------+ + bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,string obj_path,float scale=1.0f,bool inverse_winding=false) + { + DXVertex vertices[]; + uint indices[]; + //--- load model + if(!DXLoadObjModel(obj_path,vertices,indices,scale)) + return(false); + //--- set white vertices color + DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f); + int count=ArraySize(vertices); + for(int i=0; iA^@8yt9&L=Cyq01WL!kn#G1kAXe!B!)Fs2t`$R?ZJ zx9{y6W@h>NbmgkAK?g=(yV?PbtCgI88&|s5jRHOEK@$}^(o8RUqW`KDJib!R^`=ab z9=W&R=An-57}(WNr#d7$0zsj%ZndDMV3!lUW)*^}Ej-(pH3$n3=klx;uQH=#CDs|M z3Fu}uJYDk79y}*tjA1c^Lq|O|HNJ%9oU=;no>ls<$`RQG%GI+gua+;z(G*rPm>M&O zbTz#V3bQx$5$B2RQ$};Et}ma{L`&veCm=olab1^KtzM;Xzo^3AH#eBq#bGpll?ah74~-8x~j%CnJ@MWaaNy$r7<)^QniQ~H<}u<5z7 pW=Zdi_yV`ZWCnOLwX3xB5j4}!+W?&VtjV+=nV+8MC*C}prf+k}4D4~ZJ+_U~@BX}BkMud0t5#Ww_ z6K1Yp->jmsDmcI%ch6ZpA&P4Z7?bc{%-u6u9irM4o^{L$!i0!pRLv}2AO5P$Wphf?Ha~Bp}2IZy^c0P;>BA!lr;zS`nbyGF_mOs&xoZ3~Z zhg)+l#M8$cziY-{>&UN*$IU+^!agGg>?~Yb<#UAwau^bOK+Jgu^Rzb?^(+S-cG;Ol zTgOFe<(Q{7mua3oi4@L!)~@3!wU$SUqV3zoGZoyEneaaJd6&q&8=}kJyJiw{yEOTc z*~Mf=S=3$v`sK)pM7Qxs1j2yjzRxGxRepgJ&7dA72fcQ*tp9Ztr?K->(2D9&a+p>W QixtO{p2z7lvi6?-0e0T#X8-^I literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXShader.mqh b/Canvas/DX/DXShader.mqh new file mode 100644 index 0000000000000000000000000000000000000000..cac2284e951f54e8f631e2ce980a444a6034d604 GIT binary patch literal 16984 zcmeHO`%hFy6rP`(^nX}wVz8`et^EaA6ABnf5UH%j#u&2lu$uxZ7kpIhpWgQS?qN7* z9{28D7U;#88}@Pc&dizf_~tujhJXLL>2A7dccTT@wENzD2Wf7)&#rS}*P?so*4-a& z;I`bhJ8&~@&%MR>4x~5>c^2Gj_r`rh&qMbL{T`y{+wN9*-6%iZc6VGCcOPQ>L&))) zJ3^a7d@FEw1)sJc)mh0iO0$&k5E2(|wzS22$4j)b>wdwfO-Q#F$kQhO`2l*~hm5<> zA}!i+R~>Eq0Gi#$on74XHojU~xT?i6o_~X04jVq-N6#Pd^bu?wkI~jBdkC$3fX4P9 zHEl}W$(-qXwDb|~&}Vn>`2g>Qi4{IG=@mxVqWc-v`U%&DTX2sM6A$saiU@t+=JCst zdwl#0@AYwS6|WoaSNFu-$0fhdyEV6ryX%OaN9FJA>q+^n%&}AY1jmWxS9#jy)%4|^ z(mwQQdNk)}wX%%)tznjZ+~0uS=J8AiGrV(xV{Mv|$N2?BVWzjgc&Ut)q zr^Sh2b$XeFz8I-5@!SxR>a}NaPBck0+A4j3+WS;~dV^S&HD(jh&EIEFp6HagFN`3J z_YR)@h*7uAvrF*0C-An7tWWmc@9@bbjJt;?eCxZWJ^jFsrRdXvL<2lOFi(niC@TMm zUUL^m(w++U+`VviMCm6)>s((VvBh-T7rBAef2_YY#hPfzN8BRpS3tK1@E%%Ua6PBV z^SUPyr&6^~SMT6K2hhzCJa9MbWq)VTYRbKXHiz)t>&9<6Pl-CKPsS#>#THt;#~M3@ zHr7k|>=)BZ+Zcpek8FJb+S)E7maEeMfBX3U9`BYfkVO!8$TSMPlPPWDIbj;Y)~t@k z`P_b=dWjjouJ9&5Q>(|R62-{9$iv9O1dF=B(`J}!b+72#J&t~26U0$Jd%ib`i+F_^Anxcz-seJn#Ul{c!zX&t_c#v2@isA^ zZM66kBZmFNBMss2Q{~DRKG$YVW&d}aubr)})Wv7Tf#^8UyyoG5Vs zEnUTxjzj5~vHN_km+;0gj#7(v13p5pS2XHEUV3@#^}O#T%V}J#9m;mmw&to%R3)d5 zH6418aDbfuT%MI@9Wy0H(!aT46C*=!)8ELGrK&=1{jsz;+fH`1f90W87>k@WKhNP+ zc>F%@?BPxaS>sc@dkx+*p3`LAS~2<*i;TmCLflYvwV2TfB0l!(cEn!jRYSxdxzIcO z5d{Zju0~yRHQ$8=w5n3=y=e^VT)h>q04Z}7fv06Xq>tBU=dh+o+}SzDRteE2uC$7u zSkIBQ(xPWK^2=G|sRhvAj(R<_{#`!&(u}nOKRLqczEj2{J%|`pAb!a@X#F8RZQ&l< zkTnzWPJgu@znUP>GISx8OOHR)4mGD*>K^B8`YiMQE$Giy3}W{6$zY*qS@A0Nc!^CN z@m@t_=4*AI%bkyf6Mne{q>xz4W}g?$ zN-dA3-fZ=mpNXD_r7r0GvQDM?78{doN~O979!-|U^?e<^{Dt@sT%jg?ZM&MksqN+v zZNArzyM~K-u3w#E+mK`05uTUdBsyO}yTs^GmXa~*d&=8wv~^>z_IMFubTuOkGYVEk zcA=fk5}mlF4NkPKtD4WptX@(JGDlLaGh$TlRZ^>sAH%*yrJcK`=V{c!b9R@V4b8TP zfdqF;znsH8l~L|ZW8bt&zMKtTpbzHlE4b2}Mlq!WO>RTGtfv%sWu2FISd*eZGA>l3 z;cAiBZcZA%a?m&9R^l7=t!98VR?1xcD)br7g?OBLMg1J=<>)2Img+W*j(N1&DrZ3N z%Xx*hM**#CPGzg<<3`ct<0Zc8B)gjD&qlmP{eHK6r#0GFC2qn?`tW+)6&}q^!sp3P zLJP#vIzBsLJdMg6xPST#d}0OT3x81bJ6lc@&#pP630vmZ#gGe~ZHV6U#!s_zGTL1BE`FRz-7J+%4o;Ty^IvYXWhv%mmn?F1M5QR9KYdc03(I z{EO!q>Z!V2GmO#9_kNib`szk*CShK8d2)|6*y0;v0hMy#E$Y zZ(>#T_8iY?5ry%|{X6>bX?As4Nk2_q&j=tQYGpOu zZ_HH!Q(USWJ!Rvzk|=Z6%C6!{SOZN_=5+i<`Hsv*Gn`WG*PD6ebk^19V z=_T&<>*sx39k`D$EYBlw{+H*(a*RBu!Qjc-v>(B9{4bt!HAC@uAM=znM`MhWn_GTh z&&As@ZCrcCVlg7_C1x;wEZq;Bbaz*Kx`+S3i)Smcr*oG3nt5>*(LKjd6Ww6Q@Dw?dYsl`cn>X?bHPSrG2OiUk~I8n)a7gajUc?eRJie z7sXy8zuMQl6yvSh?9XP*O=M6H(cC{{PeE9D zx=|FLU!D@Lx6NiHwGuh0$ZRlo#!Yvs#*1c+jcQDZrOsJT{6egteO9B3JYR(|@~isM ztdIMuT<`eAcdlYLE)vNo&GD#Cs_{`tdP}jX zxm4<3>C}(?{ r9Q8zU=4@I=sjnxoMQWLF?s}Fao?kdp^=_q(@a6a<5l3j#`H24ji|s%B literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXSurface.mqh b/Canvas/DX/DXSurface.mqh new file mode 100644 index 0000000..11613ff --- /dev/null +++ b/Canvas/DX/DXSurface.mqh @@ -0,0 +1,162 @@ +//+------------------------------------------------------------------+ +//| DXSurface.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2000-2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +//--- +#include "DXMesh.mqh" +#include "DXMath.mqh" +#include "DXUtils.mqh" +//+------------------------------------------------------------------+ +//| 3D Box object | +//+------------------------------------------------------------------+ +class CDXSurface : public CDXMesh + { +public: + //--- surface creation flags + enum EN_SURFACE_FLAGS + { + SF_NONE =0x0, + SF_TWO_SIDED =0x1, + SF_USE_NORMALS =0x2, + }; + //--- surface color scheme + enum EN_COLOR_SCHEME + { + CS_NONE =0, + CS_JET =1, + CS_COLD_TO_HOT =2, + CS_RED_TO_GREEN=3 + }; + +protected: + uint m_data_width; + uint m_data_height; + uint m_flags; + EN_COLOR_SCHEME m_color_scheme; + +public: + CDXSurface(); + ~CDXSurface(); + //--- create bon in specified context + bool Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene,double &data[],uint m_data_widht,uint m_data_height,float data_range,const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,uint flags=SF_NONE,EN_COLOR_SCHEME color_scheme=CS_NONE); + //--- update box + bool Update(double &data[],uint m_data_widht,uint m_data_height,float data_range,const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size,uint flags=0,EN_COLOR_SCHEME color_scheme=CS_NONE); + +private: + //--- + void PrepareColors(DXVertex &vertices[],const DXVector3 &from,const DXVector3 &to); + }; +//+------------------------------------------------------------------+ +//| Class constructor | +//+------------------------------------------------------------------+ +void CDXSurface::CDXSurface() : CDXMesh() + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +void CDXSurface::~CDXSurface(void) + { + } +//+------------------------------------------------------------------+ +//| Create box in specified context | +//+------------------------------------------------------------------+ +bool CDXSurface::Create(CDXDispatcher &dispatcher,CDXInput* buffer_scene, + double &data[],uint data_width,uint data_height,float data_range, + const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size, + uint flags=SF_NONE,EN_COLOR_SCHEME color_scheme=CS_NONE) + { +//--- release previous buffers + Shutdown(); +//--- save parameters + m_data_width =data_width; + m_data_height =data_height; + m_flags =flags; + m_color_scheme=color_scheme; +//--- + DXVertex vertices[]; + uint indices[]; +//--- prepare surface vertices and indices + DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f); + if(!DXComputeSurface(data,data_width,data_height,data_range,from,to,texture_size,flags&SF_TWO_SIDED,flags&SF_USE_NORMALS,vertices,indices)) + return(false); +//--- calculate colors + PrepareColors(vertices,from,to); +//--- create mesh + return(CDXMesh::Create(dispatcher,buffer_scene,vertices,indices)); + } +//+------------------------------------------------------------------+ +//| Update box bounds | +//+------------------------------------------------------------------+ +bool CDXSurface::Update(double &data[],uint data_width,uint data_height,float data_range, + const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size, + uint flags=0,EN_COLOR_SCHEME color_scheme=CS_NONE) + { +//--- + DXVertex vertices[]; + uint indices[]; +//--- prepare surface vertices and indices + DXColor white=DXColor(1.0f,1.0f,1.0f,1.0f); + if(!DXComputeSurface(data,data_width,data_height,data_range,from,to,texture_size,flags&SF_TWO_SIDED,flags&SF_USE_NORMALS,vertices,indices)) + return(false); +//--- calculate colors + m_color_scheme=color_scheme; + PrepareColors(vertices,from,to); +//--- update vertices + bool res=CDXMesh::VerticesSet(vertices); +//--- do not update indices if vertices relations are the same + if(m_data_width!=data_width || m_data_height!=data_height || (m_flags&SF_TWO_SIDED)!=(flags&SF_TWO_SIDED)) + res=res && CDXMesh::IndicesSet(indices); +//--- check result + if(!res) + { + Shutdown(); + return(false); + } +//--- save parameters + m_data_width =data_width; + m_data_height =data_height; + m_flags =flags; +//--- success + return(true); + } +//+------------------------------------------------------------------+ +//| Update box bounds | +//+------------------------------------------------------------------+ +void CDXSurface::PrepareColors(DXVertex &vertices[],const DXVector3 &from,const DXVector3 &to) + { + uint count=ArraySize(vertices); + float scale=1.0f/(fmax(FLT_EPSILON,to.y-from.y)); + switch(m_color_scheme) + { + case CS_JET: + { + for(uint i=0; ioEGcDu8C zcLkI*r{;3)-rl~%)qro59tA@3TR?VzVE zY`mx}9G|g96*YQ@-#&eIo(w-;P1%tp?TzIiywQ}|?bmN@2sNCyZ7DhqQtx&C-KK~RFnM#L8-OT#9grDe3jQ4@i#L7{Cj&?uaS9ILfwQT^DyFefW zIxWSp6U8ip3fG(M@>=E}5zwlAYzMN7Is``&1NW_@?2+Dgfd-UWE1v8sz^K4v?;pZR6!bk)p}>KJ>q zA#F1uX6YF+a_OGtWJZ!+m&zJRZ_=!4aWiovdA8--2tII{z%(tL!L{Ple{ApSgh?Af z&P1y;Y9VW`@6Iyz{Fr4;vbD8SKQ$EeeILc>*8g0>A_|q<}<8)Y~ z4SGp$aPQ+@$6cdC+MA3>_n!jL=O|nU%S?-7uY4LiPZoVSMqSxh*EqAafFVZ?<^fl#TrEW4eSmv__dV6ioM8`eum7nccnyvog2Od+ z{&P9)VkavB$wA})fsYHMy_tZB8a> zS;H6#%R6g>S#=s)UPtVjsEW#>T@)iOVv*QXjdm4j z!`trc&XG@Y_i@7R(!y>AH8yZFsgD}lfWK^*?_}kKFGsVpr;Nz+&Wv$>xZdGww}6U$ z2Ql&ka(Wf5O=!Q3_8wYQdi_^tTHjwyyqMpA_A{;jMaO)nkr??n-sNkK^hRc){_@Cm zy7V#M2X*ASJ6HX6v=&^=?yJ4|EXb#`9O@<4%i|Sg5F0bwQ+&^}73uXPN--8$`lj~_ Dh_6SU literal 0 HcmV?d00001 diff --git a/Canvas/DX/DXUtils.mqh b/Canvas/DX/DXUtils.mqh new file mode 100644 index 0000000..4b58998 --- /dev/null +++ b/Canvas/DX/DXUtils.mqh @@ -0,0 +1,942 @@ +//+------------------------------------------------------------------+ +//| DXUtils.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2000-2024, MetaQuotes Ltd." +#property link "https://www.mql5.com" +//--- +#include "DXMath.mqh" +#include "DXData.mqh" +//--- data types in Wavefront OBJ file format +#define OBJ_DATA_UNKNOWN 0 +#define OBJ_DATA_V 1 +#define OBJ_DATA_VT 2 +#define OBJ_DATA_VN 3 +#define OBJ_DATA_F 4 +//+------------------------------------------------------------------+ +//| OBJFaceType | +//+------------------------------------------------------------------+ +struct OBJFaceType + { + int total; + int v[4]; + int t[4]; + int n[4]; + }; +//+------------------------------------------------------------------+ +//| Transforms right to left hand system, or backward | +//| TVertex must have | +//| DXVector4 normal and DXVector2 tcoord members | +//+------------------------------------------------------------------+ +template +void DXInverseWinding(TVertex &vertices[],uint &indices[]) + { +//--- proccess vertices + uint count=ArraySize(vertices); + for(uint i=0; i0) + faces[idx].v[i]=(int)StringToInteger(str_parsed_faces[0]); + if(elements>1) + faces[idx].t[i]=(int)StringToInteger(str_parsed_faces[1]); + if(elements>2) + faces[idx].n[i]=(int)StringToInteger(str_parsed_faces[2]); + } + //--- debug message + if(show_debug) + printf("%face d: %d vertex(%d,%d,%d,%d) texture(%d,%d,%d,%d) normal(%d,%d,%d,%d)",idx+1, + faces[idx].v[0],faces[idx].v[1],faces[idx].v[2],faces[idx].v[3], + faces[idx].t[0],faces[idx].t[1],faces[idx].t[2],faces[idx].t[3], + faces[idx].n[0],faces[idx].n[1],faces[idx].n[2],faces[idx].n[3]); + //--- + break; + } + + default: + { + break; + } + } + } +//--- close the file + FileClose(file_handle); +//--- + if(show_debug) + { + printf("File %s loaded successfully. Total lines: %d",filename,total_lines); + printf("total v_positions=%d",total_positions); + printf("total v_normals=%d",total_normals); + printf("total v_tcoords=%d",total_tcoords); + printf("total faces=%d",total_faces); + } +//--- + return(true); + } +//+------------------------------------------------------------------+ +//| Load static 3D models from Maya 2011 | +//| https://en.wikibooks.org/wiki/DirectX/10.0/Direct3D/Loading_Maya | +//| TVertex must have | +//| DXVector4 position, normal and DXVector2 tcoord members | +//+------------------------------------------------------------------+ +template +bool DXLoadObjModel(const string filename,TVertex &vertices[],uint &indices[],float scale=1.0f) + { +//--- intermediate data arrays + DXVector4 v_positions[]; + DXVector2 v_tcoords[]; + DXVector4 v_normals[]; + OBJFaceType faces[]; +//--- load data + if(!DXLoadObjData(filename,v_positions,v_tcoords,v_normals,faces,false)) + { + printf("Error loading model data from %s",filename); + return(false); + } +//--- + int total_v_positions=ArraySize(v_positions); + int total_v_tcoords =ArraySize(v_tcoords); + int total_v_normals =ArraySize(v_normals); + int total_faces =ArraySize(faces); +//--- check faces + if(total_faces==0) + { + printf("No model data."); + return(false); + } +//--- check consistency of the indices + int vertices_count=0; + int indices_count =0; + bool split_vertices=false; + for(int i=0; i4) + { + printf("Error in %d face, face vertices count is %d",i,faces[i].total); + return(false); + } + //--- + for(int j=0; jtotal_v_positions) + { + printf("Error in %d face, %d vertext index is %d. Total posiitons=%d",i,j,faces[i].v[j],total_v_positions); + return(false); + } + if(total_v_tcoords>0) + { + if(faces[i].t[j]<=0 || faces[i].t[j]>total_v_tcoords) + { + printf("Error in %d face, %d tcoord index is %d. Total tcoords=%d",i,j,faces[i].v[j],total_v_positions); + return(false); + } + if(faces[i].t[j]!=faces[i].v[j]) + split_vertices=true; + } + if(total_v_normals>0) + { + if(faces[i].n[j]<=0 || faces[i].n[j]>total_v_normals) + { + printf("Error in %d face, %d normal index is %d. Total normals=%d",i,j,faces[i].v[j],total_v_positions); + return(false); + } + if(faces[i].n[j]!=faces[i].v[j]) + split_vertices=true; + } + } + //--- calc counts + vertices_count+=faces[i].total; + if(faces[i].total<4) + indices_count+=3; + else + indices_count+=6; + } + printf("Data consistency checked."); +//--- prepare arrays + if(!split_vertices) + vertices_count=total_v_positions; + ArrayResize(vertices,vertices_count); + ArrayResize(indices, indices_count); + int v_idx=0,i_idx=0; + for(int i=0; i +bool DXComputeBox(const DXVector3 &from,const DXVector3 &to,TVertex &vertices[],uint &indices[]) + { +//--- prepare arrays + const int faces=6; + if(ArrayResize(vertices,4*faces)!=4*faces) + return(false); +//--- set indices + uint ind[]= {0,1,2, 2,3,0, 4,5,6, 6,7,4, 8,9,10, 10,11,8, 12,13,14, 14,15,12, 16,17,18, 18,19,16, 20,21,22, 22,23,20}; + ArrayResize(indices,0); + if(ArrayCopy(indices,ind)!=ArraySize(ind)) + return(false); +//--- prepare boundaries + float left=from.x,right=to.x,bottom=from.y,top=to.y,near=from.z,far=to.z; + if(from.x>to.x) + { + right=from.x; + left= to.x; + } + if(from.y>to.y) + { + top= from.y; + bottom=to.y; + } + if(from.z>to.z) + { + far=from.z; + near=to.z; + } +//--- left face + vertices[0].position=DXVector4(left,top, far, 1.0); + vertices[1].position=DXVector4(left,top, near,1.0); + vertices[2].position=DXVector4(left,bottom,near,1.0); + vertices[3].position=DXVector4(left,bottom,far, 1.0); + for(int i=0; i<4; i++) + vertices[i].normal=DXVector4(-1.0,0.0,0.0,0.0); +//--- right face + vertices[4].position=DXVector4(right,top, near,1.0); + vertices[5].position=DXVector4(right,top, far, 1.0); + vertices[6].position=DXVector4(right,bottom,far, 1.0); + vertices[7].position=DXVector4(right,bottom,near,1.0); + for(int i=4; i<8; i++) + vertices[i].normal=DXVector4(1.0,0.0,0.0,0.0); +//--- front face + vertices[8].position =DXVector4(left, top, near,1.0); + vertices[9].position =DXVector4(right,top, near,1.0); + vertices[10].position=DXVector4(right,bottom,near,1.0); + vertices[11].position=DXVector4(left, bottom,near,1.0); + for(int i=8; i<12; i++) + vertices[i].normal=DXVector4(0.0,0.0,-1.0,0.0); +//--- back face + vertices[12].position=DXVector4(right,top, far,1.0); + vertices[13].position=DXVector4(left, top, far,1.0); + vertices[14].position=DXVector4(left, bottom,far,1.0); + vertices[15].position=DXVector4(right,bottom,far,1.0); + for(int i=12; i<16; i++) + vertices[i].normal=DXVector4(0.0,0.0,1.0,0.0); +//--- top face + vertices[16].position=DXVector4(left, top,far, 1.0); + vertices[17].position=DXVector4(right,top,far, 1.0); + vertices[18].position=DXVector4(right,top,near,1.0); + vertices[19].position=DXVector4(left, top,near,1.0); + for(int i=16; i<20; i++) + vertices[i].normal=DXVector4(0.0,1.0,0.0,0.0); +//--- bottom face + vertices[20].position=DXVector4(left, bottom,near,1.0); + vertices[21].position=DXVector4(right,bottom,near,1.0); + vertices[22].position=DXVector4(right,bottom,far, 1.0); + vertices[23].position=DXVector4(left, bottom,far, 1.0); + for(int i=20; i<24; i++) + vertices[i].normal=DXVector4(0.0,-1.0,0.0,0.0); +//--- texture coordinates + for(int i=0; i +bool DXComputeSphere(float radius,uint tessellation,TVertex &vertices[],uint &indices[]) + { + if(tessellation<3) + tessellation=3; + uint segments_y =tessellation; + uint segments_xz=tessellation*2; +//--- prepare arrays + uint count=(segments_y+1)*(segments_xz+1); + if(ArrayResize(vertices,count)!=count) + return(false); + count=6*segments_y*(segments_xz); + if(ArrayResize(indices,count)!=count) + return(false); +//--- create rings of vertices at progressively higher latitudes. + for(uint i=0,idx=0; i<=segments_y; i++) + { + DXVector2 tcoord=DXVector2(0.0f,1.0f-(float)i/segments_y); + float latitude=(i*DX_PI/segments_y)-DX_PI_DIV2; + float dy =(float)sin(latitude); + float dxz=(float)cos(latitude); + //--- create a single ring of vertices at this latitude. + for(uint j=0; j<=segments_xz; j++,idx++) + { + float longitude=(j%segments_xz)*DX_PI_MUL2/segments_xz; + //--- normal + DXVector3 normal=DXVector3((float)sin(longitude)*dxz,dy,(float)cos(longitude)*dxz); + vertices[idx].normal =DXVector4(normal.x,normal.y,normal.z,0.0); + //--- position + DXVec3Scale(normal,normal,radius); + vertices[idx].position=DXVector4(normal.x,normal.y,normal.z,1.0); + //--- texture coords + tcoord.x = float(j)/segments_xz; + vertices[idx].tcoord =tcoord; + } + } +//--- fill the index buffer with triangles joining each pair of latitude rings. + uint stride=segments_xz+1; + uint idx=0; + for(uint i=0; i +bool DXComputeTorus(float outer_radius,float inner_radius,uint tessellation,TVertex &vertices[],uint &indices[]) + { + if(tessellation<3) + tessellation=3; +//--- prepare arrays + uint count=(tessellation+1)*(tessellation+1); + if(ArrayResize(vertices,count)!=count) + return(false); + count=6*tessellation*tessellation; + if(ArrayResize(indices,count)!=count) + return(false); +//--- + uint v=0,idx=0; + uint stride=tessellation+1; +//--- first we loop around the main ring of the torus. + for(uint i=0; i<=tessellation; i++) + { + DXVector2 tcoord=DXVector2(float(i)/tessellation,0.0f); + //--- create a transform matrix that will align geometry to slice perpendicularly though the current ring position. + DXMatrix rotation,transform; + DXMatrixRotationY(rotation,-1.0f*(i%tessellation)*DX_PI_MUL2/tessellation-DX_PI_DIV2); + DXMatrixTranslation(transform,outer_radius,0.0f,0.0f); + DXMatrixMultiply(transform,transform,rotation); + //--- now we loop along the other axis, around the side of the tube. + for(uint j=0; j<=tessellation; j++) + { + //--- calc normal and position + float angle=(j%tessellation)*DX_PI_MUL2/tessellation+DX_PI; + vertices[v].normal=DXVector4((float)cos(angle),(float)sin(angle),0.0f,0.0f); + vertices[v].position=DXVector4(vertices[v].normal.x*inner_radius,vertices[v].normal.y*inner_radius,0.0f,1.0f); + DXVec4Transform(vertices[v].normal, vertices[v].normal, transform); + DXVec4Transform(vertices[v].position,vertices[v].position,transform); + //--- calc texture coord + tcoord.y=1-float(j)/tessellation; + vertices[v].tcoord=tcoord; + v++; + //--- create indices for two triangles. + if(i +bool DXComputeCylinder(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[]) + { + return(DXComputeTruncatedCone(radius,radius,height,tessellation,vertices,indices)); + } +//+------------------------------------------------------------------+ +//| Truncated Cone | +//| TVertex must have | +//| DXVector4 position, DXVector4 normal and DXVector2 tcoord members| +//+------------------------------------------------------------------+ +template +bool DXComputeTruncatedCone(float radius_top,float radius_bottom,float height,uint tessellation,TVertex &vertices[],uint &indices[]) + { + if(tessellation<3) + tessellation=3; +//--- prepare arrays + uint count=2*(tessellation+1)+2*tessellation; + if(ArrayResize(vertices,count)!=count) + return(false); + count=6*tessellation+6*(tessellation-1); + if(ArrayResize(indices,count)!=count) + return(false); +//--- prepare normal + DXVector2 normal=DXVector2(height,radius_bottom-radius_top); + DXVec2Normalize(normal,normal); + float dy=height/2.0f; + uint v=0,idx=0; + uint stride=2; +//--- create top and bottom rings of vertices + for(uint i=0; i<=tessellation; i++) + { + float u=1.0f-(float)i/tessellation; + float angle=(i*DX_PI_MUL2/tessellation); + float dx=(float)sin(angle); + float dz=(float)cos(angle); + //--- + vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f); + vertices[v].position=DXVector4(dx*radius_bottom,-dy,dz*radius_bottom,1.0f); + vertices[v].tcoord =DXVector2(u,1.0f); + v++; + vertices[v].normal =vertices[v-1].normal; + vertices[v].position=DXVector4(dx*radius_top,dy,dz*radius_top,1.0f); + vertices[v].tcoord =DXVector2(u,0.0f); + v++; + //--- creater side surface + if(i0 && i +bool DXComputeCone(float radius,float height,uint tessellation,TVertex &vertices[],uint &indices[]) + { + if(tessellation<3) + tessellation=3; +//--- prepare arrays + uint count=2*(tessellation+1)+tessellation; + if(ArrayResize(vertices,count)!=count) + return(false); + count=3*tessellation+3*(tessellation-1); + if(ArrayResize(indices,count)!=count) + return(false); +//--- prepare normal + DXVector2 normal=DXVector2(height,radius); + DXVec2Normalize(normal,normal); + float dy=height/2.0f; + uint v=0,idx=0; + uint stride=2; +//--- create top and bottom rings of vertices + for(uint i=0; i<=tessellation; i++) + { + float u=1.0f-(float)i/tessellation; + float angle=(i*DX_PI_MUL2/tessellation); + float dx=(float)sin(angle); + float dz=(float)cos(angle); + //--- + vertices[v].normal =DXVector4(dx*normal.x,normal.y,dz*normal.x,0.0f); + vertices[v].position=DXVector4(dx*radius,-dy,dz*radius,1.0f); + vertices[v].tcoord =DXVector2(u,1.0f); + v++; + vertices[v].normal =vertices[v-1].normal; + vertices[v].position=DXVector4(0.0f,dy,0.0f,1.0f); + vertices[v].tcoord =DXVector2(u,0.0f); + v++; + //--- creater side surface + if(i0 && i +bool DXComputeSurface(double &data[],uint data_width,uint data_height,double data_range, + const DXVector3 &from,const DXVector3 &to,DXVector2 &texture_size, + bool two_sided,bool use_normals, + TVertex &vertices[],uint &indices[]) + { +//--- + if(data_width<2 || data_height<2) + return(false); +//--- prepare arrays for vertices and triangles + uint count=data_width*data_height*(two_sided?2:1); + if(!ArrayResize(vertices,count)) + return(false); + count=6*(data_width-1)*(data_height-1)*(two_sided?2:1); + if(!ArrayResize(indices,count)) + return(false); +//--- find min and max value + float min_value=+FLT_MAX; + float max_value=-FLT_MAX; + for(uint j=0; jvalue) + min_value=value; + if(max_value=data_width-1) + DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i].position); + else + DXVec4Subtract(v1,vertices[j*data_width+i-1].position,vertices[j*data_width+i+1].position); + //--- v2 + if(j<=0) + DXVec4Subtract(v2,vertices[j*data_width+i].position,vertices[(j+1)*data_width+i].position); + else + if(j>=data_height-1) + DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[j*data_width+i].position); + else + DXVec4Subtract(v2,vertices[(j-1)*data_width+i].position,vertices[(j+1)*data_width+i].position); + //--- normal + DXVec4Cross(normal,v2,v1,DXVector4(0.0f,0.0f,0.0f,1.0f)); + float inv_len=(float)(1.0/sqrt(normal.x*normal.x+normal.y*normal.y+normal.z*normal.z)); + vertices[j*data_width+i].normal.x=normal.x*inv_len; + vertices[j*data_width+i].normal.y=normal.y*inv_len; + vertices[j*data_width+i].normal.z=normal.z*inv_len; + vertices[j*data_width+i].normal.w=0.0f; + } + } + } + else + { + DXVector4 n=DXVector4(0.0f,0.0f,0.0f,0.0f); + for(int i=0; i blue > light blue > green > yellow > red > dark red | +//+---------------------------------------------------------------------+ +void DXComputeColorJet(const float value,DXColor &cout) + { + float v=value*1.1f-0.05f; + cout.r = fmin(fmax(v<0.75f ? 4*v-1.5f : 4.5f-4*v,0.0f),1.0f); + cout.g = fmin(fmax(v<0.5f ? 4*v-0.5f : 3.5f-4*v,0.0f),1.0f); + cout.b = fmin(fmax(v<0.25f ? 4*v+0.5f : 2.5f-4*v,0.0f),1.0f); + cout.a = 1.0; + } +//+---------------------------------------------------------------------+ +//| Computes hot to cold color scheme colors on [0;1] range | +//| blue > light blue > green > yellow > red | +//+---------------------------------------------------------------------+ +void DXComputeColorColdToHot(const float value,DXColor &cout) + { + float v=2*value-1.0f; + cout.r = fmin(fmax(2*v,0.0f),1.0f); + cout.g = fmin(fmax(2.0f-2*fabs(v),0.0f),1.0f); + cout.b = fmin(fmax(-2*v,0.0f),1.0f); + cout.a = 1.0; + } +//+---------------------------------------------------------------------+ +//| Computes red to green color scheme colors on [0;1] range | +//| red > yellow > dark green | +//+---------------------------------------------------------------------+ +void DXComputeColorRedToGreen(const float value,DXColor &cout) + { + if(value<=0.5) + { + cout.r=1.0f; + cout.g=DXScalarLerp(0.01f,0.95f,fmin(fmax(2*value,0.0f),1.0f)); + } + else + { + cout.r=DXScalarLerp(0.1f,1.0f, fmin(fmax(2.0f-2*value,0.0f),1.0f)); + cout.g=DXScalarLerp(0.6f,0.95f,fmin(fmax(2.0f-2*value,0.0f),1.0f)); + } + cout.b=0.0f; + cout.a=1.0f; + } +//+------------------------------------------------------------------+ diff --git a/Canvas/DX/Shaders/DefaultShaderPixel.hlsl b/Canvas/DX/Shaders/DefaultShaderPixel.hlsl new file mode 100644 index 0000000..49973c6 --- /dev/null +++ b/Canvas/DX/Shaders/DefaultShaderPixel.hlsl @@ -0,0 +1,71 @@ +//+------------------------------------------------------------------+ +//| Default Pixel Shader | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| Inputs for whole scene | +//+------------------------------------------------------------------+ +cbuffer InputScene : register(b0) + { + matrix view; + matrix projection; + float4 light_direction; + float4 light_color; + float4 ambient_color; + }; +//+------------------------------------------------------------------+ +//| Inputs for single object | +//+------------------------------------------------------------------+ +cbuffer InputObject : register(b1) + { + matrix transform; + float4 diffuse_color; + float4 emission_color; + float4 specular_color; + float specular_power; + float dummy[3]; + }; +//+------------------------------------------------------------------+ +//| Input texture | +//+------------------------------------------------------------------+ +Texture2D diffuse_tex : register(t0); +//+------------------------------------------------------------------+ +//| Texture sampler | +//+------------------------------------------------------------------+ +SamplerState diffuse_samp + { + Filter =MIN_MAG_MIP_LINEAR; + AddressU=Wrap; + AddressV=Wrap; + }; +//+------------------------------------------------------------------+ +//| Pixel shader input type | +//+------------------------------------------------------------------+ +struct PSInput + { + float4 position : SV_POSITION; + float4 camera : CAMERA; + float4 normal : NORMAL; + float2 tcoord : TEXCOORD; + float4 color : COLOR; + }; +//+------------------------------------------------------------------+ +//| Pixel shader entry point | +//+------------------------------------------------------------------+ +float4 PSMain(PSInput input) : SV_TARGET + { + float3 diffuse =saturate(-dot(light_direction.xyz,input.normal.xyz))*light_color.rgb*light_color.a; + float3 ambient =ambient_color.rgb *ambient_color.a; + float3 light =(diffuse+ambient)*diffuse_color.rgb*diffuse_color.a+emission_color.rgb*emission_color.a; + float4 specular=float4(light_color.rgb*specular_color.rgb,pow(saturate(dot(reflect(normalize(light_direction.xyz),input.normal.xyz),normalize(input.camera.xyz))),specular_power)*light_color.a*specular_color.a); + float4 clr=input.color; + //--- use texture if it exist + uint width,height; + diffuse_tex.GetDimensions(width,height); + if(width*height>0) + clr*=diffuse_tex.Sample(diffuse_samp,frac(input.tcoord)); + //--- combine light with colors + return(lerp(float4(light*clr.rgb,clr.a),float4(specular.rgb,1.0),specular.a)); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/DX/Shaders/DefaultShaderVertex.hlsl b/Canvas/DX/Shaders/DefaultShaderVertex.hlsl new file mode 100644 index 0000000..dd11fbc --- /dev/null +++ b/Canvas/DX/Shaders/DefaultShaderVertex.hlsl @@ -0,0 +1,72 @@ +//+------------------------------------------------------------------+ +//| Default Vertex Shader | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ + +//+------------------------------------------------------------------+ +//| Inputs for whole scene | +//+------------------------------------------------------------------+ +cbuffer InputScene : register(b0) + { + matrix view; + matrix projection; + float4 light_direction; + float4 light_color; + float4 ambient_color; + }; +//+------------------------------------------------------------------+ +//| Inputs for single object | +//+------------------------------------------------------------------+ +cbuffer InputObject : register(b1) + { + matrix transform; + float4 diffuse_color; + float4 emission_color; + float4 specular_color; + float specular_power; + float dummy[3]; + }; +//+------------------------------------------------------------------+ +//| Vertex shader input type | +//+------------------------------------------------------------------+ +struct VSInput + { + float4 position : POSITION; + float4 normal : NORMAL; + float2 tcoord : TEXCOORD; + float4 color : COLOR; + }; +//+------------------------------------------------------------------+ +//| Pixel shader input type | +//+------------------------------------------------------------------+ +struct PSInput + { + float4 position : SV_POSITION; + float4 camera : CAMERA; + float4 normal : NORMAL; + float2 tcoord : TEXCOORD; + float4 color : COLOR; + }; +//+------------------------------------------------------------------+ +//| Vertex shader entry point | +//+------------------------------------------------------------------+ +PSInput VSMain(VSInput input) + { + PSInput output; + //--- posiiton and camera direction + output.position=mul(input .position,transform); + output.position=mul(output.position,view); + output.camera =-output.position; + output.position=mul(output.position,projection); + //--- transform normals + output.normal = mul(input.normal, transform); + output.normal = mul(output.normal, view); + output.normal = normalize(output.normal); + //--- color and texture coordinates + output.tcoord =input.tcoord; + output.color =input.color; +//--- + return(output); + } +//+------------------------------------------------------------------+ diff --git a/Canvas/FlameCanvas.mqh b/Canvas/FlameCanvas.mqh new file mode 100644 index 0000000..cb71864 --- /dev/null +++ b/Canvas/FlameCanvas.mqh @@ -0,0 +1,751 @@ +//+------------------------------------------------------------------+ +//| FlameCanvas.mqh | +//| Copyright 2000-2024, MetaQuotes Ltd. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#include "Canvas.mqh" +#include +//+------------------------------------------------------------------+ +//| Gradient descriptors | +//+------------------------------------------------------------------+ +struct GRADIENT_COLOR + { + uint clr; // color in ARGB format + uint pos; // position of color in percentage of gradient range + }; +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +struct GRADIENT_SIZE + { + uint size; // width of gradient fill in percentage of base fill + uint pos; // position of color in percentage of gradient length + }; +//+------------------------------------------------------------------+ +//| Class CFlameCanvas | +//| Usage: generates flame | +//+------------------------------------------------------------------+ +class CFlameCanvas : public CCanvas + { +private: + //--- parameters + uint m_bar_gap; + uint m_bar_width; + uint m_chart_scale; + double m_chart_price_min; + double m_chart_price_max; + ENUM_TIMEFRAMES m_timeframe; + string m_symbol; + int m_future_bars; + int m_back_bars; + int m_rates_total; + uint m_palette[256]; // flame palette + uchar m_flame[]; // buffer for calculation of flame + uint m_time_redraw; + uint m_delay; + // bool m_resize_flag; + // int m_tick_cnt; + //--- flame parameters + datetime m_tb1; + double m_pb1; + datetime m_te1; + double m_pe1; + datetime m_tb2; + double m_pb2; + datetime m_te2; + double m_pe2; + //--- equation parameters for flame + int m_cloud_axis[100]; + double m_a1; + double m_b1; + double m_a2; + double m_b2; + int m_xb1; + int m_yb1; + int m_xe1; + int m_ye1; + int m_xb2; + int m_yb2; + int m_xe2; + int m_ye2; + +public: + CFlameCanvas(void); + ~CFlameCanvas(void); + //--- create + bool FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars=0); + void RatesTotal(const int value); + //--- setting + void PaletteSet(uint clr=0xFF0000); + //--- draw + void FlameDraw(const double &prices[],const int width,const int lenght); + void FlameSet(datetime xb1,double yb1,datetime xe1,double ye1,datetime xb2,double yb2,datetime xe2,double ye2); + //--- event handler + void ChartEventHandler(const int id,const long &lparam,const double &dparam,const string &sparam); + +protected: + bool Resize(void); + void ChartScale(void); + void FlameSet(void); + void CloudDraw(const double &prices[],const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[],const uchar t_level=255,const bool custom_gradient=true); + void FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[]); + void GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[]); + void GradientVerticalLine(int x,int y1,int y2,const GRADIENT_COLOR &gradient[]); + void GradientVerticalLineMonochrome(int x,int y1,int y2,uint clr1,uint clr2); + void FlameCreate(void); + void FlameCalculate(void); + void Delay(const uint value); + }; +//+------------------------------------------------------------------+ +//| Constructor | +//+------------------------------------------------------------------+ +CFlameCanvas::CFlameCanvas(void) : m_bar_gap(16), + m_bar_width(8), + m_chart_scale(1), + m_chart_price_min(0.0), + m_chart_price_max(0.0), + m_timeframe(PERIOD_CURRENT), + m_symbol(NULL), + m_future_bars(0), + m_back_bars(0), + m_rates_total(0), + m_time_redraw(0), + m_delay(50), + m_tb1(0), + m_pb1(0), + m_te1(0), + m_pe1(0), + m_tb2(0), + m_pb2(0), + m_te2(0), + m_pe2(0) + { + } +//+------------------------------------------------------------------+ +//| Destructor | +//+------------------------------------------------------------------+ +CFlameCanvas::~CFlameCanvas(void) + { + Destroy(); + } +//+------------------------------------------------------------------+ +//| Creates dynamic resource with object | +//+------------------------------------------------------------------+ +bool CFlameCanvas::FlameCreate(const string name,const datetime time,const int future_bars,const int back_bars) + { +//--- get chart parameters + ChartScale(); +//--- create + int width =(int)m_bar_gap*(future_bars+back_bars); + int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); + if(!CreateBitmap(0,0,name,time-back_bars*PeriodSeconds(),m_chart_price_max,width,height,COLOR_FORMAT_ARGB_NORMALIZE)) + return(false); + ArrayResize(m_flame,width*height); +//--- save parameters + m_future_bars=future_bars; + m_back_bars =back_bars; +//--- settings + PaletteSet(); + m_timeframe =Period(); + m_symbol =Symbol(); +//--- succeed + return(true); + } +//+------------------------------------------------------------------+ +//| Resize | +//+------------------------------------------------------------------+ +bool CFlameCanvas::Resize(void) + { + int x,y; +//--- get limits + double min=ChartGetDouble(0,CHART_PRICE_MIN); + double max=ChartGetDouble(0,CHART_PRICE_MAX); + if(m_chart_price_max!=max) + { + //--- move object + ObjectSetDouble(0,m_objname,OBJPROP_PRICE,0,max); + } +//--- check + if(m_chart_price_min==min && m_chart_price_max==max) + return(false); + m_chart_price_min=min; + m_chart_price_max=max; +//--- grt size + ChartTimePriceToXY(0,0,m_tb1,min,x,y); + int width =(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS)-x; + int height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); +//--- resize + if(width80) g+=dg; + if(i>160) b+=db; + } + } +//+------------------------------------------------------------------+ +//| Draws the flame | +//+------------------------------------------------------------------+ +void CFlameCanvas::FlameDraw(const double &prices[],const int width,const int lenght) + { + static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}}; + static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}}; +//--- draw + CloudDraw(prices,width,lenght,sword,flame); +//--- copy flame buffer + FlameCalculate(); +//--- start timer + EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL); + } +//+------------------------------------------------------------------+ +//| | +//+------------------------------------------------------------------+ +void CFlameCanvas::FlameDraw(const int width,const int lenght,GRADIENT_SIZE &size[],GRADIENT_COLOR &gradient[]) + { +//--- check + int total=ArraySize(m_cloud_axis); + if(total<2) + return; + if(total>lenght) + total=lenght; +//--- draw + int xb,xe; // coordinates of the segment + int ybm,yem; // coordinates of the center line + int yb1,ye1; // coordinates of the first line + int yb2,ye2; // coordinates of the second line +//--- for implementation of variable width + int w_total=ArraySize(size); + if(w_total<2) + return; + int w_i =0; + int w_is=(int)size[w_i].pos*total/100; + int w_ie=(int)size[w_i+1].pos*total/100; + double w =size[w_i].size*width/100; + double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is); +//--- draw from left to right + xb=0; + ybm=m_cloud_axis[0]; + yb1=ybm-(int)(w/2); + yb2=ybm+(int)(w/2); +//--- draw + for(int i=1;i=m_width) + break; + yb1=ye1; + yb2=ye2; + while(i>=w_ie-1 && i!=total-1) + { + w_i++; + w_is=(int)size[w_i].pos*total/100; + w_ie=(int)size[w_i+1].pos*total/100; + w =size[w_i].size*width/100; + if(w_ie==w_is) + { + //--- for "instant" resize + dw=size[w_i+1].size*width/100-w; + w+=dw; + ye1=yem-(int)(w/2); + ye2=yem+(int)(w/2); + //--- draw the segment of 'cloud' + GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient); + yb1=ye1; + yb2=ye2; + } + else + { + dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is); + break; + } + } + } +//--- copy flame buffer + FlameCalculate(); + } +//+------------------------------------------------------------------+ +//| Sets parameters of the flame and starts to draw | +//+------------------------------------------------------------------+ +void CFlameCanvas::FlameSet(void) + { + m_a1=m_bar_gap*((m_ye1-m_yb1)/((double)m_xe1-m_xb1)); + m_a2=m_bar_gap*((m_ye2-m_yb2)/((double)m_xe2-m_xb2)); + } +//+------------------------------------------------------------------+ +//| Sets parameters of the flame and starts to draw | +//+------------------------------------------------------------------+ +void CFlameCanvas::FlameSet(datetime tb1,double pb1, + datetime te1,double pe1, + datetime tb2,double pb2, + datetime te2,double pe2) + { + datetime obj_time =(datetime)ObjectGetInteger(0,m_objname,OBJPROP_TIME); + double obj_price=ObjectGetDouble(0,m_objname,OBJPROP_PRICE); + int dx,dy; +//--- save parameters + m_tb1=tb1; + m_pb1=pb1; + m_te1=te1; + m_pe1=pe1; + m_tb2=tb2; + m_pb2=pb2; + m_te2=te2; + m_pe2=pe2; +//--- resize + Resize(); +//--- convert + if(ChartTimePriceToXY(0,0,obj_time,obj_price,dx,dy)) + { + dy=m_yb1; + if(ChartTimePriceToXY(0,0,tb1,pb1,m_xb1,m_yb1)) + if(ChartTimePriceToXY(0,0,te1,pe1,m_xe1,m_ye1)) + if(ChartTimePriceToXY(0,0,tb2,pb2,m_xb2,m_yb2)) + if(ChartTimePriceToXY(0,0,te2,pe2,m_xe2,m_ye2)) + { + //--- convert to canvas coordinates + m_xb1-=dx; + m_xe1-=dx; + m_xb2-=dx; + m_xe2-=dx; + //--- + FlameSet(); + } + } +//--- start timer + EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL); + } +//+------------------------------------------------------------------+ +//| Generate array that describes the body of flame | +//+------------------------------------------------------------------+ +void CFlameCanvas::FlameCreate(void) + { + static GRADIENT_SIZE sword[]={{100,0},{150,70},{0,100}}; + static GRADIENT_COLOR flame[]={{0x00,0},{0x7F7F7F,12},{0xCCCCCC,30},{0xFFFFFF,45},{0xFFFFFF,55},{0xCCCCCC,70},{0x7F7F7F,88},{0x00,100}}; +//--- + double a=rand(); // parameter of line a*x+b + double b=rand(); // parameter of line a*x+b + double c=rand(); // parameter of sine c*Sin(d*x) + double d=rand(); // parameter of sine c*Sin(d*x) + int w=rand(); // width at the base + int l=rand(); // length +//--- normalize + a=fmod(a,(m_a2-m_a1))+m_a1; + b=(m_yb1+m_yb2)/2; + c=fmod(c,20); + d=fmod(d,3*M_PI)+M_PI; +//--- shape + w%=150; + if(w<10) + w=10; // but no less than 10 + sword[1].size=w; + w=rand(); + l%=50; + sword[1].pos=l+30; + l=rand(); +//--- sizes + w=(m_yb2-m_yb1!=0) ? w%(m_yb2-m_yb1) : 10; // proportional to the starting width + if(w<10) + w=10; // but no less than 10 + l=l%((m_xe1-m_xb1)/(int)m_bar_gap-20)+20; // proportional to length +//--- create + int total=ArraySize(m_cloud_axis); + for(int i=0;ilenght) + total=lenght; +//--- draw + int xb,xe; // coordinates of the segment + int ybm,yem; // coordinates of the center line + int yb1,ye1; // coordinates of the first line + int yb2,ye2; // coordinates of the second line + int xx; +//--- for implementation of variable width + int w_total=ArraySize(size); + if(w_total<2) + return; + int w_i =0; + int w_is=(int)size[w_i].pos*total/100; + int w_ie=(int)size[w_i+1].pos*total/100; + double w =size[w_i].size*width/100; + double dw =(size[w_i+1].size*width/100-w)/(w_ie-w_is); +//--- draw from left to right + xb=0; + ChartTimePriceToXY(0,0,0,prices[0],xx,ybm); + yb1=ybm-(int)(w/2); + yb2=ybm+(int)(w/2); +//--- draw + for(int i=1;i=m_width) + break; + yb1=ye1; + yb2=ye2; + while(i>=w_ie-1 && i!=total-1) + { + w_i++; + w_is=(int)size[w_i].pos*total/100; + w_ie=(int)size[w_i+1].pos*total/100; + w =size[w_i].size*width/100; + if(w_ie==w_is) + { + //--- for "instant" resize + dw=size[w_i+1].size*width/100-w; + w+=dw; + ye1=yem-(int)(w/2); + ye2=yem+(int)(w/2); + //--- draw the segment of 'cloud' + GradientVertical(xb,xe,yb1,ye1,yb2,ye2,gradient); + yb1=ye1; + yb2=ye2; + } + else + { + dw=(size[w_i+1].size*width/100-w)/(w_ie-w_is); + break; + } + } + } + } +//+------------------------------------------------------------------+ +//| Draws area with vertical fill using specified gradient | +//+------------------------------------------------------------------+ +void CFlameCanvas::GradientVertical(const int xb,const int xe,const int yb1,const int ye1,const int yb2,const int ye2,const GRADIENT_COLOR &gradient[]) + { +//--- it is assumed that the colors array has sufficient size and positions are already sorted in ascending order +//--- get length by X and Y + int x1 =xb; + int y1 =yb1; + int x2 =xb; + int y2 =yb2; + int dx =(xe>xb)? xe-xb : xb-xe; + int dy1=(ye1>yb1)? ye1-yb1 : yb1-ye1; + int dy2=(ye2>yb2)? ye2-yb2 : yb2-ye2; +//--- get direction by X and Y + int sx =(xb-dy1) + { + //--- try to change X coordinate of the first line + //--- draw the second line + while(x2!=xe || y2!=ye2) + { + //--- calculate coordinates of next pixel of the second line + if((er2<<1)>-dy2) + { + //--- try to change X coordinate of the second line + //--- gradient fill + GradientVerticalLine(x1,y1,y2,gradient); + er2-=dy2; + if(x2!=xe) + x2+=sx; + } + if((er2<<1)0) + { + dd=dy; + dy=1; + } + else + { + dd=-dy; + dy=-1; + } +//--- increments for the color components + dc=(double)((uchar)clr2-clr)/dd; +//--- draw + for(int i=0;y1!=y2;i++,y1+=dy) + { + int idx=y1*m_width+x; + //--- check range + if(idx<0 || idx>=ArraySize(m_flame)) + continue; + if(x>=0 && x=0 && y1m_time_redraw) + { + //--- add the body of flame + FlameCreate(); + //--- draw frame + FlameCalculate(); + Update(); + //--- calculate time for the next frame + m_time_redraw=GetTickCount()+m_delay; + } + //--- generate next event for custom timer + EventChartCustom(CONTROLS_SELF_MESSAGE,1302,0,0,NULL); + break; + } + } +//+------------------------------------------------------------------+ +//| Delay | +//+------------------------------------------------------------------+ +void CFlameCanvas::Delay(const uint value) + { +//--- too small + if(value<10) + return; +//--- start delay + uint cnt=GetTickCount()+value; +//--- delay + while(cnt>=GetTickCount()); + } +//+------------------------------------------------------------------+