From 08bcb090f6a3e6ee38c712571f24b57a4dd32fb4 Mon Sep 17 00:00:00 2001 From: HOSOKAWA Kenchi Date: Thu, 29 Mar 2012 01:46:02 +0900 Subject: [PATCH] 5 --- {dev5 => dev4}/Pages/BinomialSolver.xaml | 0 {dev5 => dev4}/Pages/BinomialSolver.xaml.cs | 0 dev4/Properties/AssemblyInfo.cs | 35 + {dev5 => dev4}/PsychlopsSilverlight4.csproj | 0 {dev5 => dev4}/PsychlopsSilverlight4.sln | 0 dev4/Shader/Gabor.fx | 59 + dev4/Shader/Gabor.ps | Bin 0 -> 936 bytes dev4/Shader/GaborAlpha.fx | 60 + dev4/Shader/GaborAlpha.ps | Bin 0 -> 832 bytes dev4/Shader/Grating.fx | 54 + dev4/Shader/Grating.ps | Bin 0 -> 628 bytes dev4/Shader/Plaid.fx | 81 + dev4/Shader/Plaid.ps | Bin 0 -> 1000 bytes {dev5 => dev4}/WriteableBitmapEx/License.txt | 0 {dev5 => dev4}/WriteableBitmapEx/Readme.txt | 0 dev4/WriteableBitmapEx/WriteableBitmapEx.XML | 940 +++++ dev4/WriteableBitmapEx/WriteableBitmapEx.dll | Bin 0 -> 24576 bytes dev4/WriteableBitmapEx/WriteableBitmapEx.pdb | Bin 0 -> 67072 bytes dev4/psychlops/core/app/app.cs | 28 + dev4/psychlops/core/app/misc.cs | 27 + {dev5 => dev4}/psychlops/core/device/clock.cs | 0 {dev5 => dev4}/psychlops/core/device/hid.cs | 0 dev4/psychlops/core/graphic/canvas.cs | 1410 ++++++++ dev4/psychlops/core/graphic/font.cs | 139 + dev4/psychlops/core/graphic/image.cs | 154 + dev4/psychlops/core/graphic/module.cs | 368 ++ dev4/psychlops/core/graphic/shape.cs | 515 +++ dev4/psychlops/core/math/interval.cs | 214 ++ dev4/psychlops/core/math/matrix.cs | 113 + dev4/psychlops/core/math/util.cs | 263 ++ .../extention/compatibility/compatibility.cs | 0 .../psychlops/extention/experiments/experiments.cs | 0 .../psychlops/extention/math/BigFloat.cs | 0 {dev5 => dev4}/psychlops/extention/math/BigInt.cs | 0 {dev5 => dev4}/psychlops/extention/math/solver.cs | 0 {dev5 => dev4}/psychlops/extention/media/dom.cs | 0 {dev5 => dev4}/psychlops/extention/media/svg.cs | 0 .../psychlops/extention/standard/CIEColor.cs | 0 .../psychlops/extention/standard/figures.cs | 0 .../psychlops/extention/standard/shader.cs | 0 .../psychlops/extention/standard/widget.cs | 0 dev4/psychlops/psychlops.cs | 1 + dev5/Properties/AssemblyInfo.cs | 28 +- dev5/PsychlopsSilverlight5.csproj | 109 + dev5/WriteableBitmapEx/WriteableBitmapEx.XML | 859 +++-- dev5/WriteableBitmapEx/WriteableBitmapEx.dll | Bin 24576 -> 31744 bytes dev5/WriteableBitmapEx/WriteableBitmapEx.pdb | Bin 67072 -> 83456 bytes dev5/psychlops/core/devices/clock.cs | 64 + dev5/psychlops/core/devices/hid.cs | 155 + dev5/psychlops/core/graphic/canvas.cs | 87 +- .../extension/compatibility/compatibility.cs | 106 + .../psychlops/extension/experiments/experiments.cs | 17 + dev5/psychlops/extension/math/BigFloat.cs | 3789 ++++++++++++++++++++ dev5/psychlops/extension/math/BigInt.cs | 2901 +++++++++++++++ dev5/psychlops/extension/math/solver.cs | 411 +++ dev5/psychlops/extension/media/dom.cs | 6 + dev5/psychlops/extension/media/svg.cs | 8 + dev5/psychlops/extension/standard/CIEColor.cs | 108 + dev5/psychlops/extension/standard/figures.cs | 43 + dev5/psychlops/extension/standard/shader.cs | 845 +++++ dev5/psychlops/extension/standard/widget.cs | 386 ++ test4/App.xaml | 8 + test4/App.xaml.cs | 66 + test4/MainPage.xaml | 22 + test4/MainPage.xaml.cs | 84 + test4/Properties/AppManifest.xml | 7 + test4/Properties/AssemblyInfo.cs | 35 + test4/PsychlopsMain.cs | 756 ++++ {test5 => test4}/PsychlopsSilverlight4test.csproj | 0 {test5 => test4}/PsychlopsSilverlight4test.sln | 0 test4/Store.cs | 883 +++++ test4/TestPage.html | 305 ++ test5/App.xaml | 2 +- test5/App.xaml.cs | 18 +- test5/MainPage.xaml | 9 +- test5/MainPage.xaml.cs | 38 +- test5/Properties/AppManifest.xml | 1 - test5/Properties/AssemblyInfo.cs | 28 +- test5/PsychlopsMain.cs | 118 +- test5/PsychlopsSilverlight5test.csproj | 118 + 80 files changed, 16442 insertions(+), 439 deletions(-) rename {dev5 => dev4}/Pages/BinomialSolver.xaml (100%) rename {dev5 => dev4}/Pages/BinomialSolver.xaml.cs (100%) create mode 100644 dev4/Properties/AssemblyInfo.cs rename {dev5 => dev4}/PsychlopsSilverlight4.csproj (100%) rename {dev5 => dev4}/PsychlopsSilverlight4.sln (100%) create mode 100644 dev4/Shader/Gabor.fx create mode 100644 dev4/Shader/Gabor.ps create mode 100644 dev4/Shader/GaborAlpha.fx create mode 100644 dev4/Shader/GaborAlpha.ps create mode 100644 dev4/Shader/Grating.fx create mode 100644 dev4/Shader/Grating.ps create mode 100644 dev4/Shader/Plaid.fx create mode 100644 dev4/Shader/Plaid.ps rename {dev5 => dev4}/WriteableBitmapEx/License.txt (100%) rename {dev5 => dev4}/WriteableBitmapEx/Readme.txt (100%) create mode 100644 dev4/WriteableBitmapEx/WriteableBitmapEx.XML create mode 100644 dev4/WriteableBitmapEx/WriteableBitmapEx.dll create mode 100644 dev4/WriteableBitmapEx/WriteableBitmapEx.pdb create mode 100644 dev4/psychlops/core/app/app.cs create mode 100644 dev4/psychlops/core/app/misc.cs rename {dev5 => dev4}/psychlops/core/device/clock.cs (100%) rename {dev5 => dev4}/psychlops/core/device/hid.cs (100%) create mode 100644 dev4/psychlops/core/graphic/canvas.cs create mode 100644 dev4/psychlops/core/graphic/font.cs create mode 100644 dev4/psychlops/core/graphic/image.cs create mode 100644 dev4/psychlops/core/graphic/module.cs create mode 100644 dev4/psychlops/core/graphic/shape.cs create mode 100644 dev4/psychlops/core/math/interval.cs create mode 100644 dev4/psychlops/core/math/matrix.cs create mode 100644 dev4/psychlops/core/math/util.cs rename {dev5 => dev4}/psychlops/extention/compatibility/compatibility.cs (100%) rename {dev5 => dev4}/psychlops/extention/experiments/experiments.cs (100%) rename {dev5 => dev4}/psychlops/extention/math/BigFloat.cs (100%) rename {dev5 => dev4}/psychlops/extention/math/BigInt.cs (100%) rename {dev5 => dev4}/psychlops/extention/math/solver.cs (100%) rename {dev5 => dev4}/psychlops/extention/media/dom.cs (100%) rename {dev5 => dev4}/psychlops/extention/media/svg.cs (100%) rename {dev5 => dev4}/psychlops/extention/standard/CIEColor.cs (100%) rename {dev5 => dev4}/psychlops/extention/standard/figures.cs (100%) rename {dev5 => dev4}/psychlops/extention/standard/shader.cs (100%) rename {dev5 => dev4}/psychlops/extention/standard/widget.cs (100%) create mode 100644 dev4/psychlops/psychlops.cs create mode 100644 dev5/PsychlopsSilverlight5.csproj create mode 100644 dev5/psychlops/core/devices/clock.cs create mode 100644 dev5/psychlops/core/devices/hid.cs create mode 100644 dev5/psychlops/extension/compatibility/compatibility.cs create mode 100644 dev5/psychlops/extension/experiments/experiments.cs create mode 100644 dev5/psychlops/extension/math/BigFloat.cs create mode 100644 dev5/psychlops/extension/math/BigInt.cs create mode 100644 dev5/psychlops/extension/math/solver.cs create mode 100644 dev5/psychlops/extension/media/dom.cs create mode 100644 dev5/psychlops/extension/media/svg.cs create mode 100644 dev5/psychlops/extension/standard/CIEColor.cs create mode 100644 dev5/psychlops/extension/standard/figures.cs create mode 100644 dev5/psychlops/extension/standard/shader.cs create mode 100644 dev5/psychlops/extension/standard/widget.cs create mode 100644 test4/App.xaml create mode 100644 test4/App.xaml.cs create mode 100644 test4/MainPage.xaml create mode 100644 test4/MainPage.xaml.cs create mode 100644 test4/Properties/AppManifest.xml create mode 100644 test4/Properties/AssemblyInfo.cs create mode 100644 test4/PsychlopsMain.cs rename {test5 => test4}/PsychlopsSilverlight4test.csproj (100%) rename {test5 => test4}/PsychlopsSilverlight4test.sln (100%) create mode 100644 test4/Store.cs create mode 100644 test4/TestPage.html create mode 100644 test5/PsychlopsSilverlight5test.csproj diff --git a/dev5/Pages/BinomialSolver.xaml b/dev4/Pages/BinomialSolver.xaml similarity index 100% rename from dev5/Pages/BinomialSolver.xaml rename to dev4/Pages/BinomialSolver.xaml diff --git a/dev5/Pages/BinomialSolver.xaml.cs b/dev4/Pages/BinomialSolver.xaml.cs similarity index 100% rename from dev5/Pages/BinomialSolver.xaml.cs rename to dev4/Pages/BinomialSolver.xaml.cs diff --git a/dev4/Properties/AssemblyInfo.cs b/dev4/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..100e216 --- /dev/null +++ b/dev4/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("PsychlopsSilverlight4")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PsychlopsSilverlight4")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから +// 見えなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。 +[assembly: Guid("a37cfddf-f475-42fb-804c-66fa131de7fb")] + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています。 +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を +// 既定値にすることができます。 +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dev5/PsychlopsSilverlight4.csproj b/dev4/PsychlopsSilverlight4.csproj similarity index 100% rename from dev5/PsychlopsSilverlight4.csproj rename to dev4/PsychlopsSilverlight4.csproj diff --git a/dev5/PsychlopsSilverlight4.sln b/dev4/PsychlopsSilverlight4.sln similarity index 100% rename from dev5/PsychlopsSilverlight4.sln rename to dev4/PsychlopsSilverlight4.sln diff --git a/dev4/Shader/Gabor.fx b/dev4/Shader/Gabor.fx new file mode 100644 index 0000000..5ec9fbf --- /dev/null +++ b/dev4/Shader/Gabor.fx @@ -0,0 +1,59 @@ +/// Amplitude of Grating +/// 0 +/// 1 +/// 1 +float contrast : register(C1); + +/// Phase of Grating +/// 0 +/// 100 +/// 100 +float frequency: register(C2); + +/// Phase of Grating +/// 0 +/// 6.283184 +/// 0 +float phase: register(C3); + +/// Orientation of Grating +/// 0 +/// 6.283184 +/// 0 +float orientation: register(C4); + +/// Half bandwidth of envelope +/// 2 +/// 8 +/// 4 +float sigma : register(C5); + +/// Transparency of Figure +/// 0 +/// 1 +/// 1 +float alpha : register(C6); + + +float rp(float2 uv) +{ + const float2 Center = float2(0.5,0.5); + float2 dir = uv - Center; // vector from center to pixel + float dist = length(dir); + //dir /= dist; + return dist; +} + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + float _r = rp(uv)*8; + float env = exp( -(_r*_r) / (2.0) ) * alpha; + + float _x = sin(orientation)*uv[0]-cos(orientation)*uv[1]; + float cl = 127.0/255.0+env*contrast*127.0/255.0*cos(frequency*_x + phase); + if(_r>3.2) if(abs(cl-127.0/255.0)<0.005) cl=127.0/255.0; + float l = cl; + + float4 color = float4(l,l,l,1.0); + return color; +} \ No newline at end of file diff --git a/dev4/Shader/Gabor.ps b/dev4/Shader/Gabor.ps new file mode 100644 index 0000000000000000000000000000000000000000..e937ad44a140fbf5583854cfd249f1072fb8a841 GIT binary patch literal 936 zcma)(F;Cl25Xa9+j007P1RJV^)v8k4Awm&BL`a0dLZx&l)Kb+2S*a0`AP$Zh*k08o z{RSOh)XiS900V95(4|t5x^-a9mtevD-+MUVtxhh!`@OsWy}M8@Np6xlZMCE|~pO)VV1Gg8_Zr~jrd7aiV`9a(3gl^dOJA|U1N8R2|WoLn2wOfJT^LIo0+17J= zv$@^0xA)xlUSMzd2i^8QoLafESgutU%9W++657SDi7;+SqT9u7%f?Eye8JoV|9Jg) z6})~``n7gddijI7rSlt?zng1Qf4`h#Lv`jwaO2^h6ZgUCSscAs`PA>%m`{SoQS=o$ z-#M4{=6QW;wfP}tg`XEDjbwocUqj$W>T^fTs&hkdoSw69QY1}E}DBZnNmSCqLEY6cc+?{auHM`YtfPAfgu$ey83rWX6i#OOKDT7`6v zLhngy>D|)ciPA4XKjK|tR_$B3?^`vij=4WUQ!|Tw7_*4=QCbuh{!ir>OW-4sBt-uJ D_dTYB literal 0 HcmV?d00001 diff --git a/dev4/Shader/GaborAlpha.fx b/dev4/Shader/GaborAlpha.fx new file mode 100644 index 0000000..0a162e8 --- /dev/null +++ b/dev4/Shader/GaborAlpha.fx @@ -0,0 +1,60 @@ +/// Amplitude of Grating +/// 0 +/// 1 +/// 1 +float contrast : register(C1); + +/// Phase of Grating +/// 0 +/// 100 +/// 100 +float frequency: register(C2); + +/// Phase of Grating +/// 0 +/// 6.283184 +/// 0 +float phase: register(C3); + +/// Orientation of Grating +/// 0 +/// 6.283184 +/// 0 +float orientation: register(C4); + +/// Half bandwidth of envelope +/// 2 +/// 8 +/// 4 +float sigma : register(C5); + +/// Transparency of Figure +/// 0 +/// 1 +/// 1 +float alpha : register(C6); + + +float rp(float2 uv) +{ + const float2 Center = float2(0.5,0.5); + float2 dir = uv - Center; // vector from center to pixel + float dist = length(dir); + //dir /= dist; + return dist; +} + +float4 main(float2 uv : TEXCOORD) : COLOR +//float4 main(float2 uv : VPOS ) : COLOR +{ + //float _r = rp(uv)*sigma*4.0; + //float env = exp( -(_r*_r) / 2.0 ) * alpha; + float _r = rp(uv)*8; + float env = exp( -(_r*_r) / (2.0) ) * alpha; + + float _x = sin(orientation)*uv[0]-cos(orientation)*uv[1]; + float l = 0.5+contrast*0.5*cos(frequency*_x + phase); + + float4 color = float4(l,l,l,1.0); + return color; +} diff --git a/dev4/Shader/GaborAlpha.ps b/dev4/Shader/GaborAlpha.ps new file mode 100644 index 0000000000000000000000000000000000000000..cdc54db9cb2e54d29aa22ef7b51400704afffa94 GIT binary patch literal 832 zcma)&KTq3G6vdAnLIPD2sccmxEF`4TAwp3>jgUwwOOapz>V*YasS%OVICb2C?ID)% z4LUwnd&yE*+QQJK8#`;h1WVm>pF>&?m1_I?z59Fj&v_Sin*K_g*nGF~<{5w=G>{d! zhz|?SH!g~1W18+$47iu)GtDNk$RF5+VM1poEHP|~?8$^(8CD>xOqkc{9eYR^E?nO1 zX$9RV^!gEwLjU6@zuP)N5Vrkpv!0T2uyIqf4Zmh+xu7wxL&+Zr2$(wi8 zpRez#TQ|Z>CSEni@n#WTCbu5ne(@e%UM2C1we!KCA$*2BiQ}&f{LX1gUUOc?XGU8X za#P@^fYFdl9{^JvzW8Hvb;-mFgPQ(pPw~vnI1uZ+50&>*UHL~YYvyt}|D4ah7i6|Y zokff^1@f~Mf0|rbaMhftZBwHw+?>n4Xmq7|kxcT1m04qkmg%#Z58=ayJgEnO^KehI yzLw&g^q+GRuXy1%7fROh9 literal 0 HcmV?d00001 diff --git a/dev4/Shader/Grating.fx b/dev4/Shader/Grating.fx new file mode 100644 index 0000000..f3ddd05 --- /dev/null +++ b/dev4/Shader/Grating.fx @@ -0,0 +1,54 @@ +/// Amplitude of Grating +/// 0 +/// 1 +/// 1 +float contrast : register(C1); + +/// Phase of Grating +/// 0 +/// 100 +/// 100 +float frequency: register(C2); + +/// Phase of Grating +/// 0 +/// 6.283184 +/// 0 +float phase: register(C3); + +/// Orientation of Grating +/// 0 +/// 6.283184 +/// 0 +float orientation: register(C4); + +/// Width of envelope +/// 1 +/// 1024 +/// 32 +float SizeH : register(C5); + +/// Height of envelope +/// 1 +/// 1024 +/// 32 +float SizeV : register(C6); + +float rp(float2 uv) +{ + const float2 Center = float2(0.5,0.5); + float2 dir = uv - Center; // vector from center to pixel + float dist = length(dir); + //dir /= dist; + return dist; +} + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + float _x = sin(orientation)*uv[0]-cos(orientation)*uv[1]; + float l = 0.5 + + contrast*0.5*cos(frequency*_x + phase); + + float4 color = float4(l,l,l,1.0); + return color; +} diff --git a/dev4/Shader/Grating.ps b/dev4/Shader/Grating.ps new file mode 100644 index 0000000000000000000000000000000000000000..1d39b76bdee40d6de969a761d7ab9f1d4ee0a2f4 GIT binary patch literal 628 zcma)&ze~eF6vtos8$|=U3I;(?hf<0y7Qw1r6zZV0a|t!2ftu7Lf^H`+b@dOB8=R7* zi@J!oy194lKjC8h-ldJ;WRjP=_xaxYagP#V_#JLh?R0N{iHHIifu*nrpA^y_vGiaG zI0|LxN^m>_SVGd7nB7a3!nuW*JxG=Wn~z!Bwme67JqiMp6qy~!872>+~<=%`}`948{2n-K}GN^ zc;Nf5IQTY+t1gRCz`xl67~AG7sQKxCV8_W W=d?c_%^=6nceKx`(8nwciGBbOA#kk# literal 0 HcmV?d00001 diff --git a/dev4/Shader/Plaid.fx b/dev4/Shader/Plaid.fx new file mode 100644 index 0000000..1441994 --- /dev/null +++ b/dev4/Shader/Plaid.fx @@ -0,0 +1,81 @@ +/// Amplitude of Grating +/// 0 +/// 1 +/// 0.5 +float contrast : register(C1); + +/// Phase of Grating +/// 0 +/// 100 +/// 100 +float frequency: register(C2); + +/// Phase of Grating +/// 0 +/// 6.283184 +/// 0 +float phase: register(C3); + +/// Orientation of Grating +/// 0 +/// 6.283184 +/// 0 +float orientation: register(C4); + +/// Amplitude of Grating2 +/// 0 +/// 1 +/// 0.5 +float contrast2 : register(C5); + +/// Phase of Grating2 +/// 0 +/// 100 +/// 100 +float frequency2: register(C6); + +/// Phase of Grating2 +/// 0 +/// 6.283184 +/// 0 +float phase2: register(C7); + +/// Orientation of Grating2 +/// 0 +/// 6.283184 +/// 0.785398 +float orientation2: register(C8); + +/// Width of envelope +/// 1 +/// 1024 +/// 32 +float SizeH : register(C9); + +/// Height of envelope +/// 1 +/// 1024 +/// 32 +float SizeV : register(C10); + + +float rp(float2 uv) +{ + const float2 Center = float2(0.5,0.5); + float2 dir = uv - Center; // vector from center to pixel + float dist = length(dir); + //dir /= dist; + return dist; +} + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + float _x1 = sin(orientation)*uv[0]-cos(orientation)*uv[1]; + float _x2 = sin(orientation2)*uv[0]-cos(orientation2)*uv[1]; + float l = 0.5 + + contrast*0.5*cos(frequency*_x1 + phase) + + contrast2*0.5*cos(frequency2*_x2 + phase2); + + float4 color = float4(l,l,l,1.0); + return color; +} diff --git a/dev4/Shader/Plaid.ps b/dev4/Shader/Plaid.ps new file mode 100644 index 0000000000000000000000000000000000000000..31eee86a0409e37832f2c2f436c95305aa30e1bf GIT binary patch literal 1000 zcma))zi-n(6vw|#(!@;!K}G6-_+eEb0Yg+-v{HmbfG!}F0gA;~S;18!mAE+Wz;=kG z{1=|>B@3`1#0VQBQa99*9kF1(?`$7U7{K=3z4v+d-hJOCsO9;u{0TN5-?@7pz-5aQ znaxFfSa2?oy%$W!LKB~a^ZCkA!vfi|Vc#^9oo&OuYt|$?XV?$T8e~g`)p<@0Hra|{ zXEdvmoi=P)vliJ!!-nBFj{HfCEXx4dFK_WwF&8^g@cbYc4`2MTT!hgm7{`7*3dbjH z7yG;ZBq(M&lWli<6%R(kD4c{lv2*p&HRs;u)~2(y>pu-5XCr*JKiZ?w@3}X6{k7Ge zyS}zAdLDw8gl_Wc=gKWA8XR>$-#+Tz|0sNs98iN&s$PV*$*ogg-uRuj@3Qpz&DV#A zec>JQEKTL-_)z8zF3GjgVc%1IHK@}*P8xuGicg>kpOveh0N0V zscN%EhlAqlQirv<&zn|x7CicxGWTV1uB|8}sp721c&eCug2`E;$4mZCi=}JKYhk|V znW + + + WriteableBitmapEx + + + + + Collection of interchange extension methods for the Silverlight WriteableBitmap class. + + + Collection of draw extension methods for the Silverlight WriteableBitmap class. + + + Collection of draw extension methods for the Silverlight WriteableBitmap class. + + + Collection of blit (copy) extension methods for the Silverlight WriteableBitmap class. + + + Collection of draw extension methods for the Silverlight WriteableBitmap class. + + + Collection of draw spline extension methods for the Silverlight WriteableBitmap class. + + + Collection of transformation extension methods for the Silverlight WriteableBitmap class. + + + + + Copies the Pixels from the WriteableBitmap into a ARGB byte array starting at a specific Pixels index. + + The WriteableBitmap. + The starting Pixels index. + The number of Pixels to copy. + The color buffer as byte ARGB values. + + + + Copies the Pixels from the WriteableBitmap into a ARGB byte array. + + The WriteableBitmap. + The number of pixels to copy. + The color buffer as byte ARGB values. + + + + Copies all the Pixels from the WriteableBitmap into a ARGB byte array. + + The WriteableBitmap. + The color buffer as byte ARGB values. + + + + Copies color information from an ARGB byte array into this WriteableBitmap starting at a specific buffer index. + + The WriteableBitmap. + The starting index in the buffer. + The number of bytes to copy from the buffer. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. + + + + Copies color information from an ARGB byte array into this WriteableBitmap. + + The WriteableBitmap. + The number of bytes to copy from the buffer. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. + + + + Copies all the color information from an ARGB byte array into this WriteableBitmap. + + The WriteableBitmap. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. + + + + Writes the WriteableBitmap as a TGA image to a stream. + Used with permission from Nokola: http://nokola.com/blog/post/2010/01/21/Quick-and-Dirty-Output-of-WriteableBitmap-as-TGA-Image.aspx + + The WriteableBitmap. + The destination stream. + + + + Loads an image from the applications resource file and fills this WriteableBitmap with it. + + The WriteableBitmap. + Only the relative path to the resource file. The assembly name is retrieved automatically. + The WriteableBitmap that was passed as parameter. + + + + Draws a colored line by connecting two points using the Bresenham algorithm. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using the Bresenham algorithm. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using a DDA algorithm (Digital Differential Analyzer). + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using a DDA algorithm (Digital Differential Analyzer). + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using an optimized DDA. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using an optimized DDA. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a colored line by connecting two points using an optimized DDA. + Uses the pixels array and the width directly for best performance. + + An array containing the pixels as int RGBA value. + The width of one scanline in the pixels array. + The height of the bitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + Draws a polyline. Add the first point also at the end of the array if the line should be closed. + + The WriteableBitmap. + The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn). + The color for the line. + + + + Draws a polyline. Add the first point also at the end of the array if the line should be closed. + + The WriteableBitmap. + The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn). + The color for the line. + + + + Draws a triangle. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The color. + + + + Draws a triangle. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The color. + + + + Draws a quad. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The x-coordinate of the 4th point. + The y-coordinate of the 4th point. + The color. + + + + Draws a quad. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The x-coordinate of the 4th point. + The y-coordinate of the 4th point. + The color. + + + + Draws a rectangle. + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color. + + + + Draws a rectangle. + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color. + + + + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). + + The WriteableBitmap. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). + + The WriteableBitmap. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. + + + + Fills the whole WriteableBitmap with a color. + + The WriteableBitmap. + The color used for filling. + + + + Fills the whole WriteableBitmap with an empty color (0). + + The WriteableBitmap. + + + + Clones the specified WriteableBitmap. + + The WriteableBitmap. + A copy of the WriteableBitmap. + + + + Applies the given function to all the pixels of the bitmap in + order to set their color. + + The WriteableBitmap. + The function to apply. With parameters x, y and a color as a result + + + + Applies the given function to all the pixels of the bitmap in + order to set their color. + + The WriteableBitmap. + The function to apply. With parameters x, y, source color and a color as a result + + + + Gets the color of the pixel at the x, y coordinate as integer. + + The WriteableBitmap. + The x coordinate of the pixel. + The y coordinate of the pixel. + The color of the pixel at x, y. + + + + Gets the color of the pixel at the x, y coordinate as a Color struct. + + The WriteableBitmap. + The x coordinate of the pixel. + The y coordinate of the pixel. + The color of the pixel at x, y as a Color struct. + + + + Sets the color of the pixel using a precalculated index (faster). + + The WriteableBitmap. + The coordinate index. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel including the alpha value and using a precalculated index (faster). + + The WriteableBitmap. + The coordinate index. + The alpha value of the color. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel including the alpha value. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The alpha value of the color. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel using a precalculated index (faster). + + The WriteableBitmap. + The coordinate index. + The color. + + + + Sets the color of the pixel. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The color. + + + + Sets the color of the pixel using an extra alpha value and a precalculated index (faster). + + The WriteableBitmap. + The coordinate index. + The alpha value of the color. + The color. + + + + Sets the color of the pixel using an extra alpha value. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The alpha value of the color. + The color. + + + + Sets the color of the pixel using a precalculated index (faster). + + The WriteableBitmap. + The coordinate index. + The color. + + + + Sets the color of the pixel. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The color. + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + The blending mode . + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The destination position in the destination bitmap. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. + The blending mode . + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. + The blending mode . + + + + Draws a filled rectangle. + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color. + + + + Draws a filled rectangle. + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color. + + + + A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). + + The WriteableBitmap. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. + + + + A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). + + The WriteableBitmap. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. + + + + Draws a filled polygon. Add the first point also at the end of the array if the line should be closed. + + The WriteableBitmap. + The points of the polygon in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn). + The color for the line. + + + + Draws a filled polygon. Add the first point also at the end of the array if the line should be closed. + + The WriteableBitmap. + The points of the polygon in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn). + The color for the line. + + + + Draws a filled quad. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The x-coordinate of the 4th point. + The y-coordinate of the 4th point. + The color. + + + + Draws a filled quad. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The x-coordinate of the 4th point. + The y-coordinate of the 4th point. + The color. + + + + Draws a filled triangle. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The color. + + + + Draws a filled triangle. + + The WriteableBitmap. + The x-coordinate of the 1st point. + The y-coordinate of the 1st point. + The x-coordinate of the 2nd point. + The y-coordinate of the 2nd point. + The x-coordinate of the 3rd point. + The y-coordinate of the 3rd point. + The color. + + + + Draws a filled, cubic Beziér spline defined by start, end and two control points. + + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color. + The pixels array. + The width of the bitmap. + The height of the bitmap. + + + + Draws a series of filled, cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Draws a series of filled, cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Computes the discrete segment points of a Cardinal spline (cubic) defined by four control points. + + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the 3rd control point. + The y-coordinate of the 3rd control point. + The x-coordinate of the 4th control point. + The y-coordinate of the 4th control point. + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color. + The pixels array. + The width of the bitmap. + The height of the bitmap. + + + + Draws a filled Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a filled Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a filled, closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a filled, closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a cubic Beziér spline defined by start, end and two control points. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color. + + + + Draws a cubic Beziér spline defined by start, end and two control points. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color. + + + + Draws a series of cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Draws a series of cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Draws a segment of a Cardinal spline (cubic) defined by four control points. + + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the 3rd control point. + The y-coordinate of the 3rd control point. + The x-coordinate of the 4th control point. + The y-coordinate of the 4th control point. + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color. + The pixels array. + The width of the bitmap. + The height of the bitmap. + + + + Draws a Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Creates a new cropped WriteableBitmap. + + The WriteableBitmap. + The x coordinate of the rectangle that defines the crop region. + The y coordinate of the rectangle that defines the crop region. + The width of the rectangle that defines the crop region. + The height of the rectangle that defines the crop region. + A new WriteableBitmap that is a cropped version of the input. + + + + Creates a new cropped WriteableBitmap. + + The WriteableBitmap. + The rectangle that defines the crop region. + A new WriteableBitmap that is a cropped version of the input. + + + + Creates a new resized WriteableBitmap. + + The WriteableBitmap. + The new desired width. + The new desired height. + The interpolation method that should be used. + A new WriteableBitmap that is a resized version of the input. + + + + The blending mode. + + + + + Alpha blendiing uses the alpha channel to combine the source and destination. + + + + + Additive blending adds the colors of the source and the destination. + + + + + Subtractive blending subtracts the source color from the destination. + + + + + Uses the source color as a mask. + + + + + Multiplies the source color with the destination color. + + + + + No blending just copies the pixels from the source. + + + + + The interpolation method. + + + + + The nearest neighbor algorithm simply selects the color of the nearest pixel. + + + + + Linear interpolation in 2D using the average of 3 neighboring pixels. + + + + diff --git a/dev4/WriteableBitmapEx/WriteableBitmapEx.dll b/dev4/WriteableBitmapEx/WriteableBitmapEx.dll new file mode 100644 index 0000000000000000000000000000000000000000..72956f01fc96eea771a12f3c19f0a4998c260dbc GIT binary patch literal 24576 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

;pOHZU9? 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 stack; + internal Internal.PrimitiveFigure[] stack; + internal int stackN = 0; + internal Line[] lineStack; + internal int lineStackN = 0; + internal Rectangle[] rectStack; + internal int rectStackN = 0; + internal ShaderField[] shaderStack; + internal int shaderStackN = 0; + internal Ellipse[] ellipseStack; + internal int ellipseStackN = 0; + internal Polygon[] polygonStack; + internal int polygonStackN = 0; + internal Letters[] lettersStack; + internal int lettersStackN = 0; + internal Image[] imageStack; + internal int imageStackN = 0; + internal Group[] groupStack; + internal int groupStackN = 0; + + public StackableDrawable() + { + stack = new Internal.PrimitiveFigure[CONST.MAX_OBJ_N]; + lineStack = new Line[CONST.MOBJ_N]; + rectStack = new Rectangle[CONST.MOBJ_N]; + ellipseStack = new Ellipse[CONST.MOBJ_N]; + shaderStack = new ShaderField[CONST.COBJ_N]; + polygonStack = new Polygon[CONST.COBJ_N]; + lettersStack = new Letters[CONST.COBJ_N]; + imageStack = new Image[CONST.HOBJ_N]; + groupStack = new Group[CONST.HOBJ_N]; + for (int i = 0; i < CONST.MOBJ_N; i++) + { + lineStack[i] = new Line(0,0,0,0); + rectStack[i] = new Rectangle(); + ellipseStack[i] = new Ellipse(); + } + for (int i = 0; i < CONST.COBJ_N; i++) + { + shaderStack[i] = new ShaderField(); + polygonStack[i] = new Polygon(); + lettersStack[i] = new Letters(); + } + for (int i = 0; i < CONST.HOBJ_N; i++) + { + imageStack[i] = new Image(); +// groupStack[i] = new Group(); + } + + + } + + public void clear() { clear(Color.black); } + public virtual void clear(Color col) { } //rect(back_panel, col); } + public virtual void pix(int x, int y, Color col) { } + public virtual void line(Line drawee) { drawee.copyToStack(this); } + public virtual void rect(Rectangle drawee) { drawee.copyToStack(this); } + public virtual void ellipse(Ellipse drawee) { drawee.copyToStack(this); } + public virtual void oval(Ellipse drawee) { drawee.copyToStack(this); } + public virtual void polygon(Polygon drawee) { drawee.copyToStack(this); } + public virtual void letters(Letters drawee) { drawee.copyToStack(this); } + public virtual void image(Image drawee) { drawee.copyToStack(this); } + public virtual void group(Group drawee) { drawee.copyToStack(this); } + public virtual void shader(ShaderField drawee) { drawee.copyToStack(this); } + + public void msg(string str, double x, double y) { msg(str, x, y, Color.white); } + public virtual void msg(string dstr, double x, double y, Color col) + { + var let = new Letters(dstr); + let.locate(x, y); + let.fill = col; + this.letters(let); + } + public void var(Type val, double x, double y) { msg(val.ToString(), x, y, Color.white); } + public void var(Type val, double x, double y, Color col) { msg(val.ToString(), x, y, col); } + + public virtual Point getCenter() { return new Point(0, 0, 0); } + } + + } + + public class Canvas : Templates.StackableDrawable + { + internal System.Windows.Controls.Canvas masterPool, prevPool; + internal System.Windows.Point[] pointPool; + internal int pointPoolN; + internal SolidColorBrush[] brushPool; + internal int brushPoolN; + + internal System.Windows.Controls.Canvas[] UIElementPool; + internal int UIElementPoolN; + internal int lastVisibleN; + + internal System.Windows.Shapes.Line[] linePool; + internal int linePoolN; + internal System.Windows.Shapes.Rectangle[] dummyRectPool; + internal System.Windows.Shapes.Rectangle[] rectPool; + internal int rectPoolN; + internal System.Windows.Shapes.Rectangle[] shaderPool; + internal int shaderPoolN; + internal System.Windows.Shapes.Ellipse[] ellipsePool; + internal int ellipsePoolN; + internal System.Windows.Shapes.Polygon[] polygonPool; + internal int polygonPoolN; + internal System.Windows.Controls.TextBlock[] lettersPool; + internal int lettersPoolN; + internal System.Windows.Controls.Image[] imagePool; + internal int imagePoolN; + internal Dictionary imagePoolT; + internal System.Windows.Controls.Canvas[] groupPool; + internal int groupPoolN; + + #region initializer + + Action flipexec; + public static IList initialize_at_canvas_initialize__ = new List(); + + public static System.Windows.Controls.UserControl default_panel; + public static System.Windows.Controls.Canvas default_api_canvas; + public static WriteableBitmap default_buffer; + internal System.Windows.Controls.Canvas api_canvas; + internal System.Windows.Controls.UserControl panel; + Rectangle back_panel; + double width_, height_; + Clock before; + + public Canvas(int wid, int hei) + { + panel = default_panel; + api_canvas = default_api_canvas; + initialize(wid, hei); + } + public Canvas(int wid, int hei, System.Windows.Controls.Canvas apicnvs, System.Windows.Controls.UserControl system) + { + panel = system; + api_canvas = apicnvs; + initialize(wid, hei); + } + + protected bool AsyncInitBool; + internal void beginInvoke(Action a) { api_canvas.Dispatcher.BeginInvoke(a); } + protected void initialize(int wid, int hei) + { + before = new Clock(); + before.update(); + var after = new Clock(); + AsyncInitBool = false; + width_ = wid; + height_ = hei; + api_canvas.Dispatcher.BeginInvoke(new Action(initialize__), wid, hei); + while (!AsyncInitBool) + { + after.update(); + if ((after - before).at_msec() > 1000) break; + } + Mouse._prime = api_canvas; + Main.drawable = this; + Main.canvas = this; + + back_panel = new Rectangle(wid, hei); + + flipexec = new Action(executeFlip); + } + protected void initialize__(int wid, int hei) + { + api_canvas.Width = wid; + api_canvas.Height = hei+20; + api_canvas.MouseMove += Mouse.Canvas_MousePos; + api_canvas.MouseLeftButtonDown += Mouse.Canvas_LDown; + api_canvas.MouseLeftButtonUp += Mouse.Canvas_LUp; + api_canvas.MouseWheel += Mouse.Canvas_MouseWheel; + panel.KeyDown += Keyboard.Canvas_KeyDown; + panel.KeyUp += Keyboard.Canvas_KeyUp; + + HtmlElement htmlHost = HtmlPage.Document.GetElementById("silverlightControlHost"); + //if (htmlHost != null) HtmlPage.Window.Alert("silverlightControlHost is null"); + htmlHost.SetStyleAttribute("width", (wid).ToString()+"px"); + htmlHost.SetStyleAttribute("height", (hei).ToString() + "px"); + htmlHost.SetStyleAttribute("margin", "2em auto auto auto"); + + pointPool = new System.Windows.Point[CONST.MOBJ_N]; + brushPool = new SolidColorBrush[CONST.MOBJ_N]; + linePool = new System.Windows.Shapes.Line[CONST.MOBJ_N]; + rectPool = new System.Windows.Shapes.Rectangle[CONST.MOBJ_N]; + ellipsePool = new System.Windows.Shapes.Ellipse[CONST.MOBJ_N]; + for (int i = 0; i < CONST.MOBJ_N; i++) + { + pointPool[i] = new System.Windows.Point(); + brushPool[i] = new SolidColorBrush(); + linePool[i] = new System.Windows.Shapes.Line(); + rectPool[i] = new System.Windows.Shapes.Rectangle(); + ellipsePool[i] = new System.Windows.Shapes.Ellipse(); + } + shaderPool = new System.Windows.Shapes.Rectangle[CONST.COBJ_N]; + polygonPool = new System.Windows.Shapes.Polygon[CONST.COBJ_N]; + lettersPool = new System.Windows.Controls.TextBlock[CONST.COBJ_N]; + for (int i = 0; i < CONST.COBJ_N; i++) + { + shaderPool[i] = new System.Windows.Shapes.Rectangle(); + polygonPool[i] = new System.Windows.Shapes.Polygon(); + lettersPool[i] = new System.Windows.Controls.TextBlock(); + } + imagePool = new System.Windows.Controls.Image[CONST.HOBJ_N]; + imagePoolT = new Dictionary(CONST.HOBJ_N); + groupPool = new System.Windows.Controls.Canvas[CONST.HOBJ_N]; + for (int i = 0; i < CONST.HOBJ_N; i++) + { + imagePool[i] = new System.Windows.Controls.Image(); + imagePoolT.Add(imagePool[i].GetHashCode(), false); + groupPool[i] = new System.Windows.Controls.Canvas(); + } + + masterPool = new System.Windows.Controls.Canvas(); + prevPool = new System.Windows.Controls.Canvas(); + api_canvas.Children.Add(masterPool); + + //api_canvas.Children.Remove(Internal.Main.widgetStack); + Psychlops.Internal.Main.widgetStack = new StackPanel(); + Psychlops.Internal.Main.widgetStack.Orientation = Orientation.Vertical; + Psychlops.Internal.Main.widgetStack.Height = hei; + api_canvas.Children.Add(Psychlops.Internal.Main.widgetStack); + Internal.Main.statusBar.Visibility = Visibility.Collapsed; + + + UIElementPool = new System.Windows.Controls.Canvas[CONST.MAX_OBJ_N]; + dummyRectPool = new System.Windows.Shapes.Rectangle[CONST.MAX_OBJ_N]; + for (int i = 0; i < CONST.MAX_OBJ_N; i++) + { + UIElementPool[i] = new System.Windows.Controls.Canvas(); + masterPool.Children.Add(UIElementPool[i]); + dummyRectPool[i] = new System.Windows.Shapes.Rectangle(); + UIElementPool[i].Children.Add(dummyRectPool[i]); + dummyRectPool[i].Visibility = Visibility.Collapsed; + } + + AsyncInitBool = true; + + // initialize at Canvas initializing + //Figures.ShaderGabor.initialize__(); + } + + internal int findEmptyInPool(Dictionary pool) + { + /* + foreach( KeyValuePair elem in pool) + { + if(elem) + }*/ + return 0; + } + + #endregion + + #region static initializer + /* + static Canvas() + { + } + */ + #endregion + + + public override void clear(Color col) + { + back_panel.fill = col; + stackN = 0; + rect(back_panel); + } + + int nextIntervalFrame = 1, chacked = 0; + public void flip(int n) + { + lock (this) + { + nextIntervalFrame = n; + chacked = 1; + } + //pointStackN = 0; + lineStackN = 0; + rectStackN = 0; + shaderStackN = 0; + polygonStackN = 0; + ellipseStackN = 0; + lettersStackN = 0; + imageStackN = 0; + groupStackN = 0; + + UIElementPoolN = 0; + brushPoolN = 0; + /* + pointPoolN = 0; + brushPoolN = 0; + linePoolN = 0; + rectPoolN = 0; + ellipsePoolN = 0; + polygonPoolN = 0; + lettersPoolN = 0; + imagePoolN = 0; + groupPoolN = 0; + * */ + + //executeFlip(); + Internal.Main.canvas_flag.WaitOne(); + } + public void flip() + { + flip(1); + } + + + #region version modifyNative2 + public void executeFlip() + { +// Clock after = new Clock(); +// after.update(); +// AppState.statusBar = ((after - before).at_msec().ToString()) + " msec"; + + Line lineS; + ShaderField shaderS; + Rectangle rectS; + Ellipse ellipseS; + Polygon polygonS; + Letters lettersS; + Image imageS; + Group groupS; + System.Windows.Shapes.Line lineP; + System.Windows.Shapes.Rectangle rectP; + System.Windows.Shapes.Rectangle shaderP; + System.Windows.Shapes.Ellipse ellipseP; + System.Windows.Shapes.Polygon polygonP; + System.Windows.Controls.TextBlock lettersP; + System.Windows.Controls.Image imageP; + System.Windows.Controls.Canvas groupP; + + lock (this) + { + nextIntervalFrame--; + } + + var cnv = UIElementPool[0]; + if (nextIntervalFrame <= 0) + { + if (chacked > 0) + { + if (stackN > 0) + { + for (int i = 0; i < stackN; i++) + { + if (null != (shaderS = stack[i] as ShaderField)) + { + if (null != (shaderP = cnv.Children[0] as System.Windows.Shapes.Rectangle)) + { + shaderS.modifyNative(shaderP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if( null != (rectS = stack[i] as Rectangle) ) + { + if (null != (rectP = cnv.Children[0] as System.Windows.Shapes.Rectangle)) + { + rectS.modifyNative(rectP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (lineS = stack[i] as Line)) + { + if (null != (lineP = cnv.Children[0] as System.Windows.Shapes.Line)) + { + lineS.modifyNative(lineP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (ellipseS = stack[i] as Ellipse)) + { + if (null != (ellipseP = cnv.Children[0] as System.Windows.Shapes.Ellipse)) + { + ellipseS.modifyNative(ellipseP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (polygonS = stack[i] as Polygon)) + { + if (null != (polygonP = cnv.Children[0] as System.Windows.Shapes.Polygon)) + { + polygonS.modifyNative(polygonP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (lettersS = stack[i] as Letters)) + { + if (null != (lettersP = cnv.Children[0] as System.Windows.Controls.TextBlock)) + { + lettersS.modifyNative(lettersP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (imageS = stack[i] as Image)) + { + if (null != (imageP = cnv.Children[0] as System.Windows.Controls.Image)) + { + imageS.modifyNative(imageP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (groupS = stack[i] as Group)) + { + if (null != (groupP = cnv.Children[0] as System.Windows.Controls.Canvas)) + { + groupS.modifyNative(groupP, this); + } + else + { + cnv.Children.Clear(); + cnv.Children.Add(stack[i].poolNative(this)); + } + } + cnv.Visibility = Visibility.Visible; + UIElementPoolN++; + cnv = UIElementPool[UIElementPoolN]; + } + for (int i = UIElementPoolN; i < lastVisibleN; i++) + { + cnv = UIElementPool[i]; + cnv.Visibility = Visibility.Collapsed; + } + lastVisibleN = UIElementPoolN; + stackN = 0; + } + lock (this) + { + chacked = 0; + } + Psychlops.Internal.Main.canvas_flag.Set(); + } + } + System.Threading.Thread.Sleep(0); + } + #endregion + + + + #region version modifyNative + /* + public void executeFlip() + { + Line lineS; + Rectangle rectS; + Ellipse ellipseS; + Polygon polygonS; + Letters lettersS; + Image imageS; + Group groupS; + System.Windows.Shapes.Line lineP; + System.Windows.Shapes.Rectangle rectP; + System.Windows.Shapes.Ellipse ellipseP; + System.Windows.Shapes.Polygon polygonP; + System.Windows.Controls.TextBlock lettersP; + System.Windows.Controls.Image imageP; + System.Windows.Controls.Canvas groupP; + + lock (this) + { + nextIntervalFrame--; + } + + var en = masterPool.Children.GetEnumerator(); + bool full = en.MoveNext(); + if (nextIntervalFrame <= 0) + { + if (chacked > 0) + { + //masterPool.Children.Clear(); + if (stackN > 0) + { + for (int i = 0; i < stackN - 2; i++) + { + if (full == false) + { + masterPool.Children.Add(stack[i].poolNative(this)); + } + else + { + if( null != (rectS = stack[i] as Rectangle) ) + { + if (null != (rectP = en.Current as System.Windows.Shapes.Rectangle)) + { + rectS.modifyNative(rectP, this); + } + } + else if (null != (lineS = stack[i] as Line)) + { + if (null != (lineP = en.Current as System.Windows.Shapes.Line)) + { + lineS.modifyNative(lineP, this); + } + } + else if (null != (ellipseS = stack[i] as Ellipse)) + { + if (null != (ellipseP = en.Current as System.Windows.Shapes.Ellipse)) + { + ellipseS.modifyNative(ellipseP, this); + } + else + { + masterPool.Children.Add(stack[i].poolNative(this)); + } + } + else if (null != (polygonS = stack[i] as Polygon)) + { + if (null != (polygonP = en.Current as System.Windows.Shapes.Polygon)) + { + polygonS.modifyNative(polygonP, this); + } + } + else if (null != (lettersS = stack[i] as Letters)) + { + if (null != (lettersP = en.Current as System.Windows.Controls.TextBlock)) + { + lettersS.modifyNative(lettersP, this); + } + } + else if (null != (imageS = stack[i] as Image)) + { + if (null != (imageP = en.Current as System.Windows.Controls.Image)) + { + imageS.modifyNative(imageP, this); + } + } + else if (null != (groupS = stack[i] as Group)) + { + if (null != (groupP = en.Current as System.Windows.Controls.Canvas)) + { + groupS.modifyNative(groupP, this); + } + } + full = en.MoveNext(); + } + } + stackN = 0; + } + lock (this) + { + chacked = 0; + } + Psychlops.Internal.Main.canvas_flag.Set(); + } + } + System.Threading.Thread.Sleep(0); + } + * */ + #endregion + + #region version poolNative 2 + /* + public void executeFlip() + { + + lock (this) + { + nextIntervalFrame--; + } + UIElementPoolN = 0; + if (nextIntervalFrame <= 0) + { + if (chacked > 0) + { + //masterPool.Children.Clear(); + if (stackN > 0) + { + for (int i = 0; i < stackN - 2; i++) + { + UIElementPool[UIElementPoolN] = stack[i].poolNative(this); + UIElementPool[UIElementPoolN].Visibility = Visibility.Visible; + UIElementPoolN++; + + } + for (int i = stackN - 2; i < 10000; i++) + { + UIElementPool[UIElementPoolN] = rectPool[i]; + UIElementPool[UIElementPoolN].Visibility = Visibility.Collapsed; + UIElementPoolN++; + } + stackN = 0; + } + lock (this) + { + chacked = 0; + } + Psychlops.Internal.Main.canvas_flag.Set(); + } + } + System.Threading.Thread.Sleep(0); + } + */ + #endregion + + + #region Properties + + public double width { get { return width_; } } + public double height { get { return height_; } } + public Point center { get { return new Point(width / 2.0, height / 2.0, 0); } } + public double getWidth() { return width; } + public double getHeight() { return height; } + public override Point getCenter() { return center; } + public double getHCenter() { return width / 2; } + public double getVCenter() { return height / 2; } + public double getRefreshRate() { return 60; } + + #endregion + + + #region compatibitily trick + + public enum Mode { window, fullscreen } + public static readonly Mode window = Mode.window, fullscreen = Mode.fullscreen; + + public Canvas(int wid, int hei, Mode mod) + { + panel = default_panel; + api_canvas = default_api_canvas; + initialize(500, 500); + } + public Canvas(Mode mod) + : base() + { + panel = default_panel; + api_canvas = default_api_canvas; + initialize(500, 500); + } + + public Canvas(int wid, int hei, Mode mod, Display.DisplayName name) + { + panel = default_panel; + api_canvas = default_api_canvas; + initialize(500, 500); + } + public Canvas(Mode mod, Display.DisplayName name) + : base() + { + panel = default_panel; + api_canvas = default_api_canvas; + initialize(500, 500); + } + + + public void showFPS(bool sw = true) { } + public void watchFPS(bool sw = true) { } + + + public void clear(double lum) + { + clear(new Color(lum)); + } + + #endregion + + + } + + + + #region primitive tokenizer + + + #region primitive + + partial struct Point + { + public static implicit operator System.Windows.Point(Point d) + { + return new System.Windows.Point(d.x, d.y); + } + + + public Point datum { get { return this; } set { this = value; } } + public Point shift(Point p) { this = this + p; return this; } + public Point centering(Point p) { this = p; return this; } + public Point getDatum() { return this; } + public Point setDatum(Point p) { this = p; return p; } + public Point shift(double x, double y, double z = 0.0) { return shift(new Point(x, y, z)); } + public Point centering() { return centering(Main.drawable.getCenter()); } + public Point centering(double x, double y, double z = 0.0) { return centering(new Point(x, y, z)); } + } + + partial struct Color + { + public static implicit operator System.Windows.Media.Color(Color d) + { + return System.Windows.Media.Color.FromArgb((byte)(d.a * 255), (byte)(d.r * 255), (byte)(d.g * 255), (byte)(d.b * 255)); + } + public static implicit operator System.Windows.Media.SolidColorBrush(Color d) + { + return new SolidColorBrush { Color = d }; + } + public System.Windows.Media.SolidColorBrush getNativeFromStack(Canvas d) + { + var tmp = d.brushPool[d.brushPoolN]; + tmp.Color = this; + d.brushPoolN++; + return tmp; + } + + } + + partial struct Stroke + { + public void apply(System.Windows.Shapes.Shape target) + { + target.Stroke = this; + //target.StrokeDashArray + target.StrokeThickness = thick; + } + public static implicit operator SolidColorBrush(Stroke d) + { + return new SolidColorBrush { Color = d.color }; + } + public System.Windows.Media.SolidColorBrush getNativeFromStack(Canvas d) + { + var tmp = d.brushPool[d.brushPoolN]; + tmp.Color = this.color; + d.brushPoolN++; + return tmp; + } + } + + #endregion + + #region Line + + partial class Line + { + public Line dup() + { + return (Line)MemberwiseClone(); + } + public Line clone() + { + return (Line)MemberwiseClone(); + } + public static implicit operator System.Windows.Shapes.Line(Line d) + { + var tmp = new System.Windows.Shapes.Line() { X1 = d.begin.x, Y1 = d.begin.y, X2 = d.end.x, Y2 = d.end.y }; + if (d.stroke.thick == 0.0) tmp.Stroke = d.fill; + else d.stroke.apply(tmp); + return tmp; + } + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.lineStack[d.lineStackN]; + tmp.begin.x = begin.x; + tmp.begin.y = begin.y; + tmp.end.x = end.x; + tmp.end.y = end.y; + tmp.fill = fill; + tmp.stroke = stroke; + d.stack[d.stackN] = tmp; + d.lineStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.linePool[d.linePoolN]; + tmp.X1 = begin.x; + tmp.Y1 = begin.y; + tmp.X2 = end.x; + tmp.Y2 = end.y; + if (stroke.thick == 0.0) tmp.Stroke = fill.getNativeFromStack(d); + else stroke.apply(tmp); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + d.linePoolN++; + return tmp; + } + public void modifyNative(System.Windows.Shapes.Line tmp, Canvas d) + { + tmp.X1 = begin.x; + tmp.Y1 = begin.y; + tmp.X2 = end.x; + tmp.Y2 = end.y; + if (stroke.thick == 0.0) tmp.Stroke = fill.getNativeFromStack(d); + else stroke.apply(tmp); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + } + } + + #endregion + + #region Rectangle + + partial class Rectangle + { + public Rectangle dup() + { + return (Rectangle)MemberwiseClone(); + } + public Rectangle clone() + { + return (Rectangle)MemberwiseClone(); + } + public static implicit operator System.Windows.Rect(Rectangle d) + { + return new System.Windows.Rect(d.v1.x, d.v1.y, d.v2.x, d.v2.y); + } + public static implicit operator System.Windows.Shapes.Rectangle(Rectangle d) + { + var tmp = new System.Windows.Shapes.Rectangle { Width = d.width, Height = d.height, Fill = d.fill }; + d.stroke.apply(tmp); + System.Windows.Controls.Canvas.SetLeft(tmp, d.left); + System.Windows.Controls.Canvas.SetTop(tmp, d.top); + return tmp; + } + + public UIElement toNative() { return this; } + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.rectStack[d.rectStackN]; + tmp.v1 = v1; + tmp.v2 = v2; + tmp.fill = fill; + d.stack[d.stackN] = tmp; + d.rectStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.rectPool[d.rectPoolN]; + tmp.Width = width; + tmp.Height = height; + tmp.Fill = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + d.rectPoolN++; + return tmp; + } + public void modifyNative(System.Windows.Shapes.Rectangle tmp, Canvas d) + { + tmp.Width = width; + tmp.Height = height; + tmp.Fill = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + } + } + + #endregion + + #region ShaderField + + partial class ShaderField + { + public System.Windows.Media.Effects.Effect shader; + protected static System.Windows.Media.SolidColorBrush dummyfill = null; + + protected static void initializeShader() + { + dummyfill = new SolidColorBrush(System.Windows.Media.Colors.Blue); + } + + public UIElement toNative() { return null; } + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.shaderStack[d.shaderStackN]; + tmp.initialize__ = initialize__; + tmp.setParameters = setParameters; + tmp.v1 = v1; + tmp.v2 = v2; + tmp.shader = shader; + d.stack[d.stackN] = tmp; + d.shaderStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.shaderPool[d.shaderPoolN]; + tmp.Width = width; + tmp.Height = height; + if (!initialized) { initialize__(); } + setParameters(); + tmp.Effect = shader; + tmp.Fill = dummyfill; + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + d.shaderPoolN++; + return tmp; + } + public void modifyNative(System.Windows.Shapes.Rectangle tmp, Canvas d) + { + tmp.Width = width; + tmp.Height = height; + if (!initialized) { initialize__(); } + setParameters(); + tmp.Effect = shader; + tmp.Fill = dummyfill; + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + } + } + + #endregion + + #region Ellipse + + partial class Ellipse + { + public Ellipse dup() + { + return (Ellipse)MemberwiseClone(); + } + public Ellipse clone() + { + return (Ellipse)MemberwiseClone(); + } + public static implicit operator System.Windows.Shapes.Ellipse(Ellipse d) + { + var tmp = new System.Windows.Shapes.Ellipse { Width = d.width, Height = d.height, Fill = d.fill }; + d.stroke.apply(tmp); + System.Windows.Controls.Canvas.SetLeft(tmp, d.left); + System.Windows.Controls.Canvas.SetTop(tmp, d.top); + return tmp; + } + + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.ellipseStack[d.ellipseStackN]; + tmp.datum = datum; + tmp.xdiameter = xdiameter; + tmp.ydiameter = ydiameter; + tmp.fill = fill; + d.stack[d.stackN] = tmp; + d.ellipseStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.ellipsePool[d.ellipsePoolN]; + tmp.Width = width; + tmp.Height = height; + tmp.Fill = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + d.ellipsePoolN++; + return tmp; + } + public void modifyNative(System.Windows.Shapes.Ellipse tmp, Canvas d) + { + tmp.Width = width; + tmp.Height = height; + tmp.Fill = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, left); + System.Windows.Controls.Canvas.SetTop(tmp, top); + tmp.Visibility = Visibility.Visible; + } + } + + #endregion + + #region Polygon + + partial class Polygon + { + public Polygon dup() + { + return (Polygon)MemberwiseClone(); + } + public Polygon clone() + { + return (Polygon)MemberwiseClone(); + } + public static implicit operator System.Windows.Shapes.Polygon(Polygon d) + { + var tmp = new System.Windows.Shapes.Polygon { Fill = d.fill }; + d.stroke.apply(tmp); + foreach (Point p in d.vertices) + { + tmp.Points.Add(p); + } + System.Windows.Controls.Canvas.SetLeft(tmp, d.datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, d.datum.y); + return tmp; + } + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.polygonStack[d.polygonStackN]; + tmp.datum = datum; + tmp.vertices.Clear(); + foreach (var v in vertices) + { + tmp.vertices.Add(v); + } + tmp.fill = fill; + d.stack[d.stackN] = tmp; + d.polygonStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.polygonPool[d.polygonPoolN]; + tmp.Fill = fill.getNativeFromStack(d); + tmp.Points.Clear(); + foreach (var v in vertices) + { + tmp.Points.Add(v); + } + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + d.polygonPoolN++; + return tmp; + } + public void modifyNative(System.Windows.Shapes.Polygon tmp, Canvas d) + { + tmp.Fill = fill.getNativeFromStack(d); + tmp.Points.Clear(); + foreach (var v in vertices) + { + tmp.Points.Add(v); + } + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + } + + } + + #endregion + + #region Letters + + partial class Letters + { + #region static initializer + internal static System.Collections.Generic.Dictionary FONT_WEIGHT_BRIDGE; + internal static System.Collections.Generic.Dictionary FONT_STYLE_BRIDGE; + internal static System.Collections.Generic.Dictionary LETTERS_H_ALIGN_BRIDGE; + static Letters() + { + FONT_WEIGHT_BRIDGE = new System.Collections.Generic.Dictionary(); + FONT_WEIGHT_BRIDGE.Add((int)Font.Weight.normal, System.Windows.FontWeights.Normal); + FONT_WEIGHT_BRIDGE.Add((int)Font.Weight.bold, System.Windows.FontWeights.Bold); + FONT_STYLE_BRIDGE = new System.Collections.Generic.Dictionary(); + FONT_STYLE_BRIDGE.Add(Font.Style.normal, System.Windows.FontStyles.Normal); + FONT_STYLE_BRIDGE.Add(Font.Style.italic, System.Windows.FontStyles.Italic); + FONT_STYLE_BRIDGE.Add(Font.Style.oblique, System.Windows.FontStyles.Italic); + LETTERS_H_ALIGN_BRIDGE = new System.Collections.Generic.Dictionary(); + LETTERS_H_ALIGN_BRIDGE.Add(Letters.HorizontalAlign.left, TextAlignment.Left); + LETTERS_H_ALIGN_BRIDGE.Add(Letters.HorizontalAlign.center, TextAlignment.Center); + LETTERS_H_ALIGN_BRIDGE.Add(Letters.HorizontalAlign.right, TextAlignment.Right); + LETTERS_H_ALIGN_BRIDGE.Add(Letters.HorizontalAlign.not_specified, TextAlignment.Left); + } + #endregion + public Letters dup() + { + return (Letters)MemberwiseClone(); + } + public Letters clone() + { + return (Letters)MemberwiseClone(); + } + public static implicit operator System.Windows.Controls.TextBlock(Letters d) + { + //var zapi_shape = new System.Windows.Documents.Glyphs(); + var tmp = new System.Windows.Controls.TextBlock { + Text = d.str, Width = 500, Height = 500, + FontSize = d.font.size, + //tmp.FontFamily = , + FontStyle = FONT_STYLE_BRIDGE[d.font.style], + FontWeight = FONT_WEIGHT_BRIDGE[d.font.weight], + TextAlignment = LETTERS_H_ALIGN_BRIDGE[d.align], + Foreground = d.fill + }; + double left = 0; + switch (d.align) + { + case Letters.HorizontalAlign.left: break; + case Letters.HorizontalAlign.center: left = tmp.Width / 2; break; + case Letters.HorizontalAlign.right: left = tmp.Width; break; + } + System.Windows.Controls.Canvas.SetLeft(tmp, d.datum.x - left); + System.Windows.Controls.Canvas.SetTop(tmp, d.datum.y - d.font.size); + return tmp; + } + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.lettersStack[d.lettersStackN]; + tmp.str = str; + tmp.datum = datum; + tmp.fill = fill; + d.stack[d.stackN] = tmp; + d.lettersStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.lettersPool[d.lettersPoolN]; + tmp.Text = str; + tmp.Width = 500; + tmp.Height = 500; + tmp.FontSize = font.size; + //tmp.FontFamily = , + tmp.FontStyle = FONT_STYLE_BRIDGE[font.style]; + tmp.FontWeight = FONT_WEIGHT_BRIDGE[font.weight]; + tmp.TextAlignment = LETTERS_H_ALIGN_BRIDGE[align]; + tmp.Foreground = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + d.lettersPoolN++; + return tmp; + } + public void modifyNative(System.Windows.Controls.TextBlock tmp, Canvas d) + { + tmp.Text = str; + tmp.Width = 500; + tmp.Height = 500; + tmp.FontSize = font.size; + //tmp.FontFamily = , + tmp.FontStyle = FONT_STYLE_BRIDGE[font.style]; + tmp.FontWeight = FONT_WEIGHT_BRIDGE[font.weight]; + tmp.TextAlignment = LETTERS_H_ALIGN_BRIDGE[align]; + tmp.Foreground = fill.getNativeFromStack(d); + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + } + } + + #endregion + + #region Image + + partial class Image + { + internal void initialize__(int wid, int hei) + { + AsyncBool = false; + Canvas.default_api_canvas.Dispatcher.BeginInvoke(new Action(create__), wid, hei); + while (!AsyncBool) { System.Threading.Thread.Sleep(10); } + } + internal void create__(int wid, int hei) + { + buffer = new WriteableBitmap(wid, hei); + AsyncBool = true; + } + internal void load__(string uri) + { + AsyncBool = false; + var ur = new System.Uri(uri, System.UriKind.RelativeOrAbsolute); + Canvas.default_api_canvas.Dispatcher.BeginInvoke(new Action(load_), ur); + while (!AsyncBool) { System.Threading.Thread.Sleep(10); } + } + internal void load_(Uri uri) + { + var bitmap = new BitmapImage(); + bitmap.CreateOptions = BitmapCreateOptions.None; + bitmap.UriSource = uri; + //try + //{ + var wbm = new System.Windows.Media.Imaging.WriteableBitmap(bitmap); + buffer = wbm; + //} + //catch (Exception e) + //{ + // buffer = new WriteableBitmap(64, 64); + // buffer.ForEach(bitmap_drawChecker); + //} + self_rect.set(buffer.PixelWidth, buffer.PixelHeight); + AsyncBool = true; + } + static System.Windows.Media.Color[] CHECKER_C; + static Image() + { + CHECKER_C = new System.Windows.Media.Color[2]; + CHECKER_C[0] = System.Windows.Media.Color.FromArgb(0, 0, 0, 0); + CHECKER_C[1] = System.Windows.Media.Color.FromArgb(128,128,128,128); + } + static System.Windows.Media.Color bitmap_drawChecker(int x, int y) + { + return ((x / 4) + (y / 4)) % 2 == 0 ? CHECKER_C[0] : CHECKER_C[1]; + } + delegate void FieldFunc1(System.Func func); + delegate void FieldFunc2(System.Func func); + public void field__(System.Func func) + { + Canvas.default_api_canvas.Dispatcher.BeginInvoke(new FieldFunc1(field___), func); + //buffer.ForEach(func); + } + public void field__(System.Func func) + { + Canvas.default_api_canvas.Dispatcher.BeginInvoke(new FieldFunc2(field___), func); + //buffer.ForEach(func); + } + public void field___(System.Func func) + { + buffer.ForEach(func); + } + public void field___(System.Func func) + { + buffer.ForEach(func); + } + + public Image clone() + { + return (Image)MemberwiseClone(); + } + public static implicit operator System.Windows.Controls.Image(Image d) + { + var tmp = new System.Windows.Controls.Image(); + tmp.Source = d.buffer; + System.Windows.Controls.Canvas.SetLeft(tmp, d.datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, d.datum.y); + return tmp; + } + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.imageStack[d.imageStackN]; + tmp.datum = datum; + tmp.buffer = buffer; + tmp.self_rect = self_rect; + d.stack[d.stackN] = tmp; + d.imageStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + var tmp = d.imagePool[d.imagePoolN]; + tmp.Source = buffer; + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + d.imagePoolN++; + return tmp; + } + public void modifyNative(System.Windows.Controls.Image tmp, Canvas d) + { + tmp.Source = buffer; + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + } + + } + + #endregion + + #region Group + + partial class Group + { + internal void initialize__() + { + Canvas.default_api_canvas.Dispatcher.BeginInvoke(new Action(create__)); + } + internal void create__() + { + cnvs = new System.Windows.Controls.Canvas(); + trans = new System.Windows.Media.TransformGroup(); + transF = new System.Windows.Media.TransformCollection(); + rotateF = new System.Windows.Media.RotateTransform(); + scaleF = new System.Windows.Media.ScaleTransform(); + translateF = new System.Windows.Media.TranslateTransform(); + transF.Add(rotateF); + transF.Add(scaleF); + transF.Add(translateF); + trans.Children = transF; + cnvs.RenderTransform = trans; + AsyncBool = true; + } + public Group clone() + { + return (Group)MemberwiseClone(); + } + + delegate void AppendFunc1(Internal.PrimitiveFigure func); + void append__(Internal.PrimitiveFigure fig) + { + fig.centering(0, 0); + UIElement e = fig.toNative(); + cnvs.Children.Add(e); + System.Windows.Controls.Canvas.SetLeft(e, fig.datum.x); + System.Windows.Controls.Canvas.SetTop(e, fig.datum.y); + } + delegate void SimpleProcedure(); + void getRotation__() { rotation_ = rotateF.Angle; } + void setRotation__() { rotateF.Angle = rotation_; } + //void getTranslation__() { rotation_ = rotateF.Angle; } + void setTranslation__() { translateF.X = datum.x; translateF.Y = datum.y; } + void setScaling__() { scaleF.ScaleX = scaling_.x; scaleF.ScaleY = scaling_.y; } + + public static implicit operator System.Windows.Controls.Canvas(Group d) + { + var tmp = d.cnvs;//new System.Windows.Controls.Canvas(); + System.Windows.Controls.Canvas.SetLeft(d.cnvs, d.datum.x); + System.Windows.Controls.Canvas.SetTop(d.cnvs, d.datum.y); + return tmp; + } + public UIElement toNative() { return this; } + + public void copyToStack(Templates.StackableDrawable d) + { + var tmp = d.groupStack[d.groupStackN]; + tmp.datum = datum; + tmp.cnvs = cnvs; + d.stack[d.stackN] = tmp; + d.groupStackN++; + d.stackN++; + } + public UIElement poolNative(Canvas d) + { + //d.groupPool[d.groupPoolN] = cnvs; + //var tmp = d.groupPool[d.groupPoolN]; + var tmp = cnvs; + System.Windows.Controls.Canvas.SetLeft(tmp, datum.x); + System.Windows.Controls.Canvas.SetTop(tmp, datum.y); + tmp.Visibility = Visibility.Visible; + //d.groupPoolN++; + return tmp; + } + public void modifyNative(System.Windows.Controls.Canvas tmp, Canvas d) + { + d.groupPool[d.groupPoolN] = cnvs; + System.Windows.Controls.Canvas.SetLeft(cnvs, datum.x); + System.Windows.Controls.Canvas.SetTop(cnvs, datum.y); + tmp.Visibility = Visibility.Visible; + } + + } + #endregion + + #endregion + + +} \ No newline at end of file diff --git a/dev4/psychlops/core/graphic/font.cs b/dev4/psychlops/core/graphic/font.cs new file mode 100644 index 0000000..8ef3a14 --- /dev/null +++ b/dev4/psychlops/core/graphic/font.cs @@ -0,0 +1,139 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace Psychlops +{ + public class Font + { + public static Font default_font; + + public enum Style { normal, italic, oblique }; + public enum Weight { normal=400, bold=700 }; + public double size; + public int weight; + public Style style; + public string family; + + static Font() + { + default_font = new Font(); + } + public Font() + { + size = 18; + weight = (int)Weight.normal; + style = Style.normal; + //family = new string[1]; + } + public Font(double size_, int weight_, Style style_, string family_) + { + size = size_; + weight = weight_; + style = style_; + //family = new string[1]; + family = family_; + } + public Font(string family_, double size_, int weight_, Style style_) + { + size = size_; + weight = weight_; + style = style_; + //family = new string[1]; + family = family_; + } + ~Font() + { + } + } + + public partial class Letters : Shape + { + protected string str_; + protected Font font_; + protected double width_, height_; + public Point datum { get; set; } + + public enum HorizontalAlign { not_specified=-1, left=0, center, right }; + //public const HorizontalAlign NOT_SPECIFIED=HorizontalAlign.not_specified, TEXT_ALIGN_LEFT=HorizontalAlign.left, TEXT_ALIGN_CENTER = HorizontalAlign.center, TEXT_ALIGN_RIGHT=HorizontalAlign.right; + public HorizontalAlign align; + + public Letters() + { + fill = Color.white; + stroke = Stroke.null_line; + str_ = ""; + font = Font.default_font; + align = HorizontalAlign.left; + } + public Letters(String init_str) + { + fill = Color.white; + stroke = Stroke.null_line; + str_ = init_str; + font_ = Font.default_font; + align = HorizontalAlign.center; + } + public Letters(String init_str, Font init_font) + { + fill = Color.white; + stroke = Stroke.null_line; + str_ = init_str; + font_ = init_font; + align = HorizontalAlign.right; + } + ~Letters() + { + } + public Font font + { + get { return font_; } + set { font_ = value; } + } + public Font getFont() { return font; } + public string str + { + get { return str_; } + set { str_ = value; } + } + public String getString() { return str; } + public Figure centering(Point p) + { + datum = p; + align = HorizontalAlign.center; + return this; + } + public Figure shift(Point p) + { + datum = datum + p; + return this; + } + public Letters locate(Point p) + { + datum = p; + return this; + } + public Letters locate(double x, double y) + { + datum = new Point(x,y); + return this; + } + + public void draw() + { + Main.drawable.letters(this); + } + + public Color fill { get; set; } + public Stroke stroke { get; set; } + + } + +} diff --git a/dev4/psychlops/core/graphic/image.cs b/dev4/psychlops/core/graphic/image.cs new file mode 100644 index 0000000..64f3c1e --- /dev/null +++ b/dev4/psychlops/core/graphic/image.cs @@ -0,0 +1,154 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + + +namespace Psychlops{ + + + public partial class Image : Internal.PrimitiveFigure + { + public WriteableBitmap buffer; + public Point datum { get; set; } + public Point getDatum() { return datum; } + public Point setDatum(Point p) { datum = p; return datum; } + public Rectangle self_rect; + protected bool AsyncBool; + + public Image() + { + self_rect = new Rectangle(1, 1); + initialize__(1, 1); + } + + public Image(string uri) + { + self_rect = new Rectangle(); + load__(uri); + } + + public Image(int wid, int hei) + { + self_rect = new Rectangle(wid, hei); + initialize__(wid, hei); + } + public Image(double wid, double hei) + { + self_rect = new Rectangle(Math.round(wid), Math.round(hei)); + initialize__((int)Math.round(wid), (int)Math.round(hei)); + } + + public Image set(int wid, int hei) + { + self_rect = new Rectangle(wid, hei); + initialize__(wid, hei); + return this; + } + public Image set(double wid, double hei) + { + self_rect = new Rectangle(Math.round(wid), Math.round(hei)); + initialize__((int)Math.round(wid), (int)Math.round(hei)); + return this; + } + + public Figure shift(Point p) + { + datum = datum + p; + return this; + } + public Figure centering(Point p) + { + datum = new Point( p.x - width / 2.0, p.y - height / 2.0); + return this; + } + public Image move_to(Point p) { datum = p; return this; } + public Image move_to(double x, double y, double z) { datum = new Point(x, y, z); return this; } + public Image locate(Point p) { datum = p; return this; } + public Image locate(double x, double y, double z) { datum = new Point(x, y, z); return this; } + + public void pix(int x, int y, Color col) + { + buffer.SetPixel(x, y, col); + } + + public void release() + { + } + + public void cache(bool on_off = true) + { + } + + public void alpha(int x, int y, double a) + { + buffer.SetPixel(x, y, (byte)(a*255), buffer.GetPixel(x, y)); + } + + public void clear(Color col) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + pix(x, y, col); + } + } + } + + + public void field(System.Func func) + { + field__(func); + } + public void field(System.Func func) + { + field__(func); + } + public void each(System.Func func) + { + field__(func); + } + public void each(System.Func func) + { + field__(func); + } + + public void load(string uri) + { + load__(uri); + } + + public void draw() + { + Main.drawable.image(this); + } + + + public double width { get { return self_rect.width; } } + public double height { get { return self_rect.height; } } + public Point center { get { return new Point(width/2.0, height/2.0); } } + public double getWidth() { return width; } + public double getHeight() { return height; } + public Point getCenter() { return center; } + public double getHcenter() { return width / 2.0; } + public double getVcenter() { return height / 2.0; } + + public double left { get { return datum.x; } } + public double right { get { return datum.x + width; } } + public double top { get { return datum.y; } } + public double bottom { get { return datum.y + height; } } + public double getLeft() { return left; } + public double getRight() { return right; } + public double getTop() { return top; } + public double getBottom() { return bottom; } + + } + + +} \ No newline at end of file diff --git a/dev4/psychlops/core/graphic/module.cs b/dev4/psychlops/core/graphic/module.cs new file mode 100644 index 0000000..8a5be98 --- /dev/null +++ b/dev4/psychlops/core/graphic/module.cs @@ -0,0 +1,368 @@ +using System; +using System.Windows; + + +namespace Psychlops +{ + public static class StaticFunctions + { + public static T[] NewArray(int x) + where T : new() + { + T[] t = new T[x]; + for (int i = 0; i < x; i++) + { + t[i] = new T(); + } + return t; + } + public static T[,] NewArray(int x, int y) + where T : new() + { + T[,] t = new T[x,y]; + for (int i = 0; i < x; i++) + { + for (int j = 0; j < x; j++) + { + t[i,j] = new T(); + } + } + return t; + } + public static T[,,] NewArray(int x, int y, int z) + where T : new() + { + T[,,] t = new T[x, y, z]; + for (int i = 0; i < x; i++) + { + for (int j = 0; j < y; j++) + { + for (int k = 0; k < z; k++) + { + t[i, j, k] = new T(); + } + } + } + return t; + } + } + + public partial struct Point + { + public double x, y, z; + public Point(double dx, double dy, double dz = 0.0) + { + x = dx; + y = dy; + z = dz; + } + public Point set(double dx, double dy, double dz = 0.0) + { + x = dx; + y = dy; + z = dz; + return this; + } + + public static Point operator +(Point lhs, Point rhs) + { + return new Point(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); + } + public static Point operator -(Point lhs, Point rhs) + { + return new Point(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); + } + public override string ToString() + { + return "X:"+ x.ToString() + " Y:"+ y.ToString() + " Z:"+ z.ToString(); + } + } + + + public partial struct Color + { + public double r, g, b, a; + public Color(double lum) + { + r = g = b = lum; + a = 1.0; + } + public Color(double red, double green, double blue, double alpha = 1.0) + { + r = red; + g = green; + b = blue; + a = alpha; + } + public void set(double lum) + { + r = g = b = lum; + a = 1.0; + } + public void set(double red, double green, double blue, double alpha = 1.0) + { + r = red; + g = green; + b = blue; + a = alpha; + } + + public override string ToString() + { + return "R:" + r.ToString() + " G:" + g.ToString() + " B:" + b.ToString() + " A:" + a.ToString(); + } + + public static readonly Color + black = new Color(0, 0, 0, 1), + red = new Color(1, 0, 0, 1), + green = new Color(0, 1, 0, 1), + blue = new Color(0, 0, 1, 1), + yellow = new Color(1, 1, 0, 1), + magenta = new Color(1, 0, 1, 1), + cyan = new Color(0, 1, 1, 1), + gray = new Color(.5, .5, .5, 1), + white = new Color(1, 1, 1, 1), + null_color = new Color(0, 0, 0, 0), + transparent = new Color(0, 0, 0, 0); + + } + + + public interface Drawable + { + Point getCenter(); + void clear(Color col); + void pix(int x, int y, Color col); + void line(Line drawee); + void rect(Rectangle drawee); + void ellipse(Ellipse drawee); + void polygon(Polygon drawee); + void letters(Letters drawee); + void image(Image drawee); + void group(Group drawee); + void shader(ShaderField drawee); + void msg(string s, double x, double y, Color c); + } + + + + public interface Figure + { + Point datum { get; set; } + Figure shift(Point p); + Figure centering(Point p); + void draw(); + } + public static class FigureExtention + { + public static Point getDatum(this Figure target) + { + return target.datum; + } + public static Point setDatum(this Figure target, Point p) + { + target.datum = p; + return target.datum; + } + public static Figure shift(this Figure target, double x, double y, double z = 0.0) + { + return target.shift(new Point(x, y, z)); + } + public static Figure centering(this Figure target) + { + return target.centering(Main.drawable.getCenter()); + } + public static Figure centering(this Figure target, double x, double y, double z = 0.0) + { + return target.centering(new Point(x, y, z)); + } + } + + namespace Internal + { + public interface PrimitiveFigure : Figure + { + UIElement toNative(); + UIElement poolNative(Canvas c); + } + } + + public partial class Group : Internal.PrimitiveFigure + { + System.Collections.Generic.List

list; + System.Windows.Controls.Canvas cnvs; + System.Windows.Media.TransformGroup trans; + System.Windows.Media.TransformCollection transF; + System.Windows.Media.RotateTransform rotateF; + SimpleProcedure setRotation_; + System.Windows.Media.ScaleTransform scaleF; + SimpleProcedure setScaling_; + System.Windows.Media.TranslateTransform translateF; + + bool AsyncBool; + double rotation_; + public double rotation + { + get { return rotation_; } + set { rotation_ = value; rotateF.Dispatcher.BeginInvoke(setRotation_); } + } + public Point axis + { + get; + set; + } + Point scaling_; + public Point scaling + { + get { return scaling_; } + set { scaling_ = value; scaleF.Dispatcher.BeginInvoke(setScaling_); } + } + + AppendFunc1 append_; + + public Group() + { + setRotation_ = new SimpleProcedure(setRotation__); + setScaling_ = new SimpleProcedure(setScaling__); + append_ = new AppendFunc1(append__); + list = new System.Collections.Generic.List
(); + AsyncBool = false; + initialize__(); + while (!AsyncBool) { } + } + + public Group append(Internal.PrimitiveFigure fig) + { + list.Add(fig); + cnvs.Dispatcher.BeginInvoke(append_, fig); + return this; + } + + public Point datum { get; set; } + public Figure shift(Point p) + { + datum = datum + p; + return this; + } + public Figure centering(Point p) + { + datum = p; + return this; + } + public void draw() + { + Main.drawable.group(this); + } + } + + + + public partial class ShaderField : Internal.PrimitiveFigure + { + public Point v1, v2; + + public ShaderField() + { + set(0,0); + } + public ShaderField(double wid, double hei) + { + initialized = false; + set(wid, hei); + } + public ShaderField(Rectangle another) + { + v1 = another.v1; + v2 = another.v2; + } + public ShaderField set(double wid, double hei) + { + v1.set(0, 0, 0); + v2.set(wid, hei, 0); + return this; + } + ShaderField set(Point po1, Point po2) + { + v1 = po1; + v2 = po2; + return this; + } + public ShaderField set(double l, double t, double r, double b) + { + v1.set(l, t, 0); + v2.set(r, b, 0); + return this; + } + public ShaderField set(ShaderField another) + { + v1 = another.v1; + v2 = another.v2; + return this; + } + + public ShaderField resize(double width, double height) + { + Point po = center; + set(width, height); + centering(po); + return this; + } + + + public Point datum + { + get { return v1; } + set { double w = width, h = height; v1 = value; v2 = v1 + new Point(w,h); } + } + public ShaderField move_to(Point p) { datum = p; return this; } + public ShaderField move_to(double x, double y, double z = 0.0) { datum = new Point(x, y, z); return this; } + public ShaderField locate(Point p) { datum = p; return this; } + public ShaderField locate(double x, double y, double z = 0.0) { datum = new Point(x, y, z); return this; } + + public Figure shift(Point p) + { + v1 += p; + v2 += p; + return this; + } + public Figure centering(Point p) + { + double h = width, v = height; + v1.x = p.x - h / 2.0; + v1.y = p.y - v / 2.0; + v2.x = v1.x + h; + v2.y = v1.y + v; + return this; + } + + public virtual void draw() + { + Main.drawable.shader(this); + } + + internal Action setParameters = initialize___; + internal Action initialize__ = initialize___; + private static void initialize___() {} + internal bool initialized = false; + + public double left { get { return v1.x; } } + public double top { get { return v1.y; } } + public double right { get { return v2.x; } } + public double bottom { get { return v2.y; } } + public double width { get { return Math.abs(v1.x - v2.x); } } + public double height { get { return Math.abs(v1.y - v2.y); } } + public Point center + { + get { return new Point((left + right) / 2, (top + bottom) / 2); } + set { centering(value); } + } + public double getLeft() { return left; } + public double getTop() { return top; } + public double getRight() { return right; } + public double getBottom() { return bottom; } + public double getWidth() { return width; } + public double getHeight() { return height; } + public Point getCenter() { return center; } + } + +} \ No newline at end of file diff --git a/dev4/psychlops/core/graphic/shape.cs b/dev4/psychlops/core/graphic/shape.cs new file mode 100644 index 0000000..9c7ed2b --- /dev/null +++ b/dev4/psychlops/core/graphic/shape.cs @@ -0,0 +1,515 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + + + +namespace Psychlops{ + + public interface Shape : Internal.PrimitiveFigure + { + Color fill { get; set; } + Stroke stroke { get; set; } + } + public static class ShapeExtention + { + public static void draw(this Shape drawee, double c) + { + drawee.draw( new Color(c) ); + } + public static void draw(this Shape drawee, Color c) + { + Color tmp_col = drawee.fill; + Stroke tmp_strk = drawee.stroke; + drawee.fill = c; + drawee.stroke = new Stroke(); + drawee.draw(); + drawee.fill = tmp_col; + drawee.stroke = tmp_strk; + } + public static void draw(this Shape drawee, Stroke strk) + { + Color tmp_col = drawee.fill; + Stroke tmp_strk = drawee.stroke; + drawee.fill = new Color(0,0,1,1); + drawee.stroke = strk; + drawee.draw(); + drawee.fill = tmp_col; + drawee.stroke = tmp_strk; + } + } + + public partial struct Stroke + { + public double thick; + public Color color; + public Stroke(Color c, double t) + { + color = c; + thick = t; + } + public void set(Color c, double t) + { + color = c; + thick = t; + } + public static readonly Stroke null_line = new Stroke(Color.null_color, 0); + public static readonly Stroke hair_line = new Stroke(Color.white, 1); + } + + public partial class Line : Shape + { + public Point begin, end; + public Point datum + { + get { return begin; } + set { begin = value; } + } + + //public static Line[] this[int x] { get { return Array(x); } } + //public static Line[,] this[int x, int y] { get { return Array(x, y); } } + public static Line[] Array(int ind) + { + Line[] l = new Line[ind]; + for(int i=0; i end.x ? begin.x : end.x; ; } } + public double bottom { get { return begin.y > end.y ? begin.y : end.y; ; } } + public double width { get { return Math.abs(begin.x - end.x); } } + public double height { get { return Math.abs(begin.y - end.y); } } + public double getLeft() { return left; } + public double getTop() { return top; } + public double getRight() { return right; } + public double getBottom() { return bottom; } + public double getWidth() { return width; } + public double getHeight() { return height; } + + public Color fill { get; set; } + public Stroke stroke { get; set; } + } + + public partial class Rectangle_ + { + public Point v1, v2; + + public double left { get { return v1.x; } } + public double top { get { return v1.y; } } + public double right { get { return v2.x; } } + public double bottom { get { return v2.y; } } + public double width { get { return Math.abs(v1.x - v2.x); } } + public double height { get { return Math.abs(v1.y - v2.y); } } + public double getLeft() { return left; } + public double getTop() { return top; } + public double getRight() { return right; } + public double getBottom() { return bottom; } + public double getWidth() { return width; } + public double getHeight() { return height; } + + + + public Color fill { get; set; } + public Stroke stroke { get; set; } + + public override string ToString() + { + return "Left:" + left.ToString() + " Top:" + top.ToString() + " Right:" + right.ToString() + " Bottom:" + bottom.ToString(); + } + } + + + public partial class Rectangle : Shape + { + public Point v1, v2; + + //public static Rectangle[] this[int x] { get { return Array(x); } } + //public static Rectangle[,] this[int x, int y] { get { return Array(x, y); } } + public static Rectangle[] Array(int ind) + { + Rectangle[] l = new Rectangle[ind]; + for (int i = 0; i < ind; i++) { l[i] = new Rectangle(); } + return l; + } + public static Rectangle[,] Array(int indx, int indy) + { + Rectangle[,] l = new Rectangle[indx, indy]; + for (int i = 0; i < indx; i++) { for (int j = 0; j < indy; j++) { l[i, j] = new Rectangle(); } } + return l; + } + + + public Rectangle() + { + fill = Color.white; + stroke = Stroke.null_line; + set(0,0); + } + public Rectangle(double wid, double hei) + { + fill = Color.white; + stroke = Stroke.null_line; + set(wid, hei); + } + public Rectangle(Rectangle another) + { + fill = Color.white; + stroke = Stroke.null_line; + v1 = another.v1; + v2 = another.v2; + } + public Rectangle set(double wid, double hei) + { + v1.set(0, 0, 0); + v2.set(wid, hei, 0); + return this; + } + Rectangle set(Point po1, Point po2) { + v1 = po1; + v2 = po2; + return this; + } + public Rectangle set(double l, double t, double r, double b) + { + v1.set(l, t, 0); + v2.set(r, b, 0); + return this; + } + public Rectangle set(Rectangle another) + { + v1 = another.v1; + v2 = another.v2; + return this; + } + + public Rectangle resize(double width, double height) + { + Point po = center; + set(width, height); + centering(po); + return this; + } + + + public Point datum + { + get { return v1; } + set { double w = width, h = height; v1 = value; v2 = v1 + new Point(w,h); } + } + public Rectangle move_to(Point p) { datum = p; return this; } + public Rectangle move_to(double x, double y, double z = 0.0) { datum = new Point(x, y, z); return this; } + public Rectangle locate(Point p) { datum = p; return this; } + public Rectangle locate(double x, double y, double z = 0.0) { datum = new Point(x, y, z); return this; } + + public Figure shift(Point p) + { + v1 += p; + v2 += p; + return this; + } + public Figure centering(Point p) + { + double h = width, v = height; + v1.x = p.x - h / 2.0; + v1.y = p.y - v / 2.0; + v2.x = v1.x + h; + v2.y = v1.y + v; + return this; + } + + public virtual void draw() + { + Main.drawable.rect(this); + } + public bool include(double x, double y) + { + return (top <= y) && (left <= x) && (bottom >= y) && (right >= x); + } + public bool include(Point p) + { + return (top <= p.y) && (left <= p.x) && (bottom >= p.y) && (right >= p.x); + } + public bool include(Rectangle rect) + { + return (top <= rect.top) && (left <= rect.left) && (bottom >= rect.bottom) && (right >= rect.right); + } + + public Rectangle alignLeft(double lef) + { + return move_to(lef, getTop(), datum.z); + } + public Rectangle alignTop(double to_) + { + return move_to(getLeft(), to_, datum.z); + } + public Rectangle alignRight(double rig) + { + return move_to(rig - getWidth(), getTop(), datum.z); + } + public Rectangle alignBottom(double bot) + { + return move_to(getLeft(), bot - getHeight(), datum.z); + } + + public void clipped_by(Rectangle source) + { + double t = top, l = left, b = bottom, r = right; + if (top < source.top) { t = source.top; } + if (left < source.left) { l = source.left; } + if (bottom > source.bottom) { b = source.bottom; } + if (right > source.right) { r = source.right; } + set(l, t, r, b); + } + public void clip(Rectangle target) + { + double t = top, l = left, b = bottom, r = right; + if (top < target.top) { t = target.top; } + if (left < target.left) { l = target.left; } + if (bottom > target.bottom) { b = target.bottom; } + if (right > target.right) { r = target.right; } + set(l, t, r, b); + } + + + public double left { get { return v1.x; } } + public double top { get { return v1.y; } } + public double right { get { return v2.x; } } + public double bottom { get { return v2.y; } } + public double width { get { return Math.abs(v1.x - v2.x); } } + public double height { get { return Math.abs(v1.y - v2.y); } } + public Point center { + get { return new Point((left + right) / 2, (top + bottom) / 2); } + set { centering(value); } + } + public double getLeft() { return left; } + public double getTop() { return top; } + public double getRight() { return right; } + public double getBottom() { return bottom; } + public double getWidth() { return width; } + public double getHeight() { return height; } + public Point getCenter() { return center; } + + + + public Color fill { get; set; } + public Stroke stroke { get; set; } + + public override string ToString() + { + return "Left:" + left.ToString() + " Top:" + top.ToString() + " Right:" + right.ToString() + " Bottom:" + bottom.ToString(); + } + } + + + public partial class Ellipse : Shape + { + public Point datum { get; set; } + public double xdiameter, ydiameter; + + //public static Ellipse[] this[int x] { get { return Array(x); } } + //public static Ellipse[,] this[int x, int y] { get { return Array(x, y); } } + public static Ellipse[] Array(int ind) + { + Ellipse[] l = new Ellipse[ind]; + for (int i = 0; i < ind; i++) { l[i] = new Ellipse(); } + return l; + } + public static Ellipse[,] Array(int indx, int indy) + { + Ellipse[,] l = new Ellipse[indx, indy]; + for (int i = 0; i < indx; i++) { for (int j = 0; j < indy; j++) { l[i, j] = new Ellipse(); } } + return l; + } + + public Ellipse() + { + fill = Color.white; + stroke = Stroke.null_line; + set(0,0); + } + public Ellipse(double wid, double hei) + { + fill = Color.white; + stroke = Stroke.null_line; + set(wid, hei); + } + + public Ellipse set(double wid, double hei) + { + xdiameter = wid; + ydiameter = hei; + return this; + } + public Ellipse resize(double width, double height) + { + Point po = center; + set(width, height); + centering(po); + return this; + } + public Figure shift(Point p) + { + datum += p; + return this; + } + public Figure centering(Point p) + { + datum = p; + return this; + } + + public virtual void draw() + { + Main.drawable.ellipse(this); + } + + public double left { get { return datum.x - xdiameter/2.0; } } + public double top { get { return datum.y - ydiameter / 2.0; } } + public double right { get { return datum.x + xdiameter / 2.0; } } + public double bottom { get { return datum.y + ydiameter / 2.0; } } + public double width { get { return Math.abs(xdiameter); } } + public double height { get { return Math.abs(ydiameter); } } + public Point center + { + get { return new Point((left + right) / 2, (top + bottom) / 2); } + set { centering(value); } + } + public double getLeft() { return left; } + public double getTop() { return top; } + public double getRight() { return right; } + public double getBottom() { return bottom; } + public double getWidth() { return width; } + public double getHeight() { return height; } + public Point getCenter() { return center; } + + public Color fill { get; set; } + public Stroke stroke { get; set; } + } + + + public partial class Polygon : Shape + { + public Point datum { get; set; } + public System.Collections.Generic.List vertices; + + public Polygon() + { + fill = Color.white; + stroke = Stroke.null_line; + vertices = new System.Collections.Generic.List(); + } + public Polygon(double[] verts) + { + fill = Color.white; + stroke = Stroke.null_line; + vertices = new System.Collections.Generic.List(); + for (int i=0; i < verts.Length; i+=2) + { + vertices.Add(new Point(verts[i], verts[i+1])); + } + + } + public Polygon(Point[] verts) + { + fill = Color.white; + stroke = Stroke.null_line; + vertices = new System.Collections.Generic.List(); + foreach (Point p in verts) + { + vertices.Add(p); + } + + } + public Polygon append(Point p) { vertices.Add(p); return this; } + public Polygon append(double x, double y) { return append(new Point(x, y, 0.0)); } + public Polygon append(double x, double y, double z) { return append(new Point(x, y, z)); } + + + public Figure shift(Point p) + { + datum = datum + p; + return this; + } + public Figure centering(Point p) + { + datum = p; + return this; + } + + public virtual void draw() + { + Main.drawable.polygon(this); + } + + public Color fill { get; set; } + public Stroke stroke { get; set; } + } + +} \ No newline at end of file diff --git a/dev4/psychlops/core/math/interval.cs b/dev4/psychlops/core/math/interval.cs new file mode 100644 index 0000000..843338f --- /dev/null +++ b/dev4/psychlops/core/math/interval.cs @@ -0,0 +1,214 @@ +using System; +using Psychlops.Internal; + + +namespace Psychlops +{ + + public struct Interval { + public enum OPERATOR { CLOSE, OPEN }; + public const OPERATOR CLOSE = OPERATOR.CLOSE, OPEN = OPERATOR.OPEN; + public struct VAL { + public double value; + public OPERATOR op; + /*public VAL() + { + val = Double.NaN; + op = OPERATOR.CLOSE; + }*/ + public VAL(double v, OPERATOR o) + { + value = v; + op = o; + } + public bool bounded() + { + return !Double.IsNaN(value) && (!Double.IsInfinity(value) || op == OPERATOR.OPEN); + } + } + public VAL begin, end; + + + /*public Interval() + { + begin = new VAL { val = Double.PositiveInfinity, op = OPERATOR.CLOSE }; + end = new VAL { val = Double.NegativeInfinity, op = OPERATOR.CLOSE }; + }*/ + public Interval(double floor_val, double ceil_val) + { + begin.value = floor_val; + begin.op = OPERATOR.CLOSE; + end.value = ceil_val; + end.op = OPERATOR.CLOSE; + } + public Interval(double floor_val, OPERATOR floor_op, double ceil_val, OPERATOR ceil_op) + { + begin.value = floor_val; + begin.op = floor_op; + end.value = ceil_val; + end.op = ceil_op; + } + + + public int int_floor() + { + double v = Math.ceil(begin.value); + if (begin.op == OPEN && v == begin.value) { return (int)v + 1; } + else return (int)v; + } + public int int_floor(int minval) + { + if(begin.valuemaxval) return maxval; + double v = Math.floor(end.value); + if (end.op == OPEN && v == end.value) { return (int)v - 1; } + else return (int)v; + } + + bool includes(double val) + { + bool result = false; + switch(begin.op) { + case OPERATOR.CLOSE: + result = begin.value <= val ? true : false; + break; + case OPERATOR.OPEN: + result = begin.value < val ? true : false; + break; + } + switch(end.op) { + case OPERATOR.CLOSE: + result = result && (end.value >= val ? true : false); + break; + case OPERATOR.OPEN: + result = result && (end.value > val ? true : false); + break; + } + return result; + } + + public bool bounded() + { + return begin.bounded() && end.bounded(); + } + + System.Collections.Generic.IEnumerable step(double steps) + { + if (steps > 0) throw new Exception("Interval: step must be a positive"); + // return new IntervalIEnumerable(this, steps); + Interval it = this; + long front_step = (it.begin.op == Interval.OPERATOR.CLOSE ? -1 : 0); + long back_step = (long)System.Math.Floor((it.end.value - it.begin.value) / steps); + if (it.end.op == Interval.OPERATOR.OPEN && 0 == System.Math.IEEERemainder(it.end.value - it.begin.value, steps)) + { + back_step -= 1; + } + while (front_step <= back_step) + yield return steps * front_step + it.begin.value; + } + + + #region accessor generation + + public static IntervalAcc operator <(double val, Interval rng) + { + return new IntervalAcc { instance = new Interval(val, OPERATOR.OPEN, Double.PositiveInfinity, OPERATOR.CLOSE) }; + } + public static IntervalAcc operator <=(double val, Interval rng) + { + return new IntervalAcc { instance = new Interval(val, OPERATOR.CLOSE, Double.PositiveInfinity, OPERATOR.CLOSE) }; + } + public static IntervalAcc operator >(double val, Interval rng) + { + return new IntervalAcc { instance = new Interval(Double.NegativeInfinity, OPERATOR.CLOSE, val, OPERATOR.OPEN) }; + } + public static IntervalAcc operator >=(double val, Interval rng) + { + return new IntervalAcc { instance = new Interval(Double.NegativeInfinity, OPERATOR.CLOSE, val, OPERATOR.CLOSE) }; + } + public static IntervalAcc operator <(Interval rng, double val) + { + return new IntervalAcc { instance = new Interval(Double.NegativeInfinity, OPERATOR.CLOSE, val, OPERATOR.OPEN) }; + } + public static IntervalAcc operator <=(Interval rng, double val) + { + return new IntervalAcc { instance = new Interval(Double.NegativeInfinity, OPERATOR.CLOSE, val, OPERATOR.CLOSE) }; + } + public static IntervalAcc operator >(Interval rng, double val) + { + return new IntervalAcc { instance = new Interval(val, OPERATOR.OPEN, Double.PositiveInfinity, OPERATOR.CLOSE) }; + } + public static IntervalAcc operator >=(Interval rng, double val) + { + return new IntervalAcc { instance = new Interval(val, OPERATOR.CLOSE, Double.PositiveInfinity, OPERATOR.CLOSE) }; + } + + #endregion + + } + + namespace Internal + { + #region accessor definition + + public struct IntervalAcc + { + public Interval instance; + + public static IntervalAcc operator <(double val, IntervalAcc rng) + { + return new IntervalAcc { instance = new Interval(val, Interval.OPERATOR.OPEN, rng.instance.end.value, rng.instance.end.op) }; + } + public static IntervalAcc operator <=(double val, IntervalAcc rng) + { + return new IntervalAcc { instance = new Interval(val, Interval.OPERATOR.CLOSE, rng.instance.end.value, rng.instance.end.op) }; + } + public static IntervalAcc operator >(double val, IntervalAcc rng) + { + return new IntervalAcc { instance = new Interval(rng.instance.begin.value, rng.instance.begin.op, val, Interval.OPERATOR.OPEN) }; + } + public static IntervalAcc operator >=(double val, IntervalAcc rng) + { + return new IntervalAcc { instance = new Interval(rng.instance.begin.value, rng.instance.begin.op, val, Interval.OPERATOR.CLOSE) }; + } + public static IntervalAcc operator <(IntervalAcc rng, double val) + { + return new IntervalAcc { instance = new Interval(rng.instance.begin.value, rng.instance.begin.op, val, Interval.OPERATOR.OPEN) }; + } + public static IntervalAcc operator <=(IntervalAcc rng, double val) + { + return new IntervalAcc { instance = new Interval(rng.instance.begin.value, rng.instance.begin.op, val, Interval.OPERATOR.CLOSE) }; + } + public static IntervalAcc operator >(IntervalAcc rng, double val) + { + return new IntervalAcc { instance = new Interval(val, Interval.OPERATOR.OPEN, rng.instance.end.value, rng.instance.end.op) }; + } + public static IntervalAcc operator >=(IntervalAcc rng, double val) + { + return new IntervalAcc { instance = new Interval(val, Interval.OPERATOR.CLOSE, rng.instance.end.value, rng.instance.end.op) }; + } + + public static implicit operator Interval(IntervalAcc rhs) + { + return rhs.instance; + } + } + + #endregion + + } + + +} \ No newline at end of file diff --git a/dev4/psychlops/core/math/matrix.cs b/dev4/psychlops/core/math/matrix.cs new file mode 100644 index 0000000..de4a836 --- /dev/null +++ b/dev4/psychlops/core/math/matrix.cs @@ -0,0 +1,113 @@ +using System; + +namespace Psychlops +{ + + + public abstract class Matrix + { + public abstract double this[int row, int col] + { + get; + set; + } + + /* + public abstract Internal.MatrixExpression this[Interval row, Interval col] + { + get; + set; + } + + + /* + public static Matrix operator +(Matrix m, double d) + { + return new Internal.MatrixExpression(); + } + */ + + public abstract int rows { get; } + public abstract int cols { get; } + public int getRows() { return rows; } + public int getCols() { return cols; } + + } + + + namespace Internal + { + + public class MatrixImplementation : Matrix + { + internal double[,] elements; + + public MatrixImplementation(int dnrow, int dncol) + { + elements = new double[dnrow, dncol]; + } + + public override double this[int row, int col] + { + get + { + return elements[row - 1, col - 1]; + } + set + { + elements[row - 1, col - 1] = value; + } + } + /* + public override MatrixExpression this[Interval row, Interval col] + { + get + { + return new MatrixExpression(this, row.int_floor(), col.int_floor(), row.int_ceil(), col.int_ceil()); + } + set + { + for(int r = 0, r<) + elements[row - 1, col - 1] = value; + } + } + * */ + + public override int rows { get { return elements.GetLength(0); } } + public override int cols { get { return elements.GetLength(1); } } + + } + + public class MatrixExpression : Matrix + { + MatrixImplementation imp; + readonly int brow, bcol; + readonly int erow, ecol; + + internal MatrixExpression(MatrixImplementation target, int dbrow, int dbcol, int derow, int decol) + { + brow = dbrow; + bcol = dbcol; + erow = derow; + ecol = decol; + imp = target; + } + + public override double this[int row, int col] + { + get + { + return imp.elements[(row - 1 - brow), (col - 1 - bcol)]; + } + set + { + imp.elements[(row - 1 - brow), (col - 1 - bcol)] = value; + } + } + + public override int rows { get { return erow - brow + 1; } } + public override int cols { get { return ecol - bcol + 1; } } + } + } + +} \ No newline at end of file diff --git a/dev4/psychlops/core/math/util.cs b/dev4/psychlops/core/math/util.cs new file mode 100644 index 0000000..f1aa817 --- /dev/null +++ b/dev4/psychlops/core/math/util.cs @@ -0,0 +1,263 @@ +using System; + +namespace Psychlops +{ + + public static class Math + { + public const double PI = 3.14159265, E = 2.718281828459045, LOG2E = 1.44269504088896340736; + public static Random random_generator; + static Math() + { + random_generator = new Random(); + } + + public static T max(T val1, T val2) where T : IComparable + { + return val1.CompareTo(val2) > 0 ? val1 : val2; + } + public static T min(T val1, T val2) where T : IComparable + { + return val1.CompareTo(val2) < 0 ? val1 : val2; + } + public static void shuffle(X[] array, int n) + { + int a; + X tmp; + for(int i = 1; i < n; i++){ + a = random(i + 1); + tmp = array[i]; + array[i] = array[a]; + array[a] = tmp; + } + } + + + public static double mod(double lhs, double rhs) + { + return lhs - System.Math.Floor(lhs/rhs)*rhs; + } + public static double abs(double x) + { + return System.Math.Abs(x); + } + public static double floor(double x) + { + return System.Math.Floor(x); + } + public static double ceil(double x) + { + return System.Math.Ceiling(x); + } + public static double sin(double x) + { + return System.Math.Sin(x); + } + public static double cos(double x) + { + return System.Math.Cos(x); + } + public static double tan(double x) + { + return System.Math.Tan(x); + } + public static double asin(double x) + { + return System.Math.Asin(x); + } + public static double acos(double x) + { + return System.Math.Acos(x); + } + public static double atan(double x) + { + return System.Math.Atan(x); + } + public static double atan(double y, double x) + { + return System.Math.Atan2(y, x); + } + public static double atan2(double y, double x) + { + return System.Math.Atan2(y, x); + } + public static double sqrt(double x) + { + return System.Math.Sqrt(x); + } + public static double pow(double x, double y) + { + return System.Math.Pow(x,y); + } + public static double exp(double x) + { + return System.Math.Exp(x); + } + public static double log(double x) + { + return System.Math.Log(x); + } + public static double log2(double val) + { + return log(val) * LOG2E; + } + public static double round(double val) + { + return System.Math.Round(val); + } + + public static double radius(double x, double y) + { + return System.Math.Sqrt(x * x + y * y); + } + + public static double random() + { + return (random_generator.NextDouble()); + } + public static int random(int x) + { + return (int)((random_generator.NextDouble()) * x); + } + public static double random(double x) + { + return (random_generator.NextDouble()) * x; + } + public static double random(double x, double y) + { + return (random_generator.NextDouble()) * (y-x) + x; + } + + + public static double gaussian(double x, double sigma) + { + return exp(- (x*x) / (2*sigma*sigma)); + } + + public static double normalDistibution(double x, double mu, double sigma) + { + return exp( -( (x-mu)*(x-mu) / (2*sigma*sigma) ) ) / sqrt(2*PI*sigma*sigma); + } + + public static double cumulativeNormalDistibution(double x, double mu, double sigma) + { + return .5 + .5*Internal.GammaFunction.erf( (x-mu)/(sigma*sqrt(2.0) ) ); + } + + } + + namespace Internal + { + public static class GammaFunction + { + /************ loggamma(x) -- gamma.c より再掲 *************/ + + static readonly double PI = 3.14159265358979324; /* $\pi$ */ + static readonly double LOG_2PI = 1.83787706640934548; /* $\log 2\pi$ */ + static readonly double N = 8; + + static readonly double B0 = 1 ; /* 以下はBernoulli数 */ + static readonly double B1 = (-1.0 / 2.0); + static readonly double B2 = ( 1.0 / 6.0); + static readonly double B4 = (-1.0 / 30.0); + static readonly double B6 = ( 1.0 / 42.0); + static readonly double B8 = (-1.0 / 30.0); + static readonly double B10 = ( 5.0 / 66.0); + static readonly double B12 = (-691.0 / 2730.0); + static readonly double B14 = ( 7.0 / 6.0); + static readonly double B16 = (-3617.0 / 510.0); + + public static double loggamma(double x) /* ガンマ関数の対数 */ + { + double v, w; + + v = 1; + while (x < N) { v *= x; x++; } + w = 1 / (x * x); + return ((((((((B16 / (16 * 15)) * w + (B14 / (14 * 13))) * w + + (B12 / (12 * 11))) * w + (B10 / (10 * 9))) * w + + (B8 / ( 8 * 7))) * w + (B6 / ( 6 * 5))) * w + + (B4 / ( 4 * 3))) * w + (B2 / ( 2 * 1))) / x + + 0.5 * LOG_2PI - Math.log(v) - x + (x - 0.5) * Math.log(x); + } + + public static double p_gamma(double a, double x, double loggamma_a) /* 本文参照 */ + { + int k; + double result, term, previous; + + if (x >= 1 + a) return 1 - q_gamma(a, x, loggamma_a); + if (x == 0) return 0; + result = term = Math.exp(a * Math.log(x) - x - loggamma_a) / a; + for (k = 1; k < 1000; k++) { + term *= x / (a + k); + previous = result; result += term; + if (result == previous) return result; + } + //throw new Exception("p_gamma(): the sequence is not convergent."); + return result; + } + + public static double q_gamma(double a, double x, double loggamma_a) /* 本文参照 */ + { + int k; + double result, w, temp, previous; + double la = 1, lb = 1 + x - a; /* Laguerreの多項式 */ + + if (x < 1 + a) return 1 - p_gamma(a, x, loggamma_a); + w = Math.exp(a * Math.log(x) - x - loggamma_a); + result = w / lb; + for (k = 2; k < 1000; k++) { + temp = ((k - 1 - a) * (lb - la) + (k + x) * lb) / k; + la = lb; lb = temp; + w *= (k - 1 - a) / k; + temp = w / (la * lb); + previous = result; result += temp; + if (result == previous) return result; + } + //throw new Exception("q_gamma(): the sequence is not convergent."); + return result; + } + + public static double p_chisq(double chisq, int df) /* カイ2乗分布の下側確率 */ + { + return p_gamma(0.5 * df, 0.5 * chisq, loggamma(0.5 * df)); + } + + public static double q_chisq(double chisq, int df) /* カイ2乗分布の上側確率 */ + { + return q_gamma(0.5 * df, 0.5 * chisq, loggamma(0.5 * df)); + } + + static readonly double LOG_PI = 1.14472988584940017; /* $\log_e \pi$ */ + + public static double erf(double x) /* Gaussの誤差関数 ${\rm erf}(x)$ */ + { + if (x >= 0) return p_gamma(0.5, x * x, LOG_PI / 2); + else return - p_gamma(0.5, x * x, LOG_PI / 2); + } + + public static double erfc(double x) /* $1 - {\rm erf}(x)$ */ + { + if (x >= 0) return q_gamma(0.5, x * x, LOG_PI / 2); + else return 1 + p_gamma(0.5, x * x, LOG_PI / 2); + } + + public static double p_normal(double x) /* 標準正規分布の下側確率 */ + { + if (x >= 0) return + 0.5 * (1 + p_gamma(0.5, 0.5 * x * x, LOG_PI / 2)); + else return + 0.5 * q_gamma(0.5, 0.5 * x * x, LOG_PI / 2); + } + + public static double q_normal(double x) /* 標準正規分布の上側確率 */ + { + if (x >= 0) return + 0.5 * q_gamma(0.5, 0.5 * x * x, LOG_PI / 2); + else return + 0.5 * (1 + p_gamma(0.5, 0.5 * x * x, LOG_PI / 2)); + } + } + } +} \ No newline at end of file diff --git a/dev5/psychlops/extention/compatibility/compatibility.cs b/dev4/psychlops/extention/compatibility/compatibility.cs similarity index 100% rename from dev5/psychlops/extention/compatibility/compatibility.cs rename to dev4/psychlops/extention/compatibility/compatibility.cs diff --git a/dev5/psychlops/extention/experiments/experiments.cs b/dev4/psychlops/extention/experiments/experiments.cs similarity index 100% rename from dev5/psychlops/extention/experiments/experiments.cs rename to dev4/psychlops/extention/experiments/experiments.cs diff --git a/dev5/psychlops/extention/math/BigFloat.cs b/dev4/psychlops/extention/math/BigFloat.cs similarity index 100% rename from dev5/psychlops/extention/math/BigFloat.cs rename to dev4/psychlops/extention/math/BigFloat.cs diff --git a/dev5/psychlops/extention/math/BigInt.cs b/dev4/psychlops/extention/math/BigInt.cs similarity index 100% rename from dev5/psychlops/extention/math/BigInt.cs rename to dev4/psychlops/extention/math/BigInt.cs diff --git a/dev5/psychlops/extention/math/solver.cs b/dev4/psychlops/extention/math/solver.cs similarity index 100% rename from dev5/psychlops/extention/math/solver.cs rename to dev4/psychlops/extention/math/solver.cs diff --git a/dev5/psychlops/extention/media/dom.cs b/dev4/psychlops/extention/media/dom.cs similarity index 100% rename from dev5/psychlops/extention/media/dom.cs rename to dev4/psychlops/extention/media/dom.cs diff --git a/dev5/psychlops/extention/media/svg.cs b/dev4/psychlops/extention/media/svg.cs similarity index 100% rename from dev5/psychlops/extention/media/svg.cs rename to dev4/psychlops/extention/media/svg.cs diff --git a/dev5/psychlops/extention/standard/CIEColor.cs b/dev4/psychlops/extention/standard/CIEColor.cs similarity index 100% rename from dev5/psychlops/extention/standard/CIEColor.cs rename to dev4/psychlops/extention/standard/CIEColor.cs diff --git a/dev5/psychlops/extention/standard/figures.cs b/dev4/psychlops/extention/standard/figures.cs similarity index 100% rename from dev5/psychlops/extention/standard/figures.cs rename to dev4/psychlops/extention/standard/figures.cs diff --git a/dev5/psychlops/extention/standard/shader.cs b/dev4/psychlops/extention/standard/shader.cs similarity index 100% rename from dev5/psychlops/extention/standard/shader.cs rename to dev4/psychlops/extention/standard/shader.cs diff --git a/dev5/psychlops/extention/standard/widget.cs b/dev4/psychlops/extention/standard/widget.cs similarity index 100% rename from dev5/psychlops/extention/standard/widget.cs rename to dev4/psychlops/extention/standard/widget.cs diff --git a/dev4/psychlops/psychlops.cs b/dev4/psychlops/psychlops.cs new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/dev4/psychlops/psychlops.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev5/Properties/AssemblyInfo.cs b/dev5/Properties/AssemblyInfo.cs index 100e216..e982c4e 100644 --- a/dev5/Properties/AssemblyInfo.cs +++ b/dev5/Properties/AssemblyInfo.cs @@ -2,34 +2,34 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 -// アセンブリに関連付けられている情報を変更するには、 -// これらの属性値を変更してください。 -[assembly: AssemblyTitle("PsychlopsSilverlight4")] +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PsychlopsSilverlight5")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PsychlopsSilverlight4")] -[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyProduct("PsychlopsSilverlight5")] +[assembly: AssemblyCopyright("Copyright © 2012")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから -// 見えなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、 -// その型の ComVisible 属性を true に設定してください。 +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。 -[assembly: Guid("a37cfddf-f475-42fb-804c-66fa131de7fb")] +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ce3deb79-1a00-456c-931d-157d2d01bdcb")] -// アセンブリのバージョン情報は、以下の 4 つの値で構成されています。 +// Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // -// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を -// 既定値にすることができます。 +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dev5/PsychlopsSilverlight5.csproj b/dev5/PsychlopsSilverlight5.csproj new file mode 100644 index 0000000..2352231 --- /dev/null +++ b/dev5/PsychlopsSilverlight5.csproj @@ -0,0 +1,109 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {1A6795E6-DD87-4D31-86F0-59C991EA6A7F} + {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + PsychlopsSilverlight5 + PsychlopsSilverlight5 + Silverlight + v5.0 + $(TargetFrameworkVersion) + false + true + true + + + + v3.5 + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT + true + true + prompt + 4 + + + + + + + + + + + WriteableBitmapEx\WriteableBitmapEx.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev5/WriteableBitmapEx/WriteableBitmapEx.XML b/dev5/WriteableBitmapEx/WriteableBitmapEx.XML index a307dd7..4763699 100644 --- a/dev5/WriteableBitmapEx/WriteableBitmapEx.XML +++ b/dev5/WriteableBitmapEx/WriteableBitmapEx.XML @@ -6,13 +6,13 @@ - Collection of interchange extension methods for the Silverlight WriteableBitmap class. + Collection of draw extension methods for the Silverlight WriteableBitmap class. - Collection of draw extension methods for the Silverlight WriteableBitmap class. + Collection of draw spline extension methods for the Silverlight WriteableBitmap class. - Collection of draw extension methods for the Silverlight WriteableBitmap class. + Collection of filter / convolution extension methods for the Silverlight WriteableBitmap class. Collection of blit (copy) extension methods for the Silverlight WriteableBitmap class. @@ -21,78 +21,375 @@ Collection of draw extension methods for the Silverlight WriteableBitmap class. - Collection of draw spline extension methods for the Silverlight WriteableBitmap class. + Collection of transformation extension methods for the Silverlight WriteableBitmap class. - Collection of transformation extension methods for the Silverlight WriteableBitmap class. + Collection of draw extension methods for the Silverlight WriteableBitmap class. + + + Collection of interchange extension methods for the Silverlight WriteableBitmap class. - + - Copies the Pixels from the WriteableBitmap into a ARGB byte array starting at a specific Pixels index. + Fills the whole WriteableBitmap with a color. The WriteableBitmap. - The starting Pixels index. - The number of Pixels to copy. - The color buffer as byte ARGB values. + The color used for filling. - + - Copies the Pixels from the WriteableBitmap into a ARGB byte array. + Fills the whole WriteableBitmap with an empty color (0). The WriteableBitmap. - The number of pixels to copy. - The color buffer as byte ARGB values. - + - Copies all the Pixels from the WriteableBitmap into a ARGB byte array. + Clones the specified WriteableBitmap. The WriteableBitmap. - The color buffer as byte ARGB values. + A copy of the WriteableBitmap. - + - Copies color information from an ARGB byte array into this WriteableBitmap starting at a specific buffer index. + Applies the given function to all the pixels of the bitmap in + order to set their color. The WriteableBitmap. - The starting index in the buffer. - The number of bytes to copy from the buffer. - The color buffer as byte ARGB values. - The WriteableBitmap that was passed as parameter. + The function to apply. With parameters x, y and a color as a result - + - Copies color information from an ARGB byte array into this WriteableBitmap. + Applies the given function to all the pixels of the bitmap in + order to set their color. The WriteableBitmap. - The number of bytes to copy from the buffer. - The color buffer as byte ARGB values. - The WriteableBitmap that was passed as parameter. + The function to apply. With parameters x, y, source color and a color as a result - + - Copies all the color information from an ARGB byte array into this WriteableBitmap. + Gets the color of the pixel at the x, y coordinate as integer. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. The WriteableBitmap. - The color buffer as byte ARGB values. - The WriteableBitmap that was passed as parameter. + The x coordinate of the pixel. + The y coordinate of the pixel. + The color of the pixel at x, y. - + - Writes the WriteableBitmap as a TGA image to a stream. - Used with permission from Nokola: http://nokola.com/blog/post/2010/01/21/Quick-and-Dirty-Output-of-WriteableBitmap-as-TGA-Image.aspx + Gets the color of the pixel at the x, y coordinate as a Color struct. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. The WriteableBitmap. - The destination stream. + The x coordinate of the pixel. + The y coordinate of the pixel. + The color of the pixel at x, y as a Color struct. - + - Loads an image from the applications resource file and fills this WriteableBitmap with it. + Gets the brightness / luminance of the pixel at the x, y coordinate as byte. The WriteableBitmap. - Only the relative path to the resource file. The assembly name is retrieved automatically. - The WriteableBitmap that was passed as parameter. + The x coordinate of the pixel. + The y coordinate of the pixel. + The brightness of the pixel at x, y. + + + + Sets the color of the pixel using a precalculated index (faster). + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The coordinate index. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel including the alpha value and using a precalculated index (faster). + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The coordinate index. + The alpha value of the color. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel including the alpha value. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The alpha value of the color. + The red value of the color. + The green value of the color. + The blue value of the color. + + + + Sets the color of the pixel using a precalculated index (faster). + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The coordinate index. + The color. + + + + Sets the color of the pixel. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The color. + + + + Sets the color of the pixel using an extra alpha value and a precalculated index (faster). + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The coordinate index. + The alpha value of the color. + The color. + + + + Sets the color of the pixel using an extra alpha value. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The alpha value of the color. + The color. + + + + Sets the color of the pixel using a precalculated index (faster). + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The coordinate index. + The color. + + + + Sets the color of the pixel. + For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop. + + The WriteableBitmap. + The x coordinate (row). + The y coordinate (column). + The color. + + + + Draws a cubic Beziér spline defined by start, end and two control points. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color. + + + + Draws a cubic Beziér spline defined by start, end and two control points. + + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color. + + + + Draws a series of cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Draws a series of cubic Beziér splines each defined by start, end and two control points. + The ending point of the previous curve is used as starting point for the next. + Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). + The color for the spline. + + + + Draws a segment of a Cardinal spline (cubic) defined by four control points. + + The x-coordinate of the 1st control point. + The y-coordinate of the 1st control point. + The x-coordinate of the 2nd control point. + The y-coordinate of the 2nd control point. + The x-coordinate of the 3rd control point. + The y-coordinate of the 3rd control point. + The x-coordinate of the 4th control point. + The y-coordinate of the 4th control point. + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color. + The pixels array. + The width of the bitmap. + The height of the bitmap. + + + + Draws a Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Draws a closed Cardinal spline (cubic) defined by a point collection. + The cardinal spline passes through each point in the collection. + + The WriteableBitmap. + The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). + The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. + The color for the spline. + + + + Gaussian blur kernel with the size 5x5 + + + + + Gaussian blur kernel with the size 3x3 + + + + + Sharpen kernel with the size 3x3 + + + + + Creates a new filtered WriteableBitmap. + + The WriteableBitmap. + The kernel used for convolution. + A new WriteableBitmap that is a filtered version of the input. + + + + Creates a new filtered WriteableBitmap. + + The WriteableBitmap. + The kernel used for convolution. + The factor used for the kernel summing. + The offset used for the kernel summing. + A new WriteableBitmap that is a filtered version of the input. + + + + Creates a new inverted WriteableBitmap and returns it. + + The WriteableBitmap. + The new inverted WriteableBitmap. + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + The blending mode . + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The destination position in the destination bitmap. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. + The blending mode . + + + + Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + + The destination WriteableBitmap. + The rectangle that defines the destination region. + The source WriteableBitmap. + The rectangle that will be copied from the source to the destination. + If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. If the BlendMode is ColorKeying, this color will be used as color key to mask all pixels with this value out. + The blending mode . @@ -174,6 +471,54 @@ The y-coordinate of the end point. The color for the line. + + + Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm + From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + + Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm + From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx + The WriteableBitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + + Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm + From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx + An array containing the pixels as int RGBA value. + The width of one scanline in the pixels array. + The height of the bitmap. + The x-coordinate of the start point. + The y-coordinate of the start point. + The x-coordinate of the end point. + The y-coordinate of the end point. + The color for the line. + + + + + Blends a specific source color on top of a destination premultiplied color + + Array containing destination color + Index of destination pixel + Source alpha (0..255) + Source non-premultiplied red and blue component in the format 0x00rr00bb + Source green component (0..255) + Draws a polyline. Add the first point also at the end of the array if the line should be closed. @@ -273,242 +618,116 @@ A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf - x2 has to be greater than x1 and y2 has to be greater than y1. - - The WriteableBitmap. - The x-coordinate of the bounding rectangle's left side. - The y-coordinate of the bounding rectangle's top side. - The x-coordinate of the bounding rectangle's right side. - The y-coordinate of the bounding rectangle's bottom side. - The color for the line. - - - - A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf - x2 has to be greater than x1 and y2 has to be greater than y1. - - The WriteableBitmap. - The x-coordinate of the bounding rectangle's left side. - The y-coordinate of the bounding rectangle's top side. - The x-coordinate of the bounding rectangle's right side. - The y-coordinate of the bounding rectangle's bottom side. - The color for the line. - - - - A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf - Uses a different parameter representation than DrawEllipse(). - - The WriteableBitmap. - The x-coordinate of the ellipses center. - The y-coordinate of the ellipses center. - The radius of the ellipse in x-direction. - The radius of the ellipse in y-direction. - The color for the line. - - - - A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf - Uses a different parameter representation than DrawEllipse(). - - The WriteableBitmap. - The x-coordinate of the ellipses center. - The y-coordinate of the ellipses center. - The radius of the ellipse in x-direction. - The radius of the ellipse in y-direction. - The color for the line. - - - - Fills the whole WriteableBitmap with a color. - - The WriteableBitmap. - The color used for filling. - - - - Fills the whole WriteableBitmap with an empty color (0). - - The WriteableBitmap. - - - - Clones the specified WriteableBitmap. - - The WriteableBitmap. - A copy of the WriteableBitmap. - - - - Applies the given function to all the pixels of the bitmap in - order to set their color. - - The WriteableBitmap. - The function to apply. With parameters x, y and a color as a result - - - - Applies the given function to all the pixels of the bitmap in - order to set their color. - - The WriteableBitmap. - The function to apply. With parameters x, y, source color and a color as a result - - - - Gets the color of the pixel at the x, y coordinate as integer. - - The WriteableBitmap. - The x coordinate of the pixel. - The y coordinate of the pixel. - The color of the pixel at x, y. - - - - Gets the color of the pixel at the x, y coordinate as a Color struct. - - The WriteableBitmap. - The x coordinate of the pixel. - The y coordinate of the pixel. - The color of the pixel at x, y as a Color struct. - - - - Sets the color of the pixel using a precalculated index (faster). - - The WriteableBitmap. - The coordinate index. - The red value of the color. - The green value of the color. - The blue value of the color. - - - - Sets the color of the pixel. - - The WriteableBitmap. - The x coordinate (row). - The y coordinate (column). - The red value of the color. - The green value of the color. - The blue value of the color. - - - - Sets the color of the pixel including the alpha value and using a precalculated index (faster). - - The WriteableBitmap. - The coordinate index. - The alpha value of the color. - The red value of the color. - The green value of the color. - The blue value of the color. - - - - Sets the color of the pixel including the alpha value. - - The WriteableBitmap. - The x coordinate (row). - The y coordinate (column). - The alpha value of the color. - The red value of the color. - The green value of the color. - The blue value of the color. + x2 has to be greater than x1 and y2 has to be greater than y1. + + The WriteableBitmap. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. - + - Sets the color of the pixel using a precalculated index (faster). + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + x2 has to be greater than x1 and y2 has to be greater than y1. The WriteableBitmap. - The coordinate index. - The color. + The x-coordinate of the bounding rectangle's left side. + The y-coordinate of the bounding rectangle's top side. + The x-coordinate of the bounding rectangle's right side. + The y-coordinate of the bounding rectangle's bottom side. + The color for the line. - + - Sets the color of the pixel. + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). The WriteableBitmap. - The x coordinate (row). - The y coordinate (column). - The color. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. - + - Sets the color of the pixel using an extra alpha value and a precalculated index (faster). + A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf + Uses a different parameter representation than DrawEllipse(). The WriteableBitmap. - The coordinate index. - The alpha value of the color. - The color. + The x-coordinate of the ellipses center. + The y-coordinate of the ellipses center. + The radius of the ellipse in x-direction. + The radius of the ellipse in y-direction. + The color for the line. - + - Sets the color of the pixel using an extra alpha value. + Creates a new cropped WriteableBitmap. The WriteableBitmap. - The x coordinate (row). - The y coordinate (column). - The alpha value of the color. - The color. + The x coordinate of the rectangle that defines the crop region. + The y coordinate of the rectangle that defines the crop region. + The width of the rectangle that defines the crop region. + The height of the rectangle that defines the crop region. + A new WriteableBitmap that is a cropped version of the input. - + - Sets the color of the pixel using a precalculated index (faster). + Creates a new cropped WriteableBitmap. The WriteableBitmap. - The coordinate index. - The color. + The rectangle that defines the crop region. + A new WriteableBitmap that is a cropped version of the input. - + - Sets the color of the pixel. + Creates a new resized WriteableBitmap. The WriteableBitmap. - The x coordinate (row). - The y coordinate (column). - The color. + The new desired width. + The new desired height. + The interpolation method that should be used. + A new WriteableBitmap that is a resized version of the input. - + - Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + Creates a new resized bitmap. - The destination WriteableBitmap. - The rectangle that defines the destination region. - The source WriteableBitmap. - The rectangle that will be copied from the source to the destination. - The blending mode . + The source pixels. + The width of the source pixels. + The height of the source pixels. + The new desired width. + The new desired height. + The interpolation method that should be used. + A new bitmap that is a resized version of the input. - + - Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + Rotates the bitmap in 90° steps clockwise and returns a new rotated WriteableBitmap. - The destination WriteableBitmap. - The rectangle that defines the destination region. - The source WriteableBitmap. - The rectangle that will be copied from the source to the destination. + The WriteableBitmap. + The angle in degress the bitmap should be rotated in 90° steps clockwise. + A new WriteableBitmap that is a rotated version of the input. - + - Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + Rotates the bitmap in any degree returns a new rotated WriteableBitmap. - The destination WriteableBitmap. - The destination position in the destination bitmap. - The source WriteableBitmap. - The rectangle that will be copied from the source to the destination. - If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. - The blending mode . + The WriteableBitmap. + Arbitrary angle in 360 Degrees (positive = clockwise). + if true: keep the size, false: adjust canvas to new size + A new WriteableBitmap that is a rotated version of the input. - + - Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this). + Flips (reflects the image) eiter vertical or horizontal. - The destination WriteableBitmap. - The rectangle that defines the destination region. - The source WriteableBitmap. - The rectangle that will be copied from the source to the destination. - If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. - The blending mode . + The WriteableBitmap. + The flip mode. + A new WriteableBitmap that is a flipped version of the input. @@ -749,142 +968,80 @@ The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. The color for the spline. - - - Draws a cubic Beziér spline defined by start, end and two control points. - - The WriteableBitmap. - The x-coordinate of the start point. - The y-coordinate of the start point. - The x-coordinate of the 1st control point. - The y-coordinate of the 1st control point. - The x-coordinate of the 2nd control point. - The y-coordinate of the 2nd control point. - The x-coordinate of the end point. - The y-coordinate of the end point. - The color. - - - - Draws a cubic Beziér spline defined by start, end and two control points. - - The WriteableBitmap. - The x-coordinate of the start point. - The y-coordinate of the start point. - The x-coordinate of the 1st control point. - The y-coordinate of the 1st control point. - The x-coordinate of the 2nd control point. - The y-coordinate of the 2nd control point. - The x-coordinate of the end point. - The y-coordinate of the end point. - The color. - - + - Draws a series of cubic Beziér splines each defined by start, end and two control points. - The ending point of the previous curve is used as starting point for the next. - Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + Copies the Pixels from the WriteableBitmap into a ARGB byte array starting at a specific Pixels index. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). - The color for the spline. + The starting Pixels index. + The number of Pixels to copy. + The color buffer as byte ARGB values. - + - Draws a series of cubic Beziér splines each defined by start, end and two control points. - The ending point of the previous curve is used as starting point for the next. - Therfore the inital curve needs four points and the subsequent 3 (2 control and 1 end point). + Copies the Pixels from the WriteableBitmap into a ARGB byte array. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn). - The color for the spline. - - - - Draws a segment of a Cardinal spline (cubic) defined by four control points. - - The x-coordinate of the 1st control point. - The y-coordinate of the 1st control point. - The x-coordinate of the 2nd control point. - The y-coordinate of the 2nd control point. - The x-coordinate of the 3rd control point. - The y-coordinate of the 3rd control point. - The x-coordinate of the 4th control point. - The y-coordinate of the 4th control point. - The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. - The color. - The pixels array. - The width of the bitmap. - The height of the bitmap. + The number of pixels to copy. + The color buffer as byte ARGB values. - + - Draws a Cardinal spline (cubic) defined by a point collection. - The cardinal spline passes through each point in the collection. + Copies all the Pixels from the WriteableBitmap into a ARGB byte array. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). - The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. - The color for the spline. + The color buffer as byte ARGB values. - + - Draws a Cardinal spline (cubic) defined by a point collection. - The cardinal spline passes through each point in the collection. + Copies color information from an ARGB byte array into this WriteableBitmap starting at a specific buffer index. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). - The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. - The color for the spline. + The starting index in the buffer. + The number of bytes to copy from the buffer. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. - + - Draws a closed Cardinal spline (cubic) defined by a point collection. - The cardinal spline passes through each point in the collection. + Copies color information from an ARGB byte array into this WriteableBitmap. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). - The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. - The color for the spline. + The number of bytes to copy from the buffer. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. - + - Draws a closed Cardinal spline (cubic) defined by a point collection. - The cardinal spline passes through each point in the collection. + Copies all the color information from an ARGB byte array into this WriteableBitmap. The WriteableBitmap. - The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn). - The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line. - The color for the spline. + The color buffer as byte ARGB values. + The WriteableBitmap that was passed as parameter. - + - Creates a new cropped WriteableBitmap. + Writes the WriteableBitmap as a TGA image to a stream. + Used with permission from Nokola: http://nokola.com/blog/post/2010/01/21/Quick-and-Dirty-Output-of-WriteableBitmap-as-TGA-Image.aspx The WriteableBitmap. - The x coordinate of the rectangle that defines the crop region. - The y coordinate of the rectangle that defines the crop region. - The width of the rectangle that defines the crop region. - The height of the rectangle that defines the crop region. - A new WriteableBitmap that is a cropped version of the input. + The destination stream. - + - Creates a new cropped WriteableBitmap. + Loads an image from the applications resource file and fills this WriteableBitmap with it. The WriteableBitmap. - The rectangle that defines the crop region. - A new WriteableBitmap that is a cropped version of the input. + Only the relative path to the resource file. The assembly name is retrieved automatically. + The WriteableBitmap that was passed as parameter. - + - Creates a new resized WriteableBitmap. + Loads an image from the applications content and fills this WriteableBitmap with it. The WriteableBitmap. - The new desired width. - The new desired height. - The interpolation method that should be used. - A new WriteableBitmap that is a resized version of the input. + Only the relative path to the content file. + The WriteableBitmap that was passed as parameter. @@ -916,6 +1073,11 @@ Multiplies the source color with the destination color. + + + Ignores the specified Color + + No blending just copies the pixels from the source. @@ -936,5 +1098,20 @@ Linear interpolation in 2D using the average of 3 neighboring pixels. + + + The mode for flipping. + + + + + Flips the image vertical (around the center of the y-axis). + + + + + Flips the image horizontal (around the center of the x-axis). + + diff --git a/dev5/WriteableBitmapEx/WriteableBitmapEx.dll b/dev5/WriteableBitmapEx/WriteableBitmapEx.dll index 72956f01fc96eea771a12f3c19f0a4998c260dbc..b08d73d0f100cad516cb9a0699e8db7389bdf259 100644 GIT binary patch literal 31744 zcmeHw34EMKm1lkZ-F->bpVT^R+3j1FELm#ViJZfh&%}x2IJOhFWC!bN+a0ylO1G5Q z35PNyGl78&2EzgqjKfWaaAueg!r%ZCj*tT{%dvrAU^#|4$jmY`1Ttj*@74FUT9TbH zlVs*MyW4iZuZ~w$uU@@+_3Bl9t*iRpBy=H!f$#Ir3-JJ+^yy*v!eA2F@%azN#r^JM z3m(w6J+@%?pW7l^acMx_VdYaA7#x9*cz+`&IAQ zEW|cV7uUV|>Fq&lXGCa0M012FA_WintUU&h!uKS;L|qDxkfIwQrk|eT48BNT_*Q{_ z_1qyuHUFP_`ccBg=WpL6#MKlyiLXV08J|-^M3J-ucyniDypOg^&?Zr}Yh!Tmmlm3$mR(yKEkoICWUjQP}Do)``b=`t*#ivJz z4HZ!iriicj5N#o0iX($Ue5Xwams6*}j+79SQ_*vV(y!9`{@Po5QMB&#eHZ_BPyAng zyyDNa-@SLkj^R&MJ^1P$?tA>pa}Rx~<7a<(>;8YLF|u!8aZ`Uy&!f4YT>px8FP8e@ zm$t4Rzu>Jq?qC1M>)!v3Pk*JfQhU>n4t{OLzW07w{$Sa_mec!o|NX{gUEf`I_ceDe zdyVzI!y5=cw&B|Jaq+ys4M6gbtRgN zB|W3{aMCkduh*q1jRHhG>{$uRXPjqS?vlDp%ufLS5F-AeA3g(-Knzp~;2`3}K^5;m zAx&_{l%}0NAq@l#X*lV-sBgCT6ljCqc>&tG{}?Dl-mR8<+_RWQY0a-6It^G&5L8I( zbaNA$EX)J&?1UW<@A$;41R~y6#5-l;ZACmM5b?Gmp5v9oPnL;acli0|pKrNB@zYTJ z?9(&H!EoaD9uRDn`&5x1X0ju8)M*RVMNw7hv{-x$XvoVY5% zWUuGyUfA#=()Hrq??>hyBU~PeXMnr?$-An*WEe-^n{4H zBxf$fOm`uSx(iX9Duc|J3rTD2O@HZy%LcVfm3R4m zS$TqKxR&M`7=n4n@GLVBdBhu@r&ArIDN06}3+{aOJSt&2ri!D)*_x`Lb_$ny2}?x8 z(dT4Xk2=Xh3Zox$>iFpyNLYXb*n2CG#zCvK7M8TMnjx{_h-GKOfxRt_&YuLn#??SC`{zxtL}4OM?w> zT4i|A_pGF6+jT>pV*$8HFWmhhRGRc6-EZb+H0i;>jwhiPoUB*f{Sf+&i5tQ6mom8$ zdkEPYmeNg|5N4#gSufFjCm|(dn5QWtd99e<*e6&txE@Vn7aAdR+^f-n=wwi(n_>QV zwNOAcFprhpC~NyNSdL;y3XDunieWve2@Fk6!SW?*DhgG}1Z?#Ls(05GXtk+El-5LN z?Zp(08hg!*my|Jn*2y?O>tz)`8)TB}>WIo@GHu9YtCMUsIq9_5DC>gEI?Al8%$y_Z zgUou$tgpp3dq_Ij=jYX)0|L1}Xj9O{9K9{8IlH~o66WeXWz&2XWF z;|2}9)v}?Sl2i$_cZ3NcumP3{p+k8u0XiN_@hB|=Xl@1wS;~S_g@8}xmSS@Os~e-q z_FW&zIp7;L4dZQ-dqG| z|LHY>HN>Ijc8bmO8Ibel2dq$C+Uuo!rubdk>n%WnYJDL>s`W*+-r`zsiCmP&G>Fi& zUfTT|+PlV*>wdsb_mXeZb73B{9Y&jjsTaqR8K2|B zR`_L^MshR6RiMS+{&!yqT9RIC_r*k0G`X&kpUx!-dV++-U11_)$;IEts7<5EO|E-{ z(p>3W@;M5i6?8z>O$m8DB4r_$vaT%T34d!JMSOvR_=Nobt8=g3Py2{xT0&y)2uWh76T1Wg&Q->YlYAu7aqGKb=_?nJi-S4iBP z)3xh-dO-3|o6oKWnefG{D=s}>z%KqWR+ymP&mtT*8+urIqpjoK3b|s6VGW3s8CD}# zlo?hlSMbc=4_(?I>!*50y{Va&K6?C@+bjQuMQH-!s&9c6Cst*|+ z5`SHpkwd*n$XccLrWNAB^B0imh5Eg=?khnQ=Y@~%a3r7}N}y`~l&fj%c?-`4+0d85G!PlDgXjt(om52-NmKD4(j`0Q4attoydiIu z>_o7Wf~#d3!88RgkX;CNQSd^!il|&AS3^y#Mn6_DjjQAZh+jZ)qHvYG5YY=M3i>3; zOWO$<#e#^f3m{nnsSI>jDv|0^lcflPeuJ z8>@u{X}ifzy^bBV(nzkvVxRSlT6k@-XmgI`!h)=qre`NrKA;7u|Ohj84&RxcAbPb9tS*6YB`2yF|i zk}K;FjuK;Iw3jCn3?^Q@HI6tu!xVRTi~oX^mhG@;`hChj{F3t>%X`50doiY1OaCpt z6MNx<$wH>!g250NZnjl($tNz!FJqC3v|`8|Vb2Tf8x?T$5zjS7yf6&r)^##$kE~iE z!wb=-xNxp?t&vqp>8fbVk>MN|>*k2FSen2wY;K8TaF{?EVcJPE{n?jG7GWt_Ew5Hq ziZXnZ-Q&Bz*jK}C8{$c5TzCYbO3mIdd|XCv#D=kI$69qe8?z9!qOH1>jXLhyLy&6= zl3{xAp;comRSj<%!d5jlWYv~EZ_wK7A!**AXT>c*n}Tpo#I2S4AoDBXcuTI-U=MvaDNw-&;doySNPJ#4MjKzd>fJ0vkS=Yl7HIYYQpqZ`L z!PSAfF|1T0$}A5m2g^~&WEke6eiIZ?O^~FUE8u59#nbCGlPh5jNz?4>#n!1>hifHm z>UF)(dI!{MP#f!Mg=j4 zs%TDGz9#cGEpfB+D=jX#V>7QYG+>2fUSqm~*RtAzU)oEMciQuGfEsYy-s{!EdAag> zQwK9ZJeZM4u3Ri@iO~tH*tOsV_DQl9j84Q2Xz^sPtOXkk=mD#+rQuhOGA%*vbzG>HAxfiY&<4d+8_AXRXrhKzkLzbtOoJDr47i(m zVKrAnW{k9n4P>;05OutZ=1^OtBX83R?|{k?68%26SmHX5C7?u39(s`rb|g5GjL`;6sEr zq|*dI#}yh1mhmue7!R|mjEAYJ_nDb6P231i+-TgMGMU^s?beK+$P0;rL`Oul&~jM%=t zUc%4&d4ME(+`LTmK|mmpe&(Bc`f_kQdQ(y9=8;tEC*0Yi;z^jNZl71@%DO&?N)&XLc)KEsO|gEJLJB*~SbWQ-PRY=;cj>I_+k*`2!W z8TBxfuEeRTI$MP&m8>2{1mpOLIPW$Q=OQ*du`ryV$8AHMQJ^-|Buxww)_L;if0X&` z@HrgYgu#hyxT<^(MbF}8HBjq{nx`yyst8e>`q zO9F46B_sZ4|}*8cu;2_$fA)0hjg*P*uTG z8Y%&MK;B6xN3c{bdF<$DCBd;CZNy@-s%|io?3^RXIZp>}Q92gJ{!MM-u$PM=QC`vl z{h#z)m{8#t(K%)vGp!CBCI-^E0XC5m?8QMeGw)^K0FKX8=V*IBy>qapnFH6sU*7wo zItOb2+hB?MD>)42ttIC1{JI0Of%`i%bc$|u7?}D_GRa3Tf#%T7jMZscIxIe4i9iA~ zcBg3@F!&&%jE(C}t?h=Jv2Z;HSGEk-?4z4oO0sbci6tD{5#MJDPQ);Z>BpRw6QRHX z9k`$IWCytKB+fNoq^2YL%ez544x#-yGh>6;d?_$-L4-+)SV^0kO*X>St*&a>=)WmG zgL#)C9e~k(BWx`#tdHQLLc|!s-GRMPc-eL(y=6(SxwR+hEpP3R%SSLLY}q{G^pWuA zAh}MCwq48U6l7UQN*&h|GRIHI<%lnr%gywovKhf<*^FL3*u51k!Zz+%k7v?rA#ZUT z2Um30;VDfu zN-t3+409|F`}#DD&p5n{r(t!*ad8P}Sn<rN%EV~(3ax?A8bdOnx>-h> zuHAKlYz+5oIMeu1Xm^F{poWq2yff`Sd-{bmfO;2|2(&zVe zY_~0Su!==p9SHI5$~IE#cIzCh*?n|tf%ZSp_s37rM3tOLI{!>?9&Du_@1_F_RM~m0 zp1L2wj;kztM%6AA47avfctlV+Zi14&k4hJnNBznplqn-rIjcSyPOf~sbx2hTqEm77 zpySF}bjPUHcxx*(H>$2e)zEkF45I34ziOHr@CVxIGjLJ>84kpiPqsRtW*usR%o?^s zO|cyZHJDOz(VBPde9x929Q(Z9=P$b%;cz zL1&Jus8Ki{VdTMlz|{fjIxh2YnAn2rJeUvj^W;KGzl4tDC#tizq6L=|tN@^CsQs{J5t4lIK3;}f93 zc^>vrR50V!%s`>tmXD%>nevuDs0ByK0g4LZ2EephF5U_ z8tBywTWgw*uIsvTmg;0X+am2oTG@hjqbuz*v@&nRENC}Ug_wk--L~m%TW8fq`-k60 z4fBl+JN-B`WO8W1y zPlO?hgA_oV29SLVYsJ7C8K&D(4paqQ3Q#W|gee)`H#hwgwMM@TzJW~v@6K~C3&)M} zCAE$V0q$* z_8hv2?u_1*!|5~(N!~?zrp?zB>9wSk4(l0;0tekI4cvUE>q9oqXX$RR1Me*~hns$? z?1Cx#QE)mJ?w*5o;Zh#)B5*2?cyzU~9;R-ej>|41UKJkj9*@A-jmq%IDjS}W@W{$I zKUUG*rtrvxDHvce+FoGSfjI0gcB<@!TOiVloAW2ITa3tR7-Mlz1IyS=-&2R%(3hZ` zcO32m4|5=a2!C&f(xOKvsi-SF@Dej!G<^gHGi%j^7S+eN<{Z7>NlKbknP0f$FGLa=~!Q!UJAkeM=n!-=Y-2ly>p_<@H;15 zM!j=_DJ`7>{7~$sPyy^!c;#oB*6xLBf+1n~NO9W_D0zK}_0`qExW~jBz#JT}oI_s) zXH<&Qc0IJz70^<+r|y|};u=5LPv=dfy6~mD{-mo2; zL;E>64TL^ZZFDhR+iIJF|l1?9_u=0fydUXitaA8HJy>LRWJYiQ*-MSmfnBRDc;vFZ1 zu+Ncr=LDV$|DBTrd`2p#igVx+_un}ouVW&RK2HUzN%~MDOJC=EA$mq2dS)PcFa3Ka z&|)wwy^gr}7J7y21?QBz7kJMEuQ^#*+>F*u?s>crG3%`gDz0SFf3-rrdh)6BtE~LO zlzMNXTv^Cv2{x4kt9{%{`;N};GVfxT8@PIL4&I&DdnrV>I$qvPlh@>TQ*bc}6W63K zPh=b!QZ*7}WvUh;;%p7idQ+ub1h?6yvmA0| z@^1j(8UyXU!qywHyU_f%0(3iR@s7)GSsF^}&DRn;H-ySBW~+oPI$rOjleMJD$f1^& zke)P}aU^WDq!EO$s0ieD4)*GcFc$6S8VkIL(AdXB(EA8h{h()B4Y(`@A17{@^To#2 z5u8o6?&dcY!m#&bm>mrJPv4CQ?k}=Oc ze-Jgo&jte$_7N1Woqp5&1XF~yFIHcFw~Wo_OBHy*8Fj?w$1HyJc|Sb;6y;krL0*+T zEw6fh3Meo*OBaJ-yrf17c=tIhqb@(6#u~c<4!Z)5Si;|H5n469E3a!_Qj4-y1OI4! z!<7lyycRfxbpT6AH8`4C0)-`0DRX4&_xHz>Gh?Jl!YyBxUd-@ zT*3}B9I>s!Wq8^pSWw|=Vb>J4A`pr`9ky0jmZHc(pAr8V#ObMnALulH z=FBNwfsQVD5>7g!83tZ>Qb(wv!B2*4`^o6#R5BdFezJ)W__ATw6&p1Al?Hrhux?7X zceJnWxG)Wyi{7Bm1J=~TPxWdcCh=Sh!WQisFXl!MmI&j0aA4hz=b~$O3A!sx{aSR{ zwOcpg*^T%~7x4}G!XV=G34!%{-W6(g5fHyw(j`C|TnBSK#|3645JqM@<~S zm(V-#HSk48xeSHT4Sd3W4=iz~xDD^*{|R7Nd`4UZ__X*cYIzPRaS_od{af0-fM)T#HSdZW*E1AiIlk(rLShVonemQ?F`?=IG^C~*BJiB`kA#~ z#32fnxYTa4Epa_yT-*v67GJb4Li#UlqVt~_S`LLb0*1x)&Q}3n>kw_9lM(a^4s4iwV#8u`Sx8V!hbCRMoOYdkSwFKe|*T};b3{7I-k&*#jC}i`?06AE49~%FPCGz+U?>yiq|sdk5UT%0V&j%n`{53x>roA@>IPKtVL)2Q69wPV@`#FKu^2;HH5M6_TJ zLwQE%nD$ArtsJ{sdq6bP&wd~;2Fz3956ZD)+9Tq9<=EZY*Tplmc!^`8KJ;Pjo1!0! z9+m43{X%?KJW!7PN_=ESc(Y{lT9n-GY zcEipi%>N1Q4rw1L$9^RaXuC;?A@8T)U`~6W9D7QXG$W((ehJRMN*gZ69)^s(k7LKg zJiR9LM$PC`m}&hN;vL$)a_m>)ZtYhbJ0{lZ%R}$hUb#}Qup)h%IIOP=J*4d}$1V?jUVBA3wj=bgR`6p-^*y04Xt$MP zgP}*XH}iO{3k_xe?P_vo(*J*L$RsMtpldqP`0NU>w};qV9SZ)mhC zEH`M~Z39%{N*EGd<@D*{ZlvqtqKe$}Nx6heZ2&aHCO}hcIcp7uARLo%tZ6u^hReBz zbKy);VhF-Cxi*Vyv(Hl7RNq6QvTS8~WxJK>mH4Wy%3M{dGNlr~GW`nBsf(+)UptuQ z-GG+J0NP?7pde0t{AV7crj%Upf-J4ZIvlhbBL+ZxpjkEH^pJ3TVf2*79~JO z+yv-~qkxLGh`0^msJIg_CSDI1NSdmjFpaac`{L8zP|+5Jb@3K%>%HalfRayf?n9jW zML_j?Qs>GI(jT8yipJ4m8!Et=UGr?5%2A_{HJP{8G@ks|!l4lliKm!9KLs>cGEJ5r zi{;1WJ~}UPAAjF=6~3CA|HyRy6QC)c1q?>8auz5JIXmTCqeHX9=Gk$6J$*6wH2Ykh zedpE*mC{w&*6&-EWH{i@x%&Qc;5>(Rb^LA`NmR(Bkum|N&y@4Qsnop=*AoW}dQ@8x zrd%EOHL1n9oUUZ1h0`zK^r;%KcVrwTCmW}QtE+XfwIXGEMfg9Ghv(DQ`S9?3aAwbx z*(v9u=f%k9OI7xKWVo^|g-<;S)=FBz4M8io$t$?UE4a-%&|w|uvJMRMDjDHbGJ2L( zvXYWY9#*zRv-`ioy1WOHuZvey$p2J0n1jQNLzu=iOqD)`9}<)jlp3iB-#jhcEABHt z5gK5aV|XLOqYQ6j_&SEi7`}tyJ%FDMeVD_aVE7rp&xXDV_;BbOfRBW}%W#1>Y3KC? z;sXpn!tj%T4dMZNO#h*H(w+eP0pMSY=j@~Uvm)Z$uKxpk68{Z2_nGh3qoT_B6lzF1 zU)5_6{yuQ#GtMGsm+=px6L79r=kyzzMtyO$Vdq|BjTm)426zNeBK=bc-)DY?;gF?5#}gR&5mFYqVFp zhwWaZ9JO=WSKZs}8?~q0*V{*t@<`~1;vd}}o4D~ONO{hEt38)Fa~tO#)1C-_)OH{b zF<6rh-u8BID)73HBU0inz{TP%fX(9VfGdFGz<>BIgjb380A9kFJ&budhqoad68~U) zER+(jMYvsT6sPTWF(d}vc5$&t0`B6J5m7+6B#r>yDsBT*_!040;CqZSM|=n2MdE8t zJI=p;1=xk$9>(0ru%F>1!;=h8G5n&~8@elWhQmUmno|sW81^%qWO$O{DTZeliV)*7 z>|xl?aFXFkhNl<`oiQ2qFzjbI$?zn@Qw+~A6b9on>|xl?aFXFkhNl>wVJJ+(?_t=_ z@D#%{428wDG3;U3&v26ANrtBwo?$3##%I{Wu%F>1!&3~;Fcc1BGVEd4&v26ANrtBw zo?$3l#%I{Wu%F>1!&3~;FieFBvxi|n!%2oG8J=QzhM|aXoeX;z_A{Jhc#@$ehOyhW zp{W+*>4?|s@7EvFf3E+t{+y0q>NBFoLZijlU|eAwFm5v5X?(%>p)ucFWo|OV;-L9Q z=3VAH&3nv`nV&YlZhp)BKHi{gvxcoB)*o5#wjQz`v;Mbb*z@fb_C@wKd)&U;zE?l3 zXN-rfG3@zk#CHI3LVz8AQW%pU0G6Tyy9wKV@wL!hHmz5*LaC1?j&NXl+P!B*#p4I9A9HjH8s; zlFyBC^5N1@p_tDN`qhZ52XDv@jf>5r6NkmF+%4Iw53Da9MDBQYY|FsVc%e9>y_tl~ z$X4WtjfFf)Zp>!~2o^@O0oukwF)OxAj1Kj8i7kcV=7FI@D!NKsmL1=bJCe=k${`AD zDCQ0x8XwJ;(D5=t5E7e;12=ET-jd4}D}p5sZ=5LJl--p*csM&cK9w*P0$HW(u(&c? z9L?r08<;4Sas#6q@)O0?M^>LDwd+WipStVNKyfTPN(mbaqc;`u6XV3~o3h1mv6FbY zA)gx;I|@0!-EFzi?1o~tlpQ@ZaG2Ytluet~m&4`2`T?;%KXzze0VnQJ~+U5U`t`5I0QDJ5C$7_a9rFpke|rz->*hW ztREiEjpuHHfKLpL7cpZfeAPf{L|ml??WmdzS7wjW{M=4+VLPTpwluyy3(+3LJlT+= z(Zp1_4l7akDriMtBbR4>jv|K^>^%s}CF~ zWydK#44PTMeWs`fr%=|8LJ6F}pmWW8a>L_?RPb`@=eQ^hh*EJ-ln#m`L*giUQWQsv z;%3G>q)>|4gH!=%u25Z!u}h7E!t*0azK2MD4+QG1z|?|a6Bwz%i310q8Hl*tsM3S@ zD+LJLl-)4^>iogqnLXgkp;%ukWe*SLkM7Qm=d+@+0sxkH%6?O$_$zUS-Hd6c2>@Xv0d5XO}U|LNnAFO8=gWAYNA;%FnV-$#*QM^ zj-m0{85;{@M_FOc&e~lZ7|tFZD2~j|+6YOVC|2|r4PTcl<*+8rMuv>iIy5^2Q#?B> z)9?4D+`z%n0)}A-$~ikYaqu85cvE$#G)xd$qsmL%PM=%E2UK1xvr8o~|94_Qk8r3o zRh?gu`8S>$#Pk}k;Ns>Z{v4PpFl!AK2eae**E8J7@G^!Q81gbz!nCFJ+80K`=X53b z<)||vo}tlE#&OF5z8FJw<`^EVN|RiBxQ_mmUP%bu^y{V8mmSDYHcUe4FxH- zl6e6tG3HS2NZE!w_3tWct2LItzs8A|-vIDmC+ z^k6VunBFZD`TX{Q!&8VHp}uiI-QWPpqeBDZ;@V=4zE?t_iuGeyV39})$d}r{#0r)H z2qrHQL451zfdbJH#Qi0N8uNonM94V90s$?mv>5d1el+XH+J{)XT(YB>y9ulB*281@ zEEz3K%BJl2KrUZed+UXp)?Bc9dQ)3>*XoUJNZYcyt9#S>j?Eii zg?_1-!pkWtt57P}(XzGs=vY?l2X&}?h?&22G&fGEBn+2!c65lviw|eV4;6+xj$F`z z&+2Sjr|{>~<=H%BtrSGJF+5aF4ia*UzsjDwjjjvM(nR_!X}B5Fb*0E+ zof`$j`8w9B9Ci=mSg}&#u$aJ3bR3~UA3yR_r2{^Z~uDRWv|`0@AJFfc=B(B^UA-yFEj+ z#U0bcuE(NKMG%Q*MA$*9iNX}d&o>K$6U!k};%*98^{f(M6#xN5PkdS& zG!T9hGE@sqz!5QxkpZF(5}Yts*5u&Qu4*1Z$FXXh5cWH8K7@z~eymCu6*0$&F>SI5 zOly+S2-l=yRorR0fODjb`f-Jcc*JK4Q5`UdND_`lMDd4MND=8VHdYDH{XqT&}=*~BA^ z+Bg6?p{L(ylSyOG^;1wa&}1snN-XzDi0k;v3?y@483Ed#8ae^mxf&J#Fskvb(KI8a zX>kG}6F?47(9u&un>B!NcQ*V5>>swFWd&$oquDU96r0=1{ zOZL9!Ut%{um3gY;s;x(MUizgcesFC=b@i1C@BieVgG$p#i6$bKhDu8ub?_g@3?YnH z)OHo8Y4BLMDJvDiI2b%Tf?4OJLSD$j?X0Nn4<^YprUB+E&9K@;FdxGy-O~xv3&m_V z5G5d+w(ij105E7C>b8a!KtipZptkA5Vh@_|v2BbTK6Q32KWRd!9LlA9%zmny@aTyV zLByXV=m3(cY};}C6hxgx_B@446Y?w{>PM9wrrdHq1`)r(krFZDNh}5~XmWv^d>xo0 zo9vW5Ur6HUMhLALOl7gAnocT)qBYAV--;+3C}Gb5p#S%aInQ^ow`D*VwQKam*V zG}&{?1!e~f0m|mv<+f=GE3Aq}F%&L75wIy}4!^fW;g}sRCqWYtvw5T{>Ql9$UfXh% z{GjFnIBb0z29AbShx)a5K(aV0=tbFrwAv06Nl+TVfqB5&=X2U zvD~wfj%q4vjoXomZb7(e&e9>rnQPZ8G(Q*IMu+@XF!D`~$y$lV-4iwV*)INY8ly&| zVPw@NCXXTC#N_it_0&XD8m$q+T2X9@Uoh3zS7Wp+XM1X9_7 z($Um-AvICTqScXXYH%W#A8*6oE)1%NxrKJItxa9FX=|%OBF6yPt!$ysBp`Kjb}&Vy zd}}tf;}CohxYEBIF4zk@4qTYdu36nS*tKS;ZB=^JK--$(Vc3`1uA%P13kDB#ug2ZM z1)7+SmiP_Yy!h!LdiPTNmoA4L^#@M|k@Uz%k%6re$7e1UycXX_2OLug3rgp-! zlL|^R9W(u%De|oFPP+i(QbaXjceJl*?{3E>MhCZXX?Q3=*QD_yO|&xY4kh;#2gcw{ zDQ|jqA1cBf%3~S)Ek`OjiEsW*_!enx+qCQP+`WJJ)hCx-_V#yVEi4 zH|6tJ4Pd*)-rH=JJ;apz{1TDsQE2~HK#lLw*Q;Tx=n;)M--Dm5NJNl_TU?G%<|OXq zLvsY5_nE5@ybdQ<`vEuO{nB0ddv90cH)QwYxgGBl@YPK5IpdeVqk3>%pS|sg& zs%fBU99TJE9Xv}-XHboX)4Sl`D(rRW((g0c-ynS>?^h1{dlh;qVK;U;v=`dLb&exE zfD%OA2DCAb-OT{@H=E%@Od&OgJfaY8IK*?HGlf`+_d$6sHB_dW_E!gxmql5smwKi8 zLp!l7(hdP*nCl=e6oI=77v03n*xUJgS!?X;e&6}A!JD8ia3x%4-XCH3J2je})rH(>^2blAezYBC60zK3( z>hCV5ejK$0oY{nQ8i_n|#<(2qZ|SoQH}j#qnJe(J0H=i7NG8Y1eIvXV9x;+Bnx9yl z%XM9cx{9c6O71(*pEdY)<4YIRx8W2{|ZLIMuU%yUlit70Id)A4j_yYJ{(g zYoXaOhS8v&D9MAKpOu!v-)c>XG|#X#kO>NP;$I5-f}~t=b2@jYMvdxz;aNs45~m51 zWRK*MctUfP`hK-fAIT#1Js1ay(Y!hzzEAOB6OZK%)Jb!3f~9kco99!Pz%2n9!|8R* q9LbsE*v)0uqhBS^e3*3(>6Cw~9J(h2*5)W4J)Io;|KR^I4*VY@``H%& literal 24576 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%0Aa5wNgumD^D4geQ|!@)Et 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 literal 67072 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

;pOHZU9? znvibArY09(-1sE#0hS`T1Cp+n_wC)+h-FJ&UC9GxSC{O}%NjL&43 zyv_$ges~WsCfcWcm*3M!XM@u@xJO>+uoiTrw`VcOuJwK{`t|X) z6DgQuh6VF?G`{=|SGS3kde(7qO|>%BuASIdfu3}{oW@i6`+5#~yYJSfkKwG+~tPz$Orx$4N;IZt(Ne_4ZB3m?1Lmj{G z-+B`FOpw>P;0b}e_5-1O?TY+5Eq_owRrKp|bl8$WPxCv}L(SMVj5;ci?~44gKwk4c zlwa0ZY5I|( z`l`UbgA?+;-*khd5t>8(#{>C867tI$FE;HdO!8M;oZI)PKUM#Oly@p}F9+o*288y8 zaYH)g^y&G?3_BtpH@Y;VqdGQH{(R)8kv}Ss@0yTr9A^EuTar&8Q8_y(r<8JfkzW>+ zLtKp6+DO{(?UML@;+MatHTM(>=9p{2{KXy9pL4dD_!U0?iLJ%xNZ!l%7_hHnm$&&X zXLCtl^GVqJrxt83MSd$Jc`u`L0IB!L#jW`FZufj$u-Ca6&e$G$qq_kipZ3RCWR7Q` z6&FpvCY}}6O^7Tc9WS@P`qr-tr6n?sd)pSwUtBc((cRd+37ZF$SRF67ulm>TAs?J* z4V+~<(|*P^(~o#NC)F&eUD}YDn7OPnQ#Z4Q*j6to6CX;5Pv}e6%lkbf;Uc$=Evd|Z z4@K1^^5&^L>1iJs%PVU%(@A++&k}h#J*{bB9_N^v<|f8zT}$MVwtLDLzf#|vw(VM1 zLL8-C+enW(()yWnw8n)x^$nR?Sf(DWWno?e7RcwcX2tj`Y#oggcs7JO9wL9Lt3&cy zt77t`)z>c}UyJ;wNS__ZYpsgO&%V^utFel zaF!d3WIRgb<>E@Kq+TK(#q`P=b9w~{y<|KJ^>jTqlYi^%`AYqQGNo;8$L(AL<$h5WV1pF#TffxHGiChy{-_S`YPvqyW| z7$1A~Mr`zZuB-@KP`egDtR z#NSWi+p+CTYkhSv)`Z6NsH2;2$1vKx4jsvR8TG$PA&ln6IJssf@^WjR=4>o4v1TUn za(Wu?FfX}gCi2V}lwk9RuP12SLi@f&{&PY54o=FG)*7f6F5Luup7cwByw=#5 zoo)@(S{maIw{uT}9_Rn#=LnihT1Wjk!nJ2n35T0@<}*uO`mW+8ewsT+c!9RfMNc|j z?)}WU_p%No?Os3Am>RpMHx0XA+l}38vAF>~sd_nYcTq6M{JvgOH{|<$z2;WP_xt+R z3BLb6`c~?#rBeDe`irwq^=ht#<@osdUia?4Z~QuQ%WMkfSfd8GgXJ$hzF#n=W^dzm z@5sn+H(+Bqy3+HqJ_hv(`X?EK5_!2;qJE0yC1Ow_FQ=z|3GvAr*E;!v39T0Lx_4NP-}`G19^)JN)}8O3#kE}ew3__!$5`J{Ifo_W z{oa4q*~A6pwH8ka|fpR{@8Io8tk-e8bzOj^%-r^M4fC$M>@+K7(iy^Ob+tx?Wqzs9PJ zkY7Ps^Febmy3+Hq-gdSF&5l*b563FxC&wz5$0Su9ik%_< z9~ndc?D-tG8Q1#d54Hg5SA9QBs;j9b!<_%q?vBVy*5~^*q&R51|1P4gtIJozJf1A$-=y)TD)?)5vyjx0I_=pY`Y2HNqGPV1(1@w`+1Tqi8AyuQKY zg|X`WpC{P+?X)%a?E5Cyw@ioJ!FC}%(%BrjzTLAqXz$%)?Hwm6v5qziq~z667W8^S$q~tmb^Tw%+=i#xCS9cYIY9@+;u`{tx-y54`V& zd9~%Xjy8$18eJXf1b>CAqiuq}sIF{bBRtjLu9-ZRJCoNwkyq&yrKdGMw9VwXbye$p zm{(6_YLC|W7~jcvO7N?OS^0yL{Na|bbv`U-A!(Jbbw1?#d$Ss(7~hqnbw1>;aP=OR zZTZBa z7{83P#+@;V@#`F)?M{rp*zpxx!f{++_`D~Jxjv4$zL3fKSr-dbzG7x5ulb?a{z56w zfg^tg@^=REe2mF!u7xpk&tjL2k3M$&kus)1I#T@8Sn4Q%#+gs%9Wq&^u{WzZh#gt^ z$gyAt-Z0aFoDSTC?!c1=Do_2Xa#UV>P>14|{7?t$3wnccvg&8aYM$u`Wyc%Y(MFbO zWY#9hYVPTvi*nzoWWSTu9A@u8`4h0GYp#x|@TI5tq9d$ho{=AwlbvbuQ+fxBIYQZT zBYU!u&62@TnR5NRyye>sQs5v=W)&kFK*gM;pyu>4un1(lv+~KyK>5Pu;BnwekUom8 z0LOs8f9>t;N#I9FpX}1~OSFpg6!1#$Y;ZNGn6(C^uIMTdndqY+r1#@zuLD)ijSg=C zKTi4!pz8ag!~4Kb^8arjdD+&q=?<_XcqiBelwEznyGeJzGp#QagC|41p}vlW^FB#M z8+}h%o}ur#4Tr`;6QN1&b8m1U-ZY5o;K%TukG}jJ$eN)tJ2K`|46jpEWXfn8wKL;D zd)@n}Q@Qt9%kMNlbbJl_eo%+1n^;SI`}P+`C-~O7u-y`Lv%Ja zoQ4h8V8gLcf9N=d8j^08CELqYt*v~?oblQeZ@<(v3RwhzdB z$QFYdL&Y=f&SDcSv_44tvzl+%X8LLtIGQwmX8KHeYSSFjB_P{T)1LFeQ%MuPvZsR= zg6hkQK>6r=5ZR~;g3P;V*L6rcJkFD932jJ#Yeh9@;tp)wWmpUAl-7FkEMM-+{AK@@^E>>(y@R=VKmH8uJp`FsK>6{l zAiffP*7Sq-0NP%&_kmZoaRdr*8H2k3hBU z$DrEug0ac>_2682!^rDP&`}KP&1dy>D@gNQ$FI>l3jB@HKQE_u6G9W5-W%jgZxnQm zlsW!_JlO^{R=Y@RjTFkhzk*#HYRvwRbT5ap<8P#ob~pfhi?qg4I0F1TsIe4|1K$QG zI3!+0DO^Wm+6p8dMIFF%K-MG^L%45eVu;3eK73Aiv&50;P>{G0bpxxx?jUg@>H%u3 zCBGCrl5`_@6nHt<2h`XXgI9z7z-z$);Ps&NZUB!ZeLFY^ybBx*-Umufu|)WFQ1agZ zN05FP90`6490THK(TSj9%Q$cwcrwUX8@-=^<4OMlECF}A{NFqL6F3RJ=6`py_O^t* zxo$BUUo8U1fXTFvU5Y{dAdRHnxux}3K5k=Ko^Rt*2jlOfkr@>D{B-&&WYq5nUfFah<(3r%G?i;Z^{?tCpu=+cKP5OP;=xQP`+}m=@UOk z2IPGG8S+-Ov2#T6K;=n)3#6kAJ^ALL2h`Zgw=X6=5WEB& z4pxB^z$M^hko%72kqGH&T@=cPYe5@dv}e*9a-<=@Z_=K}(pXUILRWSl!|B;Uxi(Fs z9Q%&jlzA33*+=|lhv}m%s6JW+DkfeD=Jy_Ba^)!=+IP>U%zJ26p88}yq~lZQDSacT zKDr51AKeTVgP#FMfwzEDK%W1n9<7nSjq*QB;W%HKw&OAvon#yCqYRDHmqE2*19%>I zzbV^~^SE3aG@tFeTT|wGxylo%Z94t~J+)yAs5U$Xstu2WYQqzt+VG#?so;~K>iaI( zQd`ufuswaKpr!rbPbf$2Vb5sV^HcCF(l43veS3!I$}1oiU+T(x3OTh!{ zoZR&eKMfuP|8rnR@DA``a6PDU6pITeZdAGd_si}ix!^T$8~i8Jyn#nj%Ajj^NfnSRmn`d@d(wq-Pb>N4a6_yG73(iT4&Dro20sI`_c6#`N583R^I*_$6x&yo#)cAb@yodA+;Chg?E4mlF&EXfo`$*sI@LurCq`%_u zA#elfZ#aAeyq`4dNc0u(Y48DX8~7mjGw`e6ufVT?zXLade*nJ@DxQA>Y}JZ$8?YVt zFnBQdEwBst2-w46FK{#IJ`S1t(W9gXIUE9hoAmJxnak0CkUkmQ0!{)S15X3L1I_{; z2j_x!gJs~8pxvAJHC*SLT_D9Q(NIYHmToSsl}0g4=STJ&sVD2dKKC?nuR9o~y`0Bn zes?sr5AynoogsB5-Ph!I!j4YF+CuCrM8@tzi?B=ADhr{`kn9w7CE_YhcLNotlW8y4 zgS%;4oqacL%DmIIkhJ3U z3&v`D%}H^Vb1)NUhk&P%9tzF^Igd2gXoiCqk{$_`ftoK1LG}aL3dgT@sPn0l;4cFu zr~MaubaVYt{Oer)jo>My^ZQP%q28~KX8qG{MZR++++zHscfcE;RGn_~2I zZ6kipOT0Gcrl*=Z^ZPSwbMJCZv75GftwEKiVQDQ= z)s*>tLDemK2GT*Fne}G@sP%`wGwV+|csS`rU{6r}r5Qt?W_7--bRUql%$z4*0%{#% z?Z{r{I3nEdTq0YF{yUCFz0SYEbLw8c_a5o6Mdr3*JFm zcrU2(9&q?g@EX$Z-p*raW5c_)Rrai6bEEyb#$9bqLERx8pD}ffG}oP{k+$#WOqugg z$reEJDIIqqt1|8erT;}x+5o5 zsNMEGi7Co%I&SQdj2*h3B>xU$oqg|NDmttgJ@!(@?{r-$yqBQ*?0XFD{WJL_%Q_|7 zgSnSgJI*rwetN7v`yN7jk1nbAA>=h~=Nr9QIX#WHeRp8WoTExF9PbL`wePM32Y`xm z9isX4#pGNWPq2r#@ApfYcm1i170@R5I+mlOv4V%Ly{@l5QS9jja_(q+UoqoY(jNge z9$Eg=&S(`l7hDa_0~K@1z>k91Wn#;v;KxW)S44a@vG-bV4QbZ1>?c9? z6(=BbJ^ZuaZKUr4*{7R5`RBo}ll}tuZEzi^_2LfjXW*US??H63yTH3au6vr;fp16m zkZuRA2es$E7sPL)`#|lz6c9{{!2eh?fBJ_Jqxu_b#N_)SoI>P_H6 z@L`Z^S|;XP3O+)5HMkl4Jg9o^0>4B0OD_Fo@Cnk|13w8q18xOh0G|S12A>AG7NIpV z%Qb-Ldte8g@xQ@t;P=5I@L5oM*dKzNXGYI~=YiWm&aa~9!Rx^vf$UR_JzoNUN}Bz9 z^gkf`?`S*7zS`uoe~x}hntg7x1AGenHTWa&H{h?p--54!JHfw#FXJMowB~vW=_#Pv zeH!>0Y2DZO12_--Be)3s6U*3@;9p6943zvQ!M~IK9QZbP5BLuF5Xd5v{W{1PuRf~4 z${!83g+Bmn4^9L-kpEGae=T?j{2M^UtWSf)tkwVR@}C0_hyPQsC%6+l0(=wf1-4=i z9SI%+Dqi&j6|ee%eZXO0UvMl~Ou5zIv80!R_)_+Aa45J690pzkeuwsa(&5eENcgvd zqrkgB`RBb39|9Gtbib@q?mR;KbFGWT&`3yW?fG?{F$6l!@f6c_t|3{GzxURf6&)pH}2&2 zAb#~?Rp`Yk*PA=8y`u?^O2DzuD5NJt7Y3y0qwgUcgZ_!=o@CNTu}kj7jv`NE`FsL& zDm2E)3=Hy%DC1I|_OY#47J|7274)Vv64rv1E zkkqcR^k7e@7gWTV(GYrUAY-L#`<;<*dQZ{5qt0IUDWW!9F}GhDjBgx5`Z#bBI0QTk z917;o6Y|$Ww-Pt(d*V{&opD9T7DJoi>o^6ywjgV~*_W1pT}e~6*+&VrM$qbPk;9|G zDWv;5)S5AsG&UF<0iH^lb033a!D*za!=QR_I%#+Y&j3#&eYV5%z%xmo57M^md~hb1 z0cU}gpkl=(Afsij4~f6r0I(Hrj9m5>b2aX9*5S*+*+?b zv+saw-al%w`w(O<1sOYYkDop?&)MYtBYr(|dpEn6*X%5U($E}8$CZ?$^lGpZxCZPD zUIofet_J&n9|hm-dS1 zJIYe#IgcV_tD%wbb=-qqTW~#iFvu7id+!Ikll}^L1o!}`c0ULX0v`g$fL{d_A-)Ed zfEz)4-0T7HUE@Fas9EdPS6b`wEpspZB8LmWZ^K{gunPPS(u|G425<}M6%IcPK1Q0p zHfzMkz$Zvw5B?`eADO*^{Ps!Gw}amW?{r9;%^rgNj6vFF_7d1`@L^DUiAO>CKem{? z1Z9}L#Ir7+eS(pD3H%}QzXYEHUk0~2EqzjD3sr7EtAX2Yj3Ke}V6S&$;}cIQ%)-Divja4Ymef0SiFxGevDc z?hBc7wSrdMcOb33p!Pi6w=wtmwI@7?^brn^0y~mE+Tj3@eQ@?Thr>X|Dm%*INuc&h zZlrB|^w*`{`}NNF^~&V+OMjiyV=_OUK9v2A zjzabyBHdFz%3S|!={jQkx?pqHJ-eAbRQ|eWJni>K?|$8LckO)dcKhp^N3gTdUaS{Z z+UuY3w3myw)1N16J+be}v(F3^kq+ZBds-8h`+RR! zLi!|dJXqpzGFU>IJ%vHVY3a>zI1ijinl;{F890gbB8S>noJzU|R6PwKvB#X#o(^V7 z6N5~go(W#<G>C^X7J!$$@ z&*5fid$b%p16%zPtv)K{9Mo1SnP|h_k=IG9_*rZow=cusv&NZYzl0C?gJYAZX z9RzGZ;l^hV$^# z^w%NqZU!|EKLefz(wEu%{wV)U$1w8j``A)Z37?CgR!}jd<6iW}gREibpOc%viU;=n zX!hD+5$Vv5hmn^Z-vVXFBOrTlolgXIl{3sCu_$T}e+tMNZtmB11a(a>PMQ<)ajkv2msSGlyiw6tEa?=;+e(h*yk;EPX9-2)mYp2YBeAGxo>L5x+kcyW?x}qKlf3yd7sP2{vF8J z_g$sTyRV8!>s&$cPRBs>+JeV|iv3)(GO?fgu363Wq2N*AaIhaZ0vrIU?~esXl2+_z zzhYwk3E*PVqrrd^F1LdEkp#1X!Q2u!# zSOLxl<)7uC{4)c}KPy1_=Ov)lmn!g9a0z%9SPgP+6V-s)*VlqS0QJ0u_U(0G3fq=~ z>>W*?b^w=?W{(%G00)7Wf%40h;F;hR;CZ0*ShEbW7MbTEYQZbvvsOfFzz)$w`Y5E` z`R^e5H>44yMS9+GH1XB%DkkFnTw12!sleF~I6-w4hC^{hqS zpNHheSo8b-B3s^%_{>N1eV5xn9e1E7f4&oxKi>svFT=G}a}IeQIF_{h_heAdK3oop zzsljqK|SlR75o~=d6jv_A?~N^@Xs#fXf1r4wd8nau&x(uf$}~%jr`SpT5kUq%2)dz z1J(ZTfNKBaV7~qNYYkh-d)Aew>*qzJ)y|EOj%U$R`+oq6|3gsie-6}q=2~%f0{9|0 z8B{+^1z#d9Ulw2UQm7ca9ef)6707u~^c#@#m*}@(yg%eu_r>}|`+;s)*nu<64xG&= z`{ieSZNF&l4S>QmjrGU$3+t2Vmre1$!p;8|cfI1^O-IvcD8&jpu)^S~=WbWDGp4_-%_eXH49Qii$r zJ0H}VL|Nuqfcp6!((GrmUjd1c*-c;tsMtVV**}4ofZ8KcXZB>U2Alxaf{FnuZ-&FU zATiZEE3^!ZuTzuh@8n$9I;Ay7&v5A3Jgr&&dH>$ju5+|-&B~A#>AOz$I{*Xl?IKpM zPVB*a+@){bdtU6CrM?fx@=9!zkFN&R=W9UvJ;L9DvCOYo>&dh4U$NKIR9;8O&+!}3 zQ~CI3wjFpAco3*Q?gZXUx(mqOz+6wd1sq43*ql8D{47`kN>9EoWZccYF2DXXU3)H} z&GEIzw_kf_`#zME_Rz+cizrX)k&drZemC$N;9&5Z;6#w|H|x$L;02^NgA2e%!3q$+ zFze3apw^uy!4HGq1y_MvLHg2M7y2alJ<_z*TtB)K{66Uo;IrVjLDrrupUw57o#1n% zBW(N;SOBX3(%=iE615`6u(w_~#p-V(^<_KGx*d zAo-zv$3{xmGR=6)|JFh}TCuNe3+j5&!61EWVsKk6MOurNfVb zr@&`kMEG6yx8OuD-Usrd&&B#7zutV2yxVhqp>eVAnn>y1hk0(J1Gom#aWdoF9wato z3qkD69tz6FF5m>xM}gRF_Q3R)i6@dPfiIi_LfAN!x(8raFF5KWeNSX0bU`#2Up|s^ zUH|-5G5PjA4k@CswTo%PS%p1jn({6(=LLS^3;TQ2k#H zb_ExKM}mvNeqaTt7*q)!4_1RGIFwH+t$88Tc-4Zm+w_I{u8uTor%9g$Hjq9KWNkF- z=Q2?1CjM*I%gew<(u}d$SEx_dlFst~GvI3Q3m{`*d|hk8L!_?;HU1w39|Nxep9JxZ z?08nAF*R>AWQ=9SptiwCO?~>nvGf{h=NcW@VznWXR?xTCyUD?CwI_A+)F}~~9 z#r(Ave=SqbtcoUatz`^7t?x-r;PcUrbnjWz8!DpAqY|HW{b!&feU{!o!aE*)(HQ7R z>YM0jLW`ddi)K)x{SJ5j``g`h|G~6~VKB!p9e&#}ZlND_)VaAhqrj$*ZDTR(@)Z$A zLNg)ep@~2Em&R0UwC;mwtxzmhoHW-}w11Vq+y-$yC)x(R0dZ|Y-;0Pe_MErs8GSv+ zZeqh`a2xarRDexI&=_bAq0Vzsv=Ukit%o*4+n}A0Zdi7QhC$Py`A{RY7FrK&fnI=KhuXEpMraH)6RLm|%x-}m zgtkK4pjbPco)+6X-ZZHIP29SQ2i&=_bYR0XYp)OLEOZOB5qbvN2}LaM-JxO7G-y7w61oX`5ZVg80u^+mEJ)AQ&Vj0+wa|KK3-ki? zI@FHEa{yEV&4(JHo1hKQR%ko)7Sx4cJ`9=$RX_^vw?Gdivl;|6{KF z3HX}i+dF*T)%TFAZ=mC+Ts=SWb~+sHT!eJj*|8!UHiLU$`oc?33Ta%DSu|jx`tLG`#Z#TI1%_39zSGn^0x$^$%?Eju0Z-=+K zdcW?<{ktoV_1~0powNH@*S<$xzqI#yuAKHx|5ev7Wv-kPon1e0_T299L}%x#uAWDI zJgv;rwA9XroE@_q{=xOB_m^wQq&Pu8#i;mww*a z{Y8gdJ2vCf#?{BQ0h7Mn;fb#Q+Bno+mjBUHuHNTeJFoD5?J!lqHnkmZ!9!Tz{VFU$ z$sG(~l%id&_)yR}mtP{b6)7@+T7+06`9i1&@<&eJmY)jCmrSZa^`lRI`$YXFpFge0 z3(FVZABpmv{jww~(I4^)TeU057NiQApx8XfUKb@nmQwqQIyYJeZGbjGPe5Et*L898@P9|* zKzFDZ((@GKplOiK%hy0_q1&MK&_-wr^bDju!4Bwk=q;!ngVqHa42^*%Lw>*Az~{}- zR%jcv9rAnb66V_sXfC9EcP*s7_f60S=n3d0=nbeNF;9E%Vo3Y%agg7GSMgc<@HNnF zkoMvmp=Y2SkoMw(p~;Z;;-!%G;|GrBdw}nQ63J{l;qH8meinH`U0vAbWAmKOh`i27{JJF_o9A@6xZ~?tXLP34)hx+d=WVljPKT3IuhS8~ z=j|EI>exJ|!v@IfH10;n<~bcMICXPexJ|6U5D4hkBj-4@&xaY@X8z;^wy9=-51`L)`SS zY5ifo9{Yo!z7ETG^2AIpU)rU4dCPb5LA)H?)ytoqS=dlky|_y6Shj11&2u`$OW$8R zcB5nSoK6rkHy!Tl$^VGGugB&&9b%@hr?z{`IyTSg5HI<@jES>zdn9y%A7l6RSsACx zobkHdd%nA_m2tYvokLx{D|<+*i-2yyOnRc=G#1{!yNJBq~B?7Nyp|n9p;Je>-i&yRWuGg`7PKWv9b@aQm*2bnfHqYrWkGzfg{nVU|i629`nlg zpMKxZ?jPNc{sn8V&2u`;C$FR5(rZb_<~f~U9_csDLY?}c9-HTMf_bFh6KhGw<~bea zk?#ln#+KEI{{WZoKb!CLg88K1EDH2kC+vD}`A$AqZ}fXX&Bh@yYzcIfqav3 zwfRnu`Q-KVTQ|GYv-wUhScBd=S9FAWi8Li2Kz4ken~2FKWGw+ zq2*ioQOuV;J#>!0y#4T03WY)*OX(cdEIX7MvZSAAXWmt1t{3|9y78fhN&J3`=e_fe z{iL$=$)_#%kf`wj`)NaoeUIt-@mC#YIefQi1;1FfWXO4y)#WwI>xZ0?DX%OWGJQ$e z;>zm9L(FBBvISL{iIojY%4(-HHe{;nD{HFjqZ3Y;IC}W#*@U&*`yL_m8th)w^U!>0{gv$q^k5LkoQJ1 zgS7JW9$`XPlS6r{$YaVhIh3b&>oVP&9Lm#say1soLwS0iEz_mRp*+2twxvAXlWVE% zmy-8XlM-$F^iI&0^z?quzAfnKot!P{>HV4u7~kZfHs~F5HzhMjD^Kra>)JwITdupX zL^e63r{_LyXp&>g)Vs(wwUD=pYb{)&XmY4by{l_nlN?*7-n+%AbCW}Qdbd__lN_t3 z_h+@Fr*~wvq^I{_vE(*6RG;2&)smm-y;W?-njF&8`>3Wh$+33oJyR{k3B5}y7YGA= zm8thdbq;v`qw@4ls6x*OPFK=@ zE$MA1uO*+?dmCEP(>oZ-Tkv^(uehZcqVEXbDx(sISo$7hOY>ddX)H*R<3Ieb?>Uq!MZA>APVoTPSmWk^L>fq^iX4C+|ne4ARP5 z-KS-}XUWrZ^vOf4pE0m4^{0N{SqE@b-Q|z7G9%>Nx zGM#LW$A?tZmXbu~Sy+9iAv&GHH$nZ7*L~nKPAZ+8S(d4)sa=w(ZYZ5yS+y)vS5>*V zqM@|Oj;gewZfW%;rL)bgsAjSg3X^K8DXSsB6C1LyeqYu%yP~W%v)^i)SXRFuE8|+$ zeruU7MD4fAX4mT8$$qPDPF-1b{i2$>C1nkq8^o)P-hS=v*EIJ;_K!YrXj0kZ;NY{( z`vCpDX~^)@jsXSwN?ug_`2StB`suFI?ar!7 z`e-P>UOKcQQ&yg-TToTA@Dj^dR9Tg&AG+kE(()ki)CYlQx$cnbRBnMemCH1`z+>-$ zmp08#a2DFYlgXU|D)1Ov_IcIyib4O3iWd-${wehmnf@8|GLlvQjC$)$y!vO<+z<<0 zaSL?T&;`=Wd~E@LIQl|%?YB?t=xN7xkj@l1p*3e3+TeFXsXh2fQ01iA6>vfr?E*Qe z(k~P=dxe6m1IGud2Xy|~9cop;xj%S_+=JsL-02#;M|=F1h;4ARejWQ{TyQX-b$-c` z5nTpd4y}abmGw{qv=mwfEr%MR6%Z=sTvi^e>j$z&9xktz2g`fqxg8;y#%Z{j6}q;; z=_PM2vG(hlq^<{WDrc_u9SL%}WZs`q1onY6dpI>Q*9JJ9;9KB!uda5mHRs!5Tu*}3 zmgAry&`@X?G#uhK0pG~7=X|5U6QI$M`t3yMBxo!&4&t_vzK?0H8I*t%po!2VXfiYf znhKo?O@pRGr$MJfIzKuCIukkz(mByA=xk^(*&=RN`s)1^uOQAaG!13?Z16R4E(Clock lhs, Clock rhs) + { + return lhs.ticks > rhs.ticks; + } + public static bool operator <(Clock lhs, Clock rhs) + { + return lhs.ticks < rhs.ticks; + } + public static bool operator >=(Clock lhs, Clock rhs) + { + return lhs.ticks >= rhs.ticks; + } + public static bool operator <=(Clock lhs, Clock rhs) + { + return lhs.ticks <= rhs.ticks; + } + + public override string ToString() + { + return ticks.ToString(); + } + } + +} diff --git a/dev5/psychlops/core/devices/hid.cs b/dev5/psychlops/core/devices/hid.cs new file mode 100644 index 0000000..ff824ae --- /dev/null +++ b/dev5/psychlops/core/devices/hid.cs @@ -0,0 +1,155 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; + + +namespace Psychlops +{ + public class HID + { + public struct Button + { + private static object locker; + static Button() + { + locker = new object(); + } + private bool pushed_, released_, pressed_; + public void down() + { + lock (locker) + { + pushed_ = true; + pressed_ = true; + } + } + public void up() + { + lock (locker) + { + released_ = true; + pressed_ = false; + } + } + public bool pushed() { lock (locker) { bool tmp = pushed_; pushed_ = false; return tmp; } } + public bool released() { lock (locker) { bool tmp = released_; released_ = false; return tmp; } } + public bool pressed() { return pressed_; } + } + } + + + public static class Mouse + { + static internal Point position_; + static internal System.Windows.UIElement _prime; + static public HID.Button left, right, middle; + static internal Point wheelDelta_; + + static public Point position { get { return position_; } } + static public int x { get { return (int)position_.x; } } + static public int y { get { return (int)position_.y; } } + static public Point wheelDelta { get { return wheelDelta_; } } + + static public void show() { } + static public void hide() { } + + static public void Canvas_MousePos(object sender, MouseEventArgs e) + { + System.Windows.Point p = e.GetPosition(_prime); + position_.set(p.X, p.Y); + } + static public void Canvas_LDown(object sender, MouseEventArgs e) + { + left.down(); + System.Windows.Point p = e.GetPosition(_prime); + position_.set(p.X, p.Y); + } + static public void Canvas_LUp(object sender, MouseEventArgs e) + { + left.up(); + System.Windows.Point p = e.GetPosition(_prime); + position_.set(p.X, p.Y); + } + static public void Canvas_MouseWheel(object sender, MouseWheelEventArgs e) + { + wheelDelta_.y += e.Delta; + System.Windows.Point p = e.GetPosition(_prime); + position_.set(p.X, p.Y); + } + } + + + public static class Keyboard + { + public class IKey + { + //internal static HID.Button[] states; + //int p; + internal HID.Button state; + public void down() { state.down(); } + public void up() { state.up(); } + public bool pushed() { return state.pushed(); } + public bool released() { return state.released(); } + public bool pressed() { return state.pressed(); } + } + static public IKey + alt, shift, ctrl, + esc, spc, rtn, + left, right, up, down, + tab, + comma, period, + q, w, e, r, t, y, u, i, o, p, + a, s, d, f, g, h, j, k, l, + z, x, c, v, b, n, m, + one, two, three, four, five, six, seven, eight, nine, zero, + pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7, pad8, pad9; + static private System.Collections.Generic.Dictionary map; + static public void Canvas_KeyUp(object sender, KeyEventArgs e) + { + if (map.ContainsKey(e.Key)) map[e.Key].up(); + } + static public void Canvas_KeyDown(object sender, KeyEventArgs e) + { + if (map.ContainsKey(e.Key)) map[e.Key].down(); + } + + + static Keyboard() + { + /* + esc = new Key(0); spc = new Key(1); rtn = new Key(2); + left = new Key(3); right = new Key(4); up = new Key(5); down = new Key(6); + comma = new Key(7); period = new Key(8); + q = new Key(32); w = new Key(33); e = new Key(34); r = new Key(35); t = new Key(36); y = new Key(37); u = new Key(38); i = new Key(39); o = new Key(40); p = new Key(41); + a = new Key(42); s = new Key(43); d = new Key(44); f = new Key(45); g = new Key(46); h = new Key(47); j = new Key(48); k = new Key(49); l = new Key(50); + z = new Key(51); x = new Key(52); c = new Key(53); v = new Key(54); b = new Key(55); n = new Key(56); m = new Key(57); + one = new Key(80); two = new Key(81); three = new Key(82); four = new Key(83); five = new Key(84); six = new Key(85); seven = new Key(86); eight = new Key(87); nine = new Key(88); zero = new Key(96); + pad0 = new Key(96); pad1 = new Key(97); pad2 = new Key(98); pad3 = new Key(99); pad4 = new Key(100); pad5 = new Key(101); pad6 = new Key(102); pad7 = new Key(103); pad8 = new Key(104); pad9 = new Key(105); + */ + alt = new IKey(); shift = new IKey(); ctrl = new IKey(); + esc = new IKey(); spc = new IKey(); rtn = new IKey(); + left = new IKey(); right = new IKey(); up = new IKey(); down = new IKey(); + comma = new IKey(); period = new IKey(); + q = new IKey(); w = new IKey(); e = new IKey(); r = new IKey(); t = new IKey(); y = new IKey(); u = new IKey(); i = new IKey(); o = new IKey(); p = new IKey(); + a = new IKey(); s = new IKey(); d = new IKey(); f = new IKey(); g = new IKey(); h = new IKey(); j = new IKey(); k = new IKey(); l = new IKey(); + z = new IKey(); x = new IKey(); c = new IKey(); v = new IKey(); b = new IKey(); n = new IKey(); m = new IKey(); + one = new IKey(); two = new IKey(); three = new IKey(); four = new IKey(); five = new IKey(); six = new IKey(); seven = new IKey(); eight = new IKey(); nine = new IKey(); zero = new IKey(); + pad0 = new IKey(); pad1 = new IKey(); pad2 = new IKey(); pad3 = new IKey(); pad4 = new IKey(); pad5 = new IKey(); pad6 = new IKey(); pad7 = new IKey(); pad8 = new IKey(); pad9 = new IKey(); + + map = new System.Collections.Generic.Dictionary(); + map.Add(Key.Alt, alt); map.Add(Key.Shift, shift); map.Add(Key.Ctrl, ctrl); + map.Add(Key.Escape, esc); map.Add(Key.Space, spc); map.Add(Key.Enter, rtn); + map.Add(Key.Left, left); map.Add(Key.Right, right); map.Add(Key.Up, up); map.Add(Key.Down, down); + map.Add(Key.Tab, tab); + map.Add(Key.PageDown, comma); map.Add(Key.PageUp, period); + map.Add(Key.Q, q); map.Add(Key.W, w); map.Add(Key.E, e); map.Add(Key.R, r); map.Add(Key.T, t); map.Add(Key.Y, y); map.Add(Key.U, u); map.Add(Key.I, i); map.Add(Key.O, o); map.Add(Key.P, p); + map.Add(Key.A, a); map.Add(Key.S, s); map.Add(Key.D, d); map.Add(Key.F, f); map.Add(Key.G, g); map.Add(Key.H, h); map.Add(Key.J, j); map.Add(Key.K, k); map.Add(Key.L, l); + map.Add(Key.Z, z); map.Add(Key.X, x); map.Add(Key.C, c); map.Add(Key.V, v); map.Add(Key.B, b); map.Add(Key.N, n); map.Add(Key.M, m); + map.Add(Key.D1, one); map.Add(Key.D2, two); map.Add(Key.D3, three); map.Add(Key.D4, four); map.Add(Key.D5, five); map.Add(Key.D6, six); map.Add(Key.D7, seven); map.Add(Key.D8, eight); map.Add(Key.D9, nine); map.Add(Key.D0, zero); + map.Add(Key.NumPad0, pad0); map.Add(Key.NumPad1, pad1); map.Add(Key.NumPad2, pad2); map.Add(Key.NumPad3, pad3); map.Add(Key.NumPad4, pad4); map.Add(Key.NumPad5, pad5); map.Add(Key.NumPad6, pad6); map.Add(Key.NumPad7, pad7); map.Add(Key.NumPad8, pad8); map.Add(Key.NumPad9, pad9); + } + } + +} \ No newline at end of file diff --git a/dev5/psychlops/core/graphic/canvas.cs b/dev5/psychlops/core/graphic/canvas.cs index e67c6ae..0560bea 100644 --- a/dev5/psychlops/core/graphic/canvas.cs +++ b/dev5/psychlops/core/graphic/canvas.cs @@ -18,10 +18,13 @@ namespace Psychlops internal static partial class CONST { - internal static readonly Int32 MAX_OBJ_N = 1500; - internal static readonly Int32 MOBJ_N = 1000; - internal static readonly Int32 COBJ_N = 300; - internal static readonly Int32 HOBJ_N = 100; + internal static readonly Int32 MAX_OBJ_N = 4096;//65535; + internal static readonly Int32 MOBJ_N = 2048;//16384; + internal static readonly Int32 COBJ_N = 300;//4096; + internal static readonly Int32 HOBJ_N = 100;//1024; + internal static readonly Int32 MOBJ_I = 500; + internal static readonly Int32 COBJ_I = 300; + internal static readonly Int32 HOBJ_I = 100; } namespace Templates @@ -33,21 +36,21 @@ namespace Psychlops internal Internal.PrimitiveFigure[] stack; internal int stackN = 0; internal Line[] lineStack; - internal int lineStackN = 0; + internal int lineStackN = 0, lineStackMAX = 0; internal Rectangle[] rectStack; - internal int rectStackN = 0; + internal int rectStackN = 0, rectStackMAX = 0; internal ShaderField[] shaderStack; - internal int shaderStackN = 0; + internal int shaderStackN = 0, shaderStackMAX = 0; internal Ellipse[] ellipseStack; - internal int ellipseStackN = 0; + internal int ellipseStackN = 0, ellipseStackMAX = 0; internal Polygon[] polygonStack; - internal int polygonStackN = 0; + internal int polygonStackN = 0, polygonStackMAX = 0; internal Letters[] lettersStack; - internal int lettersStackN = 0; + internal int lettersStackN = 0, lettersStackMAX = 0; internal Image[] imageStack; - internal int imageStackN = 0; + internal int imageStackN = 0, imageStackMAX = 0; internal Group[] groupStack; - internal int groupStackN = 0; + internal int groupStackN = 0, groupStackMAX = 0; public StackableDrawable() { @@ -60,24 +63,30 @@ namespace Psychlops lettersStack = new Letters[CONST.COBJ_N]; imageStack = new Image[CONST.HOBJ_N]; groupStack = new Group[CONST.HOBJ_N]; - for (int i = 0; i < CONST.MOBJ_N; i++) + for (int i = 0; i < CONST.MOBJ_I; i++) { - lineStack[i] = new Line(0,0,0,0); rectStack[i] = new Rectangle(); - ellipseStack[i] = new Ellipse(); } - for (int i = 0; i < CONST.COBJ_N; i++) + rectStackMAX = CONST.MOBJ_I; + for (int i = 0; i < CONST.COBJ_I; i++) { + lineStack[i] = new Line(0, 0, 0, 0); + ellipseStack[i] = new Ellipse(); shaderStack[i] = new ShaderField(); polygonStack[i] = new Polygon(); - lettersStack[i] = new Letters(); } - for (int i = 0; i < CONST.HOBJ_N; i++) + lineStackMAX = CONST.COBJ_I; + shaderStackMAX = CONST.COBJ_I; + ellipseStackMAX = CONST.COBJ_I; + polygonStackMAX = CONST.COBJ_I; + for (int i = 0; i < CONST.HOBJ_I; i++) { imageStack[i] = new Image(); + lettersStack[i] = new Letters(); // groupStack[i] = new Group(); } - + imageStackMAX = CONST.HOBJ_I; + lettersStackMAX = CONST.HOBJ_I; } @@ -116,30 +125,30 @@ namespace Psychlops internal System.Windows.Point[] pointPool; internal int pointPoolN; internal SolidColorBrush[] brushPool; - internal int brushPoolN; + internal int brushPoolN, brushPoolMAX; internal System.Windows.Controls.Canvas[] UIElementPool; internal int UIElementPoolN; internal int lastVisibleN; internal System.Windows.Shapes.Line[] linePool; - internal int linePoolN; + internal int linePoolN, linePoolMAX; internal System.Windows.Shapes.Rectangle[] dummyRectPool; internal System.Windows.Shapes.Rectangle[] rectPool; - internal int rectPoolN; + internal int rectPoolN, rectPoolMAX; internal System.Windows.Shapes.Rectangle[] shaderPool; - internal int shaderPoolN; + internal int shaderPoolN, shaderPoolMAX; internal System.Windows.Shapes.Ellipse[] ellipsePool; - internal int ellipsePoolN; + internal int ellipsePoolN, ellipsePoolMAX; internal System.Windows.Shapes.Polygon[] polygonPool; - internal int polygonPoolN; + internal int polygonPoolN, polygonPoolMAX; internal System.Windows.Controls.TextBlock[] lettersPool; - internal int lettersPoolN; + internal int lettersPoolN, lettersPoolMAX; internal System.Windows.Controls.Image[] imagePool; - internal int imagePoolN; + internal int imagePoolN, imagePoolMAX; internal Dictionary imagePoolT; internal System.Windows.Controls.Canvas[] groupPool; - internal int groupPoolN; + internal int groupPoolN, groupPoolMAX; #region initializer @@ -211,31 +220,31 @@ namespace Psychlops pointPool = new System.Windows.Point[CONST.MOBJ_N]; brushPool = new SolidColorBrush[CONST.MOBJ_N]; - linePool = new System.Windows.Shapes.Line[CONST.MOBJ_N]; rectPool = new System.Windows.Shapes.Rectangle[CONST.MOBJ_N]; - ellipsePool = new System.Windows.Shapes.Ellipse[CONST.MOBJ_N]; for (int i = 0; i < CONST.MOBJ_N; i++) { - pointPool[i] = new System.Windows.Point(); + //pointPool[i] = new System.Windows.Point(); brushPool[i] = new SolidColorBrush(); - linePool[i] = new System.Windows.Shapes.Line(); rectPool[i] = new System.Windows.Shapes.Rectangle(); - ellipsePool[i] = new System.Windows.Shapes.Ellipse(); } + ellipsePool = new System.Windows.Shapes.Ellipse[CONST.COBJ_N]; + linePool = new System.Windows.Shapes.Line[CONST.COBJ_N]; shaderPool = new System.Windows.Shapes.Rectangle[CONST.COBJ_N]; polygonPool = new System.Windows.Shapes.Polygon[CONST.COBJ_N]; - lettersPool = new System.Windows.Controls.TextBlock[CONST.COBJ_N]; for (int i = 0; i < CONST.COBJ_N; i++) { + linePool[i] = new System.Windows.Shapes.Line(); + ellipsePool[i] = new System.Windows.Shapes.Ellipse(); shaderPool[i] = new System.Windows.Shapes.Rectangle(); polygonPool[i] = new System.Windows.Shapes.Polygon(); - lettersPool[i] = new System.Windows.Controls.TextBlock(); } + lettersPool = new System.Windows.Controls.TextBlock[CONST.HOBJ_N]; imagePool = new System.Windows.Controls.Image[CONST.HOBJ_N]; imagePoolT = new Dictionary(CONST.HOBJ_N); groupPool = new System.Windows.Controls.Canvas[CONST.HOBJ_N]; for (int i = 0; i < CONST.HOBJ_N; i++) { + lettersPool[i] = new System.Windows.Controls.TextBlock(); imagePool[i] = new System.Windows.Controls.Image(); imagePoolT.Add(imagePool[i].GetHashCode(), false); groupPool[i] = new System.Windows.Controls.Canvas(); @@ -878,6 +887,14 @@ namespace Psychlops public UIElement toNative() { return this; } public void copyToStack(Templates.StackableDrawable d) { + if (d.rectStackMAX <= d.rectStackN) + { + d.rectStackMAX += CONST.HOBJ_I; + for (int i = d.rectStackN; i < d.rectStackMAX; i++) + { + d.rectStack[i] = new Rectangle(); + } + } var tmp = d.rectStack[d.rectStackN]; tmp.v1 = v1; tmp.v2 = v2; diff --git a/dev5/psychlops/extension/compatibility/compatibility.cs b/dev5/psychlops/extension/compatibility/compatibility.cs new file mode 100644 index 0000000..fe1889f --- /dev/null +++ b/dev5/psychlops/extension/compatibility/compatibility.cs @@ -0,0 +1,106 @@ + + + +namespace Psychlops +{ + + /* + namespace Internal + { + public class IndependentAccessor + { + Widgets.Slider sld; + public static IndependentAccessor operator <<( IndependentAccessor ind, Widgets.Slider sl ) + { + ind.sld = sl; + Psychlops.Main.canvas.independent_list.Add(sl); + return ind; + } + public static IndependentAccessor operator | (IndependentAccessor ind, string str) + { + label = l; + Psychlops.Main.canvas.independent_list.Add(sl); + return ind; + } + } + } + **/ + public class Procedure + { + System.Action acc; + System.Action acc_c; + int func = 0; + + public void setDesign() + { + } + public void setProcedure(System.Action ac) + { + acc = ac; + func = 1; + } + public void setProcedure(System.Action ac) + { + acc_c = ac; + func = 2; + } + public void run() + { + if (func == 1) + { + acc(); + } + } + public void run(Canvas c) + { + if (func == 2) + { + acc_c(c); + } + } + } + + public static class Display + { + public enum DisplayName { primary, secondary } + public static DisplayName primary = DisplayName.primary, secondary = DisplayName.secondary; + + public static void pix(int x, int y, Color col) { Main.canvas.pix(x, y, col); } + public static void line(Line drawee) { Main.canvas.line(drawee); } + public static void rect(Rectangle drawee) { Main.canvas.rect(drawee); } + public static void ellipse(Ellipse drawee) { Main.canvas.ellipse(drawee); } + public static void oval(Ellipse drawee) { Main.canvas.ellipse(drawee); } + public static void polygon(Polygon drawee) { Main.canvas.polygon(drawee); } + public static void letters(Letters drawee) { Main.canvas.letters(drawee); } + public static void image(Image drawee) { Main.canvas.image(drawee); } + public static void group(Group drawee) { Main.canvas.group(drawee); } + + + public static void msg(string str, double x, double y) { Main.canvas.msg(str, x, y, Color.white); } + public static void msg(string dstr, double x, double y, Color col) { Main.canvas.msg(dstr, x, y, col); } + public static void var(Type val, double x, double y) { Main.canvas.var(val, x, y, Color.white); } + public static void var(Type val, double x, double y, Color col) { Main.canvas.var(val, x, y, col); } + + + + public static void clear() { Main.canvas.clear(Color.black); } + public static void clear(double lum) { Main.canvas.clear(lum); } + public static void clear(Color col) { Main.canvas.clear(col); } + public static void flip(int n = 1) { Main.canvas.flip(n); } + + public static double width { get { return Main.canvas.width; } } + public static double height { get { return Main.canvas.height; } } + public static Point center { get { return Main.canvas.center; } } + public static double getWidth() { return width; } + public static double getHeight() { return height; } + public static Point getCenter() { return center; } + public static double getHCenter() { return Main.canvas.getHCenter(); } + public static double getVCenter() { return Main.canvas.getVCenter(); } + public static double getRefreshRate() { return Main.canvas.getRefreshRate(); } + + public static void showFPS(bool sw = true) { Main.canvas.showFPS(sw); } + public static void watchFPS(bool sw = true) { Main.canvas.watchFPS(sw); } + + } + +} diff --git a/dev5/psychlops/extension/experiments/experiments.cs b/dev5/psychlops/extension/experiments/experiments.cs new file mode 100644 index 0000000..9a1aaf0 --- /dev/null +++ b/dev5/psychlops/extension/experiments/experiments.cs @@ -0,0 +1,17 @@ +namespace Psychlops +{ +/* + public class Independent + { + public double value; + + public Independent() + { + value = 0; + } + + + public static implicit operator double(Independent i) { return i.value; } + } + */ +} diff --git a/dev5/psychlops/extension/math/BigFloat.cs b/dev5/psychlops/extension/math/BigFloat.cs new file mode 100644 index 0000000..767f5b4 --- /dev/null +++ b/dev5/psychlops/extension/math/BigFloat.cs @@ -0,0 +1,3789 @@ +// http://www.fractal-landscapes.co.uk/bigint.html + +using System; + +namespace BigNum +{ + ///

+ /// An arbitrary-precision floating-point class + /// + /// Format: + /// Each number is stored as an exponent (32-bit signed integer), and a mantissa + /// (n-bit) BigInteger. The sign of the number is stored in the BigInteger + /// + /// Applicability and Performance: + /// This class is designed to be used for small extended precisions. It may not be + /// safe (and certainly won't be fast) to use it with mixed-precision arguments. + /// It does support, but will not be efficient for, numbers over around 2048 bits. + /// + /// Notes: + /// All conversions to and from strings are slow. + /// + /// Conversions from simple integer types Int32, Int64, UInt32, UInt64 are performed + /// using the appropriate constructor, and are relatively fast. + /// + /// The class is written entirely in managed C# code, with not native or managed + /// assembler. The use of native assembler would speed up the multiplication operations + /// many times over, and therefore all higher-order operations too. + /// + public class BigFloat + { + /// + /// Floats can have 4 special value types: + /// + /// NaN: Not a number (cannot be changed using any operations) + /// Infinity: Positive infinity. Some operations e.g. Arctan() allow this input. + /// -Infinity: Negative infinity. Some operations allow this input. + /// Zero + /// + public enum SpecialValueType + { + /// + /// Not a special value + /// + NONE = 0, + /// + /// Zero + /// + ZERO, + /// + /// Positive infinity + /// + INF_PLUS, + /// + /// Negative infinity + /// + INF_MINUS, + /// + /// Not a number + /// + NAN + } + + /// + /// This affects the ToString() method. + /// + /// With Trim rounding, all insignificant zero digits are drip + /// + public enum RoundingModeType + { + /// + /// Trim non-significant zeros from ToString output after rounding + /// + TRIM, + /// + /// Keep all non-significant zeroes in ToString output after rounding + /// + EXACT + } + + /// + /// A wrapper for the signed exponent, avoiding overflow. + /// + protected struct ExponentAdaptor + { + /// + /// The 32-bit exponent + /// + public Int32 exponent + { + get { return expValue; } + set { expValue = value; } + } + + /// + /// Implicit cast to Int32 + /// + public static implicit operator Int32(ExponentAdaptor adaptor) + { + return adaptor.expValue; + } + + /// + /// Implicit cast from Int32 to ExponentAdaptor + /// + /// + /// + public static implicit operator ExponentAdaptor(Int32 value) + { + ExponentAdaptor adaptor = new ExponentAdaptor(); + adaptor.expValue = value; + return adaptor; + } + + /// + /// Overloaded increment operator + /// + public static ExponentAdaptor operator ++(ExponentAdaptor adaptor) + { + adaptor = adaptor + 1; + return adaptor; + } + + /// + /// Overloaded decrement operator + /// + public static ExponentAdaptor operator --(ExponentAdaptor adaptor) + { + adaptor = adaptor - 1; + return adaptor; + } + + /// + /// Overloaded addition operator + /// + public static ExponentAdaptor operator +(ExponentAdaptor a1, ExponentAdaptor a2) + { + if (a1.expValue == Int32.MaxValue) return a1; + + Int64 temp = (Int64)a1.expValue; + temp += (Int64)(a2.expValue); + + if (temp > (Int64)Int32.MaxValue) + { + a1.expValue = Int32.MaxValue; + } + else if (temp < (Int64)Int32.MinValue) + { + a1.expValue = Int32.MinValue; + } + else + { + a1.expValue = (Int32)temp; + } + + return a1; + } + + /// + /// Overloaded subtraction operator + /// + public static ExponentAdaptor operator -(ExponentAdaptor a1, ExponentAdaptor a2) + { + if (a1.expValue == Int32.MaxValue) return a1; + + Int64 temp = (Int64)a1.expValue; + temp -= (Int64)(a2.expValue); + + if (temp > (Int64)Int32.MaxValue) + { + a1.expValue = Int32.MaxValue; + } + else if (temp < (Int64)Int32.MinValue) + { + a1.expValue = Int32.MinValue; + } + else + { + a1.expValue = (Int32)temp; + } + + return a1; + } + + /// + /// Overloaded multiplication operator + /// + public static ExponentAdaptor operator *(ExponentAdaptor a1, ExponentAdaptor a2) + { + if (a1.expValue == Int32.MaxValue) return a1; + + Int64 temp = (Int64)a1.expValue; + temp *= (Int64)a2.expValue; + + if (temp > (Int64)Int32.MaxValue) + { + a1.expValue = Int32.MaxValue; + } + else if (temp < (Int64)Int32.MinValue) + { + a1.expValue = Int32.MinValue; + } + else + { + a1.expValue = (Int32)temp; + } + + return a1; + } + + /// + /// Overloaded division operator + /// + public static ExponentAdaptor operator /(ExponentAdaptor a1, ExponentAdaptor a2) + { + if (a1.expValue == Int32.MaxValue) return a1; + + ExponentAdaptor res = new ExponentAdaptor(); + res.expValue = a1.expValue / a2.expValue; + return res; + } + + /// + /// Overloaded right-shift operator + /// + public static ExponentAdaptor operator >>(ExponentAdaptor a1, int shift) + { + if (a1.expValue == Int32.MaxValue) return a1; + + ExponentAdaptor res = new ExponentAdaptor(); + res.expValue = a1.expValue >> shift; + return res; + } + + /// + /// Overloaded left-shift operator + /// + /// + /// + /// + public static ExponentAdaptor operator <<(ExponentAdaptor a1, int shift) + { + if (a1.expValue == 0) return a1; + + ExponentAdaptor res = new ExponentAdaptor(); + res.expValue = a1.expValue; + + if (shift > 31) + { + res.expValue = Int32.MaxValue; + } + else + { + Int64 temp = a1.expValue; + temp = temp << shift; + + if (temp > (Int64)Int32.MaxValue) + { + res.expValue = Int32.MaxValue; + } + else if (temp < (Int64)Int32.MinValue) + { + res.expValue = Int32.MinValue; + } + else + { + res.expValue = (Int32)temp; + } + } + + return res; + } + + private Int32 expValue; + } + + //************************ Constructors ************************** + + /// + /// Constructs a 128-bit BigFloat + /// + /// Sets the value to zero + /// + static BigFloat() + { + RoundingDigits = 3; + RoundingMode = RoundingModeType.TRIM; + scratch = new BigInt(new PrecisionSpec(128, PrecisionSpec.BaseType.BIN)); + } + + /// + /// Constructs a BigFloat of the required precision + /// + /// Sets the value to zero + /// + /// + public BigFloat(PrecisionSpec mantissaPrec) + { + Init(mantissaPrec); + } + + /// + /// Constructs a big float from a UInt32 to the required precision + /// + /// + /// + public BigFloat(UInt32 value, PrecisionSpec mantissaPrec) + { + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + //For efficiency, we just use a 32-bit exponent + exponent = 0; + + mantissa = new BigInt(value, new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(mantissa.Precision); + + int bit = BigInt.GetMSB(value); + if (bit == -1) return; + + int shift = mantissa.Precision.NumBits - (bit + 1); + mantissa.LSH(shift); + exponent = bit; + } + + /// + /// Constructs a BigFloat from an Int32 to the required precision + /// + /// + /// + public BigFloat(Int32 value, PrecisionSpec mantissaPrec) + { + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + //For efficiency, we just use a 32-bit exponent + exponent = 0; + UInt32 uValue; + + if (value < 0) + { + if (value == Int32.MinValue) + { + uValue = 0x80000000; + } + else + { + uValue = (UInt32)(-value); + } + } + else + { + uValue = (UInt32)value; + } + + mantissa = new BigInt(value, new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + + int bit = BigInt.GetMSB(uValue); + if (bit == -1) return; + + int shift = mantissa.Precision.NumBits - (bit + 1); + mantissa.LSH(shift); + exponent = bit; + } + + /// + /// Constructs a BigFloat from a 64-bit integer + /// + /// + /// + public BigFloat(Int64 value, PrecisionSpec mantissaPrec) + { + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + //For efficiency, we just use a 32-bit exponent + exponent = 0; + UInt64 uValue; + + if (value < 0) + { + if (value == Int64.MinValue) + { + uValue = 0x80000000; + } + else + { + uValue = (UInt64)(-value); + } + } + else + { + uValue = (UInt64)value; + } + + mantissa = new BigInt(value, new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + + int bit = BigInt.GetMSB(uValue); + if (bit == -1) return; + + int shift = mantissa.Precision.NumBits - (bit + 1); + if (shift > 0) + { + mantissa.LSH(shift); + } + else + { + mantissa.SetHighDigit((uint)(uValue >> (-shift))); + } + exponent = bit; + } + + /// + /// Constructs a BigFloat from a 64-bit unsigned integer + /// + /// + /// + public BigFloat(UInt64 value, PrecisionSpec mantissaPrec) + { + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + //For efficiency, we just use a 32-bit exponent + exponent = 0; + + int bit = BigInt.GetMSB(value); + + mantissa = new BigInt(value, new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(mantissa.Precision); + + int shift = mantissa.Precision.NumBits - (bit + 1); + if (shift > 0) + { + mantissa.LSH(shift); + } + else + { + mantissa.SetHighDigit((uint)(value >> (-shift))); + } + exponent = bit; + } + + /// + /// Constructs a BigFloat from a BigInt, using the specified precision + /// + /// + /// + public BigFloat(BigInt value, PrecisionSpec mantissaPrec) + { + if (value.IsZero()) + { + Init(mantissaPrec); + SetZero(); + return; + } + + mantissa = new BigInt(value, mantissaPrec); + exponent = BigInt.GetMSB(value); + mantissa.Normalise(); + } + + /// + /// Construct a BigFloat from a double-precision floating point number + /// + /// + /// + public BigFloat(double value, PrecisionSpec mantissaPrec) + { + if (value == 0.0) + { + Init(mantissaPrec); + return; + } + + bool sign = (value < 0) ? true : false; + + long bits = BitConverter.DoubleToInt64Bits(value); + // Note that the shift is sign-extended, hence the test against -1 not 1 + int valueExponent = (int)((bits >> 52) & 0x7ffL); + long valueMantissa = bits & 0xfffffffffffffL; + + //The mantissa is stored with the top bit implied. + valueMantissa = valueMantissa | 0x10000000000000L; + + //The exponent is biased by 1023. + exponent = valueExponent - 1023; + + //Round the number of bits to the nearest word. + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + mantissa = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + + if (newManBits >= 64) + { + //The mantissa is 53 bits now, so add 11 to put it in the right place. + mantissa.SetHighDigits(valueMantissa << 11); + } + else + { + //To get the top word of the mantissa, shift up by 11 and down by 32 = down by 21 + mantissa.SetHighDigit((uint)(valueMantissa >> 21)); + } + + mantissa.Sign = sign; + } + + /// + /// Copy constructor + /// + /// + public BigFloat(BigFloat value) + { + Init(value.mantissa.Precision); + exponent = value.exponent; + mantissa.Assign(value.mantissa); + } + + /// + /// Copy Constructor - constructs a new BigFloat with the specified precision, copying the old one. + /// + /// The value is rounded towards zero in the case where precision is decreased. The Round() function + /// should be used beforehand if a correctly rounded result is required. + /// + /// + /// + public BigFloat(BigFloat value, PrecisionSpec mantissaPrec) + { + Init(mantissaPrec); + exponent = value.exponent; + if (mantissa.AssignHigh(value.mantissa)) exponent++; + } + + /// + /// Constructs a BigFloat from a string + /// + /// + /// + public BigFloat(string value, PrecisionSpec mantissaPrec) + { + Init(mantissaPrec); + + PrecisionSpec extendedPres = new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN); + BigFloat ten = new BigFloat(10, extendedPres); + BigFloat iPart = new BigFloat(extendedPres); + BigFloat fPart = new BigFloat(extendedPres); + BigFloat tenRCP = ten.Reciprocal(); + + if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NaNSymbol)) + { + SetNaN(); + return; + } + else if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PositiveInfinitySymbol)) + { + SetInfPlus(); + return; + } + else if (value.Contains(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeInfinitySymbol)) + { + SetInfMinus(); + return; + } + + string decimalpoint = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + char[] digitChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '.' }; + + //Read in the integer part up the the decimal point. + bool sign = false; + value = value.Trim(); + + int i = 0; + + if (value.Length > i && value[i] == '-') + { + sign = true; + i++; + } + + if (value.Length > i && value[i] == '+') + { + i++; + } + + for ( ; i < value.Length; i++) + { + //break on decimal point + if (value[i] == decimalpoint[0]) break; + + int digit = Array.IndexOf(digitChars, value[i]); + if (digit < 0) break; + + //Ignore place separators (assumed either , or .) + if (digit > 9) continue; + + if (i > 0) iPart.Mul(ten); + iPart.Add(new BigFloat(digit, extendedPres)); + } + + //If we've run out of characters, assign everything and return + if (i == value.Length) + { + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + return; + } + + //Assign the characters after the decimal point to fPart + if (value[i] == '.' && i < value.Length - 1) + { + BigFloat RecipToUse = new BigFloat(tenRCP); + + for (i++; i < value.Length; i++) + { + int digit = Array.IndexOf(digitChars, value[i]); + if (digit < 0) break; + BigFloat temp = new BigFloat(digit, extendedPres); + temp.Mul(RecipToUse); + RecipToUse.Mul(tenRCP); + fPart.Add(temp); + } + } + + //If we're run out of characters, add fPart and iPart and return + if (i == value.Length) + { + iPart.Add(fPart); + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + return; + } + + if (value[i] == '+' || value[i] == '-') i++; + + if (i == value.Length) + { + iPart.Add(fPart); + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + return; + } + + //Look for exponential notation. + if ((value[i] == 'e' || value[i] == 'E') && i < value.Length - 1) + { + //Convert the exponent to an int. + int exp; + + try + { + exp = System.Convert.ToInt32(new string(value.ToCharArray()));// i + 1, value.Length - (i + 1)))); + } + catch (Exception) + { + iPart.Add(fPart); + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + return; + } + + //Raise or lower 10 to the power of the exponent + BigFloat acc = new BigFloat(1, extendedPres); + BigFloat temp = new BigFloat(1, extendedPres); + + int powerTemp = exp; + + BigFloat multiplierToUse; + + if (exp < 0) + { + multiplierToUse = new BigFloat(tenRCP); + powerTemp = -exp; + } + else + { + multiplierToUse = new BigFloat(ten); + } + + //Fast power function + while (powerTemp != 0) + { + temp.Mul(multiplierToUse); + multiplierToUse.Assign(temp); + + if ((powerTemp & 1) != 0) + { + acc.Mul(temp); + } + + powerTemp >>= 1; + } + + iPart.Add(fPart); + iPart.Mul(acc); + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + + return; + } + + iPart.Add(fPart); + iPart.mantissa.Sign = sign; + exponent = iPart.exponent; + if (mantissa.AssignHigh(iPart.mantissa)) exponent++; + + } + + private void Init(PrecisionSpec mantissaPrec) + { + int mbWords = ((mantissaPrec.NumBits) >> 5); + if ((mantissaPrec.NumBits & 31) != 0) mbWords++; + int newManBits = mbWords << 5; + + //For efficiency, we just use a 32-bit exponent + exponent = 0; + mantissa = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + //scratch = new BigInt(new PrecisionSpec(newManBits, PrecisionSpec.BaseType.BIN)); + } + + //************************** Properties ************************* + + /// + /// Read-only property. Returns the precision specification of the mantissa. + /// + /// Floating point numbers are represented as 2^exponent * mantissa, where the + /// mantissa and exponent are integers. Note that the exponent in this class is + /// always a 32-bit integer. The precision therefore specifies how many bits + /// the mantissa will have. + /// + public PrecisionSpec Precision + { + get { return mantissa.Precision; } + } + + /// + /// Writable property: + /// true iff the number is negative or in some cases zero (<0) + /// false iff the number if positive or in some cases zero (>0) + /// + public bool Sign + { + get { return mantissa.Sign; } + set { mantissa.Sign = value; } + } + + /// + /// Read-only property. + /// True if the number is NAN, INF_PLUS, INF_MINUS or ZERO + /// False if the number has any other value. + /// + public bool IsSpecialValue + { + get + { + return (exponent == Int32.MaxValue || mantissa.IsZero()); + } + } + + /// + /// Read-only property, returns the type of number this is. Special values include: + /// + /// NONE - a regular number + /// ZERO - zero + /// NAN - Not a Number (some operations will return this if their inputs are out of range) + /// INF_PLUS - Positive infinity, not really a number, but a valid input to and output of some functions. + /// INF_MINUS - Negative infinity, not really a number, but a valid input to and output of some functions. + /// + public SpecialValueType SpecialValue + { + get + { + if (exponent == Int32.MaxValue) + { + if (mantissa.IsZero()) + { + if (mantissa.Sign) return SpecialValueType.INF_MINUS; + return SpecialValueType.INF_PLUS; + } + + return SpecialValueType.NAN; + } + else + { + if (mantissa.IsZero()) return SpecialValueType.ZERO; + return SpecialValueType.NONE; + } + } + } + + //******************** Mathematical Constants ******************* + + /// + /// Gets pi to the indicated precision + /// + /// The precision to perform the calculation to + /// pi (the ratio of the area of a circle to its diameter) + public static BigFloat GetPi(PrecisionSpec precision) + { + if (pi == null || precision.NumBits <= pi.mantissa.Precision.NumBits) + { + CalculatePi(precision.NumBits); + } + + BigFloat ret = new BigFloat (precision); + ret.Assign(pi); + + return ret; + } + + /// + /// Get e to the indicated precision + /// + /// The preicision to perform the calculation to + /// e (the number for which the d/dx(e^x) = e^x) + public static BigFloat GetE(PrecisionSpec precision) + { + if (eCache == null || eCache.mantissa.Precision.NumBits < precision.NumBits) + { + CalculateEOnly(precision.NumBits); + //CalculateFactorials(precision.NumBits); + } + + BigFloat ret = new BigFloat(precision); + ret.Assign(eCache); + + return ret; + } + + + //******************** Arithmetic Functions ******************** + + /// + /// Addition (this = this + n2) + /// + /// The number to add + public void Add(BigFloat n2) + { + if (SpecialValueAddTest(n2)) return; + + if (scratch.Precision.NumBits != n2.mantissa.Precision.NumBits) + { + scratch = new BigInt(n2.mantissa.Precision); + } + + if (exponent <= n2.exponent) + { + int diff = n2.exponent - exponent; + exponent = n2.exponent; + + if (diff != 0) + { + mantissa.RSH(diff); + } + + uint carry = mantissa.Add(n2.mantissa); + + if (carry != 0) + { + mantissa.RSH(1); + mantissa.SetBit(mantissa.Precision.NumBits - 1); + exponent++; + } + + exponent -= mantissa.Normalise(); + } + else + { + int diff = exponent - n2.exponent; + + scratch.Assign(n2.mantissa); + scratch.RSH(diff); + + uint carry = scratch.Add(mantissa); + + if (carry != 0) + { + scratch.RSH(1); + scratch.SetBit(mantissa.Precision.NumBits - 1); + exponent++; + } + + mantissa.Assign(scratch); + + exponent -= mantissa.Normalise(); + } + } + + /// + /// Subtraction (this = this - n2) + /// + /// The number to subtract from this + public void Sub(BigFloat n2) + { + n2.mantissa.Sign = !n2.mantissa.Sign; + Add(n2); + n2.mantissa.Sign = !n2.mantissa.Sign; + } + + /// + /// Multiplication (this = this * n2) + /// + /// The number to multiply this by + public void Mul(BigFloat n2) + { + if (SpecialValueMulTest(n2)) return; + + //Anything times 0 = 0 + if (n2.mantissa.IsZero()) + { + mantissa.Assign(n2.mantissa); + exponent = 0; + return; + } + + mantissa.MulHi(n2.mantissa); + int shift = mantissa.Normalise(); + exponent = exponent + n2.exponent + 1 - shift; + } + + /// + /// Division (this = this / n2) + /// + /// The number to divide this by + public void Div(BigFloat n2) + { + if (SpecialValueDivTest(n2)) return; + + if (mantissa.Precision.NumBits >= 8192) + { + BigFloat rcp = n2.Reciprocal(); + Mul(rcp); + } + else + { + int shift = mantissa.DivAndShift(n2.mantissa); + exponent = exponent - (n2.exponent + shift); + } + } + + /// + /// Multiply by a power of 2 (-ve implies division) + /// + /// + public void MulPow2(int pow2) + { + exponent += pow2; + } + + /// + /// Division-based reciprocal, fastest for small precisions up to 15,000 bits. + /// + /// The reciprocal 1/this + public BigFloat Reciprocal() + { + if (mantissa.Precision.NumBits >= 8192) return ReciprocalNewton(); + + BigFloat reciprocal = new BigFloat(1u, mantissa.Precision); + reciprocal.Div(this); + return reciprocal; + } + + /// + /// Newton's method reciprocal, fastest for larger precisions over 15,000 bits. + /// + /// The reciprocal 1/this + public BigFloat ReciprocalNewton() + { + if (mantissa.IsZero()) + { + exponent = Int32.MaxValue; + return null; + } + + bool oldSign = mantissa.Sign; + int oldExponent = exponent; + + //Kill exponent for now (will re-institute later) + exponent = 0; + + bool topBit = mantissa.IsTopBitOnlyBit(); + + PrecisionSpec curPrec = new PrecisionSpec(32, PrecisionSpec.BaseType.BIN); + + BigFloat reciprocal = new BigFloat(curPrec); + BigFloat constant2 = new BigFloat(curPrec); + BigFloat temp = new BigFloat(curPrec); + BigFloat thisPrec = new BigFloat(this, curPrec); + + reciprocal.exponent = 1; + reciprocal.mantissa.SetHighDigit(3129112985u); + + constant2.exponent = 1; + constant2.mantissa.SetHighDigit(0x80000000u); + + //D is deliberately left negative for all the following operations. + thisPrec.mantissa.Sign = true; + + //Initial estimate. + reciprocal.Add(thisPrec); + + //mantissa.Sign = false; + + //Shift down into 0.5 < this < 1 range + thisPrec.mantissa.RSH(1); + + //Iteration. + int accuracyBits = 2; + int mantissaBits = mantissa.Precision.NumBits; + + //Each iteration is a pass of newton's method for RCP. + //The is a substantial optimisation to be done here... + //You can double the number of bits for the calculations + //at each iteration, meaning that the whole process only + //takes some constant multiplier of the time for the + //full-scale multiplication. + while (accuracyBits < mantissaBits) + { + //Increase the precision as needed + if (accuracyBits >= curPrec.NumBits / 2) + { + int newBits = curPrec.NumBits * 2; + if (newBits > mantissaBits) newBits = mantissaBits; + curPrec = new PrecisionSpec(newBits, PrecisionSpec.BaseType.BIN); + + reciprocal = new BigFloat(reciprocal, curPrec); + + constant2 = new BigFloat(curPrec); + constant2.exponent = 1; + constant2.mantissa.SetHighDigit(0x80000000u); + + temp = new BigFloat(temp, curPrec); + + thisPrec = new BigFloat(this, curPrec); + thisPrec.mantissa.Sign = true; + thisPrec.mantissa.RSH(1); + } + + //temp = Xn + temp.exponent = reciprocal.exponent; + temp.mantissa.Assign(reciprocal.mantissa); + //temp = -Xn * D + temp.Mul(thisPrec); + //temp = -Xn * D + 2 (= 2 - Xn * D) + temp.Add(constant2); + //reciprocal = X(n+1) = Xn * (2 - Xn * D) + reciprocal.Mul(temp); + + accuracyBits *= 2; + } + + //'reciprocal' is now the reciprocal of the shifted down, zero-exponent mantissa of 'this' + //Restore the mantissa. + //mantissa.LSH(1); + exponent = oldExponent; + //mantissa.Sign = oldSign; + + if (topBit) + { + reciprocal.exponent = -(oldExponent); + } + else + { + reciprocal.exponent = -(oldExponent + 1); + } + reciprocal.mantissa.Sign = oldSign; + + return reciprocal; + } + + /// + /// Newton's method reciprocal, fastest for larger precisions over 15,000 bits. + /// + /// The reciprocal 1/this + private BigFloat ReciprocalNewton2() + { + if (mantissa.IsZero()) + { + exponent = Int32.MaxValue; + return null; + } + + bool oldSign = mantissa.Sign; + int oldExponent = exponent; + + //Kill exponent for now (will re-institute later) + exponent = 0; + + BigFloat reciprocal = new BigFloat(mantissa.Precision); + BigFloat constant2 = new BigFloat(mantissa.Precision); + BigFloat temp = new BigFloat(mantissa.Precision); + + reciprocal.exponent = 1; + reciprocal.mantissa.SetHighDigit(3129112985u); + + constant2.exponent = 1; + constant2.mantissa.SetHighDigit(0x80000000u); + + //D is deliberately left negative for all the following operations. + mantissa.Sign = true; + + //Initial estimate. + reciprocal.Add(this); + + //mantissa.Sign = false; + + //Shift down into 0.5 < this < 1 range + mantissa.RSH(1); + + //Iteration. + int accuracyBits = 2; + int mantissaBits = mantissa.Precision.NumBits; + + //Each iteration is a pass of newton's method for RCP. + //The is a substantial optimisation to be done here... + //You can double the number of bits for the calculations + //at each iteration, meaning that the whole process only + //takes some constant multiplier of the time for the + //full-scale multiplication. + while (accuracyBits < mantissaBits) + { + //temp = Xn + temp.exponent = reciprocal.exponent; + temp.mantissa.Assign(reciprocal.mantissa); + //temp = -Xn * D + temp.Mul(this); + //temp = -Xn * D + 2 (= 2 - Xn * D) + temp.Add(constant2); + //reciprocal = X(n+1) = Xn * (2 - Xn * D) + reciprocal.Mul(temp); + + accuracyBits *= 2; + } + + //'reciprocal' is now the reciprocal of the shifted down, zero-exponent mantissa of 'this' + //Restore the mantissa. + mantissa.LSH(1); + exponent = oldExponent; + mantissa.Sign = oldSign; + + reciprocal.exponent = -(oldExponent + 1); + reciprocal.mantissa.Sign = oldSign; + + return reciprocal; + } + + /// + /// Sets this equal to the input + /// + /// + public void Assign(BigFloat n2) + { + exponent = n2.exponent; + if (mantissa.AssignHigh(n2.mantissa)) exponent++; + } + + + //********************* Comparison Functions ******************* + + /// + /// Greater than comparison + /// + /// the number to compare this to + /// true iff this is greater than n2 (this > n2) + public bool GreaterThan(BigFloat n2) + { + if (IsSpecialValue || n2.IsSpecialValue) + { + SpecialValueType s1 = SpecialValue; + SpecialValueType s2 = SpecialValue; + + if (s1 == SpecialValueType.NAN || s2 == SpecialValueType.NAN) return false; + if (s1 == SpecialValueType.INF_MINUS) return false; + if (s2 == SpecialValueType.INF_PLUS) return false; + if (s1 == SpecialValueType.INF_PLUS) return true; + if (s2 == SpecialValueType.INF_MINUS) return true; + + if (s1 == SpecialValueType.ZERO) + { + if (s2 != SpecialValueType.ZERO && n2.Sign) + { + return true; + } + else + { + return false; + } + } + + if (s2 == SpecialValueType.ZERO) + { + return !Sign; + } + } + + if (!mantissa.Sign && n2.mantissa.Sign) return true; + if (mantissa.Sign && !n2.mantissa.Sign) return false; + if (!mantissa.Sign) + { + if (exponent > n2.exponent) return true; + if (exponent < n2.exponent) return false; + } + if (mantissa.Sign) + { + if (exponent > n2.exponent) return false; + if (exponent < n2.exponent) return true; + } + + return mantissa.GreaterThan(n2.mantissa); + } + + /// + /// Less than comparison + /// + /// the number to compare this to + /// true iff this is less than n2 (this < n2) + public bool LessThan(BigFloat n2) + { + if (IsSpecialValue || n2.IsSpecialValue) + { + SpecialValueType s1 = SpecialValue; + SpecialValueType s2 = SpecialValue; + + if (s1 == SpecialValueType.NAN || s2 == SpecialValueType.NAN) return false; + if (s1 == SpecialValueType.INF_PLUS) return false; + if (s2 == SpecialValueType.INF_PLUS) return true; + if (s2 == SpecialValueType.INF_MINUS) return false; + if (s1 == SpecialValueType.INF_MINUS) return true; + + if (s1 == SpecialValueType.ZERO) + { + if (s2 != SpecialValueType.ZERO && !n2.Sign) + { + return true; + } + else + { + return false; + } + } + + if (s2 == SpecialValueType.ZERO) + { + return Sign; + } + } + + if (!mantissa.Sign && n2.mantissa.Sign) return false; + if (mantissa.Sign && !n2.mantissa.Sign) return true; + if (!mantissa.Sign) + { + if (exponent > n2.exponent) return false; + if (exponent < n2.exponent) return true; + } + if (mantissa.Sign) + { + if (exponent > n2.exponent) return true; + if (exponent < n2.exponent) return false; + } + + return mantissa.LessThan(n2.mantissa); + } + + /// + /// Greater than comparison + /// + /// the number to compare this to + /// true iff this is greater than n2 (this > n2) + public bool GreaterThan(int i) + { + BigFloat integer = new BigFloat(i, mantissa.Precision); + return GreaterThan(integer); + } + + /// + /// Less than comparison + /// + /// the number to compare this to + /// true iff this is less than n2 (this < n2) + public bool LessThan(int i) + { + BigFloat integer = new BigFloat(i, mantissa.Precision); + return LessThan(integer); + } + + /// + /// Compare to zero + /// + /// true if this is zero (this == 0) + public bool IsZero() + { + return (mantissa.IsZero()); + } + + + //******************** Mathematical Functions ****************** + + /// + /// Sets the number to the biggest integer numerically closer to zero, if possible. + /// + public void Floor() + { + //Already an integer. + if (exponent >= mantissa.Precision.NumBits) return; + + if (exponent < 0) + { + mantissa.ZeroBits(mantissa.Precision.NumBits); + exponent = 0; + return; + } + + mantissa.ZeroBits(mantissa.Precision.NumBits - (exponent + 1)); + } + + /// + /// Sets the number to its fractional component (equivalent to 'this' - (int)'this') + /// + public void FPart() + { + //Already fractional + if (exponent < 0) + { + return; + } + + //Has no fractional part + if (exponent >= mantissa.Precision.NumBits) + { + mantissa.Zero(); + exponent = 0; + return; + } + + mantissa.ZeroBitsHigh(exponent + 1); + exponent -= mantissa.Normalise(); + } + + /// + /// Calculates tan(x) + /// + public void Tan() + { + if (IsSpecialValue) + { + //Tan(x) has no limit as x->inf + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) + { + SetNaN(); + } + else if (SpecialValue == SpecialValueType.ZERO) + { + SetZero(); + } + + return; + } + + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + //Work out the sign change (involves replicating some rescaling). + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (mantissa.IsZero()) + { + return; + } + + //Rescale into 0 <= x < pi + if (GreaterThan(pi)) + { + //There will be an inherent loss of precision doing this. + BigFloat newAngle = new BigFloat(this); + newAngle.Mul(piRecip); + newAngle.FPart(); + newAngle.Mul(pi); + Assign(newAngle); + } + + //Rescale to -pi/2 <= x < pi/2 + if (!LessThan(piBy2)) + { + Sub(pi); + } + + //Now the sign of the sin determines the sign of the tan. + //tan(x) = sin(x) / sqrt(1 - sin^2(x)) + Sin(); + BigFloat denom = new BigFloat(this); + denom.Mul(this); + denom.Sub(new BigFloat(1, mantissa.Precision)); + denom.mantissa.Sign = !denom.mantissa.Sign; + + if (denom.mantissa.Sign) + { + denom.SetZero(); + } + + denom.Sqrt(); + Div(denom); + if (sign) mantissa.Sign = !mantissa.Sign; + } + + /// + /// Calculates Cos(x) + /// + public void Cos() + { + if (IsSpecialValue) + { + //Cos(x) has no limit as x->inf + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) + { + SetNaN(); + } + else if (SpecialValue == SpecialValueType.ZERO) + { + Assign(new BigFloat(1, mantissa.Precision)); + } + + return; + } + + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + Add(piBy2); + Sin(); + } + + /// + /// Calculates Sin(x): + /// This takes a little longer and is less accurate if the input is out of the range (-pi, pi]. + /// + public void Sin() + { + if (IsSpecialValue) + { + //Sin(x) has no limit as x->inf + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) + { + SetNaN(); + } + + return; + } + + //Convert to positive range (0 <= x < inf) + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + if (inverseFactorialCache == null || invFactorialCutoff != mantissa.Precision.NumBits) + { + CalculateFactorials(mantissa.Precision.NumBits); + } + + //Rescale into 0 <= x < 2*pi + if (GreaterThan(twoPi)) + { + //There will be an inherent loss of precision doing this. + BigFloat newAngle = new BigFloat(this); + newAngle.Mul(twoPiRecip); + newAngle.FPart(); + newAngle.Mul(twoPi); + Assign(newAngle); + } + + //Rescale into range 0 <= x < pi + if (GreaterThan(pi)) + { + //sin(pi + a) = sin(pi)cos(a) + sin(a)cos(pi) = 0 - sin(a) = -sin(a) + Sub(pi); + sign = !sign; + } + + BigFloat temp = new BigFloat(mantissa.Precision); + + //Rescale into range 0 <= x < pi/2 + if (GreaterThan(piBy2)) + { + temp.Assign(this); + Assign(pi); + Sub(temp); + } + + //Rescale into range 0 <= x < pi/6 to accelerate convergence. + //This is done using sin(3x) = 3sin(x) - 4sin^3(x) + Mul(threeRecip); + + if (mantissa.IsZero()) + { + exponent = 0; + return; + } + + BigFloat term = new BigFloat(this); + + BigFloat square = new BigFloat(this); + square.Mul(term); + + BigFloat sum = new BigFloat(this); + + bool termSign = true; + int length = inverseFactorialCache.Length; + int numBits = mantissa.Precision.NumBits; + + for (int i = 3; i < length; i += 2) + { + term.Mul(square); + temp.Assign(inverseFactorialCache[i]); + temp.Mul(term); + temp.mantissa.Sign = termSign; + termSign = !termSign; + + if (temp.exponent < -numBits) break; + + sum.Add(temp); + } + + //Restore the triple-angle: sin(3x) = 3sin(x) - 4sin^3(x) + Assign(sum); + sum.Mul(this); + sum.Mul(this); + Mul(new BigFloat(3, mantissa.Precision)); + sum.exponent += 2; + Sub(sum); + + //Restore the sign + mantissa.Sign = sign; + } + + /// + /// Hyperbolic Sin (sinh) function + /// + public void Sinh() + { + if (IsSpecialValue) + { + return; + } + + Exp(); + Sub(Reciprocal()); + exponent--; + } + + /// + /// Hyperbolic cosine (cosh) function + /// + public void Cosh() + { + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.ZERO) + { + Assign(new BigFloat(1, mantissa.Precision)); + } + else if (SpecialValue == SpecialValueType.INF_MINUS) + { + SetInfPlus(); + } + + return; + } + + Exp(); + Add(Reciprocal()); + exponent--; + } + + /// + /// Hyperbolic tangent function (tanh) + /// + public void Tanh() + { + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS) + { + Assign(new BigFloat(-1, mantissa.Precision)); + } + else if (SpecialValue == SpecialValueType.INF_PLUS) + { + Assign(new BigFloat(1, mantissa.Precision)); + } + + return; + } + + exponent++; + Exp(); + BigFloat temp = new BigFloat(this); + BigFloat one = new BigFloat(1, mantissa.Precision); + temp.Add(one); + Sub(one); + Div(temp); + } + + /// + /// arcsin(): the inverse function of sin(), range of (-pi/2..pi/2) + /// + public void Arcsin() + { + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS || SpecialValue == SpecialValueType.NAN) + { + SetNaN(); + return; + } + + return; + } + + BigFloat one = new BigFloat(1, mantissa.Precision); + BigFloat plusABit = new BigFloat(1, mantissa.Precision); + plusABit.exponent -= (mantissa.Precision.NumBits - (mantissa.Precision.NumBits >> 6)); + BigFloat onePlusABit = new BigFloat(1, mantissa.Precision); + onePlusABit.Add(plusABit); + + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (GreaterThan(onePlusABit)) + { + SetNaN(); + } + else if (LessThan(one)) + { + BigFloat temp = new BigFloat(this); + temp.Mul(this); + temp.Sub(one); + temp.mantissa.Sign = !temp.mantissa.Sign; + temp.Sqrt(); + temp.Add(one); + Div(temp); + Arctan(); + exponent++; + mantissa.Sign = sign; + } + else + { + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + Assign(piBy2); + if (sign) mantissa.Sign = true; + } + } + + /// + /// arccos(): the inverse function of cos(), range (0..pi) + /// + public void Arccos() + { + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS || SpecialValue == SpecialValueType.NAN) + { + SetNaN(); + } + else if (SpecialValue == SpecialValueType.ZERO) + { + Assign(new BigFloat(1, mantissa.Precision)); + exponent = 0; + Sign = false; + } + + return; + } + + BigFloat one = new BigFloat(1, mantissa.Precision); + BigFloat plusABit = new BigFloat(1, mantissa.Precision); + plusABit.exponent -= (mantissa.Precision.NumBits - (mantissa.Precision.NumBits >> 6)); + BigFloat onePlusABit = new BigFloat(1, mantissa.Precision); + onePlusABit.Add(plusABit); + + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (GreaterThan(onePlusABit)) + { + SetNaN(); + } + else if (LessThan(one)) + { + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + mantissa.Sign = sign; + BigFloat temp = new BigFloat(this); + Mul(temp); + Sub(one); + mantissa.Sign = !mantissa.Sign; + Sqrt(); + temp.Add(one); + Div(temp); + Arctan(); + exponent++; + } + else + { + if (sign) + { + if (pi == null || pi.mantissa.Precision.NumBits != mantissa.Precision.NumBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + Assign(pi); + } + else + { + mantissa.Zero(); + exponent = 0; + } + } + } + + /// + /// arctan(): the inverse function of sin(), range of (-pi/2..pi/2) + /// + public void Arctan() + { + //With 2 argument reductions, we increase precision by a minimum of 4 bits per term. + int numBits = mantissa.Precision.NumBits; + int maxTerms = numBits >> 2; + + if (pi == null || pi.mantissa.Precision.NumBits != numBits) + { + CalculatePi(mantissa.Precision.NumBits); + } + + //Make domain positive + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) + { + Assign(piBy2); + mantissa.Sign = sign; + return; + } + + return; + } + + if (reciprocals == null || reciprocals[0].mantissa.Precision.NumBits != numBits || reciprocals.Length < maxTerms) + { + CalculateReciprocals(numBits, maxTerms); + } + + bool invert = false; + BigFloat one = new BigFloat(1, mantissa.Precision); + + //Invert if outside of convergence + if (GreaterThan(one)) + { + invert = true; + Assign(Reciprocal()); + } + + //Reduce using half-angle formula: + //arctan(2x) = 2 arctan (x / (1 + sqrt(1 + x))) + + //First reduction (guarantees 2 bits per iteration) + BigFloat temp = new BigFloat(this); + temp.Mul(this); + temp.Add(one); + temp.Sqrt(); + temp.Add(one); + this.Div(temp); + + //Second reduction (guarantees 4 bits per iteration) + temp.Assign(this); + temp.Mul(this); + temp.Add(one); + temp.Sqrt(); + temp.Add(one); + this.Div(temp); + + //Actual series calculation + int length = reciprocals.Length; + BigFloat term = new BigFloat(this); + + //pow = x^2 + BigFloat pow = new BigFloat(this); + pow.Mul(this); + + BigFloat sum = new BigFloat(this); + + for (int i = 1; i < length; i++) + { + //u(n) = u(n-1) * x^2 + //t(n) = u(n) / (2n+1) + term.Mul(pow); + term.Sign = !term.Sign; + temp.Assign(term); + temp.Mul(reciprocals[i]); + + if (temp.exponent < -numBits) break; + + sum.Add(temp); + } + + //Undo the reductions. + Assign(sum); + exponent += 2; + + if (invert) + { + //Assign(Reciprocal()); + mantissa.Sign = true; + Add(piBy2); + } + + if (sign) + { + mantissa.Sign = sign; + } + } + + /// + /// Arcsinh(): the inverse sinh function + /// + public void Arcsinh() + { + //Just let all special values fall through + if (IsSpecialValue) + { + return; + } + + BigFloat temp = new BigFloat(this); + temp.Mul(this); + temp.Add(new BigFloat(1, mantissa.Precision)); + temp.Sqrt(); + Add(temp); + Log(); + } + + /// + /// Arccosh(): the inverse cosh() function + /// + public void Arccosh() + { + //acosh isn't defined for x < 1 + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.ZERO) + { + SetNaN(); + return; + } + + return; + } + + BigFloat one = new BigFloat(1, mantissa.Precision); + if (LessThan(one)) + { + SetNaN(); + return; + } + + BigFloat temp = new BigFloat(this); + temp.Mul(this); + temp.Sub(one); + temp.Sqrt(); + Add(temp); + Log(); + } + + /// + /// Arctanh(): the inverse tanh function + /// + public void Arctanh() + { + //|x| <= 1 for a non-NaN output + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.INF_MINUS || SpecialValue == SpecialValueType.INF_PLUS) + { + SetNaN(); + return; + } + + return; + } + + BigFloat one = new BigFloat(1, mantissa.Precision); + BigFloat plusABit = new BigFloat(1, mantissa.Precision); + plusABit.exponent -= (mantissa.Precision.NumBits - (mantissa.Precision.NumBits >> 6)); + BigFloat onePlusABit = new BigFloat(1, mantissa.Precision); + onePlusABit.Add(plusABit); + + bool sign = mantissa.Sign; + mantissa.Sign = false; + + if (GreaterThan(onePlusABit)) + { + SetNaN(); + } + else if (LessThan(one)) + { + BigFloat temp = new BigFloat(this); + Add(one); + one.Sub(temp); + Div(one); + Log(); + exponent--; + mantissa.Sign = sign; + } + else + { + if (sign) + { + SetInfMinus(); + } + else + { + SetInfPlus(); + } + } + } + + /// + /// Two-variable iterative square root, taken from + /// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#A_two-variable_iterative_method + /// + public void Sqrt() + { + if (mantissa.Sign || IsSpecialValue) + { + if (SpecialValue == SpecialValueType.ZERO) + { + return; + } + + if (SpecialValue == SpecialValueType.INF_MINUS || mantissa.Sign) + { + SetNaN(); + } + + return; + } + + BigFloat temp2; + BigFloat temp3 = new BigFloat(mantissa.Precision); + BigFloat three = new BigFloat(3, mantissa.Precision); + + int exponentScale = 0; + + //Rescale to 0.5 <= x < 2 + if (exponent < -1) + { + int diff = -exponent; + if ((diff & 1) != 0) + { + diff--; + } + + exponentScale = -diff; + exponent += diff; + } + else if (exponent > 0) + { + if ((exponent & 1) != 0) + { + exponentScale = exponent + 1; + exponent = -1; + } + else + { + exponentScale = exponent; + exponent = 0; + } + } + + temp2 = new BigFloat(this); + temp2.Sub(new BigFloat(1, mantissa.Precision)); + + //if (temp2.mantissa.IsZero()) + //{ + // exponent += exponentScale; + // return; + //} + + int numBits = mantissa.Precision.NumBits; + + while ((exponent - temp2.exponent) < numBits && temp2.SpecialValue != SpecialValueType.ZERO) + { + //a(n+1) = an - an*cn / 2 + temp3.Assign(this); + temp3.Mul(temp2); + temp3.MulPow2(-1); + this.Sub(temp3); + + //c(n+1) = cn^2 * (cn - 3) / 4 + temp3.Assign(temp2); + temp2.Sub(three); + temp2.Mul(temp3); + temp2.Mul(temp3); + temp2.MulPow2(-2); + } + + exponent += (exponentScale >> 1); + } + + /// + /// The natural logarithm, ln(x) + /// + public void Log() + { + if (IsSpecialValue || mantissa.Sign) + { + if (SpecialValue == SpecialValueType.INF_MINUS || mantissa.Sign) + { + SetNaN(); + } + else if (SpecialValue == SpecialValueType.ZERO) + { + SetInfMinus(); + } + + return; + } + + if (mantissa.Precision.NumBits >= 512) + { + LogAGM1(); + return; + } + + //Compute ln2. + if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits) + { + CalculateLog2(mantissa.Precision.NumBits); + } + + Log2(); + Mul(ln2cache); + } + + /// + /// Log to the base 10 + /// + public void Log10() + { + if (IsSpecialValue || mantissa.Sign) + { + if (SpecialValue == SpecialValueType.INF_MINUS || mantissa.Sign) + { + SetNaN(); + } + else if (SpecialValue == SpecialValueType.ZERO) + { + SetInfMinus(); + } + + return; + } + + //Compute ln2. + if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits) + { + CalculateLog2(mantissa.Precision.NumBits); + } + + Log(); + Mul(log10recip); + } + + /// + /// The exponential function. Less accurate for high exponents, scales poorly with the number + /// of bits. + /// + public void Exp() + { + Exp(mantissa.Precision.NumBits); + } + + /// + /// Raises a number to an integer power (positive or negative). This is a very accurate and fast function, + /// comparable to or faster than division (although it is slightly slower for + /// negative powers, obviously) + /// + /// + /// + public void Pow(int power) + { + BigFloat acc = new BigFloat(1, mantissa.Precision); + BigFloat temp = new BigFloat(1, mantissa.Precision); + + int powerTemp = power; + + if (power < 0) + { + Assign(Reciprocal()); + powerTemp = -power; + } + + //Fast power function + while (powerTemp != 0) + { + temp.Mul(this); + Assign(temp); + + if ((powerTemp & 1) != 0) + { + acc.Mul(temp); + } + + powerTemp >>= 1; + } + + Assign(acc); + } + + /// + /// Raises to an aribitrary power. This is both slow (uses Log) and inaccurate. If you need to + /// raise e^x use exp(). If you need an integer power, use the integer power function Pow(int) + /// Accuracy Note: + /// The function is only ever accurate to a maximum of 4 decimal digits + /// For every 10x larger (or smaller) the power gets, you lose an additional decimal digit + /// If you really need a precise result, do the calculation with an extra 32-bits and round + /// Domain Note: + /// This only works for powers of positive real numbers. Negative numbers will fail. + /// + /// + public void Pow(BigFloat power) + { + Log(); + Mul(power); + Exp(); + } + + + //******************** Static Math Functions ******************* + + /// + /// Returns the integer component of the input + /// + /// The input number + /// The integer component returned will always be numerically closer to zero + /// than the input: an input of -3.49 for instance would produce a value of 3. + public static BigFloat Floor(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Floor(); + return n1; + } + + /// + /// Returns the fractional (non-integer component of the input) + /// + /// The input number + public static BigFloat FPart(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.FPart(); + return n1; + } + + /// + /// Calculates tan(x) + /// + /// The angle (in radians) to find the tangent of + public static BigFloat Tan(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Tan(); + return n1; + } + + /// + /// Calculates Cos(x) + /// + /// The angle (in radians) to find the cosine of + /// This is a reasonably fast function for smaller precisions, but + /// doesn't scale well for higher precision arguments + public static BigFloat Cos(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Cos(); + return n1; + } + + /// + /// Calculates Sin(x): + /// This takes a little longer and is less accurate if the input is out of the range (-pi, pi]. + /// + /// The angle to find the sine of (in radians) + /// This is a resonably fast function, for smaller precision arguments, but doesn't + /// scale very well with the number of bits in the input. + public static BigFloat Sin(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Sin(); + return n1; + } + + /// + /// Hyperbolic Sin (sinh) function + /// + /// The number to find the hyperbolic sine of + public static BigFloat Sinh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Sinh(); + return n1; + } + + /// + /// Hyperbolic cosine (cosh) function + /// + /// The number to find the hyperbolic cosine of + public static BigFloat Cosh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Cosh(); + return n1; + } + + /// + /// Hyperbolic tangent function (tanh) + /// + /// The number to find the hyperbolic tangent of + public static BigFloat Tanh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Tanh(); + return n1; + } + + /// + /// arcsin(): the inverse function of sin(), range of (-pi/2..pi/2) + /// + /// The number to find the arcsine of (-pi/2..pi/2) + /// Note that inverse trig functions are only defined within a specific range. + /// Values outside this range will return NaN, although some margin for error is assumed. + /// + public static BigFloat Arcsin(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arcsin(); + return n1; + } + + /// + /// arccos(): the inverse function of cos(), input range (0..pi) + /// + /// The number to find the arccosine of (0..pi) + /// Note that inverse trig functions are only defined within a specific range. + /// Values outside this range will return NaN, although some margin for error is assumed. + /// + public static BigFloat Arccos(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arccos(); + return n1; + } + + /// + /// arctan(): the inverse function of sin(), input range of (-pi/2..pi/2) + /// + /// The number to find the arctangent of (-pi/2..pi/2) + /// Note that inverse trig functions are only defined within a specific range. + /// Values outside this range will return NaN, although some margin for error is assumed. + /// + public static BigFloat Arctan(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arctan(); + return n1; + } + + /// + /// Arcsinh(): the inverse sinh function + /// + /// The number to find the inverse hyperbolic sine of + public static BigFloat Arcsinh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arcsinh(); + return n1; + } + + /// + /// Arccosh(): the inverse cosh() function + /// + /// The number to find the inverse hyperbolic cosine of + public static BigFloat Arccosh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arccosh(); + return n1; + } + + /// + /// Arctanh(): the inverse tanh function + /// + /// The number to fine the inverse hyperbolic tan of + public static BigFloat Arctanh(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Arctanh(); + return n1; + } + + /// + /// Two-variable iterative square root, taken from + /// http://en.wikipedia.org/wiki/Methods_of_computing_square_roots#A_two-variable_iterative_method + /// + /// This is quite a fast function, as elementary functions go. You can expect it to take + /// about twice as long as a floating-point division. + /// + public static BigFloat Sqrt(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Sqrt(); + return n1; + } + + /// + /// The natural logarithm, ln(x) (log base e) + /// + /// This is a very slow function, despite repeated attempts at optimisation. + /// To make it any faster, different strategies would be needed for integer operations. + /// It does, however, scale well with the number of bits. + /// + /// The number to find the natural logarithm of + public static BigFloat Log(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Log(); + return n1; + } + + /// + /// Base 10 logarithm of a number + /// + /// This is a very slow function, despite repeated attempts at optimisation. + /// To make it any faster, different strategies would be needed for integer operations. + /// It does, however, scale well with the number of bits. + /// + /// The number to find the base 10 logarithm of + public static BigFloat Log10(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Log10(); + return n1; + } + + /// + /// The exponential function. Less accurate for high exponents, scales poorly with the number + /// of bits. This is quite fast for low-precision arguments. + /// + public static BigFloat Exp(BigFloat n1) + { + BigFloat res = new BigFloat(n1); + n1.Exp(); + return n1; + } + + /// + /// Raises a number to an integer power (positive or negative). This is a very accurate and fast function, + /// comparable to or faster than division (although it is slightly slower for + /// negative powers, obviously). + /// + /// The number to raise to the power + /// The power to raise it to + public static BigFloat Pow(BigFloat n1, int power) + { + BigFloat res = new BigFloat(n1); + n1.Pow(power); + return n1; + } + + /// + /// Raises to an aribitrary power. This is both slow (uses Log) and inaccurate. If you need to + /// raise e^x use exp(). If you need an integer power, use the integer power function Pow(int) + /// + /// + /// Accuracy Note: + /// The function is only ever accurate to a maximum of 4 decimal digits + /// For every 10x larger (or smaller) the power gets, you lose an additional decimal digit + /// If you really need a precise result, do the calculation with an extra 32-bits and round + /// + /// Domain Note: + /// This only works for powers of positive real numbers. Negative numbers will fail. + /// + /// The number to raise to a power + /// The power to raise it to + public static BigFloat Pow(BigFloat n1, BigFloat power) + { + BigFloat res = new BigFloat(n1); + n1.Pow(power); + return n1; + } + + //********************** Static functions ********************** + + /// + /// Adds two numbers and returns the result + /// + public static BigFloat Add(BigFloat n1, BigFloat n2) + { + BigFloat ret = new BigFloat(n1); + ret.Add(n2); + return ret; + } + + /// + /// Subtracts two numbers and returns the result + /// + public static BigFloat Sub(BigFloat n1, BigFloat n2) + { + BigFloat ret = new BigFloat(n1); + ret.Sub(n2); + return ret; + } + + /// + /// Multiplies two numbers and returns the result + /// + public static BigFloat Mul(BigFloat n1, BigFloat n2) + { + BigFloat ret = new BigFloat(n1); + ret.Mul(n2); + return ret; + } + + /// + /// Divides two numbers and returns the result + /// + public static BigFloat Div(BigFloat n1, BigFloat n2) + { + BigFloat ret = new BigFloat(n1); + ret.Div(n2); + return ret; + } + + /// + /// Tests whether n1 is greater than n2 + /// + public static bool GreaterThan(BigFloat n1, BigFloat n2) + { + return n1.GreaterThan(n2); + } + + /// + /// Tests whether n1 is less than n2 + /// + public static bool LessThan(BigFloat n1, BigFloat n2) + { + return n1.LessThan(n2); + } + + + //******************* Fast static functions ******************** + + /// + /// Adds two numbers and assigns the result to res. + /// + /// a pre-existing BigFloat to take the result + /// the first number + /// the second number + /// a handle to res + public static BigFloat Add(BigFloat res, BigFloat n1, BigFloat n2) + { + res.Assign(n1); + res.Add(n2); + return res; + } + + /// + /// Subtracts two numbers and assigns the result to res. + /// + /// a pre-existing BigFloat to take the result + /// the first number + /// the second number + /// a handle to res + public static BigFloat Sub(BigFloat res, BigFloat n1, BigFloat n2) + { + res.Assign(n1); + res.Sub(n2); + return res; + } + + /// + /// Multiplies two numbers and assigns the result to res. + /// + /// a pre-existing BigFloat to take the result + /// the first number + /// the second number + /// a handle to res + public static BigFloat Mul(BigFloat res, BigFloat n1, BigFloat n2) + { + res.Assign(n1); + res.Mul(n2); + return res; + } + + /// + /// Divides two numbers and assigns the result to res. + /// + /// a pre-existing BigFloat to take the result + /// the first number + /// the second number + /// a handle to res + public static BigFloat Div(BigFloat res, BigFloat n1, BigFloat n2) + { + res.Assign(n1); + res.Div(n2); + return res; + } + + + //************************* Operators ************************** + + /// + /// The addition operator + /// + public static BigFloat operator +(BigFloat n1, BigFloat n2) + { + return Add(n1, n2); + } + + /// + /// The subtraction operator + /// + public static BigFloat operator -(BigFloat n1, BigFloat n2) + { + return Sub(n1, n2); + } + + /// + /// The multiplication operator + /// + public static BigFloat operator *(BigFloat n1, BigFloat n2) + { + return Mul(n1, n2); + } + + /// + /// The division operator + /// + public static BigFloat operator /(BigFloat n1, BigFloat n2) + { + return Div(n1, n2); + } + + //************************** Conversions ************************* + + /// + /// Converts a BigFloat to an BigInt with the specified precision + /// + /// The number to convert + /// The precision to convert it with + /// Do we round the number if we are truncating the mantissa? + /// + public static BigInt ConvertToInt(BigFloat n1, PrecisionSpec precision, bool round) + { + BigInt ret = new BigInt(precision); + + int numBits = n1.mantissa.Precision.NumBits; + int shift = numBits - (n1.exponent + 1); + + BigFloat copy = new BigFloat(n1); + bool inc = false; + + //Rounding + if (copy.mantissa.Precision.NumBits > ret.Precision.NumBits) + { + inc = true; + + for (int i = copy.exponent + 1; i <= ret.Precision.NumBits; i++) + { + if (copy.mantissa.GetBitFromTop(i) == 0) + { + inc = false; + break; + } + } + } + + if (shift > 0) + { + copy.mantissa.RSH(shift); + } + else if (shift < 0) + { + copy.mantissa.LSH(-shift); + } + + ret.Assign(copy.mantissa); + + if (inc) ret.Increment(); + + return ret; + } + + /// + /// Returns a base-10 string representing the number. + /// + /// Note: This is inefficient and possibly inaccurate. Please use with enough + /// rounding digits (set using the RoundingDigits property) to ensure accuracy + /// + public override string ToString() + { + if (IsSpecialValue) + { + SpecialValueType s = SpecialValue; + if (s == SpecialValueType.ZERO) + { + return String.Format("0{0}0", System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); + } + else if (s == SpecialValueType.INF_PLUS) + { + return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.PositiveInfinitySymbol; + } + else if (s == SpecialValueType.INF_MINUS) + { + return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeInfinitySymbol; + } + else if (s == SpecialValueType.NAN) + { + return System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NaNSymbol; + } + else + { + return "Unrecognised special type"; + } + } + + if (scratch.Precision.NumBits != mantissa.Precision.NumBits) + { + scratch = new BigInt(mantissa.Precision); + } + + //The mantissa expresses 1.xxxxxxxxxxx + //The highest possible value for the mantissa without the implicit 1. is 0.9999999... + scratch.Assign(mantissa); + //scratch.Round(3); + scratch.Sign = false; + BigInt denom = new BigInt("0", mantissa.Precision); + denom.SetBit(mantissa.Precision.NumBits - 1); + + bool useExponentialNotation = false; + int halfBits = mantissa.Precision.NumBits / 2; + if (halfBits > 60) halfBits = 60; + int precDec = 10; + + if (exponent > 0) + { + if (exponent < halfBits) + { + denom.RSH(exponent); + } + else + { + useExponentialNotation = true; + } + } + else if (exponent < 0) + { + int shift = -(exponent); + if (shift < precDec) + { + scratch.RSH(shift); + } + else + { + useExponentialNotation = true; + } + } + + string output; + + if (useExponentialNotation) + { + int absExponent = exponent; + if (absExponent < 0) absExponent = -absExponent; + int powerOf10 = (int)((double)absExponent * Math.Log10(2.0)); + + //Use 1 extra digit of precision (this is actually 32 bits more, nb) + BigFloat thisFloat = new BigFloat(this, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + thisFloat.mantissa.Sign = false; + + //Multiplicative correction factor to bring number into range. + BigFloat one = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + BigFloat ten = new BigFloat(10, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + BigFloat tenRCP = ten.Reciprocal(); + + //Accumulator for the power of 10 calculation. + BigFloat acc = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + + BigFloat tenToUse; + + if (exponent > 0) + { + tenToUse = new BigFloat(tenRCP, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + } + else + { + tenToUse = new BigFloat(ten, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + } + + BigFloat tenToPower = new BigFloat(1, new PrecisionSpec(mantissa.Precision.NumBits + 1, PrecisionSpec.BaseType.BIN)); + + int powerTemp = powerOf10; + + //Fast power function + while (powerTemp != 0) + { + tenToPower.Mul(tenToUse); + tenToUse.Assign(tenToPower); + + if ((powerTemp & 1) != 0) + { + acc.Mul(tenToPower); + } + + powerTemp >>= 1; + } + + thisFloat.Mul(acc); + + //If we are out of range, correct. + if (thisFloat.GreaterThan(ten)) + { + thisFloat.Mul(tenRCP); + if (exponent > 0) + { + powerOf10++; + } + else + { + powerOf10--; + } + } + else if (thisFloat.LessThan(one)) + { + thisFloat.Mul(ten); + if (exponent > 0) + { + powerOf10--; + } + else + { + powerOf10++; + } + } + + //Restore the precision and the sign. + BigFloat printable = new BigFloat(thisFloat, mantissa.Precision); + printable.mantissa.Sign = mantissa.Sign; + output = printable.ToString(); + + if (exponent < 0) powerOf10 = -powerOf10; + + output = String.Format("{0}E{1}", output, powerOf10); + } + else + { + BigInt bigDigit = BigInt.Div(scratch, denom); + bigDigit.Sign = false; + scratch.Sub(BigInt.Mul(denom, bigDigit)); + + if (mantissa.Sign) + { + output = String.Format("-{0}.", bigDigit); + } + else + { + output = String.Format("{0}.", bigDigit); + } + + denom = BigInt.Div(denom, 10u); + + while (!denom.IsZero()) + { + uint digit = (uint)BigInt.Div(scratch, denom); + if (digit == 10) digit--; + scratch.Sub(BigInt.Mul(denom, digit)); + output = String.Format("{0}{1}", output, digit); + denom = BigInt.Div(denom, 10u); + } + + output = RoundString(output, RoundingDigits); + } + + return output; + } + + //**************** Special value handling for ops *************** + + private void SetNaN() + { + exponent = Int32.MaxValue; + mantissa.SetBit(mantissa.Precision.NumBits - 1); + } + + private void SetZero() + { + exponent = 0; + mantissa.Zero(); + Sign = false; + } + + private void SetInfPlus() + { + Sign = false; + exponent = Int32.MaxValue; + mantissa.Zero(); + } + + private void SetInfMinus() + { + Sign = true; + exponent = Int32.MaxValue; + mantissa.Zero(); + } + + private bool SpecialValueAddTest(BigFloat n2) + { + if (IsSpecialValue || n2.IsSpecialValue) + { + SpecialValueType s1 = SpecialValue; + SpecialValueType s2 = n2.SpecialValue; + + if (s1 == SpecialValueType.NAN) return true; + if (s2 == SpecialValueType.NAN) + { + //Set NaN and return. + SetNaN(); + return true; + } + + if (s1 == SpecialValueType.INF_PLUS) + { + //INF+ + INF- = NAN + if (s2 == SpecialValueType.INF_MINUS) + { + SetNaN(); + return true; + } + + return true; + } + + if (s1 == SpecialValueType.INF_MINUS) + { + //INF+ + INF- = NAN + if (s2 == SpecialValueType.INF_PLUS) + { + SetNaN(); + return true; + } + + return true; + } + + if (s2 == SpecialValueType.ZERO) + { + return true; + } + + if (s1 == SpecialValueType.ZERO) + { + Assign(n2); + return true; + } + } + + return false; + } + + private bool SpecialValueMulTest(BigFloat n2) + { + if (IsSpecialValue || n2.IsSpecialValue) + { + SpecialValueType s1 = SpecialValue; + SpecialValueType s2 = n2.SpecialValue; + + if (s1 == SpecialValueType.NAN) return true; + if (s2 == SpecialValueType.NAN) + { + //Set NaN and return. + SetNaN(); + return true; + } + + if (s1 == SpecialValueType.INF_PLUS) + { + //Inf+ * Inf- = Inf- + if (s2 == SpecialValueType.INF_MINUS) + { + Assign(n2); + return true; + } + + //Inf+ * 0 = NaN + if (s2 == SpecialValueType.ZERO) + { + //Set NaN and return. + SetNaN(); + return true; + } + + return true; + } + + if (s1 == SpecialValueType.INF_MINUS) + { + //Inf- * Inf- = Inf+ + if (s2 == SpecialValueType.INF_MINUS) + { + Sign = false; + return true; + } + + //Inf- * 0 = NaN + if (s2 == SpecialValueType.ZERO) + { + //Set NaN and return. + SetNaN(); + return true; + } + + return true; + } + + if (s2 == SpecialValueType.ZERO) + { + SetZero(); + return true; + } + + if (s1 == SpecialValueType.ZERO) + { + return true; + } + } + + return false; + } + + private bool SpecialValueDivTest(BigFloat n2) + { + if (IsSpecialValue || n2.IsSpecialValue) + { + SpecialValueType s1 = SpecialValue; + SpecialValueType s2 = n2.SpecialValue; + + if (s1 == SpecialValueType.NAN) return true; + if (s2 == SpecialValueType.NAN) + { + //Set NaN and return. + SetNaN(); + return true; + } + + if ((s1 == SpecialValueType.INF_PLUS || s1 == SpecialValueType.INF_MINUS)) + { + if (s2 == SpecialValueType.INF_PLUS || s2 == SpecialValueType.INF_MINUS) + { + //Set NaN and return. + SetNaN(); + return true; + } + + if (n2.Sign) + { + if (s1 == SpecialValueType.INF_PLUS) + { + SetInfMinus(); + return true; + } + + SetInfPlus(); + return true; + } + + //Keep inf + return true; + } + + if (s2 == SpecialValueType.ZERO) + { + if (s1 == SpecialValueType.ZERO) + { + SetNaN(); + return true; + } + + if (Sign) + { + SetInfMinus(); + return true; + } + + SetInfPlus(); + return true; + } + } + + return false; + } + + //****************** Internal helper functions ***************** + + /// + /// Used for fixed point speed-ups (where the extra precision is not required). Note that Denormalised + /// floats break the assumptions that underly Add() and Sub(), so they can only be used for multiplication + /// + /// + private void Denormalise(int targetExponent) + { + int diff = targetExponent - exponent; + if (diff <= 0) return; + + //This only works to reduce the precision, so if the difference implies an increase, we can't do anything. + mantissa.RSH(diff); + exponent += diff; + } + + /// + /// The binary logarithm, log2(x) - for precisions above 1000 bits, use Log() and convert the base. + /// + private void Log2() + { + if (scratch.Precision.NumBits != mantissa.Precision.NumBits) + { + scratch = new BigInt(mantissa.Precision); + } + + int bits = mantissa.Precision.NumBits; + BigFloat temp = new BigFloat(this); + BigFloat result = new BigFloat(exponent, mantissa.Precision); + BigFloat pow2 = new BigFloat(1, mantissa.Precision); + temp.exponent = 0; + int bitsCalculated = 0; + + while (bitsCalculated < bits) + { + int i; + for (i = 0; (temp.exponent == 0); i++) + { + temp.mantissa.SquareHiFast(scratch); + int shift = temp.mantissa.Normalise(); + temp.exponent += 1 - shift; + if (i + bitsCalculated >= bits) break; + } + + pow2.MulPow2(-i); + result.Add(pow2); + temp.exponent = 0; + bitsCalculated += i; + } + + this.Assign(result); + } + + /// + /// Tried the newton method for logs, but the exponential function is too slow to do it. + /// + private void LogNewton() + { + if (mantissa.IsZero() || mantissa.Sign) + { + return; + } + + //Compute ln2. + if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits) + { + CalculateLog2(mantissa.Precision.NumBits); + } + + int numBits = mantissa.Precision.NumBits; + + //Use inverse exp function with Newton's method. + BigFloat xn = new BigFloat(this); + BigFloat oldExponent = new BigFloat(xn.exponent, mantissa.Precision); + xn.exponent = 0; + this.exponent = 0; + //Hack to subtract 1 + xn.mantissa.ClearBit(numBits - 1); + //x0 = (x - 1) * log2 - this is a straight line fit between log(1) = 0 and log(2) = ln2 + xn.Mul(ln2cache); + //x0 = (x - 1) * log2 + C - this corrects for minimum error over the range. + xn.Add(logNewtonConstant); + BigFloat term = new BigFloat(mantissa.Precision); + BigFloat one = new BigFloat(1, mantissa.Precision); + + int precision = 32; + int normalPrecision = mantissa.Precision.NumBits; + + int iterations = 0; + + while (true) + { + term.Assign(xn); + term.mantissa.Sign = true; + term.Exp(precision); + term.Mul(this); + term.Sub(one); + + iterations++; + if (term.exponent < -((precision >> 1) - 4)) + { + if (precision == normalPrecision) + { + if (term.exponent < -(precision - 4)) break; + } + else + { + precision = precision << 1; + if (precision > normalPrecision) precision = normalPrecision; + } + } + + xn.Add(term); + } + + //log(2^n*s) = log(2^n) + log(s) = nlog(2) + log(s) + term.Assign(ln2cache); + term.Mul(oldExponent); + + this.Assign(xn); + this.Add(term); + } + + /// + /// Log(x) implemented as an Arithmetic-Geometric Mean. Fast for high precisions. + /// + private void LogAGM1() + { + if (mantissa.IsZero() || mantissa.Sign) + { + return; + } + + //Compute ln2. + if (ln2cache == null || mantissa.Precision.NumBits > ln2cache.mantissa.Precision.NumBits) + { + CalculateLog2(mantissa.Precision.NumBits); + } + + //Compute ln(x) using AGM formula + + //1. Re-write the input as 2^n * (0.5 <= x < 1) + int power2 = exponent + 1; + exponent = -1; + + //BigFloat res = new BigFloat(firstAGMcache); + BigFloat a0 = new BigFloat(1, mantissa.Precision); + BigFloat b0 = new BigFloat(pow10cache); + b0.Mul(this); + + BigFloat r = R(a0, b0); + + this.Assign(firstAGMcache); + this.Sub(r); + + a0.Assign(ln2cache); + a0.Mul(new BigFloat(power2, mantissa.Precision)); + this.Add(a0); + } + + private void Exp(int numBits) + { + if (IsSpecialValue) + { + if (SpecialValue == SpecialValueType.ZERO) + { + //e^0 = 1 + exponent = 0; + mantissa.SetHighDigit(0x80000000); + } + else if (SpecialValue == SpecialValueType.INF_MINUS) + { + //e^-inf = 0 + SetZero(); + } + + return; + } + + PrecisionSpec prec = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + numBits = prec.NumBits; + + if (scratch.Precision.NumBits != prec.NumBits) + { + scratch = new BigInt(prec); + } + + if (inverseFactorialCache == null || invFactorialCutoff < numBits) + { + CalculateFactorials(numBits); + } + + //let x = 1 * 'this'.mantissa (i.e. 1 <= x < 2) + //exp(2^n * x) = e^(2^n * x) = (e^x)^2n = exp(x)^2n + + int oldExponent = 0; + + if (exponent > -4) + { + oldExponent = exponent + 4; + exponent = -4; + } + + BigFloat thisSave = new BigFloat(this, prec); + BigFloat temp = new BigFloat(1, prec); + BigFloat temp2 = new BigFloat(this, prec); + BigFloat res = new BigFloat(1, prec); + int length = inverseFactorialCache.Length; + + int iterations; + for (int i = 1; i < length; i++) + { + //temp = x^i + temp.Mul(thisSave); + temp2.Assign(inverseFactorialCache[i]); + temp2.Mul(temp); + + if (temp2.exponent < -(numBits + 4)) { iterations = i; break; } + + res.Add(temp2); + } + + //res = exp(x) + //Now... x^(2^n) = (x^2)^(2^(n - 1)) + for (int i = 0; i < oldExponent; i++) + { + res.mantissa.SquareHiFast(scratch); + int shift = res.mantissa.Normalise(); + res.exponent = res.exponent << 1; + res.exponent += 1 - shift; + } + + //Deal with +/- inf + if (res.exponent == Int32.MaxValue) + { + res.mantissa.Zero(); + } + + Assign(res); + } + + /// + /// Calculates ln(2) and returns -10^(n/2 + a bit) for reuse, using the AGM method as described in + /// http://lacim.uqam.ca/~plouffe/articles/log2.pdf + /// + /// + /// + private static void CalculateLog2(int numBits) + { + //Use the AGM method formula to get log2 to N digits. + //R(a0, b0) = 1 / (1 - Sum(2^-n*(an^2 - bn^2))) + //log(1/2) = R(1, 10^-n) - R(1, 10^-n/2) + PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + PrecisionSpec extendedPres = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); + BigFloat a0 = new BigFloat(1, extendedPres); + BigFloat b0 = TenPow(-(int)((double)((numBits >> 1) + 2) * 0.302), extendedPres); + BigFloat pow10saved = new BigFloat(b0); + BigFloat firstAGMcacheSaved = new BigFloat(extendedPres); + + //save power of 10 (in normal precision) + pow10cache = new BigFloat(b0, normalPres); + + ln2cache = R(a0, b0); + + //save the first half of the log calculation + firstAGMcache = new BigFloat(ln2cache, normalPres); + firstAGMcacheSaved.Assign(ln2cache); + + b0.MulPow2(-1); + ln2cache.Sub(R(a0, b0)); + + //Convert to log(2) + ln2cache.mantissa.Sign = false; + + //Save magic constant for newton log + //First guess in range 1 <= x < 2 is x0 = ln2 * (x - 1) + C + logNewtonConstant = new BigFloat(ln2cache); + logNewtonConstant.Mul(new BigFloat(3, extendedPres)); + logNewtonConstant.exponent--; + logNewtonConstant.Sub(new BigFloat(1, extendedPres)); + logNewtonConstant = new BigFloat(logNewtonConstant, normalPres); + + //Save the inverse. + log2ecache = new BigFloat(ln2cache); + log2ecache = new BigFloat(log2ecache.Reciprocal(), normalPres); + + //Now cache log10 + //Because the log functions call this function to the precision to which they + //are called, we cannot call them without causing an infinite loop, so we need + //to inline the code. + log10recip = new BigFloat(10, extendedPres); + + { + int power2 = log10recip.exponent + 1; + log10recip.exponent = -1; + + //BigFloat res = new BigFloat(firstAGMcache); + BigFloat ax = new BigFloat(1, extendedPres); + BigFloat bx = new BigFloat(pow10saved); + bx.Mul(log10recip); + + BigFloat r = R(ax, bx); + + log10recip.Assign(firstAGMcacheSaved); + log10recip.Sub(r); + + ax.Assign(ln2cache); + ax.Mul(new BigFloat(power2, log10recip.mantissa.Precision)); + log10recip.Add(ax); + } + + log10recip = log10recip.Reciprocal(); + log10recip = new BigFloat(log10recip, normalPres); + + + //Trim to n bits + ln2cache = new BigFloat(ln2cache, normalPres); + } + + private static BigFloat TenPow(int power, PrecisionSpec precision) + { + BigFloat acc = new BigFloat(1, precision); + BigFloat temp = new BigFloat(1, precision); + + int powerTemp = power; + + BigFloat multiplierToUse = new BigFloat(10, precision); + + if (power < 0) + { + multiplierToUse = multiplierToUse.Reciprocal(); + powerTemp = -power; + } + + //Fast power function + while (powerTemp != 0) + { + temp.Mul(multiplierToUse); + multiplierToUse.Assign(temp); + + if ((powerTemp & 1) != 0) + { + acc.Mul(temp); + } + + powerTemp >>= 1; + } + + return acc; + } + + private static BigFloat R(BigFloat a0, BigFloat b0) + { + //Precision extend taken out. + int bits = a0.mantissa.Precision.NumBits; + PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); + BigFloat an = new BigFloat(a0, extendedPres); + BigFloat bn = new BigFloat(b0, extendedPres); + BigFloat sum = new BigFloat(extendedPres); + BigFloat term = new BigFloat(extendedPres); + BigFloat temp1 = new BigFloat(extendedPres); + BigFloat one = new BigFloat(1, extendedPres); + + int iteration = 0; + + for (iteration = 0; ; iteration++) + { + //Get the sum term for this iteration. + term.Assign(an); + term.Mul(an); + temp1.Assign(bn); + temp1.Mul(bn); + //term = an^2 - bn^2 + term.Sub(temp1); + //term = 2^(n-1) * (an^2 - bn^2) + term.exponent += iteration - 1; + sum.Add(term); + + if (term.exponent < -(bits - 8)) break; + + //Calculate the new AGM estimates. + temp1.Assign(an); + an.Add(bn); + //a(n+1) = (an + bn) / 2 + an.MulPow2(-1); + + //b(n+1) = sqrt(an*bn) + bn.Mul(temp1); + bn.Sqrt(); + } + + one.Sub(sum); + one = one.Reciprocal(); + return new BigFloat(one, a0.mantissa.Precision); + } + + private static void CalculateFactorials(int numBits) + { + System.Collections.Generic.List list = new System.Collections.Generic.List(64); + System.Collections.Generic.List list2 = new System.Collections.Generic.List(64); + + PrecisionSpec extendedPrecision = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); + PrecisionSpec normalPrecision = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + + BigFloat factorial = new BigFloat(1, extendedPrecision); + BigFloat reciprocal; + + //Calculate e while we're at it + BigFloat e = new BigFloat(1, extendedPrecision); + + list.Add(new BigFloat(factorial, normalPrecision)); + + for (int i = 1; i < Int32.MaxValue; i++) + { + BigFloat number = new BigFloat(i, extendedPrecision); + factorial.Mul(number); + + if (factorial.exponent > numBits) break; + + list2.Add(new BigFloat(factorial, normalPrecision)); + reciprocal = factorial.Reciprocal(); + + e.Add(reciprocal); + list.Add(new BigFloat(reciprocal, normalPrecision)); + } + + //Set the cached static values. + inverseFactorialCache = list.ToArray(); + factorialCache = list2.ToArray(); + invFactorialCutoff = numBits; + eCache = new BigFloat(e, normalPrecision); + eRCPCache = new BigFloat(e.Reciprocal(), normalPrecision); + } + + private static void CalculateEOnly(int numBits) + { + PrecisionSpec extendedPrecision = new PrecisionSpec(numBits + 1, PrecisionSpec.BaseType.BIN); + PrecisionSpec normalPrecision = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + + int iExponent = (int)(Math.Sqrt(numBits)); + + BigFloat factorial = new BigFloat(1, extendedPrecision); + BigFloat constant = new BigFloat(1, extendedPrecision); + constant.exponent -= iExponent; + BigFloat numerator = new BigFloat(constant); + BigFloat reciprocal; + + //Calculate the 2^iExponent th root of e + BigFloat e = new BigFloat(1, extendedPrecision); + + int i; + for (i = 1; i < Int32.MaxValue; i++) + { + BigFloat number = new BigFloat(i, extendedPrecision); + factorial.Mul(number); + reciprocal = factorial.Reciprocal(); + reciprocal.Mul(numerator); + + if (-reciprocal.exponent > numBits) break; + + e.Add(reciprocal); + numerator.Mul(constant); + System.GC.Collect(); + } + + for (i = 0; i < iExponent; i++) + { + numerator.Assign(e); + e.Mul(numerator); + } + + //Set the cached static values. + eCache = new BigFloat(e, normalPrecision); + eRCPCache = new BigFloat(e.Reciprocal(), normalPrecision); + } + + /// + /// Uses the Gauss-Legendre formula for pi + /// Taken from http://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_algorithm + /// + /// + private static void CalculatePi(int numBits) + { + int bits = numBits + 32; + //Precision extend taken out. + PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); + + if (scratch.Precision.NumBits != bits) + { + scratch = new BigInt(extendedPres); + } + + //a0 = 1 + BigFloat an = new BigFloat(1, extendedPres); + + //b0 = 1/sqrt(2) + BigFloat bn = new BigFloat(2, extendedPres); + bn.Sqrt(); + bn.exponent--; + + //to = 1/4 + BigFloat tn = new BigFloat(1, extendedPres); + tn.exponent -= 2; + + int pn = 0; + + BigFloat anTemp = new BigFloat(extendedPres); + + int iteration = 0; + int cutoffBits = numBits >> 5; + + for (iteration = 0; ; iteration++) + { + //Save a(n) + anTemp.Assign(an); + + //Calculate new an + an.Add(bn); + an.exponent--; + + //Calculate new bn + bn.Mul(anTemp); + bn.Sqrt(); + + //Calculate new tn + anTemp.Sub(an); + anTemp.mantissa.SquareHiFast(scratch); + anTemp.exponent += anTemp.exponent + pn + 1 - anTemp.mantissa.Normalise(); + tn.Sub(anTemp); + + anTemp.Assign(an); + anTemp.Sub(bn); + + if (anTemp.exponent < -(bits - cutoffBits)) break; + + //New pn + pn++; + } + + an.Add(bn); + an.mantissa.SquareHiFast(scratch); + an.exponent += an.exponent + 1 - an.mantissa.Normalise(); + tn.exponent += 2; + an.Div(tn); + + pi = new BigFloat(an, normalPres); + piBy2 = new BigFloat(pi); + piBy2.exponent--; + twoPi = new BigFloat(pi, normalPres); + twoPi.exponent++; + piRecip = new BigFloat(an.Reciprocal(), normalPres); + twoPiRecip = new BigFloat(piRecip); + twoPiRecip.exponent--; + //1/3 is going to be useful for sin. + threeRecip = new BigFloat((new BigFloat(3, extendedPres)).Reciprocal(), normalPres); + } + + /// + /// Calculates the odd reciprocals of the natural numbers (for atan series) + /// + /// + /// + private static void CalculateReciprocals(int numBits, int terms) + { + int bits = numBits + 32; + PrecisionSpec extendedPres = new PrecisionSpec(bits, PrecisionSpec.BaseType.BIN); + PrecisionSpec normalPres = new PrecisionSpec(numBits, PrecisionSpec.BaseType.BIN); + + System.Collections.Generic.List list = new System.Collections.Generic.List(terms); + + for (int i = 0; i < terms; i++) + { + BigFloat term = new BigFloat(i*2 + 1, extendedPres); + list.Add(new BigFloat(term.Reciprocal(), normalPres)); + } + + reciprocals = list.ToArray(); + } + + /// + /// Does decimal rounding, for numbers without E notation. + /// + /// + /// + /// + private static string RoundString(string input, int places) + { + if (places <= 0) return input; + string trim = input.Trim(); + char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + + /* + for (int i = 1; i <= places; i++) + { + //Skip decimal points. + if (trim[trim.Length - i] == '.') + { + places++; + continue; + } + + int index = Array.IndexOf(digits, trim[trim.Length - i]); + + if (index < 0) return input; + + value += ten * index; + ten *= 10; + } + * */ + + //Look for a decimal point + string decimalPoint = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; + + int indexPoint = trim.LastIndexOf(decimalPoint); + if (indexPoint < 0) + { + //We can't modify a string which doesn't have a decimal point. + return trim; + } + + int trimPoint = trim.Length - places; + if (trimPoint < indexPoint) trimPoint = indexPoint; + + bool roundDown = false; + + if (trim[trimPoint] == '.') + { + if (trimPoint + 1 >= trim.Length) + { + roundDown = true; + } + else + { + int digit = Array.IndexOf(digits, trim[trimPoint + 1]); + if (digit < 5) roundDown = true; + } + } + else + { + int digit = Array.IndexOf(digits, trim[trimPoint]); + if (digit < 5) roundDown = true; + } + + string output; + + //Round down - just return a new string without the extra digits. + if (roundDown) + { + if (RoundingMode == RoundingModeType.EXACT) + { + return trim.Substring(0, trimPoint); + } + else + { + char[] trimChars = { '0' }; + output = trim.Substring(0, trimPoint).TrimEnd(trimChars); + trimChars[0] = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; + return output.TrimEnd(trimChars); + } + } + + //Round up - bit more complicated. + char [] arrayOutput = trim.ToCharArray();//0, trimPoint); + + //Now, we round going from the back to the front. + int j; + for (j = trimPoint - 1; j >= 0; j--) + { + int index = Array.IndexOf(digits, arrayOutput[j]); + + //Skip decimal points etc... + if (index < 0) continue; + + if (index < 9) + { + arrayOutput[j] = digits[index + 1]; + break; + } + else + { + arrayOutput[j] = digits[0]; + } + } + + output = new string(arrayOutput); + + if (j < 0) + { + //Need to add a new digit. + output = String.Format("{0}{1}", "1", output); + } + + if (RoundingMode == RoundingModeType.EXACT) + { + return output; + } + else + { + char[] trimChars = { '0' }; + output = output.TrimEnd(trimChars); + trimChars[0] = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; + return output.TrimEnd(trimChars); + } + } + + //***************************** Data ***************************** + + + //Side node - this way of doing things is far from optimal, both in terms of memory use and performance. + private ExponentAdaptor exponent; + private BigInt mantissa; + + /// + /// Storage area for calculations. + /// + private static BigInt scratch; + + private static BigFloat ln2cache; //Value of ln(2) + private static BigFloat log2ecache; //Value of log2(e) = 1/ln(2) + private static BigFloat pow10cache; //Cached power of 10 for AGM log calculation + private static BigFloat log10recip; //1/ln(10) + private static BigFloat firstAGMcache; //Cached half of AGM operation. + private static BigFloat[] factorialCache; //The values of n! + private static BigFloat[] inverseFactorialCache; //Values of 1/n! up to 2^-m where m = invFactorialCutoff (below) + private static int invFactorialCutoff; //The number of significant bits for the cutoff of the inverse factorials. + private static BigFloat eCache; //Value of e cached to invFactorialCutoff bits + private static BigFloat eRCPCache; //Reciprocal of e + private static BigFloat logNewtonConstant; //1.5*ln(2) - 1 + private static BigFloat pi; //pi + private static BigFloat piBy2; //pi/2 + private static BigFloat twoPi; //2*pi + private static BigFloat piRecip; //1/pi + private static BigFloat twoPiRecip; //1/2*pi + private static BigFloat threeRecip; //1/3 + private static BigFloat[] reciprocals; //1/x + + /// + /// The number of decimal digits to round the output of ToString() by + /// + public static int RoundingDigits { get; set; } + + /// + /// The way in which ToString() should deal with insignificant trailing zeroes + /// + public static RoundingModeType RoundingMode { get; set; } + } +} \ No newline at end of file diff --git a/dev5/psychlops/extension/math/BigInt.cs b/dev5/psychlops/extension/math/BigInt.cs new file mode 100644 index 0000000..56a484e --- /dev/null +++ b/dev5/psychlops/extension/math/BigInt.cs @@ -0,0 +1,2901 @@ +// http://www.fractal-landscapes.co.uk/bigint.html + +using System; + +namespace BigNum +{ + /// + /// Specifies the desired precision for a BigInt or a BigFloat. + /// + public struct PrecisionSpec + { + /// + /// Precision can be specified in a choice of 8 bases. + /// Note that precision for decimals is approximate. + /// + public enum BaseType + { + /// + /// Binary base + /// + BIN, + /// + /// Octal base + /// + OCT, + /// + /// Decimal base + /// + DEC, + /// + /// Hexadecimal base + /// + HEX, + /// + /// 8-bits per digit + /// + BYTES, + /// + /// 16-bits per digit + /// + WORDS, + /// + /// 32-bits per digit + /// + DWORDS, + /// + /// 64-bits per digit + /// + QWORDS + } + + /// + /// Constructor: Constructs a precision specification + /// + /// The number of digits + /// The base of the digits + public PrecisionSpec(int precision, BaseType numberBase) + { + this.prec = precision; + this.nB = numberBase; + } + + /// + /// Explicit cast from integer value. + /// + /// The value in bits for the new precision specification + /// A new precision specification with the number of bits specified + public static explicit operator PrecisionSpec(int value) + { + return new PrecisionSpec(value, BaseType.BIN); + } + + /// + /// Equality test + /// + /// the first parameter + /// the second parameter + /// true iff both precisions have the same number of bits + public static bool operator ==(PrecisionSpec spec1, PrecisionSpec spec2) + { + return (spec1.NumBits == spec2.NumBits); + } + + /// + /// Inequality operator + /// + /// the first parameter + /// the second parameter + /// true iff the parameters do not have the same number of bits + public static bool operator !=(PrecisionSpec spec1, PrecisionSpec spec2) + { + return !(spec1 == spec2); + } + + /// + /// Object equality override + /// + /// the PrecisionSpec struct to compare + /// true iff obj has the same number of bits as this + public override bool Equals(object obj) + { + return NumBits == ((PrecisionSpec)obj).NumBits; + } + + /// + /// Override of the hash code + /// + /// A basic hash + public override int GetHashCode() + { + return NumBits * prec + NumBits; + } + + /// + /// The precision in units specified by the base type (e.g. number of decimal digits) + /// + public int Precision + { + get { return prec; } + } + + /// + /// The base type in which precision is specified + /// + public BaseType NumberBaseType + { + get { return nB; } + } + + /// + /// Converts the number-base to an integer + /// + public int NumberBase + { + get { return (int)nB; } + } + + /// + /// The number of bits that this PrecisionSpec structure specifies. + /// + public int NumBits + { + get + { + if (nB == BaseType.BIN) return prec; + if (nB == BaseType.OCT) return prec * 3; + if (nB == BaseType.HEX) return prec * 4; + if (nB == BaseType.BYTES) return prec * 8; + if (nB == BaseType.WORDS) return prec * 16; + if (nB == BaseType.DWORDS) return prec * 32; + if (nB == BaseType.QWORDS) return prec * 64; + + double factor = 3.322; + int bits = ((int)Math.Ceiling(factor * (double)prec)); + return bits; + } + } + + private int prec; + private BaseType nB; + } + + + /// + /// An arbitrary-precision integer class + /// + /// Format: + /// Each number consists of an array of 32-bit unsigned integers, and a bool sign + /// value. + /// + /// Applicability and Performance: + /// This class is designed to be used for small extended precisions. It may not be + /// safe (and certainly won't be fast) to use it with mixed-precision arguments. + /// It does support, but will not be efficient for, numbers over around 2048 bits. + /// + /// Notes: + /// All conversions to and from strings are slow. + /// + /// Conversions from simple integer types Int32, Int64, UInt32, UInt64 are performed + /// using the appropriate constructor, and are relatively fast. + /// + /// The class is written entirely in managed C# code, with not native or managed + /// assembler. The use of native assembler would speed up the multiplication operations + /// many times over, and therefore all higher-order operations too. + /// + public class BigInt + { + //*************** Constructors ****************** + + /// + /// Constructs an empty BigInt to the desired precision. + /// + /// + public BigInt(PrecisionSpec precision) + { + Init(precision); + } + + /// + /// Constructs a BigInt from a string, using the string length to determine the precision + /// Note, operations on BigInts of non-matching precision are slow, so avoid using this constructor + /// + /// + public BigInt(string init) + { + InitFromString(init, (PrecisionSpec)init.Length, 10); + } + + /// + /// Constructor for copying length and precision + /// + /// The BigInt to copy + /// The precision of the new BigInt + /// decides whether to copy the actual input, or just its digit length + /// //Create an integer + /// BigInt four = new BigInt(4, new PrecisionSpec(128, PrecisionSpec.BaseType.BIN)); + /// + /// //Pad four to double its usual number of digits (this does not affect the precision) + /// four.Pad(); + /// + /// //Create a new, empty integer with matching precision, also padded to twice the usual length + /// BigInt newCopy = new BigInt(four, four.Precision, true); + public BigInt(BigInt inputToCopy, PrecisionSpec precision, bool bCopyLengthOnly) + { + digitArray = new uint[inputToCopy.digitArray.Length]; + workingSet = new uint[inputToCopy.digitArray.Length]; + if (!bCopyLengthOnly) Array.Copy(inputToCopy.digitArray, digitArray, digitArray.Length); + sign = inputToCopy.sign; + pres = inputToCopy.pres; + } + + /// + /// Constructs a bigint from the string, with the desired precision, using base 10 + /// + /// + /// + public BigInt(string init, PrecisionSpec precision) + { + InitFromString(init, precision, 10); + } + + /// + /// Constructs a BigInt from a string, using the specified precision and base + /// + /// + /// + /// + public BigInt(string init, PrecisionSpec precision, int numberBase) + { + InitFromString(init, precision, numberBase); + } + + /// + /// Constructs a bigint from the input. + /// + /// + public BigInt(BigInt input) + { + digitArray = new uint[input.digitArray.Length]; + workingSet = new uint[input.digitArray.Length]; + Array.Copy(input.digitArray, digitArray, digitArray.Length); + sign = input.sign; + pres = input.pres; + } + + /// + /// Constructs a bigint from the input, matching the new precision provided + /// + public BigInt(BigInt input, PrecisionSpec precision) + { + //Casts the input to the new precision. + Init(precision); + int Min = (input.digitArray.Length < digitArray.Length) ? input.digitArray.Length : digitArray.Length; + + for (int i = 0; i < Min; i++) + { + digitArray[i] = input.digitArray[i]; + } + + sign = input.sign; + } + + /// + /// Constructs a BigInt from a UInt32 + /// + /// + /// + public BigInt(UInt32 input, PrecisionSpec precision) + { + Init(precision); + digitArray[0] = input; + } + + /// + /// Constructs a BigInt from a UInt64 + /// + /// + /// + public BigInt(UInt64 input, PrecisionSpec precision) + { + Init(precision); + digitArray[0] = (UInt32)(input & 0xffffffff); + if (digitArray.Length > 1) digitArray[1] = (UInt32)(input >> 32); + } + + /// + /// Constructs a BigInt from an Int32 + /// + /// + /// + public BigInt(Int32 input, PrecisionSpec precision) + { + Init(precision); + if (input < 0) + { + sign = true; + + if (input == Int32.MinValue) + { + digitArray[0] = 0x80000000; + } + else + { + digitArray[0] = (UInt32)(-input); + } + } + else + { + digitArray[0] = ((UInt32)input); + } + } + + /// + /// Constructs a BigInt from a UInt32 + /// + /// + /// + public BigInt(Int64 input, PrecisionSpec precision) + { + Init(precision); + if (input < 0) sign = true; + + digitArray[0] = (UInt32)(input & 0xffffffff); + + if (digitArray.Length >= 2) + { + if (input == Int64.MinValue) + { + digitArray[1] = 0x80000000; + } + else + { + digitArray[1] = (UInt32)((input >> 32) & 0x7fffffff); + } + } + } + + //***************** Properties ******************* + + /// + /// true iff the number is negative + /// + public bool Sign { get { return sign; } set { sign = value; } } + + /// + /// The precision of the number. + /// + public PrecisionSpec Precision { get { return pres; } } + + //*************** Utility Functions ************** + + /// + /// Casts a BigInt to the new precision provided. + /// Note: This will return the input if the precision already matches. + /// + /// + /// + /// + public static BigInt CastToPrecision(BigInt input, PrecisionSpec precision) + { + if (input.pres == precision) return input; + return new BigInt(input, precision); + } + + + //*************** Member Functions *************** + + /// + /// Addition and assignment - without intermediate memory allocation. + /// + /// + /// + public uint Add(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + if (sign == n2.sign) + { + return AddInternalBits(n2.digitArray); + } + else + { + bool lessThan = LtInt(this, n2); + + if (lessThan) + { + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + workingSet[i] = digitArray[i]; + digitArray[i] = n2.digitArray[i]; + } + + sign = !sign; + return SubInternalBits(workingSet); + } + else + { + return SubInternalBits(n2.digitArray); + } + } + } + + /// + /// Subtraction and assignment - without intermediate memory allocation. + /// + /// + public uint Sub(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + if (sign != n2.sign) + { + return AddInternalBits(n2.digitArray); + } + else + { + bool lessThan = LtInt(this, n2); + + if (lessThan) + { + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + workingSet[i] = digitArray[i]; + digitArray[i] = n2.digitArray[i]; + } + + sign = !sign; + return SubInternalBits(workingSet); + } + else + { + return SubInternalBits(n2.digitArray); + } + } + } + + /// + /// Multiplication and assignmnet - with minimal intermediate memory allocation. + /// + /// + public void Mul(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + int Length = n2.digitArray.Length; + + //Inner loop zero-mul avoidance + int maxDigit = 0; + for (int i = Length - 1; i >= 0; i--) + { + if (digitArray[i] != 0) + { + maxDigit = i + 1; + break; + } + } + + //Result is zero, 'this' is unchanged + if (maxDigit == 0) return; + + //Temp storage for source (both working sets are used by the calculation) + uint[] thisTemp = new uint [Length]; + for (int i = 0; i < Length; i++) + { + thisTemp[i] = digitArray[i]; + digitArray[i] = 0; + } + + for (int i = 0; i < Length; i++) + { + //Clear the working set + for (int j = 0; j < i; j++) + { + workingSet[j] = 0; + n2.workingSet[j] = 0; + } + + n2.workingSet[i] = 0; + + ulong digit = n2.digitArray[i]; + if (digit == 0) continue; + + for (int j = 0; j + i < Length && j < maxDigit; j++) + { + //Multiply n1 by each of the integer digits of n2. + ulong temp = (ulong)thisTemp[j] * digit; + //n1.workingSet stores the low bits of each piecewise multiplication + workingSet[j + i] = (uint)(temp & 0xffffffff); + if (j + i + 1 < Length) + { + //n2.workingSet stores the high bits of each multiplication + n2.workingSet[j + i + 1] = (uint)(temp >> 32); + } + } + + AddInternalBits(workingSet); + AddInternalBits(n2.workingSet); + } + + sign = (sign != n2.sign); + } + + /// + /// Squares the number. + /// + public void Square() + { + int Length = digitArray.Length; + + //Inner loop zero-mul avoidance + int maxDigit = 0; + for (int i = Length - 1; i >= 0; i--) + { + if (digitArray[i] != 0) + { + maxDigit = i + 1; + break; + } + } + + //Result is zero, 'this' is unchanged + if (maxDigit == 0) return; + + //Temp storage for source (both working sets are used by the calculation) + uint[] thisTemp = new uint[Length]; + for (int i = 0; i < Length; i++) + { + thisTemp[i] = digitArray[i]; + digitArray[i] = 0; + } + + UInt32 [] workingSet2 = new UInt32[Length]; + + for (int i = 0; i < Length; i++) + { + //Clear the working set + for (int j = 0; j < i; j++) + { + workingSet[j] = 0; + workingSet2[j] = 0; + } + + workingSet2[i] = 0; + + ulong digit = thisTemp[i]; + if (digit == 0) continue; + + for (int j = 0; j + i < Length && j < maxDigit; j++) + { + //Multiply n1 by each of the integer digits of n2. + ulong temp = (ulong)thisTemp[j] * digit; + //n1.workingSet stores the low bits of each piecewise multiplication + workingSet[j + i] = (uint)(temp & 0xffffffff); + if (j + i + 1 < Length) + { + //n2.workingSet stores the high bits of each multiplication + workingSet2[j + i + 1] = (uint)(temp >> 32); + } + } + + AddInternalBits(workingSet); + AddInternalBits(workingSet2); + } + + sign = false; + } + + /// + /// Used for floating-point multiplication + /// Stores the high bits of the multiplication only (the carry bit from the + /// lower bits is missing, so the true answer might be 1 greater). + /// + /// + public void MulHi(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + int Length = n2.digitArray.Length; + + //Inner loop zero-mul avoidance + int maxDigit = 0; + for (int i = Length - 1; i >= 0; i--) + { + if (digitArray[i] != 0) + { + maxDigit = i + 1; + break; + } + } + + //Result is zero, 'this' is unchanged + if (maxDigit == 0) return; + + //Temp storage for source (both working sets are used by the calculation) + uint[] thisTemp = new uint[Length]; + for (int i = 0; i < Length; i++) + { + thisTemp[i] = digitArray[i]; + digitArray[i] = 0; + } + + for (int i = 0; i < Length; i++) + { + //Clear the working set + for (int j = 0; j < Length; j++) + { + workingSet[j] = 0; + n2.workingSet[j] = 0; + } + + n2.workingSet[i] = 0; + + ulong digit = n2.digitArray[i]; + if (digit == 0) continue; + + //Only the high bits + if (maxDigit + i < Length - 1) continue; + + for (int j = 0; j < maxDigit; j++) + { + if (j + i + 1 < Length) continue; + //Multiply n1 by each of the integer digits of n2. + ulong temp = (ulong)thisTemp[j] * digit; + //n1.workingSet stores the low bits of each piecewise multiplication + if (j + i >= Length) + { + workingSet[j + i - Length] = (uint)(temp & 0xffffffff); + } + + //n2.workingSet stores the high bits of each multiplication + n2.workingSet[j + i + 1 - Length] = (uint)(temp >> 32); + } + + AddInternalBits(workingSet); + AddInternalBits(n2.workingSet); + } + + sign = (sign != n2.sign); + } + + /// + /// Floating-point helper function. + /// Squares the number and keeps the high bits of the calculation. + /// Takes a temporary BigInt as a working set. + /// + public void SquareHiFast(BigInt scratch) + { + int Length = digitArray.Length; + uint[] tempDigits = scratch.digitArray; + uint[] workingSet2 = scratch.workingSet; + + //Temp storage for source (both working sets are used by the calculation) + for (int i = 0; i < Length; i++) + { + tempDigits[i] = digitArray[i]; + digitArray[i] = 0; + } + + for (int i = 0; i < Length; i++) + { + //Clear the working set + for (int j = i; j < Length; j++) + { + workingSet[j] = 0; + workingSet2[j] = 0; + } + + if (i - 1 >= 0) workingSet[i - 1] = 0; + + ulong digit = tempDigits[i]; + if (digit == 0) continue; + + for (int j = 0; j < Length; j++) + { + if (j + i + 1 < Length) continue; + //Multiply n1 by each of the integer digits of n2. + ulong temp = (ulong)tempDigits[j] * digit; + //n1.workingSet stores the low bits of each piecewise multiplication + if (j + i >= Length) + { + workingSet[j + i - Length] = (uint)(temp & 0xffffffff); + } + + //n2.workingSet stores the high bits of each multiplication + workingSet2[j + i + 1 - Length] = (uint)(temp >> 32); + } + + AddInternalBits(workingSet); + AddInternalBits(workingSet2); + } + + sign = false; + } + + /// + /// This uses the schoolbook division algorithm, as decribed by http://www.treskal.com/kalle/exjobb/original-report.pdf + /// Algorithms 3.1 (implemented by Div_31) and 3.2 (implemented by Div_32) + /// + /// + public void Div(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + int OldLength = digitArray.Length; + + //First, we need to prepare the operands for division using Div_32, which requires + //That the most significant digit of n2 be set. To do this, we need to shift n2 (and therefore n1) up. + //This operation can potentially increase the precision of the operands. + int shift = MakeSafeDiv(this, n2); + + BigInt Q = new BigInt(this, this.pres, true); + BigInt R = new BigInt(this, this.pres, true); + + Div_32(this, n2, Q, R); + + //Restore n2 to its pre-shift value + n2.RSH(shift); + AssignInt(Q); + sign = (sign != n2.sign); + + //Reset the lengths of the operands + SetNumDigits(OldLength); + n2.SetNumDigits(OldLength); + } + + /// + /// This function is used for floating-point division. + /// + /// + //Given two numbers: + // In floating point 1 <= a, b < 2, meaning that both numbers have their top bits set. + // To calculate a / b, maintaining precision, we: + // 1. Double the number of digits available to both numbers. + // 2. set a = a * 2^d (where d is the number of digits) + // 3. calculate the quotient a <- q: 2^(d-1) <= q < 2^(d+1) + // 4. if a >= 2^d, s = 1, else s = 0 + // 6. shift a down by s, and undo the precision extension + // 7. return 1 - shift (change necessary to exponent) + public int DivAndShift(BigInt n2) + { + if (n2.IsZero()) return -1; + if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); + + int oldLength = digitArray.Length; + + //Double the number of digits, and shift a into the higher digits. + Pad(); + n2.Extend(); + + //Do the divide (at double precision, ouch!) + Div(n2); + + //Shift down if 'this' >= 2^d + int ret = 1; + + if (digitArray[oldLength] != 0) + { + RSH(1); + ret--; + } + + SetNumDigits(oldLength); + n2.SetNumDigits(oldLength); + + return ret; + } + + /// + /// Calculates 'this' mod n2 (using the schoolbook division algorithm as above) + /// + /// + public void Mod(BigInt n2) + { + if (n2.digitArray.Length != digitArray.Length) MakeSafe(ref n2); + + int OldLength = digitArray.Length; + + //First, we need to prepare the operands for division using Div_32, which requires + //That the most significant digit of n2 be set. To do this, we need to shift n2 (and therefore n1) up. + //This operation can potentially increase the precision of the operands. + int shift = MakeSafeDiv(this, n2); + + BigInt Q = new BigInt(this.pres); + BigInt R = new BigInt(this.pres); + + Q.digitArray = new UInt32[this.digitArray.Length]; + R.digitArray = new UInt32[this.digitArray.Length]; + + Div_32(this, n2, Q, R); + + //Restore n2 to its pre-shift value + n2.RSH(shift); + R.RSH(shift); + R.sign = (sign != n2.sign); + AssignInt(R); + + //Reset the lengths of the operands + SetNumDigits(OldLength); + n2.SetNumDigits(OldLength); + } + + /// + /// Logical left shift + /// + /// + public void LSH(int shift) + { + if (shift <= 0) return; + int length = digitArray.Length; + int digits = shift >> 5; + int rem = shift & 31; + + for (int i = length - 1; i >= digits; i--) + { + digitArray[i] = digitArray[i - digits]; + } + + if (digits > 0) + { + for (int i = digits - 1; i >= 0; i--) + { + digitArray[i] = 0; + } + } + + UInt64 lastShift = 0; + + for (int i = 0; i < length; i++) + { + UInt64 temp = (((UInt64)digitArray[i]) << rem) | lastShift; + digitArray[i] = (UInt32)(temp & 0xffffffff); + lastShift = temp >> 32; + } + } + + /// + /// Logical right-shift + /// + /// + public void RSH(int shift) + { + if (shift < 0) return; + int length = digitArray.Length; + int digits = shift >> 5; + int rem = shift & 31; + + for (int i = 0; i < length - digits; i++) + { + digitArray[i] = digitArray[i + digits]; + } + + int start = (length - digits); + if (start < 0) start = 0; + + for (int i = start; i < length; i++) + { + digitArray[i] = 0; + } + + UInt64 lastShift = 0; + + for (int i = length - 1; i >= 0; i--) + { + UInt64 temp = ((((UInt64)digitArray[i]) << 32) >> rem) | lastShift; + digitArray[i] = (UInt32)(temp >> 32); + lastShift = (temp & 0xffffffff) << 32; + } + } + + /// + /// Changes the sign of the number + /// + public void Negate() + { + sign = !sign; + } + + /// + /// Increments the number. + /// + public void Increment() + { + if (sign) + { + DecrementInt(); + } + else + { + IncrementInt(); + } + } + + /// + /// Decrements the number. + /// + public void Decrement() + { + if (sign) + { + IncrementInt(); + } + else + { + DecrementInt(); + } + } + + /// + /// Calculates the factorial 'this'! + /// + public void Factorial() + { + if (sign) return; + + //Clamp to a reasonable range. + int factToUse = (int)(digitArray[0]); + if (factToUse > 65536) factToUse = 65536; + + Zero(); + digitArray[0] = 1; + + for (uint i = 1; i <= factToUse; i++) + { + MulInternal(i); + } + } + + /// + /// Calculates 'this'^power + /// + /// + public void Power(BigInt power) + { + if (power.IsZero() || power.sign) + { + Zero(); + digitArray[0] = 1; + return; + } + + BigInt pow = new BigInt(power); + BigInt temp = new BigInt(this); + BigInt powTerm = new BigInt(this); + + pow.Decrement(); + for (; !pow.IsZero(); pow.RSH(1)) + { + if ((pow.digitArray[0] & 1) == 1) + { + temp.Mul(powTerm); + } + + powTerm.Square(); + } + + Assign(temp); + } + + //***************** Comparison member functions ***************** + + /// + /// returns true if this bigint == 0 + /// + /// + public bool IsZero() + { + for (int i = 0; i < digitArray.Length; i++) + { + if (digitArray[i] != 0) return false; + } + + return true; + } + + /// + /// true iff n2 (precision adjusted to this) is less than 'this' + /// + /// + /// + public bool LessThan(BigInt n2) + { + if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); + + if (sign) + { + if (!n2.sign) return true; + return GtInt(this, n2); + } + else + { + if (n2.sign) return false; + return LtInt(this, n2); + } + } + + /// + /// true iff n2 (precision adjusted to this) is greater than 'this' + /// + /// + /// + public bool GreaterThan(BigInt n2) + { + if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); + + if (sign) + { + if (!n2.sign) return false; + return LtInt(this, n2); + } + else + { + if (n2.sign) return true; + return GtInt(this, n2); + } + } + + /// + /// Override of base-class equals + /// + /// + /// + public override bool Equals(object obj) + { + BigInt n2 = ((BigInt)obj); + return Equals(n2); + } + + /// + /// Get hash code + /// + /// + public override int GetHashCode() + { + return (Int32)digitArray[0]; + } + + /// + /// True iff n2 (precision-adjusted to this) == n1 + /// + /// + /// + public bool Equals(BigInt n2) + { + if (IsZero() && n2.IsZero()) return true; + + if (sign != n2.sign) return false; + + int Length = digitArray.Length; + if (n2.digitArray.Length != Length) MakeSafe(ref n2); + + for (int i = 0; i < Length; i++) + { + if (digitArray[i] != n2.digitArray[i]) return false; + } + + return true; + } + + //******************* Bitwise member functions ******************** + + /// + /// Takes the bitwise complement of the number + /// + public void Complement() + { + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + digitArray[i] = ~digitArray[i]; + } + } + + /// + /// Bitwise And + /// + /// + public void And(BigInt n2) + { + int Length = digitArray.Length; + if (n2.digitArray.Length != Length) MakeSafe(ref n2); + + for (int i = 0; i < Length; i++) + { + digitArray[i] &= n2.digitArray[i]; + } + + sign &= n2.sign; + } + + /// + /// Bitwise Or + /// + /// + public void Or(BigInt n2) + { + int Length = digitArray.Length; + if (n2.digitArray.Length != Length) MakeSafe(ref n2); + + for (int i = 0; i < Length; i++) + { + digitArray[i] |= n2.digitArray[i]; + } + + sign |= n2.sign; + } + + /// + /// Bitwise Xor + /// + /// + public void Xor(BigInt n2) + { + int Length = digitArray.Length; + if (n2.digitArray.Length != Length) MakeSafe(ref n2); + + for (int i = 0; i < Length; i++) + { + digitArray[i] ^= n2.digitArray[i]; + } + + sign ^= n2.sign; + } + + //*************** Fast Static Arithmetic Functions ***************** + + /// + /// Adds n1 and n2 and puts result in dest, without intermediate memory allocation + /// (unsafe if n1 and n2 disagree in precision, safe even if dest is n1 or n2) + /// + /// + /// + /// + public static void AddFast(BigInt dest, BigInt n1, BigInt n2) + { + //We cast to the highest input precision... + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + + //Then we up the output precision if less than the input precision. + if (dest.digitArray.Length < n1.digitArray.Length) n1.MakeSafe(ref dest); + + int Length = n1.digitArray.Length; + + if (n1.sign == n2.sign) + { + //Copies sources into digit array and working set for all cases, to avoid + //problems when dest is n1 or n2 + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n2.digitArray[i]; + dest.digitArray[i] = n1.digitArray[i]; + } + dest.AddInternalBits(dest.workingSet); + dest.sign = n1.sign; + } + else + { + bool lessThan = LtInt(n1, n2); + + if (lessThan) + { + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n1.digitArray[i]; + dest.digitArray[i] = n2.digitArray[i]; + } + dest.SubInternalBits(dest.workingSet); + dest.sign = !n1.sign; + } + else + { + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n2.digitArray[i]; + dest.digitArray[i] = n1.digitArray[i]; + } + dest.SubInternalBits(dest.workingSet); + dest.sign = n1.sign; + } + } + } + + /// + /// Adds n1 and n2 and puts result in dest, without intermediate memory allocation + /// (unsafe if n1 and n2 disagree in precision, safe even if dest is n1 or n2) + /// + /// + /// + /// + public static void SubFast(BigInt dest, BigInt n1, BigInt n2) + { + //We cast to the highest input precision... + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + + //Then we up the output precision if less than the input precision. + if (dest.digitArray.Length < n1.digitArray.Length) n1.MakeSafe(ref dest); + + int Length = n1.digitArray.Length; + + if (n1.sign != n2.sign) + { + //Copies sources into digit array and working set for all cases, to avoid + //problems when dest is n1 or n2 + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n2.digitArray[i]; + dest.digitArray[i] = n1.digitArray[i]; + } + dest.AddInternalBits(dest.workingSet); + dest.sign = n1.sign; + } + else + { + bool lessThan = LtInt(n1, n2); + + if (lessThan) + { + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n1.digitArray[i]; + dest.digitArray[i] = n2.digitArray[i]; + } + dest.SubInternalBits(dest.workingSet); + dest.sign = !n1.sign; + } + else + { + for (int i = 0; i < Length; i++) + { + dest.workingSet[i] = n2.digitArray[i]; + dest.digitArray[i] = n1.digitArray[i]; + } + dest.SubInternalBits(dest.workingSet); + dest.sign = n1.sign; + } + } + } + + //*************** Static Arithmetic Functions *************** + + /// + /// signed addition of 2 numbers. + /// + /// + /// + /// + public static BigInt Add(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt result; + + if (n1.sign == n2.sign) + { + result = new BigInt(n1); + result.AddInternal(n2); + result.sign = n1.sign; + } + else + { + bool lessThan = LtInt(n1, n2); + + if (lessThan) + { + result = new BigInt(n2); + result.SubInternal(n1); + result.sign = !n1.sign; + } + else + { + result = new BigInt(n1); + result.SubInternal(n2); + result.sign = n1.sign; + } + } + + return result; + } + + /// + /// signed subtraction of 2 numbers. + /// + /// + /// + /// + public static BigInt Sub(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt result; + + if ((n1.sign && !n2.sign) || (!n1.sign && n2.sign)) + { + result = new BigInt(n1); + result.AddInternal(n2); + result.sign = n1.sign; + } + else + { + bool lessThan = LtInt(n1, n2); + + if (lessThan) + { + result = new BigInt(n2); + result.SubInternal(n1); + result.sign = !n1.sign; + } + else + { + result = new BigInt(n1); + result.SubInternal(n2); + result.sign = n1.sign; + } + } + + return result; + } + + /// + /// Multiplication of two BigInts + /// + /// + /// + /// + public static BigInt Mul(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + + BigInt result = new BigInt(n1); + result.Mul(n2); + return result; + } + + /// + /// True arbitrary precision divide. + /// + /// + /// + /// + public static BigInt Div(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt res = new BigInt(n1); + res.Div(n2); + return res; + } + + /// + /// True arbitrary-precision mod operation + /// + /// + /// + /// + public static BigInt Mod(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt res = new BigInt(n1); + res.Mod(n2); + return res; + } + + /// + /// Unsigned multiplication of a BigInt by a small number + /// + /// + /// + /// + public static BigInt Mul(BigInt n1, uint n2) + { + BigInt result = new BigInt(n1); + result.MulInternal(n2); + return result; + } + + /// + /// Division of a BigInt by a small (unsigned) number + /// + /// + /// + /// + public static BigInt Div(BigInt n1, uint n2) + { + BigInt result = new BigInt(n1); + result.DivInternal(n2); + return result; + } + + /// + /// Division and remainder of a BigInt by a small (unsigned) number + /// n1 / n2 = div remainder mod + /// + /// The number to divide (dividend) + /// The number to divide by (divisor) + /// The quotient (output parameter) + /// The remainder (output parameter) + public static void DivMod(BigInt n1, uint n2, out BigInt div, out BigInt mod) + { + div = Div(n1, n2); + mod = Mul(div, n2); + mod = Sub(n1, mod); + } + + //**************** Static Comparison Functions *************** + + /// + /// true iff n1 is less than n2 + /// + /// + /// + /// + public static bool LessThan(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + + if (n1.sign) + { + if (!n2.sign) return true; + return GtInt(n1, n2); + } + else + { + if (n2.sign) return false; + return LtInt(n1, n2); + } + } + + /// + /// true iff n1 is greater than n2 + /// + /// + /// + /// + public static bool GreaterThan(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + + if (n1.sign) + { + if (!n2.sign) return false; + return LtInt(n1, n2); + } + else + { + if (n2.sign) return true; + return GtInt(n1, n2); + } + } + + /// + /// true iff n1 == n2 + /// + /// + /// + /// + public static bool Equals(BigInt n1, BigInt n2) + { + return n1.Equals(n2); + } + + //***************** Static Bitwise Functions ***************** + + /// + /// Bitwise And + /// + /// + /// + /// + public static BigInt And(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt res = new BigInt(n1); + res.And(n2); + return res; + } + + /// + /// Bitwise Or + /// + /// + /// + /// + public static BigInt Or(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt res = new BigInt(n1); + res.Or(n2); + return res; + } + + /// + /// Bitwise Xor + /// + /// + /// + /// + public static BigInt Xor(BigInt n1, BigInt n2) + { + if (n1.digitArray.Length != n2.digitArray.Length) MakeSafe(ref n1, ref n2); + BigInt res = new BigInt(n1); + res.And(n2); + return res; + } + + //**************** Static Operator Overloads ***************** + + /// + /// Addition operator + /// + public static BigInt operator +(BigInt n1, BigInt n2) + { + return Add(n1, n2); + } + + /// + /// The subtraction operator + /// + public static BigInt operator -(BigInt n1, BigInt n2) + { + return Sub(n1, n2); + } + + /// + /// The multiplication operator + /// + public static BigInt operator *(BigInt n1, BigInt n2) + { + return Mul(n1, n2); + } + + /// + /// The division operator + /// + public static BigInt operator /(BigInt n1, BigInt n2) + { + return Div(n1, n2); + } + + /// + /// The remainder (mod) operator + /// + public static BigInt operator %(BigInt n1, BigInt n2) + { + return Mod(n1, n2); + } + + /// + /// The left-shift operator + /// + public static BigInt operator <<(BigInt n1, int n2) + { + BigInt res = new BigInt(n1); + res.LSH(n2); + return res; + } + + /// + /// The right-shift operator + /// + public static BigInt operator >>(BigInt n1, int n2) + { + BigInt res = new BigInt(n1); + res.RSH(n2); + return res; + } + + /// + /// The less than operator + /// + public static bool operator <(BigInt n1, BigInt n2) + { + return LessThan(n1, n2); + } + + /// + /// The greater than operator + /// + public static bool operator >(BigInt n1, BigInt n2) + { + return GreaterThan(n1, n2); + } + + /// + /// The less than or equal to operator + /// + public static bool operator <=(BigInt n1, BigInt n2) + { + return !GreaterThan(n1, n2); + } + + /// + /// The greater than or equal to operator + /// + public static bool operator >=(BigInt n1, BigInt n2) + { + return !LessThan(n1, n2); + } + + /// + /// The equality operator + /// + public static bool operator ==(BigInt n1, BigInt n2) + { + return Equals(n1, n2); + } + + /// + /// The inequality operator + /// + public static bool operator !=(BigInt n1, BigInt n2) + { + return !Equals(n1, n2); + } + + /// + /// The bitwise AND operator + /// + public static BigInt operator &(BigInt n1, BigInt n2) + { + return And(n1, n2); + } + + /// + /// The bitwise OR operator + /// + public static BigInt operator |(BigInt n1, BigInt n2) + { + return Or(n1, n2); + } + + /// + /// The bitwise eXclusive OR operator + /// + public static BigInt operator ^(BigInt n1, BigInt n2) + { + return Xor(n1, n2); + } + + /// + /// The increment operator + /// + public static BigInt operator ++(BigInt n1) + { + n1.Increment(); + return n1; + } + + /// + /// The decrement operator + /// + public static BigInt operator --(BigInt n1) + { + n1.Decrement(); + return n1; + } + + //**************** Private Member Functions ***************** + + /// + /// Unsigned multiplication and assignment by a small number + /// + /// + private void MulInternal(uint n2) + { + int Length = digitArray.Length; + ulong n2long = (ulong)n2; + + for (int i = 0; i < Length; i++) + { + workingSet[i] = 0; + } + + for (int i = 0; i < Length; i++) + { + if (digitArray[i] == 0) continue; + ulong temp = (ulong)digitArray[i] * n2long; + digitArray[i] = (uint)(temp & 0xffffffff); + if (i + 1 < Length) workingSet[i + 1] = (uint)(temp >> 32); + } + + AddInternalBits(workingSet); + } + + /// + /// Unsigned division and assignment by a small number + /// + /// + private void DivInternal(uint n2) + { + int Length = digitArray.Length; + ulong carry = 0; + + //Divide each digit by the small number. + for (int i = Length - 1; i >= 0; i--) + { + ulong temp = (ulong)digitArray[i] + (carry << 32); + digitArray[i] = (uint)(temp / (ulong)n2); + carry = temp % (ulong)n2; + } + } + + /// + /// Adds a signed integer to the number. + /// + /// + private void AddInternal(int n1) + { + if (n1 < 0) + { + SubInternal(-n1); + return; + } + + uint carry = 0; + int length = digitArray.Length; + + for (int i = 0; i < length && !(n1 == 0 && carry == 0); i++) + { + uint temp = digitArray[i]; + digitArray[i] += (uint)n1 + carry; + + carry = (digitArray[i] <= temp) ? 1u: 0u; + + n1 = 0; + } + } + + /// + /// Subtract a signed integer from the number. + /// This is internal because it will fail spectacularly if this number is negative or if n1 is bigger than this number. + /// + /// + private void SubInternal(int n1) + { + if (n1 < 0) + { + AddInternal(-n1); + return; + } + + uint carry = 0; + int length = digitArray.Length; + + for (int i = 0; i < length && !(n1 == 0 && carry == 0); i++) + { + uint temp = digitArray[i]; + digitArray[i] -= ((uint)n1 + carry); + + carry = (digitArray[i] >= temp) ? 1u: 0u; + + n1 = 0; + } + } + + /// + /// Adds a signed integer to the number. + /// + /// + private bool AddInternal(uint n1) + { + uint carry = 0; + int length = digitArray.Length; + + for (int i = 0; i < length && !(n1 == 0 && carry == 0); i++) + { + uint temp = digitArray[i]; + digitArray[i] += n1 + carry; + + carry = (digitArray[i] <= temp) ? 1u: 0u; + + n1 = 0; + } + + return (carry != 0); + } + + /// + /// Internally subtracts a uint from the number (sign insensitive) + /// + /// + /// + private bool SubInternal(uint n1) + { + uint carry = 0; + int length = digitArray.Length; + + for (int i = 0; i < length && !(n1 == 0 && carry == 0); i++) + { + uint temp = digitArray[i]; + digitArray[i] -= (n1 + carry); + + carry = (digitArray[i] >= temp) ? 1u: 0u; + + n1 = 0; + } + + return (carry != 0); + } + + /// + /// Internal increment function (sign insensitive) + /// + private bool IncrementInt() + { + uint carry = 1; + + int length = digitArray.Length; + + for (int i = 0; i < length && carry != 0; i++) + { + uint temp = digitArray[i]; + digitArray[i]++; + + if (digitArray[i] > temp) carry = 0; + } + + return (carry != 0); + } + + /// + /// Internal increment function (sign insensitive) + /// + private bool DecrementInt() + { + uint carry = 1; + + int length = digitArray.Length; + + for (int i = 0; i < length && carry != 0; i++) + { + uint temp = digitArray[i]; + digitArray[i]--; + + if (digitArray[i] < temp) carry = 0; + } + + return (carry != 0); + } + + /// + /// Used to add a digit array to a big int. + /// + /// + private uint AddInternalBits(uint[] digitsToAdd) + { + uint carry = 0; + + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + //Necessary because otherwise the carry calculation could go bad. + if (digitsToAdd[i] == 0 && carry == 0) continue; + + uint temp = digitArray[i]; + digitArray[i] += (digitsToAdd[i] + carry); + + carry = 0; + if (digitArray[i] <= temp) carry = 1; + } + + return carry; + } + + /// + /// Used to add with matching signs (true addition of the digit arrays) + /// This is internal because it will fail spectacularly if n1 is negative. + /// + /// + private uint AddInternal(BigInt n1) + { + return AddInternalBits(n1.digitArray); + } + + private uint SubInternalBits(uint[] digitsToAdd) + { + uint carry = 0; + + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + //Necessary because otherwise the carry calculation could go bad. + if (digitsToAdd[i] == 0 && carry == 0) continue; + + uint temp = digitArray[i]; + digitArray[i] -= (digitsToAdd[i] + carry); + + carry = 0; + if (digitArray[i] >= temp) carry = 1; + } + + return carry; + } + + /// + /// Used to subtract n1 (true subtraction of digit arrays) - n1 must be less than or equal to this number + /// + /// + private uint SubInternal(BigInt n1) + { + return SubInternalBits(n1.digitArray); + } + + /// + /// Returns the length of the BigInt in 32-bit words for a given decimal precision + /// + /// + /// + private static int GetRequiredDigitsForPrecision(PrecisionSpec precision) + { + int bits = precision.NumBits; + return ((bits - 1) >> 5) + 1; + } + + /// + /// Initialises the BigInt to a desired decimal precision + /// + /// + private void Init(PrecisionSpec precision) + { + int numDigits = GetRequiredDigitsForPrecision(precision); + digitArray = new uint[numDigits]; + workingSet = new uint[numDigits]; + pres = precision; + } + + /// + /// Initialises the BigInt from a string, given a base and precision + /// + /// + /// + /// + private void InitFromString(string init, PrecisionSpec precision, int numberBase) + { + PrecisionSpec test; + if (numberBase == 2) + { + test = new PrecisionSpec(init.Length, PrecisionSpec.BaseType.BIN); + } + else if (numberBase == 8) + { + test = new PrecisionSpec(init.Length, PrecisionSpec.BaseType.OCT); + } + else if (numberBase == 10) + { + test = new PrecisionSpec(init.Length, PrecisionSpec.BaseType.DEC); + } + else if (numberBase == 16) + { + test = new PrecisionSpec(init.Length, PrecisionSpec.BaseType.HEX); + } + else + { + throw new ArgumentOutOfRangeException(); + } + + //if (test.NumBits > precision.NumBits) precision = test; + Init(precision); + FromStringInt(init, numberBase); + } + + //************ Helper Functions for floating point ************* + + /// + /// Returns true if only the top bit is set: i.e. if the floating-point number is a power of 2 + /// + /// + public bool IsTopBitOnlyBit() + { + int length = digitArray.Length; + + if (digitArray[length - 1] != 0x80000000u) return false; + length--; + for (int i = 0; i < length; i++) + { + if (digitArray[i] != 0) return false; + } + + return true; + } + + /// + /// Zeroes the n most significant bits of the number + /// + /// + public void ZeroBitsHigh(int bits) + { + //Already done. + if (bits <= 0) return; + + int length = digitArray.Length; + + //The entire digit array. + if ((bits >> 5) > length) + { + bits = length << 5; + } + + int remBits = (bits & 31); + int startDigit = length - ((bits >> 5) + 1); + + if (remBits != 0) + { + digitArray[startDigit] = digitArray[startDigit] & (0xffffffffu >> remBits); + } + + for (int i = startDigit + 1; i < length; i++) + { + digitArray[i] = 0; + } + } + + /// + /// Zeroes the least-significant n bits. + /// + /// + public void ZeroBits(int bits) + { + //Already done. + if (bits <= 0) return; + + //The entire digit array. + if ((bits >> 5) > digitArray.Length) + { + bits = digitArray.Length << 5; + } + + int remBits = (bits & 31); + int startDigit = bits >> 5; + + if (remBits != 0) + { + UInt32 startMask = 0xffffffffu & ~(UInt32)(((1 << remBits) - 1)); + digitArray[startDigit] = digitArray[startDigit] & startMask; + } + + for (int i = startDigit - 1; i >= 0; i--) + { + digitArray[i] = 0; + } + } + + /// + /// Sets the number to 0 + /// + public void Zero() + { + int length = digitArray.Length; + + for (int i = 0; i < length; i++) + { + digitArray[i] = 0; + } + } + + /// + /// Rounds off the least significant bits of the number. + /// Can only round off up to 31 bits. + /// + /// number of bits to round + /// + public bool Round(int bits) + { + //Always less than 32 bits, please! + if (bits < 32) + { + uint pow2 = 1u << bits; + uint test = digitArray[0] & (pow2 >> 1); + + //Zero the lower bits + digitArray[0] = digitArray[0] & ~(pow2 - 1); + + if (test != 0) + { + bool bRet = AddInternal(pow2); + digitArray[digitArray.Length - 1] = digitArray[digitArray.Length - 1] | 0x80000000; + return bRet; + } + } + + return false; + } + + /// + /// Used for casting between BigFloats of different precisions - this assumes + /// that the number is a normalised mantissa. + /// + /// + /// true if a round-up caused the high bits to become zero + public bool AssignHigh(BigInt n2) + { + int length = digitArray.Length; + int length2 = n2.digitArray.Length; + int minWords = (length < length2) ? length: length2; + bool bRet = false; + + for (int i = 1; i <= minWords; i++) + { + digitArray[length - i] = n2.digitArray[length2 - i]; + } + + if (length2 > length && n2.digitArray[length2 - (length + 1)] >= 0x80000000) + { + bRet = IncrementInt(); + + //Because we are assuming normalisation, we set the top bit (it will already be set if + //bRet is false. + digitArray[length - 1] = digitArray[length - 1] | 0x80000000; + } + + sign = n2.sign; + + return bRet; + } + + /// + /// Used for casting between long ints or doubles and floating-point numbers + /// + /// + public void SetHighDigits(Int64 digits) + { + digitArray[digitArray.Length - 1] = (uint)(digits >> 32); + if (digitArray.Length > 1) digitArray[digitArray.Length - 2] = (uint)(digits & 0xffffffff); + } + + /// + /// Used for casting between ints and doubles or floats. + /// + /// + public void SetHighDigit(UInt32 digit) + { + digitArray[digitArray.Length - 1] = digit; + } + + /// + /// Helper function for floating-point - extends the number to twice the precision + /// and shifts the digits into the upper bits. + /// + public void Pad() + { + int length = digitArray.Length; + int digits = length << 1; + + UInt32[] newDigitArray = new UInt32[digits]; + workingSet = new UInt32[digits]; + + for (int i = 0; i < length; i++) + { + newDigitArray[i + length] = digitArray[i]; + } + + digitArray = newDigitArray; + } + + /// + /// Helper function for floating-point - extends the number to twice the precision... + /// This is a necessary step in floating-point division. + /// + public void Extend() + { + SetNumDigits(digitArray.Length * 2); + } + + /// + /// Gets the highest big of the integer (used for floating point stuff) + /// + /// + public uint GetTopBit() + { + return (digitArray[digitArray.Length - 1] >> 31); + } + + /// + /// Used for floating point multiplication, this shifts the number so that + /// the highest bit is set, and returns the number of places shifted. + /// + /// + public int Normalise() + { + if (IsZero()) return 0; + + int MSD = GetMSD(); + int digitShift = (digitArray.Length - (MSD + 1)); + int bitShift = (31 - GetMSB(digitArray[MSD])) + (digitShift << 5); + LSH(bitShift); + return bitShift; + } + + /// + /// Gets the most significant bit + /// + /// the input to search for the MSB in + /// -1 if the input was zero, the position of the MSB otherwise + public static int GetMSB(UInt32 value) + { + if (value == 0) return -1; + + uint mask1 = 0xffff0000; + uint mask2 = 0xff00; + uint mask3 = 0xf0; + uint mask4 = 0xc; //1100 in binary + uint mask5 = 0x2; //10 in binary + + int iPos = 0; + + //Unrolled binary search for the most significant bit. + if ((value & mask1) != 0) iPos += 16; + mask2 <<= iPos; + + if ((value & mask2) != 0) iPos += 8; + mask3 <<= iPos; + + if ((value & mask3) != 0) iPos += 4; + mask4 <<= iPos; + + if ((value & mask4) != 0) iPos += 2; + mask5 <<= iPos; + + if ((value & mask5) != 0) iPos++; + + return iPos; + } + + /// + /// Gets the most significant bit + /// + /// the input to search for the MSB in + /// -1 if the input was zero, the position of the MSB otherwise + public static int GetMSB(UInt64 value) + { + if (value == 0) return -1; + + UInt64 mask0 = 0xffffffff00000000ul; + UInt64 mask1 = 0xffff0000; + UInt64 mask2 = 0xff00; + UInt64 mask3 = 0xf0; + UInt64 mask4 = 0xc; //1100 in binary + UInt64 mask5 = 0x2; //10 in binary + + int iPos = 0; + + //Unrolled binary search for the most significant bit. + if ((value & mask0) != 0) iPos += 32; + mask1 <<= iPos; + + if ((value & mask1) != 0) iPos += 16; + mask2 <<= iPos; + + if ((value & mask2) != 0) iPos += 8; + mask3 <<= iPos; + + if ((value & mask3) != 0) iPos += 4; + mask4 <<= iPos; + + if ((value & mask4) != 0) iPos += 2; + mask5 <<= iPos; + + if ((value & mask5) != 0) iPos++; + + return iPos; + } + + /// + /// Gets the most significant bit + /// + /// the input to search for the MSB in + /// -1 if the input was zero, the position of the MSB otherwise + public static int GetMSB(BigInt value) + { + int digit = value.GetMSD(); + int bit = GetMSB(value.digitArray[digit]); + return (digit << 5) + bit; + } + + //**************** Helper Functions for Div ******************** + + /// + /// Gets the index of the most significant digit + /// + /// + private int GetMSD() + { + for (int i = digitArray.Length - 1; i >= 0; i--) + { + if (digitArray[i] != 0) return i; + } + + return 0; + } + + /// + /// Gets the required bitshift for the Div_32 algorithm + /// + /// + private int GetDivBitshift() + { + uint digit = digitArray[GetMSD()]; + uint mask1 = 0xffff0000; + uint mask2 = 0xff00; + uint mask3 = 0xf0; + uint mask4 = 0xc; //1100 in binary + uint mask5 = 0x2; //10 in binary + + int iPos = 0; + + //Unrolled binary search for the most significant bit. + if ((digit & mask1) != 0) iPos += 16; + mask2 <<= iPos; + + if ((digit & mask2) != 0) iPos += 8; + mask3 <<= iPos; + + if ((digit & mask3) != 0) iPos += 4; + mask4 <<= iPos; + + if ((digit & mask4) != 0) iPos += 2; + mask5 <<= iPos; + + if ((digit & mask5) != 0) return 30 - iPos; + + return 31 - iPos; + } + + /// + /// Shifts and optionally precision-extends the arguments to prepare for Div_32 + /// + /// + /// + private static int MakeSafeDiv(BigInt n1, BigInt n2) + { + int shift = n2.GetDivBitshift(); + int n1MSD = n1.GetMSD(); + + uint temp = n1.digitArray[n1MSD]; + if (n1MSD == n1.digitArray.Length - 1 && ((temp << shift) >> shift) != n1.digitArray[n1MSD]) + { + //Precision-extend n1 and n2 if necessary + int digits = n1.digitArray.Length; + n1.SetNumDigits(digits + 1); + n2.SetNumDigits(digits + 1); + } + + //Logical left-shift n1 and n2 + n1.LSH(shift); + n2.LSH(shift); + + return shift; + } + + /// + /// Schoolbook division helper function. + /// + /// + /// + /// Quotient output value + /// Remainder output value + private static void Div_31(BigInt n1, BigInt n2, BigInt Q, BigInt R) + { + int digitsN1 = n1.GetMSD() + 1; + int digitsN2 = n2.GetMSD() + 1; + + if ((digitsN1 > digitsN2)) + { + BigInt n1New = new BigInt(n2); + n1New.DigitShiftSelfLeft(1); + + //If n1 >= n2 * 2^32 + if (!LtInt(n1, n1New)) + { + n1New.sign = n1.sign; + SubFast(n1New, n1, n1New); + + Div_32(n1New, n2, Q, R); + + //Q = (A - B*2^32)/B + 2^32 + Q.Add2Pow32Self(); + return; + } + } + + UInt32 q = 0; + + if (digitsN1 >= 2) + { + UInt64 q64 = ((((UInt64)n1.digitArray[digitsN1 - 1]) << 32) + n1.digitArray[digitsN1 - 2]) / (UInt64)n2.digitArray[digitsN2 - 1]; + + if (q64 > 0xfffffffful) + { + q = 0xffffffff; + } + else + { + q = (UInt32)q64; + } + } + + BigInt temp = Mul(n2, q); + + if (GtInt(temp, n1)) + { + temp.SubInternalBits(n2.digitArray); + q--; + + if (GtInt(temp, n1)) + { + temp.SubInternalBits(n2.digitArray); + q--; + } + } + + Q.Zero(); + Q.digitArray[0] = q; + R.Assign(n1); + R.SubInternalBits(temp.digitArray); + } + + /// + /// Schoolbook division algorithm + /// + /// + /// + /// + /// + private static void Div_32(BigInt n1, BigInt n2, BigInt Q, BigInt R) + { + int digitsN1 = n1.GetMSD() + 1; + int digitsN2 = n2.GetMSD() + 1; + + //n2 is bigger than n1 + if (digitsN1 < digitsN2) + { + R.AssignInt(n1); + Q.Zero(); + return; + } + + if (digitsN1 == digitsN2) + { + //n2 is bigger than n1 + if (LtInt(n1, n2)) + { + R.AssignInt(n1); + Q.Zero(); + return; + } + + //n2 >= n1, but less the 2x n1 (initial conditions make this certain) + Q.Zero(); + Q.digitArray[0] = 1; + R.Assign(n1); + R.SubInternalBits(n2.digitArray); + return; + } + + int digits = digitsN1 - (digitsN2 + 1); + + //Algorithm Div_31 can be used to get the answer in O(n) time. + if (digits == 0) + { + Div_31(n1, n2, Q, R); + return; + } + + BigInt n1New = DigitShiftRight(n1, digits); + BigInt s = DigitTruncate(n1, digits); + + BigInt Q2 = new BigInt(n1, n1.pres, true); + BigInt R2 = new BigInt(n1, n1.pres, true); + + Div_31(n1New, n2, Q2, R2); + + R2.DigitShiftSelfLeft(digits); + R2.Add(s); + + Div_32(R2, n2, Q, R); + + Q2.DigitShiftSelfLeft(digits); + Q.Add(Q2); + } + + /// + /// Sets the n-th bit of the number to 1 + /// + /// the index of the bit to set + public void SetBit(int bit) + { + int digit = (bit >> 5); + if (digit >= digitArray.Length) return; + digitArray[digit] = digitArray[digit] | (1u << (bit - (digit << 5))); + } + + /// + /// Sets the n-th bit of the number to 0 + /// + /// the index of the bit to set + public void ClearBit(int bit) + { + int digit = (bit >> 5); + if (digit >= digitArray.Length) return; + digitArray[digit] = digitArray[digit] & (~(1u << (bit - (digit << 5)))); + } + + /// + /// Returns the n-th bit, counting from the MSB to the LSB + /// + /// the index of the bit to return + /// 1 if the bit is 1, 0 otherwise + public uint GetBitFromTop(int bit) + { + if (bit < 0) return 0; + int wordCount = (bit >> 5); + int upBit = 31 - (bit & 31); + if (wordCount >= digitArray.Length) return 0; + + return ((digitArray[digitArray.Length - (wordCount + 1)] & (1u << upBit)) >> upBit); + } + + /// + /// Assigns n2 to 'this' + /// + /// + public void Assign(BigInt n2) + { + if (digitArray.Length != n2.digitArray.Length) MakeSafe(ref n2); + sign = n2.sign; + AssignInt(n2); + } + + /// + /// Assign n2 to 'this', safe only if precision-matched + /// + /// + /// + private void AssignInt(BigInt n2) + { + int Length = digitArray.Length; + + for (int i = 0; i < Length; i++) + { + digitArray[i] = n2.digitArray[i]; + } + } + + private static BigInt DigitShiftRight(BigInt n1, int digits) + { + BigInt res = new BigInt(n1); + + int Length = res.digitArray.Length; + + for (int i = 0; i < Length - digits; i++) + { + res.digitArray[i] = res.digitArray[i + digits]; + } + + for (int i = Length - digits; i < Length; i++) + { + res.digitArray[i] = 0; + } + + return res; + } + + private void DigitShiftSelfRight(int digits) + { + for (int i = digits; i < digitArray.Length; i++) + { + digitArray[i - digits] = digitArray[i]; + } + + for (int i = digitArray.Length - digits; i < digitArray.Length; i++) + { + digitArray[i] = 0; + } + } + + private void DigitShiftSelfLeft(int digits) + { + for (int i = digitArray.Length - 1; i >= digits; i--) + { + digitArray[i] = digitArray[i - digits]; + } + + for (int i = digits - 1; i >= 0; i--) + { + digitArray[i] = 0; + } + } + + private static BigInt DigitTruncate(BigInt n1, int digits) + { + BigInt res = new BigInt(n1); + + for (int i = res.digitArray.Length - 1; i >= digits; i--) + { + res.digitArray[i] = 0; + } + + return res; + } + + private void Add2Pow32Self() + { + int Length = digitArray.Length; + + uint carry = 1; + + for (int i = 1; i < Length; i++) + { + uint temp = digitArray[i]; + digitArray[i] += carry; + if (digitArray[i] > temp) return; + } + + return; + } + + /// + /// Sets the number of digits without changing the precision + /// This method is made public only to facilitate fixed-point operations + /// It should under no circumstances be used for anything else, because + /// it breaks the BigNum(PrecisionSpec precision) constructor in dangerous + /// and unpredictable ways. + /// + /// + public void SetNumDigits(int digits) + { + if (digits == digitArray.Length) return; + + UInt32[] newDigitArray = new UInt32[digits]; + workingSet = new UInt32[digits]; + + int numCopy = (digits < digitArray.Length) ? digits : digitArray.Length; + + for (int i = 0; i < numCopy; i++) + { + newDigitArray[i] = digitArray[i]; + } + + digitArray = newDigitArray; + } + + //********************** Explicit casts *********************** + + /// + /// Cast to int + /// + /// + /// + public static explicit operator Int32(BigInt value) + { + if (value.digitArray[0] == 0x80000000 && value.sign) return Int32.MinValue; + int res = (int)(value.digitArray[0] & 0x7fffffff); + if (value.sign) res = -res; + return res; + } + + /// + /// explicit cast to unsigned int. + /// + /// + /// + public static explicit operator UInt32(BigInt value) + { + if (value.sign) return (UInt32)((Int32)(value)); + return (UInt32)value.digitArray[0]; + } + + /// + /// explicit cast to 64-bit signed integer. + /// + /// + /// + public static explicit operator Int64(BigInt value) + { + if (value.digitArray.Length < 2) return (value.sign ? -((Int64)value.digitArray[0]): ((Int64)value.digitArray[0])); + UInt64 ret = (((UInt64)value.digitArray[1]) << 32) + (UInt64)value.digitArray[0]; + if (ret == 0x8000000000000000L && value.sign) return Int64.MinValue; + Int64 signedRet = (Int64)(ret & 0x7fffffffffffffffL); + if (value.sign) signedRet = -signedRet; + return signedRet; + } + + /// + /// Explicit cast to UInt64 + /// + /// + /// + public static explicit operator UInt64(BigInt value) + { + if (value.sign) return (UInt64)((Int64)(value)); + if (value.digitArray.Length < 2) return (UInt64)value.digitArray[0]; + return ((((UInt64)value.digitArray[1]) << 32) + (UInt64)value.digitArray[0]); + } + + /// + /// Cast to string + /// + /// + /// + public static explicit operator string(BigInt value) + { + return value.ToString(); + } + + /// + /// Cast from string - this is not wholly safe, because precision is not + /// specified. You should try to construct a BigInt with the appropriate + /// constructor instead. + /// + /// The decimal string to convert to a BigInt + /// A BigInt of the precision required to encompass the string + public static explicit operator BigInt(string value) + { + return new BigInt(value); + } + + //********************* ToString members ********************** + + /// + /// Converts this to a string, in the specified base + /// + /// the base to use (min 2, max 16) + /// a string representation of the number + public string ToString(int numberBase) + { + char[] digitChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + string output = ""; + + BigInt clone = new BigInt(this); + clone.sign = false; + + int numDigits = 0; + while (!clone.IsZero()) + { + if (numberBase == 10 && (numDigits % 3) == 0 && numDigits != 0) + { + output = String.Format(",{0}", output); + } + else if (numberBase != 10 && (numDigits % 8) == 0 && numDigits != 0) + { + output = String.Format(" {0}", output); + } + + BigInt div, mod; + DivMod(clone, (uint)numberBase, out div, out mod); + int iMod = (int)mod; + output = String.Format("{0}{1}", digitChars[(int)mod], output); + + numDigits++; + + clone = div; + } + + if (output.Length == 0) output = String.Format("0"); + + if (sign) output = String.Format("-{0}", output); + + return output; + } + + /// + /// Converts the number to a string, in base 10 + /// + /// a string representation of the number in base 10 + public override string ToString() + { + return ToString(10); + } + + //***************** Internal helper functions ***************** + + private void FromStringInt(string init, int numberBase) + { + char [] digitChars = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + string formattedInput = init.Trim().ToUpper(); + + for (int i = 0; i < formattedInput.Length; i++) + { + int digitIndex = Array.IndexOf(digitChars, formattedInput[i]); + + //Skip fractional part altogether + if (formattedInput[i] == '.') break; + + //skip non-digit characters. + if (digitIndex < 0) continue; + + //Multiply + MulInternal((uint)numberBase); + + //Add + AddInternal(digitIndex); + } + + if (init.Length > 0 && init[0] == '-') sign = true; + } + + /// + /// Sign-insensitive less than comparison. + /// unsafe if n1 and n2 disagree in precision + /// + /// + /// + /// + private static bool LtInt(BigInt n1, BigInt n2) + { + //MakeSafe(ref n1, ref n2); + + for (int i = n1.digitArray.Length - 1; i >= 0; i--) + { + if (n1.digitArray[i] < n2.digitArray[i]) return true; + if (n1.digitArray[i] > n2.digitArray[i]) return false; + } + + //equal + return false; + } + + /// + /// Sign-insensitive greater than comparison. + /// unsafe if n1 and n2 disagree in precision + /// + /// + /// + /// + private static bool GtInt(BigInt n1, BigInt n2) + { + //MakeSafe(ref n1, ref n2); + + for (int i = n1.digitArray.Length - 1; i >= 0; i--) + { + if (n1.digitArray[i] > n2.digitArray[i]) return true; + if (n1.digitArray[i] < n2.digitArray[i]) return false; + } + + //equal + return false; + } + + /// + /// Makes sure the numbers have matching precisions + /// + /// + /// + private static void MakeSafe(ref BigInt n1, ref BigInt n2) + { + if (n1.digitArray.Length == n2.digitArray.Length) + { + return; + } + else if (n1.digitArray.Length > n2.digitArray.Length) + { + n2 = new BigInt(n2, n1.pres); + } + else + { + n1 = new BigInt(n1, n2.pres); + } + } + + /// + /// Makes sure the numbers have matching precisions + /// + /// the number to match to this + private void MakeSafe(ref BigInt n2) + { + n2 = new BigInt(n2, pres); + n2.SetNumDigits(digitArray.Length); + } + + + private PrecisionSpec pres; + private bool sign; + private uint[] digitArray; + private uint[] workingSet; + } + +} \ No newline at end of file diff --git a/dev5/psychlops/extension/math/solver.cs b/dev5/psychlops/extension/math/solver.cs new file mode 100644 index 0000000..8ff2b38 --- /dev/null +++ b/dev5/psychlops/extension/math/solver.cs @@ -0,0 +1,411 @@ +using System.Threading; +using System; +using BigNum; + +namespace Psychlops +{ + + namespace Solver + { + internal static class CONST + { + public static readonly uint MAX_ARG = 5; + } + + + static public class Constants + { + public static readonly int LIMIT = 5; + public delegate double Func0(double x); + public delegate double Func1(double x, double a); + public delegate double Func2(double x, double a, double b); + public delegate double Func3(double x, double a, double b, double c); + public delegate double Func4(double x, double a, double b, double c, double d); + public delegate double Func5(double x, double a, double b, double c, double d, double e); + } + + + static public class Binomial + { + public static double factorial(int fact) + { + double x = 1; + for (int i = 1; i <= fact; i++) + { + x *= i; + } + return x; + } + public static double combination(int nn, int kk) + { + return (double)(Int64)(factorial(nn) / (factorial(kk) * factorial(nn - kk))); + /* + BigInt n = new BigInt((Int64)nn, new PrecisionSpec()); + BigInt k = new BigInt((Int64)kk, new PrecisionSpec()); + BigInt n_k = n - k; + n.Factorial(); + k.Factorial(); + n_k.Factorial(); + return (double)(Int64)(n / (k * n_k) ); + * */ + } + public static double likelihood(double pp, int yes, int no) + { + double p; + if (pp < 0) p = 0; else if (pp > 1) p = 1; else p = pp; + return combination(yes + no, yes) * System.Math.Pow(p, yes) * System.Math.Pow(1 - p, no); + } + + public static double likelihood(double pp, int yes, int no, double comb) + { + double p; + if (pp < 0) p = 0; else if (pp > 1) p = 1; else p = pp; + return comb * System.Math.Pow(p, yes) * System.Math.Pow(1 - p, no); + } + + } + + + public class BernoulliProcess + { + public struct Data + { + public double x; + public int pos, neg, comb; + public void set(double cond, int posit, int negat) + { + x = cond; + pos = posit; + neg = negat; + } + + }; + public Data[] elems; + public int length; + public void set(double[][] num) + { + elems = new Data[num.GetLength(0)]; + for (int i = 0; i < elems.GetLength(0); i++) + { + elems[i].set(num[i][0], (int)num[i][1], (int)num[i][2]); + } + } + }; + + + + class BinomialLikelihoodThread + { + public bool finished; + public static bool started; + internal Thread th; + public static BinomialLikelihoodThread current; + + public Interval[] itvl; + public double[] step; + public double[] champ; + public double champ_like; + public BernoulliProcess data; + + + public Constants.Func0 func0; + public Constants.Func1 func1; + public Constants.Func2 func2; + public Constants.Func3 func3; + public Constants.Func4 func4; + public Constants.Func5 func5; + + + public BinomialLikelihoodThread() + { + itvl = new Interval[CONST.MAX_ARG]; + step = new double[CONST.MAX_ARG]; + champ = new double[CONST.MAX_ARG]; + data = new BernoulliProcess(); + } + public void waitLoop() + { + finished = false; + for (int i = 0; i < CONST.MAX_ARG; i++) + { + champ[i] = 0; + } + current = this; + started = false; + } + + public void loop1() { waitLoop(); th = new Thread(new ThreadStart(loop1_)); th.Start(); } + public void loop2() { waitLoop(); th = new Thread(new ThreadStart(loop2_)); th.Start(); } + public void loop3() { waitLoop(); th = new Thread(new ThreadStart(loop3_)); th.Start(); } + public void loop4() { waitLoop(); th = new Thread(new ThreadStart(loop4_)); th.Start(); } + public void loop5() { waitLoop(); th = new Thread(new ThreadStart(loop5_)); th.Start(); } + void loop1_() + { + started = true; + double p; + double like = 1.0; + champ_like=0.0; + int L = data.length; + for (double a = itvl[0].begin.value; a < itvl[0].end.value; a += step[0]) + { + like = 1.0; + for(int i=0; i champ_like) { + champ_like = like; + champ[0] = a; + } + } + finished = true; + } + void loop2_() + { + started = true; + double p = 0.0; + double like = 1.0; + champ_like = 0.0; + int L = data.length; + for (double a = itvl[0].begin.value; a < itvl[0].end.value; a += step[0]) + { + for (double b = itvl[1].begin.value; b < itvl[1].end.value; b += step[1]) + { + like = 1.0; + for (int i = 0; i < L; i++) + { + p = func2(data.elems[i].x, a, b); + like *= Binomial.likelihood(p, data.elems[i].pos, data.elems[i].neg, data.elems[i].comb); + } + if (like > champ_like) + { + champ_like = like; + champ[0] = a; + champ[1] = b; + } + } + } + finished = true; + } + void loop3_() + { + started = true; + double p; + double like = 1.0; + champ_like = 0.0; + int L = data.length; + for (double a = itvl[0].begin.value; a < itvl[0].end.value; a += step[0]) + { + for (double b = itvl[1].begin.value; b < itvl[1].end.value; b += step[1]) + { + for (double c = itvl[2].begin.value; c < itvl[2].end.value; c += step[2]) + { + like = 1.0; + for (int i = 0; i < L; i++) + { + p = func3(data.elems[i].x, a, b, c); + like *= Binomial.likelihood(p, data.elems[i].pos, data.elems[i].neg, data.elems[i].comb); + } + if (like > champ_like) + { + champ_like = like; + champ[0] = a; + champ[1] = b; + champ[1] = c; + } + } + } + } + finished = true; + } + void loop4_() + { + started = true; + finished = true; + } + void loop5_() + { + started = true; + finished = true; + } + }; + + + + public class BinomialLikelihood + { + /* + public static void showWindow(Constants.Func1 f) + { + Main.canvas.api_canvas.Dispatcher.BeginInvoke(new Action(showWindow_), f); + } + internal static void showWindow_(Constants.Func1 f) + { + System.Windows.Controls.ChildWindow page = new PsychlopsSilverlight4.Pages.BinomialSolver(f); + page.Show(); + } + public static void showWindow(Constants.Func2 f) + { + Main.canvas.api_canvas.Dispatcher.BeginInvoke(new Action(showWindow_), f); + } + internal static void showWindow_(Constants.Func2 f) + { + System.Windows.Controls.ChildWindow page = new PsychlopsSilverlight4.Pages.BinomialSolver(f); + page.Show(); + } + public static void showWindow(Constants.Func3 f) + { + Main.canvas.api_canvas.Dispatcher.BeginInvoke(new Action(showWindow_), f); + } + internal static void showWindow_(Constants.Func3 f) + { + System.Windows.Controls.ChildWindow page = new PsychlopsSilverlight4.Pages.BinomialSolver(f); + page.Show(); + } + */ + + public int iteration; + + public Interval[] itvl; + public double[] step; + public double[] champ; + public double champ_like; + public BernoulliProcess data; + + public Constants.Func0 func0; + public Constants.Func1 func1; + public Constants.Func2 func2; + public Constants.Func3 func3; + public Constants.Func4 func4; + public Constants.Func5 func5; + + public BinomialLikelihood() + { + itvl = new Interval[CONST.MAX_ARG]; + step = new double[CONST.MAX_ARG]; + champ = new double[CONST.MAX_ARG]; + iteration = 2; + } + + public void begin(Constants.Func0 d_func) + { } + public void begin(Constants.Func1 d_func) + { + func1 = d_func; + + BinomialLikelihoodThread[] l = new BinomialLikelihoodThread[4]; + for (int i = 0; i < 4; i++) { l[i] = new BinomialLikelihoodThread(); } + + for (int k = 0; k < iteration; k++) + { + begin_base(l); + for (int i = 0; i < 4; i++) + { + l[i].data = data; + l[i].func1 = d_func; + l[i].loop1(); + } + end_base(l); + } + } + public void begin(Constants.Func2 d_func) + { + func2 = d_func; + + BinomialLikelihoodThread[] l = new BinomialLikelihoodThread[4]; + for (int i = 0; i < 4; i++) { l[i] = new BinomialLikelihoodThread(); } + + for (int k = 0; k < iteration; k++) + { + begin_base(l); + for (int i = 0; i < 4; i++) + { + l[i].data = data; + l[i].func2 = d_func; + l[i].loop2(); + } + end_base(l); + } + } + public void begin(Constants.Func3 d_func) + { + func3 = d_func; + + BinomialLikelihoodThread[] l = new BinomialLikelihoodThread[4]; + for (int i = 0; i < 4; i++) { l[i] = new BinomialLikelihoodThread(); } + + for (int k = 0; k < iteration; k++) + { + begin_base(l); + for(int i=0; i<4; i++) { + l[i].data = data; + l[i].func3 = d_func; + l[i].loop3(); + } + end_base(l); + } + } + + public void begin(Constants.Func4 d_func) + { + } + public void begin(Constants.Func5 d_func) + { + } + + void begin_base(BinomialLikelihoodThread[] l) + { + champ_like = 0; + + data.length = data.elems.GetLength(0); + for (int i = 0; i < data.elems.GetLength(0); i++) + { + data.elems[i].comb = (int)Binomial.combination(data.elems[i].pos + data.elems[i].neg, data.elems[i].pos); + } + + for (int j = 0; j < Constants.LIMIT; j++) { step[j] = (itvl[j].end.value - itvl[j].begin.value) / 256.0; } + for (int i = 0; i < 4; i++) + { + l[i].itvl[0] = new Interval((itvl[0].begin.value) + (i * step[0] * 64), (itvl[0].begin.value) + ((i + 1) * step[0] * 64)); + l[i].step[0] = step[0]; + for (int j = 1; j < Constants.LIMIT; j++) + { + l[i].itvl[j] = itvl[j]; + l[i].step[j] = step[j]; + } + l[i].data = data; + } + } + + void end_base(BinomialLikelihoodThread[] l) + { + //for (int i = 0; i < 4; i++) { l[i].th.Join(); } + while (!l[0].finished || !l[1].finished || !l[2].finished || !l[3].finished) { Thread.Sleep(100); } + + for(int j=0; j itvl[j].end.value ? itvl[j].end.value : champ[j] + r / 8.0; + itvl[j] = new Interval(low, high); + } + + } + + + } + + + } + +} \ No newline at end of file diff --git a/dev5/psychlops/extension/media/dom.cs b/dev5/psychlops/extension/media/dom.cs new file mode 100644 index 0000000..a733682 --- /dev/null +++ b/dev5/psychlops/extension/media/dom.cs @@ -0,0 +1,6 @@ + +namespace Psychlops +{ + + +} \ No newline at end of file diff --git a/dev5/psychlops/extension/media/svg.cs b/dev5/psychlops/extension/media/svg.cs new file mode 100644 index 0000000..b2558b7 --- /dev/null +++ b/dev5/psychlops/extension/media/svg.cs @@ -0,0 +1,8 @@ + +namespace Psychlops +{ + + + + +} \ No newline at end of file diff --git a/dev5/psychlops/extension/standard/CIEColor.cs b/dev5/psychlops/extension/standard/CIEColor.cs new file mode 100644 index 0000000..50df53c --- /dev/null +++ b/dev5/psychlops/extension/standard/CIEColor.cs @@ -0,0 +1,108 @@ +using System; + +namespace Psychlops +{ + namespace ColorSpaces + { + /* + * CIE 1931 + * R: 700 nm + * G: 546.1 nm + * B: 435.8 nm + * White Point: Illuminant E + */ + public struct CIERGB + { + public double R, G, B; + + public CIEXYZ convertToCIEXYZ() + { + double[,] b = + { + { 0.49, 0.31, 0.20 }, + { 0.17697, 0.81240, 0.01063 }, + { 0.00, 0.01, 0.99 } + }; + + CIEXYZ v; + + v.X = b[0, 0] * R + b[0, 1] * G + b[0, 2] * B; + v.Y = b[1, 0] * R + b[1, 1] * G + b[1, 2] * B; + v.Z = b[2, 0] * R + b[2, 1] * G + b[2, 2] * B; + + return v; + } + } + + /* + * CIE 1931 + */ + public struct CIEXYZ + { + public double X, Y, Z; + + public CIExyY convertToCIExyY() + { + CIExyY v; + + double denominator = X + Y + Z; + v.x = X / denominator; + v.y = Y / denominator; + v.Y = Y; + + return v; + } + } + + public struct CIExyY + { + public double x, y, Y; + + public CIEXYZ convertToCIEXYZ() + { + CIEXYZ v; + + v.X = Y / y * x; + v.Y = Y; + v.Z = Y / y * (1 - x - y); + + return v; + } + + // Yn = 1.0 when RGB of white point is { 1, 1, 1 } + public CIELuv convertToCIELuv(double Yn = 1.0) + { + + CIELuv v; + + double denominator = (-2 * x + 12 * y + 3); + double up = 4 * x / denominator; + double vp = 9 * y / denominator; + + double Yd = Y / Yn; + v.L = Yd > System.Math.Pow(6 / 29, 3) ? 116 * System.Math.Pow(Yd, 3) : System.Math.Pow(29 / 3, 3) * Yd; + v.u = 13 * v.L * (up - 0.2009); + v.v = 13 * v.L * (vp - 0.4610); + + return v; + } + } + + /* L*u*v* + * CIE 1976 + * standard illuminant C + */ + public struct CIELuv + { + public double L, u, v; + + } + + public struct CIELab + { + public double L, a, b; + + } + + } +} \ No newline at end of file diff --git a/dev5/psychlops/extension/standard/figures.cs b/dev5/psychlops/extension/standard/figures.cs new file mode 100644 index 0000000..948aecc --- /dev/null +++ b/dev5/psychlops/extension/standard/figures.cs @@ -0,0 +1,43 @@ + +using System.Windows.Media.Imaging; +namespace Psychlops +{ + + public static partial class Figures + { + + public static void drawGrating(ref Image img, int width, int height, double wavelength, double contrast, double orientation, double phase) + { + double width_half = width / 2.0, height_half = height / 2.0; + if (img==null || img.width != width || img.height != height) + img = new Image(width, height); + double freq = 2 * Math.PI / wavelength; + img.field( + (x, y) => new Color(.5 + contrast * .5 * Math.sin(phase + (Math.sin(orientation)*x-Math.cos(orientation)*y) * freq)) + ); + } + public static void drawGaussian(ref Image img, double sigma, double factor) + { + int width = (int)(sigma * 8), height = (int)(sigma * 8); + double width_half = width / 2.0, height_half = height / 2.0; + if (img == null || img.width != width || img.height != height) + img = new Image(width, height); + img.field( + (x, y) => new Color(factor * Math.gaussian(Math.radius(x - width_half, y - height_half), width / 8.0)) + ); + } + + public static void drawGabor(ref Image img, double sigma, double frequency, double contrast, double orientation, double phase) + { + int width = (int)(sigma * 8), height = (int)(sigma * 8); + double width_half = width / 2.0, height_half = height / 2.0; + if (img==null || img.width != width || img.height != height) + img = new Image(width, height); + double freq = 2 * Math.PI * frequency; + img.field( + (x, y) => new Color(.5 + contrast * Math.gaussian(Math.radius(x - width_half, y - height_half), width / 8.0) * .5 * Math.sin(phase + (Math.sin(orientation) * x - Math.cos(orientation) * y) * freq)) + ); + } + + } +} diff --git a/dev5/psychlops/extension/standard/shader.cs b/dev5/psychlops/extension/standard/shader.cs new file mode 100644 index 0000000..7d5387a --- /dev/null +++ b/dev5/psychlops/extension/standard/shader.cs @@ -0,0 +1,845 @@ +//------------------------------------------------------------------------------ +// +// このコードはツールによって生成されました。 +// ランタイム バージョン:4.0.30319.1 +// +// このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 +// コードが再生成されるときに損失したりします。 +// +//------------------------------------------------------------------------------ + +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Effects; +using System.Windows.Media.Media3D; + + +namespace Psychlops +{ + + + public static partial class Figures + { + + public class ShaderGrating : ShaderField + { + protected Shader.GratingProgram ps = null; + public ShaderGrating() + { + initialize__ = initialize___; + setParameters = setParameters__; + } + internal void initialize() + { + if (!initialized) + { + Main.canvas.beginInvoke(initialize___); + initialized = true; + } + } + internal void initialize___() + { + if (!initialized) + { + if (ps == null) + { + ps = new Shader.GratingProgram(); + initializeShader(); + } + shader = ps; + initialized = true; + } + } + + public double contrast = 1.0, wavelength = 20.0, phase = 0.0, orientation = 0.0; + public ShaderGrating setSize(double s) { set(s, s); return this; } + public ShaderGrating setSize(double h, double v) { set(h, v); return this; } + + public void setParameters__() + { + ps.Contrast = contrast; + ps.WaveLength = wavelength; + ps.Phase = phase; + ps.Orientation = orientation; + ps.SizeH = width; + ps.SizeV = height; + ps.Update(); + } + + + } + + + public class ShaderPlaid : ShaderField + { + protected Shader.PlaidProgram ps = null; + public ShaderPlaid() + { + initialize__ = initialize___; + setParameters = setParameters__; + } + internal void initialize() + { + if (!initialized) + { + Main.canvas.beginInvoke(initialize___); + initialized = true; + } + } + internal void initialize___() + { + if (!initialized) + { + if (ps == null) + { + ps = new Shader.PlaidProgram(); + initializeShader(); + } + shader = ps; + initialized = true; + } + } + + public double contrast = 0.5, wavelength = 20.0, phase = 0.0, orientation = 0.0; + public double contrast2 = 0.5, wavelength2 = 20.0, phase2 = 0.0, orientation2 = Math.PI / 4; + public ShaderPlaid setSize(double s) { set(s, s); return this; } + public ShaderPlaid setSize(double h, double v) { set(h, v); return this; } + + public void setParameters__() + { + ps.Contrast = contrast; + ps.WaveLength = wavelength; + ps.Phase = phase; + ps.Orientation = orientation; + ps.Size = width; + ps.Contrast2 = contrast; + ps.WaveLength2 = wavelength; + ps.Phase2 = phase; + ps.Orientation2 = orientation; + ps.SizeH = width; + ps.SizeV = height; + ps.Update(); + } + + + } + + + public class ShaderGabor : ShaderField + { + protected Shader.GaborProgram ps = null; + public ShaderGabor() + { + initialize__ = initialize___; + setParameters = setParameters__; + } + internal void initialize() + { + if (!initialized) + { + Main.canvas.beginInvoke(initialize___); + initialized = true; + } + } + internal void initialize___() + { + if (!initialized) + { + if (ps == null) + { + ps = new Shader.GaborProgram(); + initializeShader(); + } + shader = ps; + initialized = true; + } + } + + //public double contrast { get; set; } + //public double frequency { get; set; } + //public double phase { get; set; } + //public double orientation { get; set; } + public double contrast = 1.0, wavelength = 20.0, phase = 0.0, orientation = 0.0, alpha = 1.0; + public ShaderGabor setSigma(double s) + { + set(s*8, s*8); + return this; + } + + public void setParameters__() + { + ps.Contrast = contrast; + ps.WaveLength = wavelength; + ps.Phase = phase; + ps.Orientation = orientation; + ps.Sigma = 4.0; + ps.Size = width; + ps.Alpha = alpha; + ps.Update(); + } + + + } + + + public class ShaderGaborAlpha : ShaderField + { + protected Shader.GaborAlphaProgram ps = null; + public ShaderGaborAlpha() + { + initialize__ = initialize___; + setParameters = setParameters__; + } + internal void initialize() + { + if (!initialized) + { + Main.canvas.beginInvoke(initialize___); + initialized = true; + } + } + internal void initialize___() + { + if (!initialized) + { + if (ps == null) + { + ps = new Shader.GaborAlphaProgram(); + initializeShader(); + } + shader = ps; + initialized = true; + } + } + + //public double contrast { get; set; } + //public double frequency { get; set; } + //public double phase { get; set; } + //public double orientation { get; set; } + public double contrast = 1.0, wavelength = 20.0, phase = 0.0, orientation = 0.0, alpha = 1.0; + public ShaderGaborAlpha setSigma(double s) + { + set(s * 8, s * 8); + return this; + } + + public void setParameters__() + { + ps.Contrast = contrast; + ps.WaveLength = wavelength; + ps.Phase = phase; + ps.Orientation = orientation; + ps.Sigma = 4.0; + ps.Size = width; + ps.Alpha = alpha; + ps.Update(); + } + + + } + } + + + namespace Shader + { + internal static partial class Connector + { + internal delegate void PixelShaderConnector(ShaderEffect q, Uri s); + } + + + public abstract class ShaderProgram : ShaderEffect + { + } + + #region GratingProgram + public class GratingProgram : ShaderProgram + { + public static readonly Uri ps = new Uri(@"/PsychlopsSilverlight5;component/Shader/Grating.ps", UriKind.Relative); + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GratingProgram), 0); + public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(1D)), PixelShaderConstantCallback(1))); + public static readonly DependencyProperty FrequencyProperty = DependencyProperty.Register("Frequency", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(100D)), PixelShaderConstantCallback(2))); + public static readonly DependencyProperty PhaseProperty = DependencyProperty.Register("Phase", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(3))); + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(4))); + public static readonly DependencyProperty SizeHProperty = DependencyProperty.Register("SizeH", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(32D)), PixelShaderConstantCallback(5))); + public static readonly DependencyProperty SizeVProperty = DependencyProperty.Register("SizeV", typeof(double), typeof(GratingProgram), new PropertyMetadata(((double)(32D)), PixelShaderConstantCallback(6))); + public GratingProgram() + { + PixelShader pixelShader = new PixelShader(); + pixelShader.UriSource = ps; + this.PixelShader = pixelShader; + + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SizeHProperty); + this.UpdateShaderValue(SizeVProperty); + + Size = 200; + Contrast = 1.0; + WaveLength = 100.0; + Orientation = 0.0; + SizeH = 32.0; + SizeV = 32.0; + } + + public void Update() + { + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SizeHProperty); + this.UpdateShaderValue(SizeVProperty); + } + + + private double size__, wavelength__; + public double Size { get { return size__; } set { size__ = value; setFrequency(); } } + private void setFrequency() + { + double freq = size__ * 2.0 * Math.PI / (wavelength__); + this.SetValue(FrequencyProperty, freq); + } + + /// Amplitude of Grating + public double Contrast + { + get + { + return ((double)(this.GetValue(ContrastProperty))) * 2.0; + } + set + { + this.SetValue(ContrastProperty, value / 2.0); + } + } + /// Phase of Grating + public double WaveLength + { + get + { + return wavelength__; + } + set + { + wavelength__ = value; + setFrequency(); + } + } + /// Phase of Grating + public double Phase + { + get + { + return ((double)(this.GetValue(PhaseProperty))); + } + set + { + this.SetValue(PhaseProperty, value); + } + } + /// Orientation of Grating + public double Orientation + { + get + { + return ((double)(this.GetValue(OrientationProperty))); + } + set + { + this.SetValue(OrientationProperty, value); + } + } + /// Width of envelope + public double SizeH + { + get + { + return ((double)(this.GetValue(SizeHProperty))); + } + set + { + this.SetValue(SizeHProperty, value); + } + } + /// Height of Figure + public double SizeV + { + get + { + return ((double)(this.GetValue(SizeVProperty))); + } + set + { + this.SetValue(SizeVProperty, value); + } + } + } + #endregion + + #region PlaidProgram + public class PlaidProgram : ShaderProgram + { + public static readonly Uri ps = new Uri(@"/PsychlopsSilverlight5;component/Shader/Plaid.ps", UriKind.Relative); + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(PlaidProgram), 0); + public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0.5D)), PixelShaderConstantCallback(1))); + public static readonly DependencyProperty FrequencyProperty = DependencyProperty.Register("Frequency", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(100D)), PixelShaderConstantCallback(2))); + public static readonly DependencyProperty PhaseProperty = DependencyProperty.Register("Phase", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(3))); + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(4))); + public static readonly DependencyProperty Contrast2Property = DependencyProperty.Register("Contrast2", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0.5D)), PixelShaderConstantCallback(5))); + public static readonly DependencyProperty Frequency2Property = DependencyProperty.Register("Frequency2", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(100D)), PixelShaderConstantCallback(6))); + public static readonly DependencyProperty Phase2Property = DependencyProperty.Register("Phase2", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(7))); + public static readonly DependencyProperty Orientation2Property = DependencyProperty.Register("Orientation2", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(0.785398D)), PixelShaderConstantCallback(8))); + public static readonly DependencyProperty SizeHProperty = DependencyProperty.Register("SizeH", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(32D)), PixelShaderConstantCallback(9))); + public static readonly DependencyProperty SizeVProperty = DependencyProperty.Register("SizeV", typeof(double), typeof(PlaidProgram), new PropertyMetadata(((double)(32D)), PixelShaderConstantCallback(10))); + public PlaidProgram() + { + PixelShader pixelShader = new PixelShader(); + pixelShader.UriSource = ps; + this.PixelShader = pixelShader; + + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(Contrast2Property); + this.UpdateShaderValue(Frequency2Property); + this.UpdateShaderValue(Phase2Property); + this.UpdateShaderValue(Orientation2Property); + this.UpdateShaderValue(SizeHProperty); + this.UpdateShaderValue(SizeVProperty); + + Size = 200; + Contrast = 0.5; + WaveLength = 100.0; + Phase = 0.0; + Orientation = 0.0; + Contrast2 = 0.5; + WaveLength2 = 100.0; + Phase2 = 0.0; + Orientation2 = Math.PI/4; + SizeH = 32.0; + SizeV = 32.0; + } + + public void Update() + { + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(Contrast2Property); + this.UpdateShaderValue(Frequency2Property); + this.UpdateShaderValue(Phase2Property); + this.UpdateShaderValue(Orientation2Property); + this.UpdateShaderValue(SizeHProperty); + this.UpdateShaderValue(SizeVProperty); + } + + + private double size__, wavelength__, wavelength2__; + public double Size { get { return size__; } set { size__ = value; setFrequency(); } } + private void setFrequency() + { + double freq = size__ * 2.0 * Math.PI / (wavelength__); + this.SetValue(FrequencyProperty, freq); + double freq2 = size__ * 2.0 * Math.PI / (wavelength2__); + this.SetValue(Frequency2Property, freq); + } + + /// Amplitude of Grating + public double Contrast + { + get + { + return ((double)(this.GetValue(ContrastProperty))) * 2.0; + } + set + { + this.SetValue(ContrastProperty, value / 2.0); + } + } + /// Phase of Grating + public double WaveLength + { + get + { + return wavelength__; + } + set + { + wavelength__ = value; + setFrequency(); + } + } + /// Phase of Grating + public double Phase + { + get + { + return ((double)(this.GetValue(PhaseProperty))); + } + set + { + this.SetValue(PhaseProperty, value); + } + } + /// Orientation of Grating + public double Orientation + { + get + { + return ((double)(this.GetValue(OrientationProperty))); + } + set + { + this.SetValue(OrientationProperty, value); + } + } + /// Amplitude of Grating + public double Contrast2 + { + get + { + return ((double)(this.GetValue(Contrast2Property))) * 2.0; + } + set + { + this.SetValue(Contrast2Property, value / 2.0); + } + } + /// Phase of Grating + public double WaveLength2 + { + get + { + return wavelength2__; + } + set + { + wavelength2__ = value; + setFrequency(); + } + } + /// Phase2 of Grating + public double Phase2 + { + get + { + return ((double)(this.GetValue(Phase2Property))); + } + set + { + this.SetValue(Phase2Property, value); + } + } + /// Orientation2 of Grating + public double Orientation2 + { + get + { + return ((double)(this.GetValue(Orientation2Property))); + } + set + { + this.SetValue(Orientation2Property, value); + } + } + /// Width of envelope + public double SizeH + { + get + { + return ((double)(this.GetValue(SizeHProperty))); + } + set + { + this.SetValue(SizeHProperty, value); + } + } + /// Height of Figure + public double SizeV + { + get + { + return ((double)(this.GetValue(SizeVProperty))); + } + set + { + this.SetValue(SizeVProperty, value); + } + } + } + #endregion + + #region GaborProgram + public class GaborProgram : ShaderProgram + { + public static readonly Uri ps = new Uri(@"/PsychlopsSilverlight5;component/Shader/Gabor.ps", UriKind.Relative); + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GaborProgram), 0); + public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(1D)), PixelShaderConstantCallback(1))); + public static readonly DependencyProperty FrequencyProperty = DependencyProperty.Register("Frequency", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(100D)), PixelShaderConstantCallback(2))); + public static readonly DependencyProperty PhaseProperty = DependencyProperty.Register("Phase", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(3))); + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(4))); + public static readonly DependencyProperty SigmaProperty = DependencyProperty.Register("Sigma", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(4D)), PixelShaderConstantCallback(5))); + public static readonly DependencyProperty AlphaProperty = DependencyProperty.Register("Alpha", typeof(double), typeof(GaborProgram), new PropertyMetadata(((double)(1D)), PixelShaderConstantCallback(6))); + public GaborProgram() + { + PixelShader pixelShader = new PixelShader(); + pixelShader.UriSource = ps; + this.PixelShader = pixelShader; + + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SigmaProperty); + this.UpdateShaderValue(AlphaProperty); + + Size = 200; + Contrast = 1.0; + WaveLength = 100.0; + Orientation = 0.0; + Sigma = 4.0; + Alpha = 1.0; + } + + public void Update() + { + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SigmaProperty); + this.UpdateShaderValue(AlphaProperty); + } + + + private double size__, wavelength__; + public double Size { get { return size__; } set { size__ = value; setFrequency(); } } + private void setFrequency() + { + double freq = size__ * 2.0 * Math.PI / (wavelength__); + this.SetValue(FrequencyProperty, freq); + } + + /// Amplitude of Grating + public double Contrast + { + get + { + return ((double)(this.GetValue(ContrastProperty))) * 2.0; + } + set + { + this.SetValue(ContrastProperty, value / 2.0); + } + } + /// Phase of Grating + public double WaveLength + { + get + { + return wavelength__; + } + set + { + wavelength__ = value; + setFrequency(); + } + } + /// Phase of Grating + public double Phase + { + get + { + return ((double)(this.GetValue(PhaseProperty))); + } + set + { + this.SetValue(PhaseProperty, value); + } + } + /// Orientation of Grating + public double Orientation + { + get + { + return ((double)(this.GetValue(OrientationProperty))); + } + set + { + this.SetValue(OrientationProperty, value); + } + } + /// Half bandwidth of envelope + public double Sigma + { + get + { + return ((double)(this.GetValue(SigmaProperty))); + } + set + { + this.SetValue(SigmaProperty, value); + } + } + /// Transparency of Figure + public double Alpha + { + get + { + return ((double)(this.GetValue(AlphaProperty))); + } + set + { + this.SetValue(AlphaProperty, value); + } + } + } + #endregion + + #region GaborAlphaProgram + public class GaborAlphaProgram : ShaderProgram + { + public static readonly Uri ps = new Uri(@"/PsychlopsSilverlight5;component/Shader/GaborAlpha.ps", UriKind.Relative); + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GaborAlphaProgram), 0); + public static readonly DependencyProperty ContrastProperty = DependencyProperty.Register("Contrast", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(1D)), PixelShaderConstantCallback(1))); + public static readonly DependencyProperty FrequencyProperty = DependencyProperty.Register("Frequency", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(100D)), PixelShaderConstantCallback(2))); + public static readonly DependencyProperty PhaseProperty = DependencyProperty.Register("Phase", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(3))); + public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(0D)), PixelShaderConstantCallback(4))); + public static readonly DependencyProperty SigmaProperty = DependencyProperty.Register("Sigma", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(4D)), PixelShaderConstantCallback(5))); + public static readonly DependencyProperty AlphaProperty = DependencyProperty.Register("Alpha", typeof(double), typeof(GaborAlphaProgram), new PropertyMetadata(((double)(1D)), PixelShaderConstantCallback(6))); + public GaborAlphaProgram() + { + PixelShader pixelShader = new PixelShader(); + pixelShader.UriSource = ps; + this.PixelShader = pixelShader; + + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SigmaProperty); + this.UpdateShaderValue(AlphaProperty); + + Size = 200; + Contrast = 1.0; + WaveLength = 100.0; + Orientation = 0.0; + Sigma = 4.0; + Alpha = 1.0; + } + + public void Update() + { + this.UpdateShaderValue(InputProperty); + this.UpdateShaderValue(ContrastProperty); + this.UpdateShaderValue(FrequencyProperty); + this.UpdateShaderValue(PhaseProperty); + this.UpdateShaderValue(OrientationProperty); + this.UpdateShaderValue(SigmaProperty); + this.UpdateShaderValue(AlphaProperty); + } + + + private double size__, wavelength__; + public double Size { get { return size__; } set { size__ = value; setFrequency(); } } + private void setFrequency() + { + double freq = size__ * 2.0 * Math.PI / (wavelength__); + this.SetValue(FrequencyProperty, freq); + } + + /// Amplitude of Grating + public double Contrast + { + get + { + return ((double)(this.GetValue(ContrastProperty))) * 2.0; + } + set + { + this.SetValue(ContrastProperty, value / 2.0); + } + } + /// Phase of Grating + public double WaveLength + { + get + { + return wavelength__; + } + set + { + wavelength__ = value; + setFrequency(); + } + } + /// Phase of Grating + public double Phase + { + get + { + return ((double)(this.GetValue(PhaseProperty))); + } + set + { + this.SetValue(PhaseProperty, value); + } + } + /// Orientation of Grating + public double Orientation + { + get + { + return ((double)(this.GetValue(OrientationProperty))); + } + set + { + this.SetValue(OrientationProperty, value); + } + } + /// Half bandwidth of envelope + public double Sigma + { + get + { + return ((double)(this.GetValue(SigmaProperty))); + } + set + { + this.SetValue(SigmaProperty, value); + } + } + /// Transparency of Figure + public double Alpha + { + get + { + return ((double)(this.GetValue(AlphaProperty))); + } + set + { + this.SetValue(AlphaProperty, value); + } + } + } + #endregion + + } +} \ No newline at end of file diff --git a/dev5/psychlops/extension/standard/widget.cs b/dev5/psychlops/extension/standard/widget.cs new file mode 100644 index 0000000..51d6087 --- /dev/null +++ b/dev5/psychlops/extension/standard/widget.cs @@ -0,0 +1,386 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; +using System.Windows.Browser; +using System.Windows.Data; + + + + +namespace Psychlops +{ + + /* + class Widget : public Figure { + public: + class Event { + public: + }; + }; + * */ + + namespace Widgets + { + /* + struct Theme { + static bool initialized__; + static Image default_button, default_horiz_grad; + enum TYPE_TAG { NORMAL, ALERT }; + public: + Color normal_foreground[2], active_foreground[2]; + Color normal_background[2], active_background[2], over_background[2]; + Stroke normal_stroke[2], active_stroke[2]; + Image button_back, *horiz_grad; + + static Theme current; + static void initialize(); + }; + * */ + + /* + class WidgetRect : Widget + { + protected: + HumanInterfaceDevice::ButtonStateHolder mleft; + bool pushed_; + + public: + //Theme *theme; + //Theme::TYPE_TAG theme_type; + public Rectangle area; + public Letters label; + + public abstract WidgetRect(); + public virtual const Point getDatum() const; + public virtual WidgetRect& setDatum(const Point&); + public virtual WidgetRect& centering(const Point&); + public double getWidth() const; + public double getHeight() const; + public double getLeft() const; + public double getTop() const; + public double getRight() const; + public double getBottom() const; + public WidgetRect& alignLeft(const double lef); + public WidgetRect& alignTop(const double to_); + public WidgetRect& alignRight(const double rig); + public WidgetRect& alignBottom(const double bot); + + public virtual WidgetRect& set(double wid, double hei); + public virtual WidgetRect& set(std::wstring name); + public virtual WidgetRect& set(std::wstring name, double hei); + public virtual WidgetRect& setLabel(std::wstring s); + public virtual WidgetRect& draw(Drawable &target = *Drawable::prime); + public bool pushed(); + public WidgetRect& pushThis(); + }; + */ + + /* + public class Slider + { + public string label; + public Rectangle outer; + public Rectangle inner; + public Stroke outer_strk; + + public Slider() + { + set(0, 0); + } + public Slider(double width, double height) + { + set(width, height); + } + public Slider set(double width, double height) + { + outer = new Rectangle(width, height); + outer.fill = Color.gray; + inner = new Rectangle(); + inner.fill = Color.blue; + outer_strk = new Stroke(); + outer_strk.color = Color.white; + outer_strk.thick = 1; + return this; + } + public void checkClick() + { + if (outer.include(Mouse.position)) + { + outer.stroke = outer_strk; + } + } + } + */ + + public class Button + { + public static Color bgcolor; + + internal System.Windows.Controls.Button uibutton; + + public Button(string l) + { + uibutton = new System.Windows.Controls.Button(); + uibutton.Content = l; + } + public void str(string l) { } + } + + public class Slider + { + public static Color bgcolor; + + internal System.Windows.UIElement instance; + internal System.Windows.Controls.Slider uislider; + internal System.Windows.Controls.TextBlock uilabel; + internal System.Windows.Controls.TextBox uibox; + internal string label; + internal Interval range; + internal SelfDelegate get_; + internal SelfDelegate set_; + internal double retval, small_step, large_step; + internal bool setted_; + + static Slider() + { + bgcolor.set(.5,.5, .5, .3); + } + + public Slider(string l, Interval r, double initialvalue) + { + retval = initialvalue; + range = r; + label = l; + large_step = Double.NaN; + small_step = Double.NaN; + setted_ = false; + Psychlops.Widgets.Connector.stackSlider(this); + get_ = new SelfDelegate(get__); + set_ = new SelfDelegate(set__); + while (!setted_) { System.Threading.Thread.Sleep(10); } + } + public Slider(string l, Interval r, double step1, double step2) + { + retval = r.begin.value; + range = r; + label = l; + large_step = Math.max(step1, step2); + small_step = Math.min(step1, step2); + setted_ = false; + Psychlops.Widgets.Connector.stackSlider(this); + get_ = new SelfDelegate(get__); + set_ = new SelfDelegate(set__); + while (!setted_) { System.Threading.Thread.Sleep(10); } + } + public static implicit operator double(Slider s) + { + return s.value; + } + + public delegate void SelfDelegate(System.Windows.Controls.Slider s); + public void get__(System.Windows.Controls.Slider s) { retval = s.Value; } + public void set__(System.Windows.Controls.Slider s) { s.Value = retval; } + + public double getValue() + { + if (uislider != null) { + uislider.Dispatcher.BeginInvoke(get_, uislider); + return retval; + } else { + return 0; + } + } + public void setValue(double v) + { + if (uislider != null) + { + retval = v; + uislider.Dispatcher.BeginInvoke(set_, uislider); + } + } + + public double value + { + get { return getValue(); } + set { setValue(value); } + } + public bool changed { get; set; } + } + + + public class Browser + { + public class Element + { + HtmlElement elem; + System.Object retval; + + public Element() + { + retval = 0; + } + + public static Element byID(string id_tag) + { + return getElementById(id_tag); + } + public static Element getElementById(string id_tag) + { + Element tmp = new Element(); + Internal.Main.widgetStack.Dispatcher.BeginInvoke(new Connector.VoidString(tmp.getElementById__), id_tag); + return tmp; + } + void getElementById__(string id_tag) + { + elem = HtmlPage.Document.GetElementById(id_tag); + } + + public void setProperty(string name, System.Object value) + { + Internal.Main.widgetStack.Dispatcher.BeginInvoke(new Connector.VoidStringObject(setProperty__), name, value); + } + public void setProperty__(string name, System.Object value) + { + elem.SetProperty(name, value); + } + + + public static implicit operator double(Element i) { return i.getValueAsDouble(); } + public double getValueAsDouble() + { + double v = 0; + getProperty("value"); + try + { + v = double.Parse(retval.ToString()); + } + catch (FormatException e) + { + v = 0; + } + return v; + } + + public static implicit operator string(Element i) { return i.getProperty("value").ToString(); } + public System.Object getProperty(string name) + { + Internal.Main.widgetStack.Dispatcher.BeginInvoke(new Connector.VoidString(getProperty__), name); + return retval; + } + public void getProperty__(string name) + { + retval = elem.GetProperty(name); + } + } + } + + + internal static partial class Connector + { + internal delegate void VoidString(string s); + internal delegate void VoidStringObject(String s, System.Object o); + + public class Double02fFormatter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string formatString = parameter as string; + if (!string.IsNullOrEmpty(formatString)) + { + return string.Format(culture, formatString, value); + } + return string.Format("{0, -8:F}", value); + } + public object ConvertBack(object val, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + string str = val.ToString(); + double result; + var objCultureInfo = new System.Globalization.CultureInfo("en-US"); + if (Double.TryParse(str, System.Globalization.NumberStyles.Number, objCultureInfo, out result)) + { + return result; + } + return val; + } + } + + delegate void StackSlider_(Slider s); + internal static void stackSlider(Slider s) + { + Internal.Main.widgetStack.Dispatcher.BeginInvoke(new StackSlider_(stackSlider__), s); + } + static void stackSlider__(Slider ss) + { + var label = new System.Windows.Controls.TextBlock { Text = ss.label }; + label.Foreground = Color.white; + System.Windows.Controls.Canvas.SetLeft(label, 3); + System.Windows.Controls.Canvas.SetTop(label, 3); + ss.uilabel = label; + + var holder = new System.Windows.Controls.Canvas { Width = 200, Height = 50 }; + var val = new System.Windows.Controls.TextBox { Width = 100 }; + System.Windows.Controls.Canvas.SetLeft(val, 100); + var slide = new System.Windows.Controls.Slider { + Minimum = ss.range.begin.value, Maximum = ss.range.end.value, + Name = ss.label, + Value = ss.retval, + Tag = ss, Width = 200, + }; + if (!Double.IsNaN(ss.large_step)) + { + slide.LargeChange = ss.large_step; + } + if (!Double.IsNaN(ss.small_step)) + { + slide.SmallChange = ss.small_step; + } + slide.ValueChanged += slider_ValueChanged; + System.Windows.Controls.Canvas.SetTop(slide, label.ActualHeight - 3); + var b = new System.Windows.Data.Binding { + Path = new PropertyPath("Value"), + Mode = System.Windows.Data.BindingMode.TwoWay, + Source = slide, + Converter = new Double02fFormatter() + }; + val.SetBinding(TextBox.TextProperty, b); + ss.uislider = slide; + ss.uibox = val; + val.Background = Color.null_color; + val.BorderBrush = Color.null_color; + val.Foreground = Color.white; + val.TextAlignment = TextAlignment.Right; + + holder.Background = Slider.bgcolor; + + holder.Children.Add(label); + holder.Children.Add(val); + holder.Children.Add(slide); + holder.Height = label.ActualHeight + slide.ActualHeight + val.ActualHeight + 15; + ss.instance = holder; + Internal.Main.widgetStack.Children.Add(ss.instance); + ss.setted_ = true; + } + private static void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + System.Windows.Controls.Slider sl = sender as System.Windows.Controls.Slider; + if (sl != null) + { + double rounded = Math.round((sl.Value - sl.Minimum) / sl.SmallChange) * sl.SmallChange; + if ((sl.Value - sl.Minimum) * sl.SmallChange != rounded) + { + sl.Value = rounded; + } + } + } + } + + } + +} + diff --git a/test4/App.xaml b/test4/App.xaml new file mode 100644 index 0000000..f82402d --- /dev/null +++ b/test4/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/test4/App.xaml.cs b/test4/App.xaml.cs new file mode 100644 index 0000000..e091cde --- /dev/null +++ b/test4/App.xaml.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PsychlopsSilverlight4test +{ + public partial class App : Application + { + + public App() + { + this.Startup += this.Application_Startup; + this.Exit += this.Application_Exit; + this.UnhandledException += this.Application_UnhandledException; + + InitializeComponent(); + } + + private void Application_Startup(object sender, StartupEventArgs e) + { + this.RootVisual = new MainPage(); + } + + private void Application_Exit(object sender, EventArgs e) + { + + } + private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) + { + // アプリケーションがデバッガーの外側で実行されている場合、ブラウザーの + // 例外メカニズムによって例外が報告されます。これにより、IE ではステータス バーに + // 黄色の通知アイコンが表示され、Firefox にはスクリプト エラーが表示されます。 + if (!System.Diagnostics.Debugger.IsAttached) + { + + // メモ : これにより、アプリケーションは例外がスローされた後も実行され続け、例外は + // ハンドルされません。 + // 実稼動アプリケーションでは、このエラー処理は、Web サイトにエラーを報告し、 + // アプリケーションを停止させるものに置換される必要があります。 + e.Handled = true; + Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); + } + } + private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) + { + try + { + string errorMsg = e.ExceptionObject.Message + e.ExceptionObject.StackTrace; + errorMsg = errorMsg.Replace('"', '\'').Replace("\r\n", @"\n"); + + System.Windows.Browser.HtmlPage.Window.Eval("throw new Error(\"Unhandled Error in Silverlight Application " + errorMsg + "\");"); + } + catch (Exception) + { + } + } + } +} diff --git a/test4/MainPage.xaml b/test4/MainPage.xaml new file mode 100644 index 0000000..6487be3 --- /dev/null +++ b/test4/MainPage.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + Loading... + + diff --git a/test4/MainPage.xaml.cs b/test4/MainPage.xaml.cs new file mode 100644 index 0000000..a125451 --- /dev/null +++ b/test4/MainPage.xaml.cs @@ -0,0 +1,84 @@ +using Psychlops; +using System; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Effects; +using System.Windows.Media.Media3D; + + +namespace PsychlopsSilverlight4test +{ + public partial class MainPage : System.Windows.Controls.UserControl + { + //System.Collections.Generic.IEnumerator main_routine; + public System.Windows.Controls.Image master; + public PsychlopsSilverlightApp.PsychlopsMain main; +#if DEBUG + public System.Windows.Controls.TextBlock DebugConsole; +#endif + System.Windows.Controls.Image img; + public MainPage() + { + InitializeComponent(); + // Show fps counter + + Canvas.default_api_canvas = LayoutRoot; + Canvas.default_panel = this; + this.IsTabStop = true; + this.IsEnabled = true; + this.MouseEnter += getFocusMouseEnter; + System.Windows.Application.Current.Host.Settings.MaxFrameRate = 60; + System.Windows.Application.Current.Host.Settings.EnableFrameRateCounter = true; + System.Windows.Application.Current.Host.Settings.EnableCacheVisualization = false; + //System.Windows.Application.Current.Host.Settings.EnableGPUAcceleration = false; + // + System.Windows.Media.CompositionTarget.Rendering += new System.EventHandler(CompositionTarget_Rendering); + //main_routine = new System.ComponentModel.BackgroundWorker(); + //main_routine.DoWork += new System.ComponentModel.DoWorkEventHandler(psychlops_main); + //main_routine.RunWorkerAsync(); + + main = new PsychlopsSilverlightApp.PsychlopsMain(); +// Psychlops.AppState.statusBar. + Psychlops.Internal.Main.routine = new System.Threading.Thread(main.psychlops_main); + Psychlops.Internal.Main.routine.Start(); + //main.initialize(); + //main_routine = main.psychlops_main(); + + Psychlops.Internal.Main.statusBar = AppStatusBar; + Psychlops.Internal.Main.layoutRoot = LayoutRoot; + //Psychlops.Internal.Main.widgetStack = Controller; + AppStatusBar.Text = "Now starting Psychlops environment... please wait a minute"; + + +//eff__ = new Psychlops.Shader.GaborProgram(); +//rect__ = new System.Windows.Shapes.Rectangle(); +//rect__.Width = 200; +//rect__.Height = 200; +//rect__.Effect = eff__; +//rect__.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Blue); + } + protected void getFocusMouseEnter(object sender, System.Windows.Input.MouseEventArgs e) { + this.Focus(); +//if(!b) { LayoutRoot.Children.Add(rect__); b = true; } + } +//System.Windows.Shapes.Rectangle rect__; +//Psychlops.Shader.GaborProgram eff__; +//bool b = false; + + private void CompositionTarget_Rendering(object sender, System.EventArgs e) + { + //Controller.Children.Clear(); + if (Main.canvas != null) Main.canvas.executeFlip(); + //if (nextIntervalFrame-- <= 0) + //{ + // main_routine.MoveNext(); + // nextIntervalFrame = main_routine.Current; + //} + } + + } + + + +} + diff --git a/test4/Properties/AppManifest.xml b/test4/Properties/AppManifest.xml new file mode 100644 index 0000000..151cfa5 --- /dev/null +++ b/test4/Properties/AppManifest.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/test4/Properties/AssemblyInfo.cs b/test4/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..82192e3 --- /dev/null +++ b/test4/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("PsychlopsSilverlight4test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PsychlopsSilverlight4test")] +[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから +// 見えなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。 +[assembly: Guid("631fa262-58de-42ab-ba9d-79cb7cf7e289")] + +// アセンブリのバージョン情報は、以下の 4 つの値で構成されています。 +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を +// 既定値にすることができます。 +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test4/PsychlopsMain.cs b/test4/PsychlopsMain.cs new file mode 100644 index 0000000..e69a680 --- /dev/null +++ b/test4/PsychlopsMain.cs @@ -0,0 +1,756 @@ +using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + + + public void psychlops_main() { + + Canvas display = new Canvas(768,768); + + double rect_size = 100; + double rect_lum = 0.5; + double bg_lum = 0.5; + double size = 250.0; + double sigma; + sigma = size/6.0; + double lambda; + lambda = 6*sigma; + int envelopemode = 0; + double refresh = display.getRefreshRate(); + double[] SF=new double[2]; + + SF[0]=4.0; + SF[1]=4.0; + + double phase=0; + + double[] initphase = new double[2]; + double _i, _j, _x, _y, _x2, _y2, col; + double amplitude = 1.0; + + Rectangle fixation1 = new Rectangle(3,9); + Rectangle fixation2 = new Rectangle(9,3); + fixation1.centering(); + fixation2.centering(); + Rectangle contrastrect = new Rectangle(); + contrastrect.set(760,760); + + Image envelope = new Image(); + Psychlops.Color bglum= new Color(0.5,0.5,0.5); + Psychlops.Color masklum = new Color(0.5,0.5,0.5,0.5); + + Interval rng = new Interval(); + + + Psychlops.Widgets.Slider TF; + TF = new Psychlops.Widgets.Slider("TF", 0.25<=rng<=2.0 , 0.1); + TF.value=0.75; + + + + Psychlops.Widgets.Slider SFs; + SFs = new Psychlops.Widgets.Slider("SF", 4.0<=rng<=16.0 , 4.0); + SFs.value = 4.0; + + Psychlops.Widgets.Slider contrast; + contrast= new Psychlops.Widgets.Slider("Contrast", 0.0<=rng<=1.0 , 0.1); + contrast.value = 0.1; + + + var element = StaticFunctions.NewArray(2); + for (int i = 0; i < element.Length; i++){ + element[i].setSigma(sigma); // set a size of patch as a halfwidth of Gaussian + element[i].orientation = Math.PI/4.0+Math.PI/2.0*(1-i);//Math.PI/2*(rand()%2);// //set orientations + initphase[i]=0; + } + + + + envelope.set(size, size); + envelope.clear(bglum); + + for(int i=0; i0){ + envelope.centering().shift(size*0.6,-size*0.6).draw(); + envelope.centering().shift(-size*0.6,size*0.6).draw(); + } + masklum.set(bg_lum, bg_lum, bg_lum, 1-contrast); + contrastrect.centering(); + contrastrect.draw(masklum); + fixation1.centering().shift(size*2,0.0); + fixation1.draw(Color.red); + fixation2.centering().shift(size*2,0.0); + fixation2.draw(Color.red); + + Display.var(Mouse.x, 300, 300); + Display.var(Mouse.y, 300, 320); + + Display.flip(); + + + } + +} + + + + } + +} +/* + +using Psychlops; +//Position Bias Program +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + Canvas cnvs; + Image img, img2, img3; + int isize = 40; + int frames; + Psychlops.Widgets.Slider tfreq; + Psychlops.Widgets.Slider contrast; + Psychlops.Widgets.Slider lambda; + + + + public void psychlops_main() + { + cnvs = new Canvas(300, 600); + Interval rng = new Interval(); + tfreq = new Psychlops.Widgets.Slider("Temporal Frequency(Hz)", -5 <= rng <= 5, 3.0); + contrast = new Psychlops.Widgets.Slider("Contrast", 0.0 <= rng <= 1.0, 0.25); + lambda = new Psychlops.Widgets.Slider("Wave Length", 10.0 <= rng <= 120.0, 30); + + img = new Image(isize * 2, isize * 2); + img2 = new Image(isize * 2, isize * 2); + img3 = new Image(isize * 2, isize * 2); + + var gabor1 = StaticFunctions.NewArray(100); + foreach (var g in gabor1) + { + g.setSigma(isize/8).centering().shift(Math.random(300) - 150, Math.random(600) - 300); + //g.setSize(isize).centering().shift(Math.random(300) - 150, Math.random(600) - 300); + g.orientation = Math.random(2 * Math.PI); + //g.orientation2 = Math.random(2 * Math.PI); + } + + while (true) + { + cnvs.clear(new Color(0.5)); + + Figures.drawGabor(ref img, isize / 8, 1 / lambda, contrast, 0.5 * Math.PI, (double)frames * 2.0 * Math.PI * tfreq / 60); + Figures.drawGabor(ref img2, isize / 8, 1 / lambda, contrast, 0.5 * Math.PI, (double)frames * 2.0 * Math.PI * -tfreq / 60); + Figures.drawGabor(ref img3, isize / 8, 1 / lambda, contrast, 0.5 * Math.PI, (double)frames * 2.0 * Math.PI * tfreq / 60); + + + //img.centering().shift(0, -isize * 1.5).draw(); + //img2.centering().draw(); + //img3.centering().shift(0, isize * 1.5).draw(); + + + //foreach (var g in gabor1) + for (int i = 0; i < gabor1.Length; i++) + { + gabor1[i].wavelength = lambda; + gabor1[i].phase = (double)frames * 2.0 * Math.PI * tfreq / 60; + gabor1[i].contrast = contrast; + //g.wavelength2 = lambda * 2; + //g.phase2 = (double)frames * 2.0 * Math.PI * tfreq / 60; + //g.contrast2 = contrast / 2; + gabor1[i].draw(); + } + + if (!Mouse.left.pressed()) frames++; + + cnvs.flip(); + } + } + } +} + + +/* +///+ Prefix linkto BasicCode1 +//// Lines for set up Psychlops environment +using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + ///- Prefix linkto BasicCode1 + + + ///+ Main Routine + //// Psychlops runs at the first line of this function psychlops_main(). + public void psychlops_main() + { + + ///+ 0 SetGlobal + ////Prepare global parameters + Rectangle p1 = new Rectangle(1,1); + Color col1 = new Color(); + double width=50; + double height=10; + double lambda=60; + double lmean=0.25, contrast = 0.5; + double x0 = 0; + double y0 = 0; + double pitch = 1; + Canvas disp = new Canvas(Canvas.window, Display.secondary); + + var fieldW_ = Psychlops.Widgets.Browser.Element.byID("fieldW"); + var fieldH_ = Psychlops.Widgets.Browser.Element.byID("fieldH"); + var wL_ = Psychlops.Widgets.Browser.Element.byID("wL"); + var Cont_ = Psychlops.Widgets.Browser.Element.byID("Cont"); + var ix_ = Psychlops.Widgets.Browser.Element.byID("ix"); + var iy_ = Psychlops.Widgets.Browser.Element.byID("iy"); + var gap_ = Psychlops.Widgets.Browser.Element.byID("gap"); + ///- 0 SetGlobal + + ///+ 1 Initialize + ////initialize + p1.centering().shift(-width*0.5 + x0, -height*0.5 + y0); //Move a point to the initial position. + ///- 1 Initialize + + ///+ 2 Drawing + ////drawing objects + for(int i=0; i < width; i++) + { + col1.set(lmean*((contrast*Math.sin((2*Math.PI*i/lambda)))+1)); //Set a color. + + ///+ 2.1 Drawing a column + ////drawing a column + for(int j=0;j < height; j++) + { + p1.draw(col1); + p1.shift(0, pitch); + } + ///- 2.1 Drawing a column + + p1.shift(1, -height * pitch); //back to vertical intitial point after drawing one column. + } + disp.flip(); + ///- 2 Drawing + + while (!Keyboard.spc.pushed()) { }; //Wait until space key is pressed. + } + + + } + +} + * */ + + +/* +///+ Prefix +//// Lines for set up Psychlops environment +using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + {///- Prefix + + + + ///+ Main Routine + //// Psychlops runs at the first line of this function psychlops_main(). + public void psychlops_main() + { + Canvas window = new Canvas(300, 300); //Create a window. Here, window variables are preset mode. + + var FIGURE = Psychlops.Widgets.Browser.Element.byID("FIGURE"); + var SIZE_X_ = Psychlops.Widgets.Browser.Element.byID("SIZE_X"); + var SIZE_Y_ = Psychlops.Widgets.Browser.Element.byID("SIZE_Y"); + var COLOR_R = Psychlops.Widgets.Browser.Element.byID("COLOR_R"); + var COLOR_G = Psychlops.Widgets.Browser.Element.byID("COLOR_G"); + var COLOR_B = Psychlops.Widgets.Browser.Element.byID("COLOR_B"); + var COLOR_BG = Psychlops.Widgets.Browser.Element.byID("COLOR_BG"); + var Period_Size = Psychlops.Widgets.Browser.Element.byID("Period_Size"); + var Period_Position = Psychlops.Widgets.Browser.Element.byID("Period_Position"); + var Speed_HSize_ = Psychlops.Widgets.Browser.Element.byID("Speed_HSize"); + var Speed_VSize = Psychlops.Widgets.Browser.Element.byID("Speed_VSize"); + var Speed_VMotion = Psychlops.Widgets.Browser.Element.byID("Speed_VMotion"); + double SIZE_X = SIZE_X_, SIZE_Y = SIZE_Y_; + + ///+ 2 + //// Set a figure size, position and color. + Rectangle figure = new Rectangle(); + Ellipse figure2 = new Ellipse(); + figure.set(SIZE_X_, SIZE_Y_); // Set the size of figure. + figure2.set(SIZE_X_, SIZE_Y_); // Set the size of figure. + figure.centering().shift(0, 0); // Move the figure to the starting point. + figure2.centering().shift(0, 0); // Move the figure to the starting point. + ///- 2 + + int frame = 0; + int looming_direction = 1, motion_dir = 1; + double direction = 1.0; + //looming_direction is a variable for size change. + //motion_dir is a variable for motion direction. + ///+ 3 Drawing loop + //// Draw each frames in a "while loop". + while (!Keyboard.esc.pushed()) + { //exit a program when the escape key is pressed down. + window.clear(COLOR_BG); // Clear the window with a designated gray-scale level. + + ///+ 3.1 + //// Calculate object's position and size for each frame. + + if (frame % Period_Size == 0) + { // a direction of size change will reverse at designated frames. + looming_direction = looming_direction * -1; + } + + if (frame % Period_Position == 0) + { // motion direction will reverse at designated frames. + motion_dir *= -1; + } + + ///- 3.1 + + ///+ 3.2 + //// Settting figure's properties + figure.resize(figure.getWidth() + looming_direction * 1, // Scaling the figure in a direction given by "looming_direction". + figure.getHeight() + looming_direction * 1);// Scaling the figure in a direction given by "looming_direction". + figure.shift(motion_dir * 1.0, motion_dir * Speed_VMotion);// Move the figure for 1 pixel in direction given by "motion_dir". + figure2.resize(figure.getWidth() + looming_direction * 1, // Scaling the figure in a direction given by "looming_direction". + figure.getHeight() + direction * Speed_VSize);// Scaling the figure in a direction given by "looming_direction". + figure2.shift(motion_dir * 1.0, motion_dir * Speed_VMotion);// Move the figure for 1 pixel in direction given by "motion_dir". + /* + figure.resize(figure.getWidth() + looming_direction * 1, // Scaling the figure in a direction given by "looming_direction". + figure.getHeight() + looming_direction * 1);// Scaling the figure in a direction given by "looming_direction". + figure.shift(motion_dir * 1.0, motion_dir * Speed_VMotion);// Move the figure for 1 pixel in direction given by "motion_dir". + figure2.resize(figure.getWidth() + looming_direction * 1, // Scaling the figure in a direction given by "looming_direction". + figure.getHeight() + direction * Speed_VSize);// Scaling the figure in a direction given by "looming_direction". + figure2.shift(motion_dir * 1.0, motion_dir * Speed_VMotion);// Move the figure for 1 pixel in direction given by "motion_dir". + * * / + ///- 3.2 + + ///+ 3.3 + ////Drawing + if ("Rectangle".Equals(FIGURE)) + { + figure.draw(new Color(COLOR_R, COLOR_G, COLOR_B)); // Drawing the figure with a designated color at designated position. + } + else + { + figure2.draw(new Color(COLOR_R, COLOR_G, COLOR_B)); // Drawing the figure with a designated color at designated position. + } + window.flip(); // Reflect the drawing for the display by flipping frame buffers. + ///- 3.3 + + ///+ 3.4 + ////make a step for next frames + frame++; + ///- 3.4 + } + ///- 3 Drawing loop + + } + ///- Main Routine + + + } + +} + + + +/* +//The reversed-phi motion. +//Anstis (1970) +//Phi movement as a subtraction process. Vision Res 10:1411?1430 + +///+ Prefix +//// Include Psychlops Package +using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + + ///- Prefix + + ///+ Stimulus drawing function + //// A function for stimulus drawing (main body) + void RectLuminance() + { + + ///+ Preperation + //// Declare and initialize local variables + double rect_size = 20; + + double bg_lum = 0.5; + double radii = 200; + double rect_lum; + int element_number = 25; + int radial_row = 5; + + double rotation_tf = 0.2; + double polarity = 1; + double rotation; + + double contrastflag = 1; + int period = 3; + double refresh; + + Canvas display = new Canvas(Canvas.window); //Prepare drawing window + refresh = Display.getRefreshRate(); + + Ellipse rect = new Ellipse(); + rect.set(rect_size, rect_size); + + Ellipse fixation = new Ellipse(5, 5); + fixation.centering(); + + Letters let1 = new Letters("Press Space key to change stimulus type"); + let1.centering().shift(-180, 220); + Letters let2 = new Letters("Phi"); + let2.centering().shift(-10, 200); + Letters let3 = new Letters("Reversed-Phi"); + let3.centering().shift(-60, 200); + + rotation = Math.random(2 * Math.PI); + + ///+ user interface + ////register variables to demo circumstances + Interval rng = new Interval(); + + Psychlops.Widgets.Slider rect_contrast; + rect_contrast = new Psychlops.Widgets.Slider("Contrast", 0.1 <= rng <= 1.0, 0.1); + rect_contrast.value = 0.5; + ///- user interface + ///- Preperation + + ///+ Main loop + ////Main loop + int frame = 0; + //AppState::setThreadPriority(AppState::HIGH); + while (true) + { + frame++; + if (Keyboard.spc.pushed()) contrastflag = -contrastflag; + if (contrastflag < 0) polarity = -polarity; + rotation = 2 * Math.PI * rotation_tf * frame * period / refresh; + + for (int frame_now = 0; frame_now < period; frame_now++) + { + Display.clear(new Color(bg_lum)); + for (int j = 0; j < radial_row; j++) + { + rect.resize(rect_size * (j + radial_row) / 10.0, rect_size * (j + radial_row) / 10.0); + for (double i = 0; i < element_number; i++) + { + rect_lum = bg_lum * (1.0 + polarity * rect_contrast); + rect.centering().shift(radii * (j + radial_row) / 10.0 * Math.cos(rotation + i * 2 * Math.PI / element_number), radii * (j + radial_row) / 10.0 * Math.sin(rotation + i * 2 * Math.PI / element_number)); + rect.draw(rect_lum); + } + } + fixation.draw(Color.red); + let1.draw(); + if (contrastflag > 0) let2.draw(); + else let3.draw(); + Display.flip(); + } + } + ///- Main loop + //AppState::setThreadPriority(AppState::NORMAL); + } + ///- Stimulus drawing function + + ///+ Main function for demo circumstances + //// Psychlops Main function + public void psychlops_main() + { + ///+ Demo circumstances + //// Spells for run demonstration circumstances + Procedure p = new Procedure(); + //p.setDesign(Procedure::DEMO); //Designate that this is a demo. + p.setProcedure(RectLuminance); //The argument name is a name of drawing function. + p.run(); + ///- Demo circumstances + } + ///- Main function for demo circumstances + + } + +} + + + +/* +//Two types of plaid motion +//E. H. Adelson and J. A. Movshon (1982). +//Phenomenal coherence of moving visual patterns. Nature 300, 523-525 + +///+ Prefix +//// Include Psychlops Package +using Psychlops; + + namespace PsychlopsSilverlightApp + { + + public class PsychlopsMain + { + ///- Prefix + + ///+ Global + // Struct for component paremeters + struct component + { + public double contrast; + public double orientation; + public double lambda; + public double tf; + } + ///- Global + + ///+ Stimulus drawing function + //// A function for stimulus drawing (main body) + void drawgratingmovie(Image[] img, component c1, component c2, double contrast, int start, int maxframe, double bg_lum, double alpha) + { + double _xp, _xp2, col1, col2; + double contrast1, contrast2; + double imageheight, imagewidth; + + contrast1 = contrast*c1.contrast/(c1.contrast+c2.contrast); + contrast2 = contrast*c2.contrast/(c1.contrast+c2.contrast); + + imageheight = img[0].getHeight(); + imagewidth = img[0].getWidth(); + + for(int frame=start; frame(2, 120); + Image[,] component_movie = StaticFunctions.NewArray(4, 120); + Image envelope = new Image(), envelope_small = new Image(); + + for(int i=0; i<120; i++) + { + movie[0, i].set(rect_size, rect_size); + movie[1, i].set(rect_size, rect_size); + component_movie[0, i].set(rect_size/2, rect_size/2); + component_movie[1, i].set(rect_size/2, rect_size/2); + component_movie[2, i].set(rect_size/2, rect_size/2); + component_movie[3, i].set(rect_size/2, rect_size/2); + } + envelope.set(rect_size, rect_size); + envelope_small.set(rect_size/2, rect_size/2); + + Letters let1 = new Letters(), let2 = new Letters(), let3 = new Letters(); + let1.str = "Component1"; + let2.str = "Component2"; + let3.str = "Superposition"; + let1.centering().shift(-rect_size*1.25, -rect_size); + //let1.cache(); + let2.centering().shift(rect_size*1.25, -rect_size); + //let2.cache(); + let3.centering().shift(0.0, rect_size*0.75); + //let3.cache(); + + component c1, c2, c0; + c0 = new component{ 0.0,0.0,1.0,1.0 }; //dummy for component movie + ///+ type-I + // Prepare Type-I plaid movie + center_orientation = 0.0; + orientation_offset = Math.PI/6; + contrast[0] = 1.0, contrast[1] = 1.0; + lambda[0] = 30.0, lambda[1] = 30.0; + tf[0] = 1.0; tf[1] = 1.0; + + c1 = {contrast[0], center_orientation-orientation_offset, lambda[0], tf[0]}; + c2 = {contrast[1], center_orientation+orientation_offset, lambda[1], tf[1]}; + + drawgratingmovie(movie[0],c1, c2, 0.5, 0, refresh_int, bg_lum, 1.0); + drawgratingmovie(component_movie[0],c0, c2, 0.5, 0, refresh_int, bg_lum, 1.0); + drawgratingmovie(component_movie[1],c1, c0, 0.5, 0, refresh_int, bg_lum, 1.0); + ///- type-I + + ///+ type-II + // Prepare Type-II plaid movie + center_orientation = -0.0, orientation_offset = Math.PI/12; + contrast[0] = 1.0, contrast[1] = 1.0; + lambda[0] = 30.0, lambda[1] = 30.0; + tf[0] = 1.0; tf[1] = 4.0; + + c1 = {contrast[0], center_orientation-orientation_offset, lambda[0], tf[0]}; + c2 = {contrast[1], center_orientation+orientation_offset, lambda[1], tf[1]}; + + drawgratingmovie(movie[1],c1, c2, 0.5, 0, refresh_int, bg_lum, 1.0); + drawgratingmovie(component_movie[2],c0, c2, 0.5, 0, refresh_int, bg_lum, 1.0); + drawgratingmovie(component_movie[3],c1, c0, 0.5, 0, refresh_int, bg_lum, 1.0); + ///- type-II + + ///+ gaussian + //draw Gaussian envelopes + envelope.clear(new Color(bg_lum)); //clear offscreen image + double _x, _y; + for(int i=0; i0){ + movie[movienum, frame].centering().draw(); + envelope.centering().draw(); + component_movie[movienum*2, frame].centering().shift(-rect_size*0.5, -rect_size).draw(); + envelope_small.centering().shift(-rect_size*0.5, -rect_size).draw(); + component_movie[movienum*2+1, frame].centering().shift(rect_size*0.5, -rect_size).draw(); + envelope_small.centering().shift(rect_size*0.5, -rect_size).draw(); + } + + ///+ draw user interface + let1.draw(0.75); + let2.draw(0.75); + let3.draw(0.75); + stimulus_type.draw(); + stimulus_type2.draw(); + if(stimulus_type2.getSelected())duration_slider.draw(); + ///- draw user interface + Display.flip(); + if(frame++ >= period) frame -= refresh_int; + } + ///- Main loop + + } + ///- Stimulus drawing function + + ///+ Main function for demo circumstances + public void psychlops_main() { + ///+ Demo circumstances + //// Spells for run demonstration circumstances + Procedure p = new Procedure(); + //p.setDesign(Procedure::DEMO); //Designate that this is a demo. + p.setProcedure(drawplaid); //The argument name is a name of drawing function. + p.run(); + ///- Demo circumstances + } + } +} +*/ diff --git a/test5/PsychlopsSilverlight4test.csproj b/test4/PsychlopsSilverlight4test.csproj similarity index 100% rename from test5/PsychlopsSilverlight4test.csproj rename to test4/PsychlopsSilverlight4test.csproj diff --git a/test5/PsychlopsSilverlight4test.sln b/test4/PsychlopsSilverlight4test.sln similarity index 100% rename from test5/PsychlopsSilverlight4test.sln rename to test4/PsychlopsSilverlight4test.sln diff --git a/test4/Store.cs b/test4/Store.cs new file mode 100644 index 0000000..d77d038 --- /dev/null +++ b/test4/Store.cs @@ -0,0 +1,883 @@ + +/* +using Psychlops; + +namespace PsychlopsSilverlight4test +{ + + public class PsychlopsMain + { + void RectLuminance() + { + Canvas display = new Canvas(Canvas.window); + Psychlops.Solver.BinomialLikelihood.showWindow(Math.cumulativeNormalDistibution); + Letters le = new Letters("Reload to restart"); + le.fill = Color.black; + le.align = Letters.HorizontalAlign.center; + + while (!Keyboard.esc.pushed()) + { + Display.clear(Color.white); + le.centering().shift(-200,-10).draw(); + Display.flip(); + } + + } + + + public void psychlops_main() + { + RectLuminance(); + } + + + } + +} + +*/ + + + +/* +using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + + Psychlops.Widgets.Slider rect_size, freq, contrast; + + void PositionalBiasisInMovingGabor() + { + double vel = 30.0; + double StimWidth = 120; + + Interval rng = new Interval(); + Psychlops.Widgets.Slider rect_size, freq, contrast; + rect_size = new Psychlops.Widgets.Slider("Rect Size", 1 < rng < 500, 120.0); + freq = new Psychlops.Widgets.Slider("Sptial Frequency", 0.0 <= rng <= 1.0, 0.125); + contrast = new Psychlops.Widgets.Slider("Contrast", 0.0 <= rng <= 1.0, 0.1); + + int IMAGES = (int)vel; + + Image[] GaborIMG = new Image[200]; + for (int i = 0; i < 200; i++) GaborIMG[i] = new Image(); + + Color clrcol = new Color(), clrFP = new Color(); + Display.clear(0.5); + clrcol.set(128.0 / 255.0); + clrFP.set(128.0 / 255.0, 0.0, 0.0); + + int phase = 0; + double distance = rect_size; + bool changed = true, stop = false; + double prev_contrast = 0.0, prev_freq = 0.0, prev_size = 0.0; + + while (!Keyboard.esc.pushed()) + { + Display.clear(); + + if (prev_contrast != contrast || prev_freq != freq || prev_size != rect_size) changed = true; + prev_contrast = contrast; + prev_freq = freq; + prev_size = rect_size; + if (changed) + { + for (int i = 0; i < 2; i++) + { + for (int j = 0; j < IMAGES; j++) + { + Figures.drawGabor(ref GaborIMG[i * IMAGES + j], rect_size / 8.0, freq, contrast, Math.PI * 0.5, 2.0 * Math.PI * j / IMAGES); + distance = rect_size; + } + } + changed = false; + } + + if (!stop) + { + Display.clear(127.0 / 255.0); + for (int i = 0; i < 3; i++) + { + switch (i) + { + case 0: + GaborIMG[IMAGES - phase - 1].centering().shift(0, -distance); + GaborIMG[IMAGES - phase - 1].draw(); break; + case 1: + GaborIMG[phase].centering().shift(0, 0); + GaborIMG[phase].draw(); break; + case 2: + GaborIMG[IMAGES - phase - 1].centering().shift(0, +distance); + GaborIMG[IMAGES - phase - 1].draw(); break; + } + } + } + if (Keyboard.spc.pushed()) stop = !stop; + //GaborIMG[0].draw(); + + phase++; + phase %= IMAGES; + //if (!stop) Display.flip(); + Display.flip(); + + } + + } + + + public void psychlops_main() + { + + Canvas display = new Canvas(Canvas.window); + + PositionalBiasisInMovingGabor(); + + } + + + + } + +} + */ +/* + * + * using Psychlops; + +namespace PsychlopsSilverlightApp +{ + + public class PsychlopsMain + { + + Psychlops.Canvas cnvs; + int i; + double x, y, z, t, p, temp, xx, yy; + + //Set Target Initial Value + double TargetEcce = 100.0, TargetSize = 5.0, TargetNumber = 5, Rotate = 0.0; + + //Set Background Initial Value + double Axis = 0.0, BGRadii = 150, BGSize = 5.0; + Psychlops.Widgets.Slider thetaSpeed, DotNumber; + Image img; + + + + public void psychlops_main() + { + + cnvs = new Canvas(500, 500); + +// Psychlops.Solver.BinomialLikelihood.showWindow(Math.cumulativeNormalDistibution); +// img = new Image("Resources/logo.png"); + + Interval rng = new Interval(); + thetaSpeed = new Psychlops.Widgets.Slider("Label", -10 <= rng <= 10, 3.0); + DotNumber = new Psychlops.Widgets.Slider("DotNum", 0 <= rng <= 100, 50.0); + + //Declare background dots and target + Rectangle[] BGDot = new Rectangle[2048]; + for (int i = 0; i < 2048; i++) + { + BGDot[i]=new Rectangle(); + } + Rectangle[] Target= new Rectangle[10]; + for(int i=0; i<10; i++){ + Target[i]=new Rectangle(); + } + + + + //Declare Matrix to keep back ground dots' coordinate value + double[] DotX=new double[2048]; + double[] DotY = new double[2048]; + + AppState.statusBar = "dvcscxz"; + //Set Independent variables to manipulate + + + //Initialize positions of background dots + for(int i=0; i<2048; i++){ + t=2.0*Math.PI*Math.random(1.0); + p=2.0*Math.PI*Math.random(1.0); + DotX[i]=t; + DotY[i]=p; + + x=BGRadii*Math.cos(t)*Math.cos(p); + y=BGRadii*Math.sin(t)*Math.cos(p); + BGDot[i].set(BGSize, BGSize); + BGDot[i].centering().shift(x,y); + } + + //Initialize positions of targets + for(int i=0; i<10; i++)Target[i].set(TargetSize, TargetSize); + + double COS, SIN; + //DotNumber = 200; + //Main stimulus loop + while(true){ + //Clear the main window + cnvs.clear(); + + + temp=Axis/360*2*Math.PI; + COS=Math.cos(temp); + SIN=Math.sin(temp); + + //Calculate positions of background dots and set them + for(int i=0; i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + +

Basic Code Step1

+ +
+ +
+ +
+ +
+///+ Prefix
+//// Lines for set up Psychlops environment
+#include <psychlops.h>
+using namespace Psychlops;
+///- Prefix
+ 
+///+ Main Routine
+//// Psychlops runs at the first line of this function psychlops_main().
+void psychlops_main() {
+	///+ 1
+    //// Set a window for drawing stimulus
+    Canvas window(Canvas::window); //Create a window. Here, window variables are preset mode.
+    // You can write this statement as" Canvas window(Canvas::fullscreen); " for fullscreen mode
+    // or as " Canvas window(1024, 768, 32, 60.0); " This statement changes screenmode to 1024 x 768, 32bit color, 60 Hz refresh.
+	///- 1
+	
+	///+ 2
+	////  Set a figure size, position and color.
+    Psychlops::Rectangle/*ID:FIGURE selector Rectangle Ellipse*/ figure; //Create a figure (Rectangle or Ellipse) variable.
+    
+    figure.set( 100/*ID:SIZE_X 1 200 10*/, 100/*ID:SIZE_Y 1 200 10*/ ); // Set the size of figure.
+    figure.centering();   // Centering the figure in the window
+    figure.shift( 10/*ID:SHIFT_X -100 100 10*/, 10/*ID:SHIFT_Y -100 100 10*/ ); // Displacing the figure by designated vector.    
+    figure.draw( Color( 1.0/*ID:COLOR_R 0 1.0 0.1*/, 0.0/*ID:COLOR_G 0.0 1.0 0.1*/, 0.0/*ID:COLOR_B 0 1.0 0.1*/) ); // Drawing the rectangle with a designated color.
+    
+    window.flip(); // Reflect the drawing for the display by flipping frame buffers.
+                   // Till this point, you will not see drawn figures.
+	///- 2
+    
+	///+ 3 
+	//// Detect a Keyboard input
+    while(!Keyboard::esc.pushed()) {} //exit a program when the escape key is pressed down.
+    ///- 3
+ 
+}
+///- Main Routine
+
+
+ +
+ + +
+
Bin/Debug/PsychlopsSilverlight4test.xap
+
+ +
+ +
+ + +
+ +
+ +
+ 上記の文字はPsychlopsのコードです。そしてコードの実行結果はコード右のウィンドウに表示されています。
+ コード内の太文字をクリックするとマウス操作で値を変えることができ、結果がすぐに反映されます。 + また、コード内の青い文字をロールオーバすると、ポップアップで説明が現れます。 +
+ +
+
+ + diff --git a/test5/App.xaml b/test5/App.xaml index f82402d..6795859 100644 --- a/test5/App.xaml +++ b/test5/App.xaml @@ -1,6 +1,6 @@  diff --git a/test5/App.xaml.cs b/test5/App.xaml.cs index e091cde..eba91ad 100644 --- a/test5/App.xaml.cs +++ b/test5/App.xaml.cs @@ -10,7 +10,7 @@ using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; -namespace PsychlopsSilverlight4test +namespace PsychlopsSilverlight5test { public partial class App : Application { @@ -33,22 +33,24 @@ namespace PsychlopsSilverlight4test { } + private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e) { - // アプリケーションがデバッガーの外側で実行されている場合、ブラウザーの - // 例外メカニズムによって例外が報告されます。これにより、IE ではステータス バーに - // 黄色の通知アイコンが表示され、Firefox にはスクリプト エラーが表示されます。 + // If the app is running outside of the debugger then report the exception using + // the browser's exception mechanism. On IE this will display it a yellow alert + // icon in the status bar and Firefox will display a script error. if (!System.Diagnostics.Debugger.IsAttached) { - // メモ : これにより、アプリケーションは例外がスローされた後も実行され続け、例外は - // ハンドルされません。 - // 実稼動アプリケーションでは、このエラー処理は、Web サイトにエラーを報告し、 - // アプリケーションを停止させるものに置換される必要があります。 + // NOTE: This will allow the application to continue running after an exception has been thrown + // but not handled. + // For production applications this error handling should be replaced with something that will + // report the error to the website and stop the application. e.Handled = true; Deployment.Current.Dispatcher.BeginInvoke(delegate { ReportErrorToDOM(e); }); } } + private void ReportErrorToDOM(ApplicationUnhandledExceptionEventArgs e) { try diff --git a/test5/MainPage.xaml b/test5/MainPage.xaml index 6487be3..e445449 100644 --- a/test5/MainPage.xaml +++ b/test5/MainPage.xaml @@ -1,7 +1,8 @@ - @@ -13,7 +14,7 @@ - + diff --git a/test5/MainPage.xaml.cs b/test5/MainPage.xaml.cs index a125451..daeca3d 100644 --- a/test5/MainPage.xaml.cs +++ b/test5/MainPage.xaml.cs @@ -1,13 +1,17 @@ -using Psychlops; -using System; +using System; +using System.Collections.Generic; +using System.Linq; using System.Windows; using System.Windows.Media; using System.Windows.Media.Effects; using System.Windows.Media.Media3D; +using Psychlops; -namespace PsychlopsSilverlight4test + +namespace PsychlopsSilverlight5test { + public partial class MainPage : System.Windows.Controls.UserControl { //System.Collections.Generic.IEnumerator main_routine; @@ -38,7 +42,7 @@ namespace PsychlopsSilverlight4test //main_routine.RunWorkerAsync(); main = new PsychlopsSilverlightApp.PsychlopsMain(); -// Psychlops.AppState.statusBar. + // Psychlops.AppState.statusBar. Psychlops.Internal.Main.routine = new System.Threading.Thread(main.psychlops_main); Psychlops.Internal.Main.routine.Start(); //main.initialize(); @@ -50,20 +54,21 @@ namespace PsychlopsSilverlight4test AppStatusBar.Text = "Now starting Psychlops environment... please wait a minute"; -//eff__ = new Psychlops.Shader.GaborProgram(); -//rect__ = new System.Windows.Shapes.Rectangle(); -//rect__.Width = 200; -//rect__.Height = 200; -//rect__.Effect = eff__; -//rect__.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Blue); + //eff__ = new Psychlops.Shader.GaborProgram(); + //rect__ = new System.Windows.Shapes.Rectangle(); + //rect__.Width = 200; + //rect__.Height = 200; + //rect__.Effect = eff__; + //rect__.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Blue); } - protected void getFocusMouseEnter(object sender, System.Windows.Input.MouseEventArgs e) { + protected void getFocusMouseEnter(object sender, System.Windows.Input.MouseEventArgs e) + { this.Focus(); -//if(!b) { LayoutRoot.Children.Add(rect__); b = true; } + //if(!b) { LayoutRoot.Children.Add(rect__); b = true; } } -//System.Windows.Shapes.Rectangle rect__; -//Psychlops.Shader.GaborProgram eff__; -//bool b = false; + //System.Windows.Shapes.Rectangle rect__; + //Psychlops.Shader.GaborProgram eff__; + //bool b = false; private void CompositionTarget_Rendering(object sender, System.EventArgs e) { @@ -78,7 +83,4 @@ namespace PsychlopsSilverlight4test } - - } - diff --git a/test5/Properties/AppManifest.xml b/test5/Properties/AppManifest.xml index 151cfa5..a955232 100644 --- a/test5/Properties/AppManifest.xml +++ b/test5/Properties/AppManifest.xml @@ -3,5 +3,4 @@ > - diff --git a/test5/Properties/AssemblyInfo.cs b/test5/Properties/AssemblyInfo.cs index 82192e3..d0e5e5d 100644 --- a/test5/Properties/AssemblyInfo.cs +++ b/test5/Properties/AssemblyInfo.cs @@ -2,34 +2,34 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 -// アセンブリに関連付けられている情報を変更するには、 -// これらの属性値を変更してください。 -[assembly: AssemblyTitle("PsychlopsSilverlight4test")] +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("PsychlopsSilverlight5test")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("PsychlopsSilverlight4test")] -[assembly: AssemblyCopyright("Copyright © 2010")] +[assembly: AssemblyProduct("PsychlopsSilverlight5test")] +[assembly: AssemblyCopyright("Copyright © 2012")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから -// 見えなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、 -// その型の ComVisible 属性を true に設定してください。 +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] -// このプロジェクトが COM に公開される場合、次の GUID がタイプ ライブラリの ID になります。 -[assembly: Guid("631fa262-58de-42ab-ba9d-79cb7cf7e289")] +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e46af21d-13fa-4844-bb31-0b4b5d85935f")] -// アセンブリのバージョン情報は、以下の 4 つの値で構成されています。 +// Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // -// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を -// 既定値にすることができます。 +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test5/PsychlopsMain.cs b/test5/PsychlopsMain.cs index e69a680..f1d7bb8 100644 --- a/test5/PsychlopsMain.cs +++ b/test5/PsychlopsMain.cs @@ -1,14 +1,125 @@ -using Psychlops; +//Psychlops Code Template +// Please visit following web site to get sample codes. +// http://psychlops.sourceforge.jp/ja/?StartCode +// CodeDresser at following address is also available to view the code. +// http://visitope.org/Tools/codedresser.html + +///+ 0 Setup Psychlops Circumstances +//// 0 Setup Psychlops Circumstances +using Psychlops; namespace PsychlopsSilverlightApp { public class PsychlopsMain { + // Psychlops Win32 1.5.5 / 20110927 + ///- 0 Setup Psychlops Circumstances + Psychlops.Widgets.Slider SOAFrames, gap, wavelength, contrast; public void psychlops_main() { + ///+ 1 Declaration ///////////////////////////////////////////////////////////// + //// 1 Declaration + // declare default window and variables for its parameters + Canvas cnvs = new Canvas(900,600, Canvas.window, Display.primary); + double CANVAS_FRAMENUM; + int CANVAS_REFRESHRATE; + Color DEFAULT_BG_COLOR = new Color(); + + int gratingwidth = 500, gratingheight = 100; + double shift=0.0; + Color color = new Color(0.0,0.0,0.0); + Rectangle grating = new Rectangle(); + + + Interval rng = new Interval(); + + //declare local variables around here + + ///- 1 Declaration ///////////////////////////////////////////////////////////// + + ///+ 2 Initialization ////////////////////////////////////////////////////////// + //// 2 Initialization + + // Set initial values for local variables + CANVAS_REFRESHRATE = (int)cnvs.getRefreshRate(); + CANVAS_FRAMENUM = 0; + DEFAULT_BG_COLOR.set(127.0/255.0,127.0/255.0,127.0/255.0,1.0); // default background color is 127/255 mid-gray + + + SOAFrames = new Psychlops.Widgets.Slider("SOA Frames", 1 <=rng<=20 , 1 , 1); + SOAFrames.value = 5; + gap = new Psychlops.Widgets.Slider("Grating gap" , 0 <=rng<=200 , 1 , 10); + gap.value = 20; + wavelength = new Psychlops.Widgets.Slider("Wave Length", 1 <= rng <= 50, 1, 10); + wavelength.value = 30.0; + contrast = new Psychlops.Widgets.Slider("Contrast" , 0.0<=rng<=1.0 , 0.1 , 0.01); + contrast.value = 0.1; + + // Draw Offline images around here + + // Offline Movie calculation using Image array around here + + ///- 2 Initialization ////////////////////////////////////////////////////////// + + ///+ 3 Drawing ///////////////////////////////////////////////////////////////// + //// 3 Drawing + while(!Keyboard.esc.pushed()) { + cnvs.clear(DEFAULT_BG_COLOR); + + //Write draw commands for realtime figure calculation and drawing around here + + + if(Math.mod(CANVAS_FRAMENUM,SOAFrames) == 0){shift += 1;} + + grating.set(1,gratingheight).centering().shift(-gratingwidth/2, 0); + for(int i=0; i + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {23D94AAA-B4EC-47BD-AD7D-EE6BAD0A909C} + {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + PsychlopsSilverlight5test + PsychlopsSilverlight5test + Silverlight + v5.0 + $(TargetFrameworkVersion) + true + ja + true + true + PsychlopsSilverlight5test.xap + Properties\AppManifest.xml + PsychlopsSilverlight5test.App + PsychlopsSilverlight5testTestPage.html + true + true + false + Properties\OutOfBrowserSettings.xml + false + true + + + + + + v3.5 + + + true + full + false + Bin\Debug + DEBUG;TRACE;SILVERLIGHT + true + true + prompt + 4 + + + pdbonly + true + Bin\Release + TRACE;SILVERLIGHT + true + true + prompt + 4 + + + + + + + + + + + + + App.xaml + + + MainPage.xaml + + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + + + + + + + {1A6795E6-DD87-4D31-86F0-59C991EA6A7F} + PsychlopsSilverlight5 + + + + + + + + + + + + \ No newline at end of file -- 2.11.0